837365bdb9d60847945b19e0b3f4e1074975031d
[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 BLOCK_GetBigBlockOffset(ULONG index)
273 {
274 if (index == 0xffffffff)
275 index = 0;
276 else
277 index ++;
278
279 return index * BIG_BLOCK_SIZE;
280 }
281
282 /************************************************************************
283 ** Storage32BaseImpl implementation
284 */
285 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
286 ULARGE_INTEGER offset,
287 void* buffer,
288 ULONG size,
289 ULONG* bytesRead)
290 {
291 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
292 }
293
294 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
295 ULARGE_INTEGER offset,
296 const void* buffer,
297 const ULONG size,
298 ULONG* bytesWritten)
299 {
300 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
301 }
302
303 /************************************************************************
304 * Storage32BaseImpl_QueryInterface (IUnknown)
305 *
306 * This method implements the common QueryInterface for all IStorage32
307 * implementations contained in this file.
308 *
309 * See Windows documentation for more details on IUnknown methods.
310 */
311 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
312 IStorage* iface,
313 REFIID riid,
314 void** ppvObject)
315 {
316 StorageBaseImpl *This = (StorageBaseImpl *)iface;
317
318 if ( (This==0) || (ppvObject==0) )
319 return E_INVALIDARG;
320
321 *ppvObject = 0;
322
323 if (IsEqualGUID(&IID_IUnknown, riid) ||
324 IsEqualGUID(&IID_IStorage, riid))
325 {
326 *ppvObject = This;
327 }
328 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
329 {
330 *ppvObject = &This->pssVtbl;
331 }
332
333 if ((*ppvObject)==0)
334 return E_NOINTERFACE;
335
336 IStorage_AddRef(iface);
337
338 return S_OK;
339 }
340
341 /************************************************************************
342 * Storage32BaseImpl_AddRef (IUnknown)
343 *
344 * This method implements the common AddRef for all IStorage32
345 * implementations contained in this file.
346 *
347 * See Windows documentation for more details on IUnknown methods.
348 */
349 static ULONG WINAPI StorageBaseImpl_AddRef(
350 IStorage* iface)
351 {
352 StorageBaseImpl *This = (StorageBaseImpl *)iface;
353 ULONG ref = InterlockedIncrement(&This->ref);
354
355 TRACE("(%p) AddRef to %d\n", This, ref);
356
357 return ref;
358 }
359
360 /************************************************************************
361 * Storage32BaseImpl_Release (IUnknown)
362 *
363 * This method implements the common Release for all IStorage32
364 * implementations contained in this file.
365 *
366 * See Windows documentation for more details on IUnknown methods.
367 */
368 static ULONG WINAPI StorageBaseImpl_Release(
369 IStorage* iface)
370 {
371 StorageBaseImpl *This = (StorageBaseImpl *)iface;
372
373 ULONG ref = InterlockedDecrement(&This->ref);
374
375 TRACE("(%p) ReleaseRef to %d\n", This, ref);
376
377 if (ref == 0)
378 {
379 /*
380 * Since we are using a system of base-classes, we want to call the
381 * destructor of the appropriate derived class. To do this, we are
382 * using virtual functions to implement the destructor.
383 */
384 StorageBaseImpl_Destroy(This);
385 }
386
387 return ref;
388 }
389
390 /************************************************************************
391 * Storage32BaseImpl_OpenStream (IStorage)
392 *
393 * This method will open the specified stream object from the current storage.
394 *
395 * See Windows documentation for more details on IStorage methods.
396 */
397 static HRESULT WINAPI StorageBaseImpl_OpenStream(
398 IStorage* iface,
399 const OLECHAR* pwcsName, /* [string][in] */
400 void* reserved1, /* [unique][in] */
401 DWORD grfMode, /* [in] */
402 DWORD reserved2, /* [in] */
403 IStream** ppstm) /* [out] */
404 {
405 StorageBaseImpl *This = (StorageBaseImpl *)iface;
406 StgStreamImpl* newStream;
407 DirEntry currentEntry;
408 DirRef streamEntryRef;
409 HRESULT res = STG_E_UNKNOWN;
410
411 TRACE("(%p, %s, %p, %x, %d, %p)\n",
412 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
413
414 if ( (pwcsName==NULL) || (ppstm==0) )
415 {
416 res = E_INVALIDARG;
417 goto end;
418 }
419
420 *ppstm = NULL;
421
422 if ( FAILED( validateSTGM(grfMode) ) ||
423 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
424 {
425 res = STG_E_INVALIDFLAG;
426 goto end;
427 }
428
429 /*
430 * As documented.
431 */
432 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
433 {
434 res = STG_E_INVALIDFUNCTION;
435 goto end;
436 }
437
438 if (This->reverted)
439 {
440 res = STG_E_REVERTED;
441 goto end;
442 }
443
444 /*
445 * Check that we're compatible with the parent's storage mode, but
446 * only if we are not in transacted mode
447 */
448 if(!(This->openFlags & STGM_TRANSACTED)) {
449 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
450 {
451 res = STG_E_INVALIDFLAG;
452 goto end;
453 }
454 }
455
456 /*
457 * Search for the element with the given name
458 */
459 streamEntryRef = findElement(
460 This,
461 This->storageDirEntry,
462 pwcsName,
463 &currentEntry);
464
465 /*
466 * If it was found, construct the stream object and return a pointer to it.
467 */
468 if ( (streamEntryRef!=DIRENTRY_NULL) &&
469 (currentEntry.stgType==STGTY_STREAM) )
470 {
471 if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
472 {
473 /* A single stream cannot be opened a second time. */
474 res = STG_E_ACCESSDENIED;
475 goto end;
476 }
477
478 newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
479
480 if (newStream!=0)
481 {
482 newStream->grfMode = grfMode;
483 *ppstm = (IStream*)newStream;
484
485 IStream_AddRef(*ppstm);
486
487 res = S_OK;
488 goto end;
489 }
490
491 res = E_OUTOFMEMORY;
492 goto end;
493 }
494
495 res = STG_E_FILENOTFOUND;
496
497 end:
498 if (res == S_OK)
499 TRACE("<-- IStream %p\n", *ppstm);
500 TRACE("<-- %08x\n", res);
501 return res;
502 }
503
504 /************************************************************************
505 * Storage32BaseImpl_OpenStorage (IStorage)
506 *
507 * This method will open a new storage object from the current storage.
508 *
509 * See Windows documentation for more details on IStorage methods.
510 */
511 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
512 IStorage* iface,
513 const OLECHAR* pwcsName, /* [string][unique][in] */
514 IStorage* pstgPriority, /* [unique][in] */
515 DWORD grfMode, /* [in] */
516 SNB snbExclude, /* [unique][in] */
517 DWORD reserved, /* [in] */
518 IStorage** ppstg) /* [out] */
519 {
520 StorageBaseImpl *This = (StorageBaseImpl *)iface;
521 StorageInternalImpl* newStorage;
522 StorageBaseImpl* newTransactedStorage;
523 DirEntry currentEntry;
524 DirRef storageEntryRef;
525 HRESULT res = STG_E_UNKNOWN;
526
527 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
528 iface, debugstr_w(pwcsName), pstgPriority,
529 grfMode, snbExclude, reserved, ppstg);
530
531 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
532 {
533 res = E_INVALIDARG;
534 goto end;
535 }
536
537 if (This->openFlags & STGM_SIMPLE)
538 {
539 res = STG_E_INVALIDFUNCTION;
540 goto end;
541 }
542
543 /* as documented */
544 if (snbExclude != NULL)
545 {
546 res = STG_E_INVALIDPARAMETER;
547 goto end;
548 }
549
550 if ( FAILED( validateSTGM(grfMode) ))
551 {
552 res = STG_E_INVALIDFLAG;
553 goto end;
554 }
555
556 /*
557 * As documented.
558 */
559 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
560 (grfMode & STGM_DELETEONRELEASE) ||
561 (grfMode & STGM_PRIORITY) )
562 {
563 res = STG_E_INVALIDFUNCTION;
564 goto end;
565 }
566
567 if (This->reverted)
568 return STG_E_REVERTED;
569
570 /*
571 * Check that we're compatible with the parent's storage mode,
572 * but only if we are not transacted
573 */
574 if(!(This->openFlags & STGM_TRANSACTED)) {
575 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
576 {
577 res = STG_E_ACCESSDENIED;
578 goto end;
579 }
580 }
581
582 *ppstg = NULL;
583
584 storageEntryRef = findElement(
585 This,
586 This->storageDirEntry,
587 pwcsName,
588 &currentEntry);
589
590 if ( (storageEntryRef!=DIRENTRY_NULL) &&
591 (currentEntry.stgType==STGTY_STORAGE) )
592 {
593 if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
594 {
595 /* A single storage cannot be opened a second time. */
596 res = STG_E_ACCESSDENIED;
597 goto end;
598 }
599
600 newStorage = StorageInternalImpl_Construct(
601 This,
602 grfMode,
603 storageEntryRef);
604
605 if (newStorage != 0)
606 {
607 if (grfMode & STGM_TRANSACTED)
608 {
609 res = Storage_ConstructTransacted(&newStorage->base, &newTransactedStorage);
610
611 if (FAILED(res))
612 {
613 HeapFree(GetProcessHeap(), 0, newStorage);
614 goto end;
615 }
616
617 *ppstg = (IStorage*)newTransactedStorage;
618 }
619 else
620 {
621 *ppstg = (IStorage*)newStorage;
622 }
623
624 list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
625
626 res = S_OK;
627 goto end;
628 }
629
630 res = STG_E_INSUFFICIENTMEMORY;
631 goto end;
632 }
633
634 res = STG_E_FILENOTFOUND;
635
636 end:
637 TRACE("<-- %08x\n", res);
638 return res;
639 }
640
641 /************************************************************************
642 * Storage32BaseImpl_EnumElements (IStorage)
643 *
644 * This method will create an enumerator object that can be used to
645 * retrieve information about all the elements in the storage object.
646 *
647 * See Windows documentation for more details on IStorage methods.
648 */
649 static HRESULT WINAPI StorageBaseImpl_EnumElements(
650 IStorage* iface,
651 DWORD reserved1, /* [in] */
652 void* reserved2, /* [size_is][unique][in] */
653 DWORD reserved3, /* [in] */
654 IEnumSTATSTG** ppenum) /* [out] */
655 {
656 StorageBaseImpl *This = (StorageBaseImpl *)iface;
657 IEnumSTATSTGImpl* newEnum;
658
659 TRACE("(%p, %d, %p, %d, %p)\n",
660 iface, reserved1, reserved2, reserved3, ppenum);
661
662 if ( (This==0) || (ppenum==0))
663 return E_INVALIDARG;
664
665 if (This->reverted)
666 return STG_E_REVERTED;
667
668 newEnum = IEnumSTATSTGImpl_Construct(
669 This,
670 This->storageDirEntry);
671
672 if (newEnum!=0)
673 {
674 *ppenum = (IEnumSTATSTG*)newEnum;
675
676 IEnumSTATSTG_AddRef(*ppenum);
677
678 return S_OK;
679 }
680
681 return E_OUTOFMEMORY;
682 }
683
684 /************************************************************************
685 * Storage32BaseImpl_Stat (IStorage)
686 *
687 * This method will retrieve information about this storage object.
688 *
689 * See Windows documentation for more details on IStorage methods.
690 */
691 static HRESULT WINAPI StorageBaseImpl_Stat(
692 IStorage* iface,
693 STATSTG* pstatstg, /* [out] */
694 DWORD grfStatFlag) /* [in] */
695 {
696 StorageBaseImpl *This = (StorageBaseImpl *)iface;
697 DirEntry currentEntry;
698 HRESULT res = STG_E_UNKNOWN;
699
700 TRACE("(%p, %p, %x)\n",
701 iface, pstatstg, grfStatFlag);
702
703 if ( (This==0) || (pstatstg==0))
704 {
705 res = E_INVALIDARG;
706 goto end;
707 }
708
709 if (This->reverted)
710 {
711 res = STG_E_REVERTED;
712 goto end;
713 }
714
715 res = StorageBaseImpl_ReadDirEntry(
716 This,
717 This->storageDirEntry,
718 &currentEntry);
719
720 if (SUCCEEDED(res))
721 {
722 StorageUtl_CopyDirEntryToSTATSTG(
723 This,
724 pstatstg,
725 &currentEntry,
726 grfStatFlag);
727
728 pstatstg->grfMode = This->openFlags;
729 pstatstg->grfStateBits = This->stateBits;
730 }
731
732 end:
733 if (res == S_OK)
734 {
735 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
736 }
737 TRACE("<-- %08x\n", res);
738 return res;
739 }
740
741 /************************************************************************
742 * Storage32BaseImpl_RenameElement (IStorage)
743 *
744 * This method will rename the specified element.
745 *
746 * See Windows documentation for more details on IStorage methods.
747 */
748 static HRESULT WINAPI StorageBaseImpl_RenameElement(
749 IStorage* iface,
750 const OLECHAR* pwcsOldName, /* [in] */
751 const OLECHAR* pwcsNewName) /* [in] */
752 {
753 StorageBaseImpl *This = (StorageBaseImpl *)iface;
754 DirEntry currentEntry;
755 DirRef currentEntryRef;
756
757 TRACE("(%p, %s, %s)\n",
758 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
759
760 if (This->reverted)
761 return STG_E_REVERTED;
762
763 currentEntryRef = findElement(This,
764 This->storageDirEntry,
765 pwcsNewName,
766 &currentEntry);
767
768 if (currentEntryRef != DIRENTRY_NULL)
769 {
770 /*
771 * There is already an element with the new name
772 */
773 return STG_E_FILEALREADYEXISTS;
774 }
775
776 /*
777 * Search for the old element name
778 */
779 currentEntryRef = findElement(This,
780 This->storageDirEntry,
781 pwcsOldName,
782 &currentEntry);
783
784 if (currentEntryRef != DIRENTRY_NULL)
785 {
786 if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
787 StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
788 {
789 WARN("Element is already open; cannot rename.\n");
790 return STG_E_ACCESSDENIED;
791 }
792
793 /* Remove the element from its current position in the tree */
794 removeFromTree(This, This->storageDirEntry,
795 currentEntryRef);
796
797 /* Change the name of the element */
798 strcpyW(currentEntry.name, pwcsNewName);
799
800 /* Delete any sibling links */
801 currentEntry.leftChild = DIRENTRY_NULL;
802 currentEntry.rightChild = DIRENTRY_NULL;
803
804 StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
805 &currentEntry);
806
807 /* Insert the element in a new position in the tree */
808 insertIntoTree(This, This->storageDirEntry,
809 currentEntryRef);
810 }
811 else
812 {
813 /*
814 * There is no element with the old name
815 */
816 return STG_E_FILENOTFOUND;
817 }
818
819 return S_OK;
820 }
821
822 /************************************************************************
823 * Storage32BaseImpl_CreateStream (IStorage)
824 *
825 * This method will create a stream object within this storage
826 *
827 * See Windows documentation for more details on IStorage methods.
828 */
829 static HRESULT WINAPI StorageBaseImpl_CreateStream(
830 IStorage* iface,
831 const OLECHAR* pwcsName, /* [string][in] */
832 DWORD grfMode, /* [in] */
833 DWORD reserved1, /* [in] */
834 DWORD reserved2, /* [in] */
835 IStream** ppstm) /* [out] */
836 {
837 StorageBaseImpl *This = (StorageBaseImpl *)iface;
838 StgStreamImpl* newStream;
839 DirEntry currentEntry, newStreamEntry;
840 DirRef currentEntryRef, newStreamEntryRef;
841 HRESULT hr;
842
843 TRACE("(%p, %s, %x, %d, %d, %p)\n",
844 iface, debugstr_w(pwcsName), grfMode,
845 reserved1, reserved2, ppstm);
846
847 if (ppstm == 0)
848 return STG_E_INVALIDPOINTER;
849
850 if (pwcsName == 0)
851 return STG_E_INVALIDNAME;
852
853 if (reserved1 || reserved2)
854 return STG_E_INVALIDPARAMETER;
855
856 if ( FAILED( validateSTGM(grfMode) ))
857 return STG_E_INVALIDFLAG;
858
859 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
860 return STG_E_INVALIDFLAG;
861
862 if (This->reverted)
863 return STG_E_REVERTED;
864
865 /*
866 * As documented.
867 */
868 if ((grfMode & STGM_DELETEONRELEASE) ||
869 (grfMode & STGM_TRANSACTED))
870 return STG_E_INVALIDFUNCTION;
871
872 /*
873 * Don't worry about permissions in transacted mode, as we can always write
874 * changes; we just can't always commit them.
875 */
876 if(!(This->openFlags & STGM_TRANSACTED)) {
877 /* Can't create a stream on read-only storage */
878 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
879 return STG_E_ACCESSDENIED;
880
881 /* Can't create a stream with greater access than the parent. */
882 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
883 return STG_E_ACCESSDENIED;
884 }
885
886 if(This->openFlags & STGM_SIMPLE)
887 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
888
889 *ppstm = 0;
890
891 currentEntryRef = findElement(This,
892 This->storageDirEntry,
893 pwcsName,
894 &currentEntry);
895
896 if (currentEntryRef != DIRENTRY_NULL)
897 {
898 /*
899 * An element with this name already exists
900 */
901 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
902 {
903 IStorage_DestroyElement(iface, pwcsName);
904 }
905 else
906 return STG_E_FILEALREADYEXISTS;
907 }
908
909 /*
910 * memset the empty entry
911 */
912 memset(&newStreamEntry, 0, sizeof(DirEntry));
913
914 newStreamEntry.sizeOfNameString =
915 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
916
917 if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
918 return STG_E_INVALIDNAME;
919
920 strcpyW(newStreamEntry.name, pwcsName);
921
922 newStreamEntry.stgType = STGTY_STREAM;
923 newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
924 newStreamEntry.size.u.LowPart = 0;
925 newStreamEntry.size.u.HighPart = 0;
926
927 newStreamEntry.leftChild = DIRENTRY_NULL;
928 newStreamEntry.rightChild = DIRENTRY_NULL;
929 newStreamEntry.dirRootEntry = DIRENTRY_NULL;
930
931 /* call CoFileTime to get the current time
932 newStreamEntry.ctime
933 newStreamEntry.mtime
934 */
935
936 /* newStreamEntry.clsid */
937
938 /*
939 * Create an entry with the new data
940 */
941 hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
942 if (FAILED(hr))
943 return hr;
944
945 /*
946 * Insert the new entry in the parent storage's tree.
947 */
948 hr = insertIntoTree(
949 This,
950 This->storageDirEntry,
951 newStreamEntryRef);
952 if (FAILED(hr))
953 {
954 StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
955 return hr;
956 }
957
958 /*
959 * Open the stream to return it.
960 */
961 newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
962
963 if (newStream != 0)
964 {
965 *ppstm = (IStream*)newStream;
966
967 IStream_AddRef(*ppstm);
968 }
969 else
970 {
971 return STG_E_INSUFFICIENTMEMORY;
972 }
973
974 return S_OK;
975 }
976
977 /************************************************************************
978 * Storage32BaseImpl_SetClass (IStorage)
979 *
980 * This method will write the specified CLSID in the directory entry of this
981 * storage.
982 *
983 * See Windows documentation for more details on IStorage methods.
984 */
985 static HRESULT WINAPI StorageBaseImpl_SetClass(
986 IStorage* iface,
987 REFCLSID clsid) /* [in] */
988 {
989 StorageBaseImpl *This = (StorageBaseImpl *)iface;
990 HRESULT hRes;
991 DirEntry currentEntry;
992
993 TRACE("(%p, %p)\n", iface, clsid);
994
995 if (This->reverted)
996 return STG_E_REVERTED;
997
998 hRes = StorageBaseImpl_ReadDirEntry(This,
999 This->storageDirEntry,
1000 &currentEntry);
1001 if (SUCCEEDED(hRes))
1002 {
1003 currentEntry.clsid = *clsid;
1004
1005 hRes = StorageBaseImpl_WriteDirEntry(This,
1006 This->storageDirEntry,
1007 &currentEntry);
1008 }
1009
1010 return hRes;
1011 }
1012
1013 /************************************************************************
1014 ** Storage32Impl implementation
1015 */
1016
1017 /************************************************************************
1018 * Storage32BaseImpl_CreateStorage (IStorage)
1019 *
1020 * This method will create the storage object within the provided storage.
1021 *
1022 * See Windows documentation for more details on IStorage methods.
1023 */
1024 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
1025 IStorage* iface,
1026 const OLECHAR *pwcsName, /* [string][in] */
1027 DWORD grfMode, /* [in] */
1028 DWORD reserved1, /* [in] */
1029 DWORD reserved2, /* [in] */
1030 IStorage **ppstg) /* [out] */
1031 {
1032 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1033
1034 DirEntry currentEntry;
1035 DirEntry newEntry;
1036 DirRef currentEntryRef;
1037 DirRef newEntryRef;
1038 HRESULT hr;
1039
1040 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1041 iface, debugstr_w(pwcsName), grfMode,
1042 reserved1, reserved2, ppstg);
1043
1044 if (ppstg == 0)
1045 return STG_E_INVALIDPOINTER;
1046
1047 if (This->openFlags & STGM_SIMPLE)
1048 {
1049 return STG_E_INVALIDFUNCTION;
1050 }
1051
1052 if (pwcsName == 0)
1053 return STG_E_INVALIDNAME;
1054
1055 *ppstg = NULL;
1056
1057 if ( FAILED( validateSTGM(grfMode) ) ||
1058 (grfMode & STGM_DELETEONRELEASE) )
1059 {
1060 WARN("bad grfMode: 0x%x\n", grfMode);
1061 return STG_E_INVALIDFLAG;
1062 }
1063
1064 if (This->reverted)
1065 return STG_E_REVERTED;
1066
1067 /*
1068 * Check that we're compatible with the parent's storage mode
1069 */
1070 if ( !(This->openFlags & STGM_TRANSACTED) &&
1071 STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1072 {
1073 WARN("access denied\n");
1074 return STG_E_ACCESSDENIED;
1075 }
1076
1077 currentEntryRef = findElement(This,
1078 This->storageDirEntry,
1079 pwcsName,
1080 &currentEntry);
1081
1082 if (currentEntryRef != DIRENTRY_NULL)
1083 {
1084 /*
1085 * An element with this name already exists
1086 */
1087 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1088 ((This->openFlags & STGM_TRANSACTED) ||
1089 STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
1090 {
1091 hr = IStorage_DestroyElement(iface, pwcsName);
1092 if (FAILED(hr))
1093 return hr;
1094 }
1095 else
1096 {
1097 WARN("file already exists\n");
1098 return STG_E_FILEALREADYEXISTS;
1099 }
1100 }
1101 else if (!(This->openFlags & STGM_TRANSACTED) &&
1102 STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1103 {
1104 WARN("read-only storage\n");
1105 return STG_E_ACCESSDENIED;
1106 }
1107
1108 memset(&newEntry, 0, sizeof(DirEntry));
1109
1110 newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1111
1112 if (newEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
1113 {
1114 FIXME("name too long\n");
1115 return STG_E_INVALIDNAME;
1116 }
1117
1118 strcpyW(newEntry.name, pwcsName);
1119
1120 newEntry.stgType = STGTY_STORAGE;
1121 newEntry.startingBlock = BLOCK_END_OF_CHAIN;
1122 newEntry.size.u.LowPart = 0;
1123 newEntry.size.u.HighPart = 0;
1124
1125 newEntry.leftChild = DIRENTRY_NULL;
1126 newEntry.rightChild = DIRENTRY_NULL;
1127 newEntry.dirRootEntry = DIRENTRY_NULL;
1128
1129 /* call CoFileTime to get the current time
1130 newEntry.ctime
1131 newEntry.mtime
1132 */
1133
1134 /* newEntry.clsid */
1135
1136 /*
1137 * Create a new directory entry for the storage
1138 */
1139 hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
1140 if (FAILED(hr))
1141 return hr;
1142
1143 /*
1144 * Insert the new directory entry into the parent storage's tree
1145 */
1146 hr = insertIntoTree(
1147 This,
1148 This->storageDirEntry,
1149 newEntryRef);
1150 if (FAILED(hr))
1151 {
1152 StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
1153 return hr;
1154 }
1155
1156 /*
1157 * Open it to get a pointer to return.
1158 */
1159 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1160
1161 if( (hr != S_OK) || (*ppstg == NULL))
1162 {
1163 return hr;
1164 }
1165
1166
1167 return S_OK;
1168 }
1169
1170
1171 /***************************************************************************
1172 *
1173 * Internal Method
1174 *
1175 * Reserve a directory entry in the file and initialize it.
1176 */
1177 static HRESULT StorageImpl_CreateDirEntry(
1178 StorageBaseImpl *base,
1179 const DirEntry *newData,
1180 DirRef *index)
1181 {
1182 StorageImpl *storage = (StorageImpl*)base;
1183 ULONG currentEntryIndex = 0;
1184 ULONG newEntryIndex = DIRENTRY_NULL;
1185 HRESULT hr = S_OK;
1186 BYTE currentData[RAW_DIRENTRY_SIZE];
1187 WORD sizeOfNameString;
1188
1189 do
1190 {
1191 hr = StorageImpl_ReadRawDirEntry(storage,
1192 currentEntryIndex,
1193 currentData);
1194
1195 if (SUCCEEDED(hr))
1196 {
1197 StorageUtl_ReadWord(
1198 currentData,
1199 OFFSET_PS_NAMELENGTH,
1200 &sizeOfNameString);
1201
1202 if (sizeOfNameString == 0)
1203 {
1204 /*
1205 * The entry exists and is available, we found it.
1206 */
1207 newEntryIndex = currentEntryIndex;
1208 }
1209 }
1210 else
1211 {
1212 /*
1213 * We exhausted the directory entries, we will create more space below
1214 */
1215 newEntryIndex = currentEntryIndex;
1216 }
1217 currentEntryIndex++;
1218
1219 } while (newEntryIndex == DIRENTRY_NULL);
1220
1221 /*
1222 * grow the directory stream
1223 */
1224 if (FAILED(hr))
1225 {
1226 BYTE emptyData[RAW_DIRENTRY_SIZE];
1227 ULARGE_INTEGER newSize;
1228 ULONG entryIndex;
1229 ULONG lastEntry = 0;
1230 ULONG blockCount = 0;
1231
1232 /*
1233 * obtain the new count of blocks in the directory stream
1234 */
1235 blockCount = BlockChainStream_GetCount(
1236 storage->rootBlockChain)+1;
1237
1238 /*
1239 * initialize the size used by the directory stream
1240 */
1241 newSize.u.HighPart = 0;
1242 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1243
1244 /*
1245 * add a block to the directory stream
1246 */
1247 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1248
1249 /*
1250 * memset the empty entry in order to initialize the unused newly
1251 * created entries
1252 */
1253 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1254
1255 /*
1256 * initialize them
1257 */
1258 lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
1259
1260 for(
1261 entryIndex = newEntryIndex + 1;
1262 entryIndex < lastEntry;
1263 entryIndex++)
1264 {
1265 StorageImpl_WriteRawDirEntry(
1266 storage,
1267 entryIndex,
1268 emptyData);
1269 }
1270 }
1271
1272 UpdateRawDirEntry(currentData, newData);
1273
1274 hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
1275
1276 if (SUCCEEDED(hr))
1277 *index = newEntryIndex;
1278
1279 return hr;
1280 }
1281
1282 /***************************************************************************
1283 *
1284 * Internal Method
1285 *
1286 * Mark a directory entry in the file as free.
1287 */
1288 static HRESULT StorageImpl_DestroyDirEntry(
1289 StorageBaseImpl *base,
1290 DirRef index)
1291 {
1292 HRESULT hr;
1293 BYTE emptyData[RAW_DIRENTRY_SIZE];
1294 StorageImpl *storage = (StorageImpl*)base;
1295
1296 memset(&emptyData, 0, RAW_DIRENTRY_SIZE);
1297
1298 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1299
1300 return hr;
1301 }
1302
1303
1304 /***************************************************************************
1305 *
1306 * Internal Method
1307 *
1308 * Destroy an entry, its attached data, and all entries reachable from it.
1309 */
1310 static HRESULT DestroyReachableEntries(
1311 StorageBaseImpl *base,
1312 DirRef index)
1313 {
1314 HRESULT hr = S_OK;
1315 DirEntry data;
1316 ULARGE_INTEGER zero;
1317
1318 zero.QuadPart = 0;
1319
1320 if (index != DIRENTRY_NULL)
1321 {
1322 hr = StorageBaseImpl_ReadDirEntry(base, index, &data);
1323
1324 if (SUCCEEDED(hr))
1325 hr = DestroyReachableEntries(base, data.dirRootEntry);
1326
1327 if (SUCCEEDED(hr))
1328 hr = DestroyReachableEntries(base, data.leftChild);
1329
1330 if (SUCCEEDED(hr))
1331 hr = DestroyReachableEntries(base, data.rightChild);
1332
1333 if (SUCCEEDED(hr))
1334 hr = StorageBaseImpl_StreamSetSize(base, index, zero);
1335
1336 if (SUCCEEDED(hr))
1337 hr = StorageBaseImpl_DestroyDirEntry(base, index);
1338 }
1339
1340 return hr;
1341 }
1342
1343
1344 /****************************************************************************
1345 *
1346 * Internal Method
1347 *
1348 * Case insensitive comparison of DirEntry.name by first considering
1349 * their size.
1350 *
1351 * Returns <0 when name1 < name2
1352 * >0 when name1 > name2
1353 * 0 when name1 == name2
1354 */
1355 static LONG entryNameCmp(
1356 const OLECHAR *name1,
1357 const OLECHAR *name2)
1358 {
1359 LONG diff = lstrlenW(name1) - lstrlenW(name2);
1360
1361 while (diff == 0 && *name1 != 0)
1362 {
1363 /*
1364 * We compare the string themselves only when they are of the same length
1365 */
1366 diff = toupperW(*name1++) - toupperW(*name2++);
1367 }
1368
1369 return diff;
1370 }
1371
1372 /****************************************************************************
1373 *
1374 * Internal Method
1375 *
1376 * Add a directory entry to a storage
1377 */
1378 static HRESULT insertIntoTree(
1379 StorageBaseImpl *This,
1380 DirRef parentStorageIndex,
1381 DirRef newEntryIndex)
1382 {
1383 DirEntry currentEntry;
1384 DirEntry newEntry;
1385
1386 /*
1387 * Read the inserted entry
1388 */
1389 StorageBaseImpl_ReadDirEntry(This,
1390 newEntryIndex,
1391 &newEntry);
1392
1393 /*
1394 * Read the storage entry
1395 */
1396 StorageBaseImpl_ReadDirEntry(This,
1397 parentStorageIndex,
1398 &currentEntry);
1399
1400 if (currentEntry.dirRootEntry != DIRENTRY_NULL)
1401 {
1402 /*
1403 * The root storage contains some element, therefore, start the research
1404 * for the appropriate location.
1405 */
1406 BOOL found = 0;
1407 DirRef current, next, previous, currentEntryId;
1408
1409 /*
1410 * Keep a reference to the root of the storage's element tree
1411 */
1412 currentEntryId = currentEntry.dirRootEntry;
1413
1414 /*
1415 * Read
1416 */
1417 StorageBaseImpl_ReadDirEntry(This,
1418 currentEntry.dirRootEntry,
1419 &currentEntry);
1420
1421 previous = currentEntry.leftChild;
1422 next = currentEntry.rightChild;
1423 current = currentEntryId;
1424
1425 while (found == 0)
1426 {
1427 LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
1428
1429 if (diff < 0)
1430 {
1431 if (previous != DIRENTRY_NULL)
1432 {
1433 StorageBaseImpl_ReadDirEntry(This,
1434 previous,
1435 &currentEntry);
1436 current = previous;
1437 }
1438 else
1439 {
1440 currentEntry.leftChild = newEntryIndex;
1441 StorageBaseImpl_WriteDirEntry(This,
1442 current,
1443 &currentEntry);
1444 found = 1;
1445 }
1446 }
1447 else if (diff > 0)
1448 {
1449 if (next != DIRENTRY_NULL)
1450 {
1451 StorageBaseImpl_ReadDirEntry(This,
1452 next,
1453 &currentEntry);
1454 current = next;
1455 }
1456 else
1457 {
1458 currentEntry.rightChild = newEntryIndex;
1459 StorageBaseImpl_WriteDirEntry(This,
1460 current,
1461 &currentEntry);
1462 found = 1;
1463 }
1464 }
1465 else
1466 {
1467 /*
1468 * Trying to insert an item with the same name in the
1469 * subtree structure.
1470 */
1471 return STG_E_FILEALREADYEXISTS;
1472 }
1473
1474 previous = currentEntry.leftChild;
1475 next = currentEntry.rightChild;
1476 }
1477 }
1478 else
1479 {
1480 /*
1481 * The storage is empty, make the new entry the root of its element tree
1482 */
1483 currentEntry.dirRootEntry = newEntryIndex;
1484 StorageBaseImpl_WriteDirEntry(This,
1485 parentStorageIndex,
1486 &currentEntry);
1487 }
1488
1489 return S_OK;
1490 }
1491
1492 /****************************************************************************
1493 *
1494 * Internal Method
1495 *
1496 * Find and read the element of a storage with the given name.
1497 */
1498 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
1499 const OLECHAR *name, DirEntry *data)
1500 {
1501 DirRef currentEntry;
1502
1503 /* Read the storage entry to find the root of the tree. */
1504 StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
1505
1506 currentEntry = data->dirRootEntry;
1507
1508 while (currentEntry != DIRENTRY_NULL)
1509 {
1510 LONG cmp;
1511
1512 StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
1513
1514 cmp = entryNameCmp(name, data->name);
1515
1516 if (cmp == 0)
1517 /* found it */
1518 break;
1519
1520 else if (cmp < 0)
1521 currentEntry = data->leftChild;
1522
1523 else if (cmp > 0)
1524 currentEntry = data->rightChild;
1525 }
1526
1527 return currentEntry;
1528 }
1529
1530 /****************************************************************************
1531 *
1532 * Internal Method
1533 *
1534 * Find and read the binary tree parent of the element with the given name.
1535 *
1536 * If there is no such element, find a place where it could be inserted and
1537 * return STG_E_FILENOTFOUND.
1538 */
1539 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
1540 const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
1541 ULONG *relation)
1542 {
1543 DirRef childEntry;
1544 DirEntry childData;
1545
1546 /* Read the storage entry to find the root of the tree. */
1547 StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
1548
1549 *parentEntry = storageEntry;
1550 *relation = DIRENTRY_RELATION_DIR;
1551
1552 childEntry = parentData->dirRootEntry;
1553
1554 while (childEntry != DIRENTRY_NULL)
1555 {
1556 LONG cmp;
1557
1558 StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
1559
1560 cmp = entryNameCmp(childName, childData.name);
1561
1562 if (cmp == 0)
1563 /* found it */
1564 break;
1565
1566 else if (cmp < 0)
1567 {
1568 *parentData = childData;
1569 *parentEntry = childEntry;
1570 *relation = DIRENTRY_RELATION_PREVIOUS;
1571
1572 childEntry = parentData->leftChild;
1573 }
1574
1575 else if (cmp > 0)
1576 {
1577 *parentData = childData;
1578 *parentEntry = childEntry;
1579 *relation = DIRENTRY_RELATION_NEXT;
1580
1581 childEntry = parentData->rightChild;
1582 }
1583 }
1584
1585 if (childEntry == DIRENTRY_NULL)
1586 return STG_E_FILENOTFOUND;
1587 else
1588 return S_OK;
1589 }
1590
1591
1592 /*************************************************************************
1593 * CopyTo (IStorage)
1594 */
1595 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1596 IStorage* iface,
1597 DWORD ciidExclude, /* [in] */
1598 const IID* rgiidExclude, /* [size_is][unique][in] */
1599 SNB snbExclude, /* [unique][in] */
1600 IStorage* pstgDest) /* [unique][in] */
1601 {
1602 IEnumSTATSTG *elements = 0;
1603 STATSTG curElement, strStat;
1604 HRESULT hr;
1605 IStorage *pstgTmp, *pstgChild;
1606 IStream *pstrTmp, *pstrChild;
1607 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1608 int i;
1609
1610 TRACE("(%p, %d, %p, %p, %p)\n",
1611 iface, ciidExclude, rgiidExclude,
1612 snbExclude, pstgDest);
1613
1614 if ( pstgDest == 0 )
1615 return STG_E_INVALIDPOINTER;
1616
1617 /*
1618 * Enumerate the elements
1619 */
1620 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1621
1622 if ( hr != S_OK )
1623 return hr;
1624
1625 /*
1626 * set the class ID
1627 */
1628 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1629 IStorage_SetClass( pstgDest, &curElement.clsid );
1630
1631 for(i = 0; i < ciidExclude; ++i)
1632 {
1633 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1634 skip_storage = TRUE;
1635 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1636 skip_stream = TRUE;
1637 else
1638 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1639 }
1640
1641 do
1642 {
1643 /*
1644 * Obtain the next element
1645 */
1646 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1647
1648 if ( hr == S_FALSE )
1649 {
1650 hr = S_OK; /* done, every element has been copied */
1651 break;
1652 }
1653
1654 if ( snbExclude )
1655 {
1656 WCHAR **snb = snbExclude;
1657 skip = FALSE;
1658 while ( *snb != NULL && !skip )
1659 {
1660 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1661 skip = TRUE;
1662 ++snb;
1663 }
1664 }
1665
1666 if ( skip )
1667 goto cleanup;
1668
1669 if (curElement.type == STGTY_STORAGE)
1670 {
1671 if(skip_storage)
1672 goto cleanup;
1673
1674 /*
1675 * open child source storage
1676 */
1677 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1678 STGM_READ|STGM_SHARE_EXCLUSIVE,
1679 NULL, 0, &pstgChild );
1680
1681 if (hr != S_OK)
1682 goto cleanup;
1683
1684 /*
1685 * create a new storage in destination storage
1686 */
1687 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1688 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1689 0, 0,
1690 &pstgTmp );
1691 /*
1692 * if it already exist, don't create a new one use this one
1693 */
1694 if (hr == STG_E_FILEALREADYEXISTS)
1695 {
1696 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1697 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1698 NULL, 0, &pstgTmp );
1699 }
1700
1701 if (hr == S_OK)
1702 {
1703 /*
1704 * do the copy recursively
1705 */
1706 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1707 NULL, pstgTmp );
1708
1709 IStorage_Release( pstgTmp );
1710 }
1711
1712 IStorage_Release( pstgChild );
1713 }
1714 else if (curElement.type == STGTY_STREAM)
1715 {
1716 if(skip_stream)
1717 goto cleanup;
1718
1719 /*
1720 * create a new stream in destination storage. If the stream already
1721 * exist, it will be deleted and a new one will be created.
1722 */
1723 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1724 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1725 0, 0, &pstrTmp );
1726
1727 if (hr != S_OK)
1728 goto cleanup;
1729
1730 /*
1731 * open child stream storage
1732 */
1733 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1734 STGM_READ|STGM_SHARE_EXCLUSIVE,
1735 0, &pstrChild );
1736
1737 if (hr == S_OK)
1738 {
1739 /*
1740 * Get the size of the source stream
1741 */
1742 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1743
1744 /*
1745 * Set the size of the destination stream.
1746 */
1747 IStream_SetSize(pstrTmp, strStat.cbSize);
1748
1749 /*
1750 * do the copy
1751 */
1752 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1753 NULL, NULL );
1754
1755 IStream_Release( pstrChild );
1756 }
1757
1758 IStream_Release( pstrTmp );
1759 }
1760 else
1761 {
1762 WARN("unknown element type: %d\n", curElement.type);
1763 }
1764
1765 cleanup:
1766 CoTaskMemFree(curElement.pwcsName);
1767 } while (hr == S_OK);
1768
1769 /*
1770 * Clean-up
1771 */
1772 IEnumSTATSTG_Release(elements);
1773
1774 return hr;
1775 }
1776
1777 /*************************************************************************
1778 * MoveElementTo (IStorage)
1779 */
1780 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1781 IStorage* iface,
1782 const OLECHAR *pwcsName, /* [string][in] */
1783 IStorage *pstgDest, /* [unique][in] */
1784 const OLECHAR *pwcsNewName,/* [string][in] */
1785 DWORD grfFlags) /* [in] */
1786 {
1787 FIXME("(%p %s %p %s %u): stub\n", iface,
1788 debugstr_w(pwcsName), pstgDest,
1789 debugstr_w(pwcsNewName), grfFlags);
1790 return E_NOTIMPL;
1791 }
1792
1793 /*************************************************************************
1794 * Commit (IStorage)
1795 *
1796 * Ensures that any changes made to a storage object open in transacted mode
1797 * are reflected in the parent storage
1798 *
1799 * NOTES
1800 * Wine doesn't implement transacted mode, which seems to be a basic
1801 * optimization, so we can ignore this stub for now.
1802 */
1803 static HRESULT WINAPI StorageImpl_Commit(
1804 IStorage* iface,
1805 DWORD grfCommitFlags)/* [in] */
1806 {
1807 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1808 return S_OK;
1809 }
1810
1811 /*************************************************************************
1812 * Revert (IStorage)
1813 *
1814 * Discard all changes that have been made since the last commit operation
1815 */
1816 static HRESULT WINAPI StorageImpl_Revert(
1817 IStorage* iface)
1818 {
1819 TRACE("(%p)\n", iface);
1820 return S_OK;
1821 }
1822
1823 /*************************************************************************
1824 * DestroyElement (IStorage)
1825 *
1826 * Strategy: This implementation is built this way for simplicity not for speed.
1827 * I always delete the topmost element of the enumeration and adjust
1828 * the deleted element pointer all the time. This takes longer to
1829 * do but allow to reinvoke DestroyElement whenever we encounter a
1830 * storage object. The optimisation resides in the usage of another
1831 * enumeration strategy that would give all the leaves of a storage
1832 * first. (postfix order)
1833 */
1834 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1835 IStorage* iface,
1836 const OLECHAR *pwcsName)/* [string][in] */
1837 {
1838 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1839
1840 HRESULT hr = S_OK;
1841 DirEntry entryToDelete;
1842 DirRef entryToDeleteRef;
1843
1844 TRACE("(%p, %s)\n",
1845 iface, debugstr_w(pwcsName));
1846
1847 if (pwcsName==NULL)
1848 return STG_E_INVALIDPOINTER;
1849
1850 if (This->reverted)
1851 return STG_E_REVERTED;
1852
1853 if ( !(This->openFlags & STGM_TRANSACTED) &&
1854 STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1855 return STG_E_ACCESSDENIED;
1856
1857 entryToDeleteRef = findElement(
1858 This,
1859 This->storageDirEntry,
1860 pwcsName,
1861 &entryToDelete);
1862
1863 if ( entryToDeleteRef == DIRENTRY_NULL )
1864 {
1865 return STG_E_FILENOTFOUND;
1866 }
1867
1868 if ( entryToDelete.stgType == STGTY_STORAGE )
1869 {
1870 hr = deleteStorageContents(
1871 This,
1872 entryToDeleteRef,
1873 entryToDelete);
1874 }
1875 else if ( entryToDelete.stgType == STGTY_STREAM )
1876 {
1877 hr = deleteStreamContents(
1878 This,
1879 entryToDeleteRef,
1880 entryToDelete);
1881 }
1882
1883 if (hr!=S_OK)
1884 return hr;
1885
1886 /*
1887 * Remove the entry from its parent storage
1888 */
1889 hr = removeFromTree(
1890 This,
1891 This->storageDirEntry,
1892 entryToDeleteRef);
1893
1894 /*
1895 * Invalidate the entry
1896 */
1897 if (SUCCEEDED(hr))
1898 StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
1899
1900 return hr;
1901 }
1902
1903
1904 /******************************************************************************
1905 * Internal stream list handlers
1906 */
1907
1908 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1909 {
1910 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1911 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1912 }
1913
1914 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1915 {
1916 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1917 list_remove(&(strm->StrmListEntry));
1918 }
1919
1920 static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry)
1921 {
1922 StgStreamImpl *strm;
1923
1924 LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1925 {
1926 if (strm->dirEntry == streamEntry)
1927 {
1928 return TRUE;
1929 }
1930 }
1931
1932 return FALSE;
1933 }
1934
1935 static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry)
1936 {
1937 StorageInternalImpl *childstg;
1938
1939 LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1940 {
1941 if (childstg->base.storageDirEntry == storageEntry)
1942 {
1943 return TRUE;
1944 }
1945 }
1946
1947 return FALSE;
1948 }
1949
1950 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1951 {
1952 struct list *cur, *cur2;
1953 StgStreamImpl *strm=NULL;
1954 StorageInternalImpl *childstg=NULL;
1955
1956 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1957 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1958 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1959 strm->parentStorage = NULL;
1960 list_remove(cur);
1961 }
1962
1963 LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
1964 childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
1965 StorageBaseImpl_Invalidate( &childstg->base );
1966 }
1967
1968 if (stg->transactedChild)
1969 {
1970 StorageBaseImpl_Invalidate(stg->transactedChild);
1971
1972 stg->transactedChild = NULL;
1973 }
1974 }
1975
1976
1977 /*********************************************************************
1978 *
1979 * Internal Method
1980 *
1981 * Delete the contents of a storage entry.
1982 *
1983 */
1984 static HRESULT deleteStorageContents(
1985 StorageBaseImpl *parentStorage,
1986 DirRef indexToDelete,
1987 DirEntry entryDataToDelete)
1988 {
1989 IEnumSTATSTG *elements = 0;
1990 IStorage *childStorage = 0;
1991 STATSTG currentElement;
1992 HRESULT hr;
1993 HRESULT destroyHr = S_OK;
1994 StorageInternalImpl *stg, *stg2;
1995
1996 /* Invalidate any open storage objects. */
1997 LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
1998 {
1999 if (stg->base.storageDirEntry == indexToDelete)
2000 {
2001 StorageBaseImpl_Invalidate(&stg->base);
2002 }
2003 }
2004
2005 /*
2006 * Open the storage and enumerate it
2007 */
2008 hr = StorageBaseImpl_OpenStorage(
2009 (IStorage*)parentStorage,
2010 entryDataToDelete.name,
2011 0,
2012 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2013 0,
2014 0,
2015 &childStorage);
2016
2017 if (hr != S_OK)
2018 {
2019 return hr;
2020 }
2021
2022 /*
2023 * Enumerate the elements
2024 */
2025 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2026
2027 do
2028 {
2029 /*
2030 * Obtain the next element
2031 */
2032 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2033 if (hr==S_OK)
2034 {
2035 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2036
2037 CoTaskMemFree(currentElement.pwcsName);
2038 }
2039
2040 /*
2041 * We need to Reset the enumeration every time because we delete elements
2042 * and the enumeration could be invalid
2043 */
2044 IEnumSTATSTG_Reset(elements);
2045
2046 } while ((hr == S_OK) && (destroyHr == S_OK));
2047
2048 IStorage_Release(childStorage);
2049 IEnumSTATSTG_Release(elements);
2050
2051 return destroyHr;
2052 }
2053
2054 /*********************************************************************
2055 *
2056 * Internal Method
2057 *
2058 * Perform the deletion of a stream's data
2059 *
2060 */
2061 static HRESULT deleteStreamContents(
2062 StorageBaseImpl *parentStorage,
2063 DirRef indexToDelete,
2064 DirEntry entryDataToDelete)
2065 {
2066 IStream *pis;
2067 HRESULT hr;
2068 ULARGE_INTEGER size;
2069 StgStreamImpl *strm, *strm2;
2070
2071 /* Invalidate any open stream objects. */
2072 LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2073 {
2074 if (strm->dirEntry == indexToDelete)
2075 {
2076 TRACE("Stream deleted %p\n", strm);
2077 strm->parentStorage = NULL;
2078 list_remove(&strm->StrmListEntry);
2079 }
2080 }
2081
2082 size.u.HighPart = 0;
2083 size.u.LowPart = 0;
2084
2085 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2086 entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2087
2088 if (hr!=S_OK)
2089 {
2090 return(hr);
2091 }
2092
2093 /*
2094 * Zap the stream
2095 */
2096 hr = IStream_SetSize(pis, size);
2097
2098 if(hr != S_OK)
2099 {
2100 return hr;
2101 }
2102
2103 /*
2104 * Release the stream object.
2105 */
2106 IStream_Release(pis);
2107
2108 return S_OK;
2109 }
2110
2111 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
2112 {
2113 switch (relation)
2114 {
2115 case DIRENTRY_RELATION_PREVIOUS:
2116 entry->leftChild = new_target;
2117 break;
2118 case DIRENTRY_RELATION_NEXT:
2119 entry->rightChild = new_target;
2120 break;
2121 case DIRENTRY_RELATION_DIR:
2122 entry->dirRootEntry = new_target;
2123 break;
2124 default:
2125 assert(0);
2126 }
2127 }
2128
2129 /*************************************************************************
2130 *
2131 * Internal Method
2132 *
2133 * This method removes a directory entry from its parent storage tree without
2134 * freeing any resources attached to it.
2135 */
2136 static HRESULT removeFromTree(
2137 StorageBaseImpl *This,
2138 DirRef parentStorageIndex,
2139 DirRef deletedIndex)
2140 {
2141 HRESULT hr = S_OK;
2142 DirEntry entryToDelete;
2143 DirEntry parentEntry;
2144 DirRef parentEntryRef;
2145 ULONG typeOfRelation;
2146
2147 hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
2148
2149 if (hr != S_OK)
2150 return hr;
2151
2152 /*
2153 * Find the element that links to the one we want to delete.
2154 */
2155 hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
2156 &parentEntry, &parentEntryRef, &typeOfRelation);
2157
2158 if (hr != S_OK)
2159 return hr;
2160
2161 if (entryToDelete.leftChild != DIRENTRY_NULL)
2162 {
2163 /*
2164 * Replace the deleted entry with its left child
2165 */
2166 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
2167
2168 hr = StorageBaseImpl_WriteDirEntry(
2169 This,
2170 parentEntryRef,
2171 &parentEntry);
2172 if(FAILED(hr))
2173 {
2174 return hr;
2175 }
2176
2177 if (entryToDelete.rightChild != DIRENTRY_NULL)
2178 {
2179 /*
2180 * We need to reinsert the right child somewhere. We already know it and
2181 * its children are greater than everything in the left tree, so we
2182 * insert it at the rightmost point in the left tree.
2183 */
2184 DirRef newRightChildParent = entryToDelete.leftChild;
2185 DirEntry newRightChildParentEntry;
2186
2187 do
2188 {
2189 hr = StorageBaseImpl_ReadDirEntry(
2190 This,
2191 newRightChildParent,
2192 &newRightChildParentEntry);
2193 if (FAILED(hr))
2194 {
2195 return hr;
2196 }
2197
2198 if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
2199 newRightChildParent = newRightChildParentEntry.rightChild;
2200 } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
2201
2202 newRightChildParentEntry.rightChild = entryToDelete.rightChild;
2203
2204 hr = StorageBaseImpl_WriteDirEntry(
2205 This,
2206 newRightChildParent,
2207 &newRightChildParentEntry);
2208 if (FAILED(hr))
2209 {
2210 return hr;
2211 }
2212 }
2213 }
2214 else
2215 {
2216 /*
2217 * Replace the deleted entry with its right child
2218 */
2219 setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
2220
2221 hr = StorageBaseImpl_WriteDirEntry(
2222 This,
2223 parentEntryRef,
2224 &parentEntry);
2225 if(FAILED(hr))
2226 {
2227 return hr;
2228 }
2229 }
2230
2231 return hr;
2232 }
2233
2234
2235 /******************************************************************************
2236 * SetElementTimes (IStorage)
2237 */
2238 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2239 IStorage* iface,
2240 const OLECHAR *pwcsName,/* [string][in] */
2241 const FILETIME *pctime, /* [in] */
2242 const FILETIME *patime, /* [in] */
2243 const FILETIME *pmtime) /* [in] */
2244 {
2245 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2246 return S_OK;
2247 }
2248
2249 /******************************************************************************
2250 * SetStateBits (IStorage)
2251 */
2252 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2253 IStorage* iface,
2254 DWORD grfStateBits,/* [in] */
2255 DWORD grfMask) /* [in] */
2256 {
2257 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2258
2259 if (This->reverted)
2260 return STG_E_REVERTED;
2261
2262 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2263 return S_OK;
2264 }
2265
2266 static HRESULT StorageImpl_BaseWriteDirEntry(StorageBaseImpl *base,
2267 DirRef index, const DirEntry *data)
2268 {
2269 StorageImpl *This = (StorageImpl*)base;
2270 return StorageImpl_WriteDirEntry(This, index, data);
2271 }
2272
2273 static HRESULT StorageImpl_BaseReadDirEntry(StorageBaseImpl *base,
2274 DirRef index, DirEntry *data)
2275 {
2276 StorageImpl *This = (StorageImpl*)base;
2277 return StorageImpl_ReadDirEntry(This, index, data);
2278 }
2279
2280 static BlockChainStream **StorageImpl_GetFreeBlockChainCacheEntry(StorageImpl* This)
2281 {
2282 int i;
2283
2284 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2285 {
2286 if (!This->blockChainCache[i])
2287 {
2288 return &This->blockChainCache[i];
2289 }
2290 }
2291
2292 i = This->blockChainToEvict;
2293
2294 BlockChainStream_Destroy(This->blockChainCache[i]);
2295 This->blockChainCache[i] = NULL;
2296
2297 This->blockChainToEvict++;
2298 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2299 This->blockChainToEvict = 0;
2300
2301 return &This->blockChainCache[i];
2302 }
2303
2304 static BlockChainStream **StorageImpl_GetCachedBlockChainStream(StorageImpl *This,
2305 DirRef index)
2306 {
2307 int i, free_index=-1;
2308
2309 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2310 {
2311 if (!This->blockChainCache[i])
2312 {
2313 if (free_index == -1) free_index = i;
2314 }
2315 else if (This->blockChainCache[i]->ownerDirEntry == index)
2316 {
2317 return &This->blockChainCache[i];
2318 }
2319 }
2320
2321 if (free_index == -1)
2322 {
2323 free_index = This->blockChainToEvict;
2324
2325 BlockChainStream_Destroy(This->blockChainCache[free_index]);
2326 This->blockChainCache[free_index] = NULL;
2327
2328 This->blockChainToEvict++;
2329 if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
2330 This->blockChainToEvict = 0;
2331 }
2332
2333 This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
2334 return &This->blockChainCache[free_index];
2335 }
2336
2337 static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
2338 ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
2339 {
2340 StorageImpl *This = (StorageImpl*)base;
2341 DirEntry data;
2342 HRESULT hr;
2343 ULONG bytesToRead;
2344
2345 hr = StorageImpl_ReadDirEntry(This, index, &data);
2346 if (FAILED(hr)) return hr;
2347
2348 if (data.size.QuadPart == 0)
2349 {
2350 *bytesRead = 0;
2351 return S_OK;
2352 }
2353
2354 if (offset.QuadPart + size > data.size.QuadPart)
2355 {
2356 bytesToRead = data.size.QuadPart - offset.QuadPart;
2357 }
2358 else
2359 {
2360 bytesToRead = size;
2361 }
2362
2363 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2364 {
2365 SmallBlockChainStream *stream;
2366
2367 stream = SmallBlockChainStream_Construct(This, NULL, index);
2368 if (!stream) return E_OUTOFMEMORY;
2369
2370 hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2371
2372 SmallBlockChainStream_Destroy(stream);
2373
2374 return hr;
2375 }
2376 else
2377 {
2378 BlockChainStream *stream = NULL;
2379
2380 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2381 if (!stream) return E_OUTOFMEMORY;
2382
2383 hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
2384
2385 return hr;
2386 }
2387 }
2388
2389 static HRESULT StorageImpl_StreamSetSize(StorageBaseImpl *base, DirRef index,
2390 ULARGE_INTEGER newsize)
2391 {
2392 StorageImpl *This = (StorageImpl*)base;
2393 DirEntry data;
2394 HRESULT hr;
2395 SmallBlockChainStream *smallblock=NULL;
2396 BlockChainStream **pbigblock=NULL, *bigblock=NULL;
2397
2398 hr = StorageImpl_ReadDirEntry(This, index, &data);
2399 if (FAILED(hr)) return hr;
2400
2401 /* In simple mode keep the stream size above the small block limit */
2402 if (This->base.openFlags & STGM_SIMPLE)
2403 newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
2404
2405 if (data.size.QuadPart == newsize.QuadPart)
2406 return S_OK;
2407
2408 /* Create a block chain object of the appropriate type */
2409 if (data.size.QuadPart == 0)
2410 {
2411 if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2412 {
2413 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2414 if (!smallblock) return E_OUTOFMEMORY;
2415 }
2416 else
2417 {
2418 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2419 bigblock = *pbigblock;
2420 if (!bigblock) return E_OUTOFMEMORY;
2421 }
2422 }
2423 else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2424 {
2425 smallblock = SmallBlockChainStream_Construct(This, NULL, index);
2426 if (!smallblock) return E_OUTOFMEMORY;
2427 }
2428 else
2429 {
2430 pbigblock = StorageImpl_GetCachedBlockChainStream(This, index);
2431 bigblock = *pbigblock;
2432 if (!bigblock) return E_OUTOFMEMORY;
2433 }
2434
2435 /* Change the block chain type if necessary. */
2436 if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
2437 {
2438 bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
2439 if (!bigblock)
2440 {
2441 SmallBlockChainStream_Destroy(smallblock);
2442 return E_FAIL;
2443 }
2444
2445 pbigblock = StorageImpl_GetFreeBlockChainCacheEntry(This);
2446 *pbigblock = bigblock;
2447 }
2448 else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2449 {
2450 smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock);
2451 if (!smallblock)
2452 return E_FAIL;
2453 }
2454
2455 /* Set the size of the block chain. */
2456 if (smallblock)
2457 {
2458 SmallBlockChainStream_SetSize(smallblock, newsize);
2459 SmallBlockChainStream_Destroy(smallblock);
2460 }
2461 else
2462 {
2463 BlockChainStream_SetSize(bigblock, newsize);
2464 }
2465
2466 /* Set the size in the directory entry. */
2467 hr = StorageImpl_ReadDirEntry(This, index, &data);
2468 if (SUCCEEDED(hr))
2469 {
2470 data.size = newsize;
2471
2472 hr = StorageImpl_WriteDirEntry(This, index, &data);
2473 }
2474 return hr;
2475 }
2476
2477 static HRESULT StorageImpl_StreamWriteAt(StorageBaseImpl *base, DirRef index,
2478 ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
2479 {
2480 StorageImpl *This = (StorageImpl*)base;
2481 DirEntry data;
2482 HRESULT hr;
2483 ULARGE_INTEGER newSize;
2484
2485 hr = StorageImpl_ReadDirEntry(This, index, &data);
2486 if (FAILED(hr)) return hr;
2487
2488 /* Grow the stream if necessary */
2489 newSize.QuadPart = 0;
2490 newSize.QuadPart = offset.QuadPart + size;
2491
2492 if (newSize.QuadPart > data.size.QuadPart)
2493 {
2494 hr = StorageImpl_StreamSetSize(base, index, newSize);
2495 if (FAILED(hr))
2496 return hr;
2497
2498 hr = StorageImpl_ReadDirEntry(This, index, &data);
2499 if (FAILED(hr)) return hr;
2500 }
2501
2502 if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
2503 {
2504 SmallBlockChainStream *stream;
2505
2506 stream = SmallBlockChainStream_Construct(This, NULL, index);
2507 if (!stream) return E_OUTOFMEMORY;
2508
2509 hr = SmallBlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2510
2511 SmallBlockChainStream_Destroy(stream);
2512
2513 return hr;
2514 }
2515 else
2516 {
2517 BlockChainStream *stream;
2518
2519 stream = *StorageImpl_GetCachedBlockChainStream(This, index);
2520 if (!stream) return E_OUTOFMEMORY;
2521
2522 hr = BlockChainStream_WriteAt(stream, offset, size, buffer, bytesWritten);
2523
2524 return hr;
2525 }
2526 }
2527
2528 /*
2529 * Virtual function table for the IStorage32Impl class.
2530 */
2531 static const IStorageVtbl Storage32Impl_Vtbl =
2532 {
2533 StorageBaseImpl_QueryInterface,
2534 StorageBaseImpl_AddRef,
2535 StorageBaseImpl_Release,
2536 StorageBaseImpl_CreateStream,
2537 StorageBaseImpl_OpenStream,
2538 StorageBaseImpl_CreateStorage,
2539 StorageBaseImpl_OpenStorage,
2540 StorageBaseImpl_CopyTo,
2541 StorageBaseImpl_MoveElementTo,
2542 StorageImpl_Commit,
2543 StorageImpl_Revert,
2544 StorageBaseImpl_EnumElements,
2545 StorageBaseImpl_DestroyElement,
2546 StorageBaseImpl_RenameElement,
2547 StorageBaseImpl_SetElementTimes,
2548 StorageBaseImpl_SetClass,
2549 StorageBaseImpl_SetStateBits,
2550 StorageBaseImpl_Stat
2551 };
2552
2553 static const StorageBaseImplVtbl StorageImpl_BaseVtbl =
2554 {
2555 StorageImpl_Destroy,
2556 StorageImpl_Invalidate,
2557 StorageImpl_CreateDirEntry,
2558 StorageImpl_BaseWriteDirEntry,
2559 StorageImpl_BaseReadDirEntry,
2560 StorageImpl_DestroyDirEntry,
2561 StorageImpl_StreamReadAt,
2562 StorageImpl_StreamWriteAt,
2563 StorageImpl_StreamSetSize
2564 };
2565
2566 static HRESULT StorageImpl_Construct(
2567 HANDLE hFile,
2568 LPCOLESTR pwcsName,
2569 ILockBytes* pLkbyt,
2570 DWORD openFlags,
2571 BOOL fileBased,
2572 BOOL create,
2573 StorageImpl** result)
2574 {
2575 StorageImpl* This;
2576 HRESULT hr = S_OK;
2577 DirEntry currentEntry;
2578 DirRef currentEntryRef;
2579 WCHAR fullpath[MAX_PATH];
2580
2581 if ( FAILED( validateSTGM(openFlags) ))
2582 return STG_E_INVALIDFLAG;
2583
2584 This = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
2585 if (!This)
2586 return E_OUTOFMEMORY;
2587
2588 memset(This, 0, sizeof(StorageImpl));
2589
2590 list_init(&This->base.strmHead);
2591
2592 list_init(&This->base.storageHead);
2593
2594 This->base.lpVtbl = &Storage32Impl_Vtbl;
2595 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2596 This->base.baseVtbl = &StorageImpl_BaseVtbl;
2597 This->base.openFlags = (openFlags & ~STGM_CREATE);
2598 This->base.ref = 1;
2599 This->base.create = create;
2600
2601 This->base.reverted = 0;
2602
2603 This->hFile = hFile;
2604
2605 if(pwcsName) {
2606 if (!GetFullPathNameW(pwcsName, MAX_PATH, fullpath, NULL))
2607 {
2608 lstrcpynW(fullpath, pwcsName, MAX_PATH);
2609 }
2610 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2611 (lstrlenW(fullpath)+1)*sizeof(WCHAR));
2612 if (!This->pwcsName)
2613 {
2614 hr = STG_E_INSUFFICIENTMEMORY;
2615 goto end;
2616 }
2617 strcpyW(This->pwcsName, fullpath);
2618 This->base.filename = This->pwcsName;
2619 }
2620
2621 /*
2622 * Initialize the big block cache.
2623 */
2624 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2625 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2626 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2627 pLkbyt,
2628 openFlags,
2629 This->bigBlockSize,
2630 fileBased);
2631
2632 if (This->bigBlockFile == 0)
2633 {
2634 hr = E_FAIL;
2635 goto end;
2636 }
2637
2638 if (create)
2639 {
2640 ULARGE_INTEGER size;
2641 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2642
2643 /*
2644 * Initialize all header variables:
2645 * - The big block depot consists of one block and it is at block 0
2646 * - The directory table starts at block 1
2647 * - There is no small block depot
2648 */
2649 memset( This->bigBlockDepotStart,
2650 BLOCK_UNUSED,
2651 sizeof(This->bigBlockDepotStart));
2652
2653 This->bigBlockDepotCount = 1;
2654 This->bigBlockDepotStart[0] = 0;
2655 This->rootStartBlock = 1;
2656 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2657 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2658 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2659 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2660 This->extBigBlockDepotCount = 0;
2661
2662 StorageImpl_SaveFileHeader(This);
2663
2664 /*
2665 * Add one block for the big block depot and one block for the directory table
2666 */
2667 size.u.HighPart = 0;
2668 size.u.LowPart = This->bigBlockSize * 3;
2669 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2670
2671 /*
2672 * Initialize the big block depot
2673 */
2674 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2675 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2676 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2677 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2678 }
2679 else
2680 {
2681 /*
2682 * Load the header for the file.
2683 */
2684 hr = StorageImpl_LoadFileHeader(This);
2685
2686 if (FAILED(hr))
2687 {
2688 goto end;
2689 }
2690 }
2691
2692 /*
2693 * There is no block depot cached yet.
2694 */
2695 This->indexBlockDepotCached = 0xFFFFFFFF;
2696
2697 /*
2698 * Start searching for free blocks with block 0.
2699 */
2700 This->prevFreeBlock = 0;
2701
2702 /*
2703 * Create the block chain abstractions.
2704 */
2705 if(!(This->rootBlockChain =
2706 BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
2707 {
2708 hr = STG_E_READFAULT;
2709 goto end;
2710 }
2711
2712 if(!(This->smallBlockDepotChain =
2713 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2714 DIRENTRY_NULL)))
2715 {
2716 hr = STG_E_READFAULT;
2717 goto end;
2718 }
2719
2720 /*
2721 * Write the root storage entry (memory only)
2722 */
2723 if (create)
2724 {
2725 DirEntry rootEntry;
2726 /*
2727 * Initialize the directory table
2728 */
2729 memset(&rootEntry, 0, sizeof(rootEntry));
2730 MultiByteToWideChar( CP_ACP, 0, rootEntryName, -1, rootEntry.name,
2731 sizeof(rootEntry.name)/sizeof(WCHAR) );
2732 rootEntry.sizeOfNameString = (strlenW(rootEntry.name)+1) * sizeof(WCHAR);
2733 rootEntry.stgType = STGTY_ROOT;
2734 rootEntry.leftChild = DIRENTRY_NULL;
2735 rootEntry.rightChild = DIRENTRY_NULL;
2736 rootEntry.dirRootEntry = DIRENTRY_NULL;
2737 rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
2738 rootEntry.size.u.HighPart = 0;
2739 rootEntry.size.u.LowPart = 0;
2740
2741 StorageImpl_WriteDirEntry(This, 0, &rootEntry);
2742 }
2743
2744 /*
2745 * Find the ID of the root storage.
2746 */
2747 currentEntryRef = 0;
2748
2749 do
2750 {
2751 hr = StorageImpl_ReadDirEntry(
2752 This,
2753 currentEntryRef,
2754 &currentEntry);
2755
2756 if (SUCCEEDED(hr))
2757 {
2758 if ( (currentEntry.sizeOfNameString != 0 ) &&
2759 (currentEntry.stgType == STGTY_ROOT) )
2760 {
2761 This->base.storageDirEntry = currentEntryRef;
2762 }
2763 }
2764
2765 currentEntryRef++;
2766
2767 } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
2768
2769 if (FAILED(hr))
2770 {
2771 hr = STG_E_READFAULT;
2772 goto end;
2773 }
2774
2775 /*
2776 * Create the block chain abstraction for the small block root chain.
2777 */
2778 if(!(This->smallBlockRootChain =
2779 BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
2780 {
2781 hr = STG_E_READFAULT;
2782 }
2783
2784 end:
2785 if (FAILED(hr))
2786 {
2787 IStorage_Release((IStorage*)This);
2788 *result = NULL;
2789 }
2790 else
2791 *result = This;
2792
2793 return hr;
2794 }
2795
2796 static void StorageImpl_Invalidate(StorageBaseImpl* iface)
2797 {
2798 StorageImpl *This = (StorageImpl*) iface;
2799
2800 StorageBaseImpl_DeleteAll(&This->base);
2801
2802 This->base.reverted = 1;
2803 }
2804
2805 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2806 {
2807 StorageImpl *This = (StorageImpl*) iface;
2808 int i;
2809 TRACE("(%p)\n", This);
2810
2811 StorageImpl_Invalidate(iface);
2812
2813 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2814
2815 BlockChainStream_Destroy(This->smallBlockRootChain);
2816 BlockChainStream_Destroy(This->rootBlockChain);
2817 BlockChainStream_Destroy(This->smallBlockDepotChain);
2818
2819 for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
2820 BlockChainStream_Destroy(This->blockChainCache[i]);
2821
2822 if (This->bigBlockFile)
2823 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2824 HeapFree(GetProcessHeap(), 0, This);
2825 }
2826
2827 /******************************************************************************
2828 * Storage32Impl_GetNextFreeBigBlock
2829 *
2830 * Returns the index of the next free big block.
2831 * If the big block depot is filled, this method will enlarge it.
2832 *
2833 */
2834 static ULONG StorageImpl_GetNextFreeBigBlock(
2835 StorageImpl* This)
2836 {
2837 ULONG depotBlockIndexPos;
2838 BYTE depotBuffer[BIG_BLOCK_SIZE];
2839 BOOL success;
2840 ULONG depotBlockOffset;
2841 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2842 ULONG nextBlockIndex = BLOCK_SPECIAL;
2843 int depotIndex = 0;
2844 ULONG freeBlock = BLOCK_UNUSED;
2845
2846 depotIndex = This->prevFreeBlock / blocksPerDepot;
2847 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2848
2849 /*
2850 * Scan the entire big block depot until we find a block marked free
2851 */
2852 while (nextBlockIndex != BLOCK_UNUSED)
2853 {
2854 if (depotIndex < COUNT_BBDEPOTINHEADER)
2855 {
2856 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2857
2858 /*
2859 * Grow the primary depot.
2860 */
2861 if (depotBlockIndexPos == BLOCK_UNUSED)
2862 {
2863 depotBlockIndexPos = depotIndex*blocksPerDepot;
2864
2865 /*
2866 * Add a block depot.
2867 */
2868 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2869 This->bigBlockDepotCount++;
2870 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2871
2872 /*
2873 * Flag it as a block depot.
2874 */
2875 StorageImpl_SetNextBlockInChain(This,
2876 depotBlockIndexPos,
2877 BLOCK_SPECIAL);
2878
2879 /* Save new header information.
2880 */
2881 StorageImpl_SaveFileHeader(This);
2882 }
2883 }
2884 else
2885 {
2886 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2887
2888 if (depotBlockIndexPos == BLOCK_UNUSED)
2889 {
2890 /*
2891 * Grow the extended depot.
2892 */
2893 ULONG extIndex = BLOCK_UNUSED;
2894 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2895 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2896
2897 if (extBlockOffset == 0)
2898 {
2899 /* We need an extended block.
2900 */
2901 extIndex = Storage32Impl_AddExtBlockDepot(This);
2902 This->extBigBlockDepotCount++;
2903 depotBlockIndexPos = extIndex + 1;
2904 }
2905 else
2906 depotBlockIndexPos = depotIndex * blocksPerDepot;
2907
2908 /*
2909 * Add a block depot and mark it in the extended block.
2910 */
2911 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2912 This->bigBlockDepotCount++;
2913 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2914
2915 /* Flag the block depot.
2916 */
2917 StorageImpl_SetNextBlockInChain(This,
2918 depotBlockIndexPos,
2919 BLOCK_SPECIAL);
2920
2921 /* If necessary, flag the extended depot block.
2922 */
2923 if (extIndex != BLOCK_UNUSED)
2924 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2925
2926 /* Save header information.
2927 */
2928 StorageImpl_SaveFileHeader(This);
2929 }
2930 }
2931
2932 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2933
2934 if (success)
2935 {
2936 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2937 ( nextBlockIndex != BLOCK_UNUSED))
2938 {
2939 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2940
2941 if (nextBlockIndex == BLOCK_UNUSED)
2942 {
2943 freeBlock = (depotIndex * blocksPerDepot) +
2944 (depotBlockOffset/sizeof(ULONG));
2945 }
2946
2947 depotBlockOffset += sizeof(ULONG);
2948 }
2949 }
2950
2951 depotIndex++;
2952 depotBlockOffset = 0;
2953 }
2954
2955 /*
2956 * make sure that the block physically exists before using it
2957 */
2958 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2959
2960 This->prevFreeBlock = freeBlock;
2961
2962 return freeBlock;
2963 }
2964
2965 /******************************************************************************
2966 * Storage32Impl_AddBlockDepot
2967 *
2968 * This will create a depot block, essentially it is a block initialized
2969 * to BLOCK_UNUSEDs.
2970 */
2971 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2972 {
2973 BYTE blockBuffer[BIG_BLOCK_SIZE];
2974
2975 /*
2976 * Initialize blocks as free
2977 */
2978 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2979 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2980 }
2981
2982 /******************************************************************************
2983 * Storage32Impl_GetExtDepotBlock
2984 *
2985 * Returns the index of the block that corresponds to the specified depot
2986 * index. This method is only for depot indexes equal or greater than
2987 * COUNT_BBDEPOTINHEADER.
2988 */
2989 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2990 {
2991 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2992 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2993 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2994 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2995 ULONG blockIndex = BLOCK_UNUSED;
2996 ULONG extBlockIndex = This->extBigBlockDepotStart;
2997
2998 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2999
3000 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
3001 return BLOCK_UNUSED;
3002
3003 while (extBlockCount > 0)
3004 {
3005 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3006 extBlockCount--;
3007 }
3008
3009 if (extBlockIndex != BLOCK_UNUSED)
3010 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
3011 extBlockOffset * sizeof(ULONG), &blockIndex);
3012
3013 return blockIndex;
3014 }
3015
3016 /******************************************************************************
3017 * Storage32Impl_SetExtDepotBlock
3018 *
3019 * Associates the specified block index to the specified depot index.
3020 * This method is only for depot indexes equal or greater than
3021 * COUNT_BBDEPOTINHEADER.
3022 */
3023 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3024 {
3025 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3026 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3027 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3028 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3029 ULONG extBlockIndex = This->extBigBlockDepotStart;
3030
3031 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3032
3033 while (extBlockCount > 0)
3034 {
3035 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
3036 extBlockCount--;
3037 }
3038
3039 if (extBlockIndex != BLOCK_UNUSED)
3040 {
3041 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3042 extBlockOffset * sizeof(ULONG),
3043 blockIndex);
3044 }
3045 }
3046
3047 /******************************************************************************
3048 * Storage32Impl_AddExtBlockDepot
3049 *
3050 * Creates an extended depot block.
3051 */
3052 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
3053 {
3054 ULONG numExtBlocks = This->extBigBlockDepotCount;
3055 ULONG nextExtBlock = This->extBigBlockDepotStart;
3056 BYTE depotBuffer[BIG_BLOCK_SIZE];
3057 ULONG index = BLOCK_UNUSED;
3058 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3059 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3060 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3061
3062 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3063 blocksPerDepotBlock;
3064
3065 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3066 {
3067 /*
3068 * The first extended block.
3069 */
3070 This->extBigBlockDepotStart = index;
3071 }
3072 else
3073 {
3074 unsigned int i;
3075 /*
3076 * Follow the chain to the last one.
3077 */
3078 for (i = 0; i < (numExtBlocks - 1); i++)
3079 {
3080 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
3081 }
3082
3083 /*
3084 * Add the new extended block to the chain.
3085 */
3086 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3087 index);
3088 }
3089
3090 /*
3091 * Initialize this block.
3092 */
3093 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3094 StorageImpl_WriteBigBlock(This, index, depotBuffer);
3095
3096 return index;
3097 }
3098
3099 /******************************************************************************
3100 * Storage32Impl_FreeBigBlock
3101 *
3102 * This method will flag the specified block as free in the big block depot.
3103 */
3104 static void StorageImpl_FreeBigBlock(
3105 StorageImpl* This,
3106 ULONG blockIndex)
3107 {
3108 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
3109
3110 if (blockIndex < This->prevFreeBlock)
3111 This->prevFreeBlock = blockIndex;
3112 }
3113
3114 /************************************************************************
3115 * Storage32Impl_GetNextBlockInChain
3116 *
3117 * This method will retrieve the block index of the next big block in
3118 * in the chain.
3119 *
3120 * Params: This - Pointer to the Storage object.
3121 * blockIndex - Index of the block to retrieve the chain
3122 * for.
3123 * nextBlockIndex - receives the return value.
3124 *
3125 * Returns: This method returns the index of the next block in the chain.
3126 * It will return the constants:
3127 * BLOCK_SPECIAL - If the block given was not part of a
3128 * chain.
3129 * BLOCK_END_OF_CHAIN - If the block given was the last in
3130 * a chain.
3131 * BLOCK_UNUSED - If the block given was not past of a chain
3132 * and is available.
3133 * BLOCK_EXTBBDEPOT - This block is part of the extended
3134 * big block depot.
3135 *
3136 * See Windows documentation for more details on IStorage methods.
3137 */
3138 static HRESULT StorageImpl_GetNextBlockInChain(
3139 StorageImpl* This,
3140 ULONG blockIndex,
3141 ULONG* nextBlockIndex)
3142 {
3143 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3144 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3145 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3146 BYTE depotBuffer[BIG_BLOCK_SIZE];
3147 BOOL success;
3148 ULONG depotBlockIndexPos;
3149 int index;
3150
3151 *nextBlockIndex = BLOCK_SPECIAL;
3152
3153 if(depotBlockCount >= This->bigBlockDepotCount)
3154 {
3155 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
3156 This->bigBlockDepotCount);
3157 return STG_E_READFAULT;
3158 }
3159
3160 /*
3161 * Cache the currently accessed depot block.
3162 */
3163 if (depotBlockCount != This->indexBlockDepotCached)
3164 {
3165 This->indexBlockDepotCached = depotBlockCount;
3166
3167 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3168 {
3169 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3170 }
3171 else
3172 {
3173 /*
3174 * We have to look in the extended depot.
3175 */
3176 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3177 }
3178
3179 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
3180
3181 if (!success)
3182 return STG_E_READFAULT;
3183
3184 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
3185 {
3186 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
3187 This->blockDepotCached[index] = *nextBlockIndex;
3188 }
3189 }
3190
3191 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
3192
3193 return S_OK;
3194 }
3195
3196 /******************************************************************************
3197 * Storage32Impl_GetNextExtendedBlock
3198 *
3199 * Given an extended block this method will return the next extended block.
3200 *
3201 * NOTES:
3202 * The last ULONG of an extended block is the block index of the next
3203 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3204 * depot.
3205 *
3206 * Return values:
3207 * - The index of the next extended block
3208 * - BLOCK_UNUSED: there is no next extended block.
3209 * - Any other return values denotes failure.
3210 */
3211 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3212 {
3213 ULONG nextBlockIndex = BLOCK_SPECIAL;
3214 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3215
3216 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3217 &nextBlockIndex);
3218
3219 return nextBlockIndex;
3220 }
3221
3222 /******************************************************************************
3223 * Storage32Impl_SetNextBlockInChain
3224 *
3225 * This method will write the index of the specified block's next block
3226 * in the big block depot.
3227 *
3228 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3229 * do the following
3230 *
3231 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3232 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3233 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3234 *
3235 */
3236 static void StorageImpl_SetNextBlockInChain(
3237 StorageImpl* This,
3238 ULONG blockIndex,
3239 ULONG nextBlock)
3240 {
3241 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3242 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3243 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3244 ULONG depotBlockIndexPos;
3245
3246 assert(depotBlockCount < This->bigBlockDepotCount);
3247 assert(blockIndex != nextBlock);
3248
3249 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3250 {
3251 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3252 }
3253 else
3254 {
3255 /*
3256 * We have to look in the extended depot.
3257 */
3258 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3259 }
3260
3261 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3262 nextBlock);
3263 /*
3264 * Update the cached block depot, if necessary.
3265 */
3266 if (depotBlockCount == This->indexBlockDepotCached)
3267 {
3268 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3269 }
3270 }
3271
3272 /******************************************************************************
3273 * Storage32Impl_LoadFileHeader
3274 *
3275 * This method will read in the file header, i.e. big block index -1.
3276 */
3277 static HRESULT StorageImpl_LoadFileHeader(
3278 StorageImpl* This)
3279 {
3280 HRESULT hr = STG_E_FILENOTFOUND;
3281 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3282 BOOL success;
3283 int index;
3284
3285 TRACE("\n");
3286 /*
3287 * Get a pointer to the big block of data containing the header.
3288 */
3289 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3290
3291 /*
3292 * Extract the information from the header.
3293 */
3294 if (success)
3295 {
3296 /*
3297 * Check for the "magic number" signature and return an error if it is not
3298 * found.
3299 */
3300 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3301 {
3302 return STG_E_OLDFORMAT;
3303 }
3304
3305 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3306 {
3307 return STG_E_INVALIDHEADER;
3308 }
3309
3310 StorageUtl_ReadWord(
3311 headerBigBlock,
3312 OFFSET_BIGBLOCKSIZEBITS,
3313 &This->bigBlockSizeBits);
3314
3315 StorageUtl_ReadWord(
3316 headerBigBlock,
3317 OFFSET_SMALLBLOCKSIZEBITS,
3318 &This->smallBlockSizeBits);
3319
3320 StorageUtl_ReadDWord(
3321 headerBigBlock,
3322 OFFSET_BBDEPOTCOUNT,
3323 &This->bigBlockDepotCount);
3324
3325 StorageUtl_ReadDWord(
3326 headerBigBlock,
3327 OFFSET_ROOTSTARTBLOCK,
3328 &This->rootStartBlock);
3329
3330 StorageUtl_ReadDWord(
3331 headerBigBlock,
3332 OFFSET_SBDEPOTSTART,
3333 &This->smallBlockDepotStart);
3334
3335 StorageUtl_ReadDWord(
3336 headerBigBlock,
3337 OFFSET_EXTBBDEPOTSTART,
3338 &This->extBigBlockDepotStart);
3339
3340 StorageUtl_ReadDWord(
3341 headerBigBlock,
3342 OFFSET_EXTBBDEPOTCOUNT,
3343 &This->extBigBlockDepotCount);
3344
3345 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3346 {
3347 StorageUtl_ReadDWord(
3348 headerBigBlock,
3349 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3350 &(This->bigBlockDepotStart[index]));
3351 }
3352
3353 /*
3354 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3355 */
3356 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3357 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3358
3359 /*
3360 * Right now, the code is making some assumptions about the size of the
3361 * blocks, just make sure they are what we're expecting.
3362 */
3363 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3364 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3365 {
3366 WARN("Broken OLE storage file\n");
3367 hr = STG_E_INVALIDHEADER;
3368 }
3369 else
3370 hr = S_OK;
3371 }
3372
3373 return hr;
3374 }
3375
3376 /******************************************************************************
3377 * Storage32Impl_SaveFileHeader
3378 *
3379 * This method will save to the file the header, i.e. big block -1.
3380 */
3381 static void StorageImpl_SaveFileHeader(
3382 StorageImpl* This)
3383 {
3384 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3385 int index;
3386 BOOL success;
3387
3388 /*
3389 * Get a pointer to the big block of data containing the header.
3390 */
3391 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3392
3393 /*
3394 * If the block read failed, the file is probably new.
3395 */
3396 if (!success)
3397 {
3398 /*
3399 * Initialize for all unknown fields.
3400 */
3401 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3402
3403 /*
3404 * Initialize the magic number.
3405 */
3406 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3407
3408 /*
3409 * And a bunch of things we don't know what they mean
3410 */
3411 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3412 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3413 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3414 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3415 }
3416
3417 /*
3418 * Write the information to the header.
3419 */
3420 StorageUtl_WriteWord(
3421 headerBigBlock,
3422 OFFSET_BIGBLOCKSIZEBITS,
3423 This->bigBlockSizeBits);
3424
3425 StorageUtl_WriteWord(
3426 headerBigBlock,
3427 OFFSET_SMALLBLOCKSIZEBITS,
3428 This->smallBlockSizeBits);
3429
3430 StorageUtl_WriteDWord(
3431 headerBigBlock,
3432 OFFSET_BBDEPOTCOUNT,
3433 This->bigBlockDepotCount);
3434
3435 StorageUtl_WriteDWord(
3436 headerBigBlock,
3437 OFFSET_ROOTSTARTBLOCK,
3438 This->rootStartBlock);
3439
3440 StorageUtl_WriteDWord(
3441 headerBigBlock,
3442 OFFSET_SBDEPOTSTART,
3443 This->smallBlockDepotStart);
3444
3445 StorageUtl_WriteDWord(
3446 headerBigBlock,
3447 OFFSET_SBDEPOTCOUNT,
3448 This->smallBlockDepotChain ?
3449 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3450
3451 StorageUtl_WriteDWord(
3452 headerBigBlock,
3453 OFFSET_EXTBBDEPOTSTART,
3454 This->extBigBlockDepotStart);
3455
3456 StorageUtl_WriteDWord(
3457 headerBigBlock,
3458 OFFSET_EXTBBDEPOTCOUNT,
3459 This->extBigBlockDepotCount);
3460
3461 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3462 {
3463 StorageUtl_WriteDWord(
3464 headerBigBlock,
3465 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3466 (This->bigBlockDepotStart[index]));
3467 }
3468
3469 /*
3470 * Write the big block back to the file.
3471 */
3472 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3473 }
3474
3475 /******************************************************************************
3476 * StorageImpl_ReadRawDirEntry
3477 *
3478 * This method will read the raw data from a directory entry in the file.
3479 *
3480 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3481 */
3482 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
3483 {
3484 ULARGE_INTEGER offset;
3485 HRESULT hr;
3486 ULONG bytesRead;
3487
3488 offset.u.HighPart = 0;
3489 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3490
3491 hr = BlockChainStream_ReadAt(
3492 This->rootBlockChain,
3493 offset,
3494 RAW_DIRENTRY_SIZE,
3495 buffer,
3496 &bytesRead);
3497
3498 return hr;
3499 }
3500
3501 /******************************************************************************
3502 * StorageImpl_WriteRawDirEntry
3503 *
3504 * This method will write the raw data from a directory entry in the file.
3505 *
3506 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3507 */
3508 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3509 {
3510 ULARGE_INTEGER offset;
3511 HRESULT hr;
3512 ULONG bytesRead;
3513
3514 offset.u.HighPart = 0;
3515 offset.u.LowPart = index * RAW_DIRENTRY_SIZE;
3516
3517 hr = BlockChainStream_WriteAt(
3518 This->rootBlockChain,
3519 offset,
3520 RAW_DIRENTRY_SIZE,
3521 buffer,
3522 &bytesRead);
3523
3524 return hr;
3525 }
3526
3527 /******************************************************************************
3528 * UpdateRawDirEntry
3529 *
3530 * Update raw directory entry data from the fields in newData.
3531 *
3532 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3533 */
3534 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3535 {
3536 memset(buffer, 0, RAW_DIRENTRY_SIZE);
3537
3538 memcpy(
3539 buffer + OFFSET_PS_NAME,
3540 newData->name,
3541 DIRENTRY_NAME_BUFFER_LEN );
3542
3543 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3544
3545 StorageUtl_WriteWord(
3546 buffer,
3547 OFFSET_PS_NAMELENGTH,
3548 newData->sizeOfNameString);
3549
3550 StorageUtl_WriteDWord(
3551 buffer,
3552 OFFSET_PS_LEFTCHILD,
3553 newData->leftChild);
3554
3555 StorageUtl_WriteDWord(
3556 buffer,
3557 OFFSET_PS_RIGHTCHILD,
3558 newData->rightChild);
3559
3560 StorageUtl_WriteDWord(
3561 buffer,
3562 OFFSET_PS_DIRROOT,
3563 newData->dirRootEntry);
3564
3565 StorageUtl_WriteGUID(
3566 buffer,
3567 OFFSET_PS_GUID,
3568 &newData->clsid);
3569
3570 StorageUtl_WriteDWord(
3571 buffer,
3572 OFFSET_PS_CTIMELOW,
3573 newData->ctime.dwLowDateTime);
3574
3575 StorageUtl_WriteDWord(
3576 buffer,
3577 OFFSET_PS_CTIMEHIGH,
3578 newData->ctime.dwHighDateTime);
3579
3580 StorageUtl_WriteDWord(
3581 buffer,
3582 OFFSET_PS_MTIMELOW,
3583 newData->mtime.dwLowDateTime);
3584
3585 StorageUtl_WriteDWord(
3586 buffer,
3587 OFFSET_PS_MTIMEHIGH,
3588 newData->ctime.dwHighDateTime);
3589
3590 StorageUtl_WriteDWord(
3591 buffer,
3592 OFFSET_PS_STARTBLOCK,
3593 newData->startingBlock);
3594
3595 StorageUtl_WriteDWord(
3596 buffer,
3597 OFFSET_PS_SIZE,
3598 newData->size.u.LowPart);
3599 }
3600
3601 /******************************************************************************
3602 * Storage32Impl_ReadDirEntry
3603 *
3604 * This method will read the specified directory entry.
3605 */
3606 HRESULT StorageImpl_ReadDirEntry(
3607 StorageImpl* This,
3608 DirRef index,
3609 DirEntry* buffer)
3610 {
3611 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3612 HRESULT readRes;
3613
3614 readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3615
3616 if (SUCCEEDED(readRes))
3617 {
3618 memset(buffer->name, 0, sizeof(buffer->name));
3619 memcpy(
3620 buffer->name,
3621 (WCHAR *)currentEntry+OFFSET_PS_NAME,
3622 DIRENTRY_NAME_BUFFER_LEN );
3623 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3624
3625 memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3626
3627 StorageUtl_ReadWord(
3628 currentEntry,
3629 OFFSET_PS_NAMELENGTH,
3630 &buffer->sizeOfNameString);
3631
3632 StorageUtl_ReadDWord(
3633 currentEntry,
3634 OFFSET_PS_LEFTCHILD,
3635 &buffer->leftChild);
3636
3637 StorageUtl_ReadDWord(
3638 currentEntry,
3639 OFFSET_PS_RIGHTCHILD,
3640 &buffer->rightChild);
3641
3642 StorageUtl_ReadDWord(
3643 currentEntry,
3644 OFFSET_PS_DIRROOT,
3645 &buffer->dirRootEntry);
3646
3647 StorageUtl_ReadGUID(
3648 currentEntry,
3649 OFFSET_PS_GUID,
3650 &buffer->clsid);
3651
3652 StorageUtl_ReadDWord(
3653 currentEntry,
3654 OFFSET_PS_CTIMELOW,
3655 &buffer->ctime.dwLowDateTime);
3656
3657 StorageUtl_ReadDWord(
3658 currentEntry,
3659 OFFSET_PS_CTIMEHIGH,
3660 &buffer->ctime.dwHighDateTime);
3661
3662 StorageUtl_ReadDWord(
3663 currentEntry,
3664 OFFSET_PS_MTIMELOW,
3665 &buffer->mtime.dwLowDateTime);
3666
3667 StorageUtl_ReadDWord(
3668 currentEntry,
3669 OFFSET_PS_MTIMEHIGH,
3670 &buffer->mtime.dwHighDateTime);
3671
3672 StorageUtl_ReadDWord(
3673 currentEntry,
3674 OFFSET_PS_STARTBLOCK,
3675 &buffer->startingBlock);
3676
3677 StorageUtl_ReadDWord(
3678 currentEntry,
3679 OFFSET_PS_SIZE,
3680 &buffer->size.u.LowPart);
3681
3682 buffer->size.u.HighPart = 0;
3683 }
3684
3685 return readRes;
3686 }
3687
3688 /*********************************************************************
3689 * Write the specified directory entry to the file
3690 */
3691 HRESULT StorageImpl_WriteDirEntry(
3692 StorageImpl* This,
3693 DirRef index,
3694 const DirEntry* buffer)
3695 {
3696 BYTE currentEntry[RAW_DIRENTRY_SIZE];
3697 HRESULT writeRes;
3698
3699 UpdateRawDirEntry(currentEntry, buffer);
3700
3701 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3702 return writeRes;
3703 }
3704
3705 static BOOL StorageImpl_ReadBigBlock(
3706 StorageImpl* This,
3707 ULONG blockIndex,
3708 void* buffer)
3709 {
3710 ULARGE_INTEGER ulOffset;
3711 DWORD read;
3712
3713 ulOffset.u.HighPart = 0;
3714 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3715
3716 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3717 return (read == This->bigBlockSize);
3718 }
3719
3720 static BOOL StorageImpl_ReadDWordFromBigBlock(
3721 StorageImpl* This,
3722 ULONG blockIndex,
3723 ULONG offset,
3724 DWORD* value)
3725 {
3726 ULARGE_INTEGER ulOffset;
3727 DWORD read;
3728 DWORD tmp;
3729
3730 ulOffset.u.HighPart = 0;
3731 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3732 ulOffset.u.LowPart += offset;
3733
3734 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3735 *value = lendian32toh(tmp);
3736 return (read == sizeof(DWORD));
3737 }
3738
3739 static BOOL StorageImpl_WriteBigBlock(
3740 StorageImpl* This,
3741 ULONG blockIndex,
3742 const void* buffer)
3743 {
3744 ULARGE_INTEGER ulOffset;
3745 DWORD wrote;
3746
3747 ulOffset.u.HighPart = 0;
3748 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3749
3750 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3751 return (wrote == This->bigBlockSize);
3752 }
3753
3754 static BOOL StorageImpl_WriteDWordToBigBlock(
3755 StorageImpl* This,
3756 ULONG blockIndex,
3757 ULONG offset,
3758 DWORD value)
3759 {
3760 ULARGE_INTEGER ulOffset;
3761 DWORD wrote;
3762
3763 ulOffset.u.HighPart = 0;
3764 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3765 ulOffset.u.LowPart += offset;
3766
3767 value = htole32(value);
3768 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3769 return (wrote == sizeof(DWORD));
3770 }
3771
3772 /******************************************************************************
3773 * Storage32Impl_SmallBlocksToBigBlocks
3774 *
3775 * This method will convert a small block chain to a big block chain.
3776 * The small block chain will be destroyed.
3777 */
3778 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3779 StorageImpl* This,
3780 SmallBlockChainStream** ppsbChain)
3781 {
3782 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3783 ULARGE_INTEGER size, offset;
3784 ULONG cbRead, cbWritten;
3785 ULARGE_INTEGER cbTotalRead;
3786 DirRef streamEntryRef;
3787 HRESULT resWrite = S_OK;
3788 HRESULT resRead;
3789 DirEntry streamEntry;
3790 BYTE *buffer;
3791 BlockChainStream *bbTempChain = NULL;
3792 BlockChainStream *bigBlockChain = NULL;
3793
3794 /*
3795 * Create a temporary big block chain that doesn't have
3796 * an associated directory entry. This temporary chain will be
3797 * used to copy data from small blocks to big blocks.
3798 */
3799 bbTempChain = BlockChainStream_Construct(This,
3800 &bbHeadOfChain,
3801 DIRENTRY_NULL);
3802 if(!bbTempChain) return NULL;
3803 /*
3804 * Grow the big block chain.
3805 */
3806 size = SmallBlockChainStream_GetSize(*ppsbChain);
3807 BlockChainStream_SetSize(bbTempChain, size);
3808
3809 /*
3810 * Copy the contents of the small block chain to the big block chain
3811 * by small block size increments.
3812 */
3813 offset.u.LowPart = 0;
3814 offset.u.HighPart = 0;
3815 cbTotalRead.QuadPart = 0;
3816
3817 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3818 do
3819 {
3820 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3821 offset,
3822 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3823 buffer,
3824 &cbRead);
3825 if (FAILED(resRead))
3826 break;
3827
3828 if (cbRead > 0)
3829 {
3830 cbTotalRead.QuadPart += cbRead;
3831
3832 resWrite = BlockChainStream_WriteAt(bbTempChain,
3833 offset,
3834 cbRead,
3835 buffer,
3836 &cbWritten);
3837
3838 if (FAILED(resWrite))
3839 break;
3840
3841 offset.u.LowPart += cbRead;
3842 }
3843 } while (cbTotalRead.QuadPart < size.QuadPart);
3844 HeapFree(GetProcessHeap(),0,buffer);
3845
3846 size.u.HighPart = 0;
3847 size.u.LowPart = 0;
3848
3849 if (FAILED(resRead) || FAILED(resWrite))
3850 {
3851 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3852 BlockChainStream_SetSize(bbTempChain, size);
3853 BlockChainStream_Destroy(bbTempChain);
3854 return NULL;
3855 }
3856
3857 /*
3858 * Destroy the small block chain.
3859 */
3860 streamEntryRef = (*ppsbChain)->ownerDirEntry;
3861 SmallBlockChainStream_SetSize(*ppsbChain, size);
3862 SmallBlockChainStream_Destroy(*ppsbChain);
3863 *ppsbChain = 0;
3864
3865 /*
3866 * Change the directory entry. This chain is now a big block chain
3867 * and it doesn't reside in the small blocks chain anymore.
3868 */
3869 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3870
3871 streamEntry.startingBlock = bbHeadOfChain;
3872
3873 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3874
3875 /*
3876 * Destroy the temporary entryless big block chain.
3877 * Create a new big block chain associated with this entry.
3878 */
3879 BlockChainStream_Destroy(bbTempChain);
3880 bigBlockChain = BlockChainStream_Construct(This,
3881 NULL,
3882 streamEntryRef);
3883
3884 return bigBlockChain;
3885 }
3886
3887 /******************************************************************************
3888 * Storage32Impl_BigBlocksToSmallBlocks
3889 *
3890 * This method will convert a big block chain to a small block chain.
3891 * The big block chain will be destroyed on success.
3892 */
3893 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3894 StorageImpl* This,
3895 BlockChainStream** ppbbChain)
3896 {
3897 ULARGE_INTEGER size, offset, cbTotalRead;
3898 ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3899 DirRef streamEntryRef;
3900 HRESULT resWrite = S_OK, resRead;
3901 DirEntry streamEntry;
3902 BYTE* buffer;
3903 SmallBlockChainStream* sbTempChain;
3904
3905 TRACE("%p %p\n", This, ppbbChain);
3906
3907 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3908 DIRENTRY_NULL);
3909
3910 if(!sbTempChain)
3911 return NULL;
3912
3913 size = BlockChainStream_GetSize(*ppbbChain);
3914 SmallBlockChainStream_SetSize(sbTempChain, size);
3915
3916 offset.u.HighPart = 0;
3917 offset.u.LowPart = 0;
3918 cbTotalRead.QuadPart = 0;
3919 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3920 do
3921 {
3922 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3923 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3924 buffer, &cbRead);
3925
3926 if(FAILED(resRead))
3927 break;
3928
3929 if(cbRead > 0)
3930 {
3931 cbTotalRead.QuadPart += cbRead;
3932
3933 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3934 cbRead, buffer, &cbWritten);
3935
3936 if(FAILED(resWrite))
3937 break;
3938
3939 offset.u.LowPart += cbRead;
3940 }
3941 }while(cbTotalRead.QuadPart < size.QuadPart);
3942 HeapFree(GetProcessHeap(), 0, buffer);
3943
3944 size.u.HighPart = 0;
3945 size.u.LowPart = 0;
3946
3947 if(FAILED(resRead) || FAILED(resWrite))
3948 {
3949 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3950 SmallBlockChainStream_SetSize(sbTempChain, size);
3951 SmallBlockChainStream_Destroy(sbTempChain);
3952 return NULL;
3953 }
3954
3955 /* destroy the original big block chain */
3956 streamEntryRef = (*ppbbChain)->ownerDirEntry;
3957 BlockChainStream_SetSize(*ppbbChain, size);
3958 BlockChainStream_Destroy(*ppbbChain);
3959 *ppbbChain = NULL;
3960
3961 StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3962 streamEntry.startingBlock = sbHeadOfChain;
3963 StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3964
3965 SmallBlockChainStream_Destroy(sbTempChain);
3966 return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3967 }
3968
3969 static HRESULT CreateSnapshotFile(StorageBaseImpl* original, StorageBaseImpl **snapshot)
3970 {
3971 HRESULT hr;
3972 DirEntry parentData, snapshotData;
3973
3974 hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_DELETEONRELEASE,
3975 0, (IStorage**)snapshot);
3976
3977 if (SUCCEEDED(hr))
3978 {
3979 hr = StorageBaseImpl_ReadDirEntry(original,
3980 original->storageDirEntry, &parentData);
3981
3982 if (SUCCEEDED(hr))
3983 hr = StorageBaseImpl_ReadDirEntry((*snapshot),
3984 (*snapshot)->storageDirEntry, &snapshotData);
3985
3986 if (SUCCEEDED(hr))
3987 {
3988 memcpy(snapshotData.name, parentData.name, sizeof(snapshotData.name));
3989 snapshotData.sizeOfNameString = parentData.sizeOfNameString;
3990 snapshotData.stgType = parentData.stgType;
3991 snapshotData.clsid = parentData.clsid;
3992 snapshotData.ctime = parentData.ctime;
3993 snapshotData.mtime = parentData.mtime;
3994 hr = StorageBaseImpl_WriteDirEntry((*snapshot),
3995 (*snapshot)->storageDirEntry, &snapshotData);
3996 }
3997
3998 if