ReactOS  0.4.14-dev-614-gbfd8a84
storage32.c
Go to the documentation of this file.
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 
42 #include "windef.h"
43 #include "winbase.h"
44 #include "winnls.h"
45 #include "winuser.h"
46 #include "wine/debug.h"
47 
48 #include "storage32.h"
49 #include "ole2.h" /* For Write/ReadClassStm */
50 
51 #include "winreg.h"
52 #include "wine/wingdi16.h"
53 #include "compobj_private.h"
54 
56 
57 
58 /*
59  * These are signatures to detect the type of Document file.
60  */
61 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
62 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
63 
64 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
65 
66 
67 /****************************************************************************
68  * StorageInternalImpl definitions.
69  *
70  * Definition of the implementation structure for the IStorage interface.
71  * This one implements the IStorage interface for storage that are
72  * inside another storage.
73  */
74 typedef struct StorageInternalImpl
75 {
77 
78  /*
79  * Entry in the parent's stream tracking list
80  */
82 
85 
86 static const IStorageVtbl StorageInternalImpl_Vtbl;
88 
89 typedef struct TransactedDirEntry
90 {
91  /* If applicable, a reference to the original DirEntry in the transacted
92  * parent. If this is a newly-created entry, DIRENTRY_NULL. */
94 
95  /* True if this entry is being used. */
97 
98  /* True if data is up to date. */
100 
101  /* True if this entry has been modified. */
103 
104  /* True if this entry's stream has been modified. */
106 
107  /* True if this entry has been deleted in the transacted storage, but the
108  * delete has not yet been committed. */
110 
111  /* If this entry's stream has been modified, a reference to where the stream
112  * is stored in the snapshot file. */
114 
115  /* This directory entry's data, including any changes that have been made. */
117 
118  /* A reference to the parent of this node. This is only valid while we are
119  * committing changes. */
121 
122  /* A reference to a newly-created entry in the transacted parent. This is
123  * always equal to transactedParentEntry except when committing changes. */
126 
127 
128 /****************************************************************************
129  * Transacted storage object.
130  */
132 {
134 
135  /*
136  * Modified streams are temporarily saved to the scratch file.
137  */
139 
140  /* The directory structure is kept here, so that we can track how these
141  * entries relate to those in the parent storage. */
145 
146  /*
147  * Changes are committed to the transacted parent.
148  */
150 
151  /* The transaction signature from when we last committed */
154 
155 static const IStorageVtbl TransactedSnapshotImpl_Vtbl;
157 
158 typedef struct TransactedSharedImpl
159 {
161 
162  /*
163  * Snapshot and uncommitted changes go here.
164  */
166 
167  /*
168  * Changes are committed to the transacted parent.
169  */
171 
172  /* The transaction signature from when we last committed */
175 
176 
177 /****************************************************************************
178  * BlockChainStream definitions.
179  *
180  * The BlockChainStream class is a utility class that is used to create an
181  * abstraction of the big block chains in the storage file.
182  */
183 
185 {
186  /* This represents a range of blocks that happen reside in consecutive sectors. */
190 };
191 
192 typedef struct BlockChainBlock
193 {
200 
202 {
213 };
214 
215 /* Returns the number of blocks that comprises this chain.
216  * This is not the size of the stream as the last block may not be full!
217  */
219 {
220  return This->numBlocks;
221 }
222 
230 
231 
232 /****************************************************************************
233  * SmallBlockChainStream definitions.
234  *
235  * The SmallBlockChainStream class is a utility class that is used to create an
236  * abstraction of the small block chains in the storage file.
237  */
238 
240 {
244 };
245 
252 
253 
254 /************************************************************************
255  * STGM Functions
256  ***********************************************************************/
257 
258 /************************************************************************
259  * This method validates an STGM parameter that can contain the values below
260  *
261  * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
262  * The stgm values contained in 0xffff0000 are bitmasks.
263  *
264  * STGM_DIRECT 0x00000000
265  * STGM_TRANSACTED 0x00010000
266  * STGM_SIMPLE 0x08000000
267  *
268  * STGM_READ 0x00000000
269  * STGM_WRITE 0x00000001
270  * STGM_READWRITE 0x00000002
271  *
272  * STGM_SHARE_DENY_NONE 0x00000040
273  * STGM_SHARE_DENY_READ 0x00000030
274  * STGM_SHARE_DENY_WRITE 0x00000020
275  * STGM_SHARE_EXCLUSIVE 0x00000010
276  *
277  * STGM_PRIORITY 0x00040000
278  * STGM_DELETEONRELEASE 0x04000000
279  *
280  * STGM_CREATE 0x00001000
281  * STGM_CONVERT 0x00020000
282  * STGM_FAILIFTHERE 0x00000000
283  *
284  * STGM_NOSCRATCH 0x00100000
285  * STGM_NOSNAPSHOT 0x00200000
286  */
288 {
289  DWORD access = STGM_ACCESS_MODE(stgm);
290  DWORD share = STGM_SHARE_MODE(stgm);
291  DWORD create = STGM_CREATE_MODE(stgm);
292 
293  if (stgm&~STGM_KNOWN_FLAGS)
294  {
295  ERR("unknown flags %08x\n", stgm);
296  return E_FAIL;
297  }
298 
299  switch (access)
300  {
301  case STGM_READ:
302  case STGM_WRITE:
303  case STGM_READWRITE:
304  break;
305  default:
306  return E_FAIL;
307  }
308 
309  switch (share)
310  {
315  break;
316  case 0:
317  if (!(stgm & STGM_TRANSACTED))
318  return E_FAIL;
319  break;
320  default:
321  return E_FAIL;
322  }
323 
324  switch (create)
325  {
326  case STGM_CREATE:
327  case STGM_FAILIFTHERE:
328  break;
329  default:
330  return E_FAIL;
331  }
332 
333  /*
334  * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
335  */
336  if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
337  return E_FAIL;
338 
339  /*
340  * STGM_CREATE | STGM_CONVERT
341  * if both are false, STGM_FAILIFTHERE is set to TRUE
342  */
343  if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
344  return E_FAIL;
345 
346  /*
347  * STGM_NOSCRATCH requires STGM_TRANSACTED
348  */
349  if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
350  return E_FAIL;
351 
352  /*
353  * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
354  * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
355  */
356  if ( (stgm & STGM_NOSNAPSHOT) &&
357  (!(stgm & STGM_TRANSACTED) ||
358  share == STGM_SHARE_EXCLUSIVE ||
359  share == STGM_SHARE_DENY_WRITE) )
360  return E_FAIL;
361 
362  return S_OK;
363 }
364 
365 /************************************************************************
366  * GetShareModeFromSTGM
367  *
368  * This method will return a share mode flag from a STGM value.
369  * The STGM value is assumed valid.
370  */
372 {
373  switch (STGM_SHARE_MODE(stgm))
374  {
375  case 0:
376  assert(stgm & STGM_TRANSACTED);
377  /* fall-through */
381  return FILE_SHARE_WRITE;
384  return FILE_SHARE_READ;
385  }
386  ERR("Invalid share mode!\n");
387  assert(0);
388  return 0;
389 }
390 
391 /************************************************************************
392  * GetAccessModeFromSTGM
393  *
394  * This method will return an access mode flag from a STGM value.
395  * The STGM value is assumed valid.
396  */
398 {
399  switch (STGM_ACCESS_MODE(stgm))
400  {
401  case STGM_READ:
402  return GENERIC_READ;
403  case STGM_WRITE:
404  case STGM_READWRITE:
405  return GENERIC_READ | GENERIC_WRITE;
406  }
407  ERR("Invalid access mode!\n");
408  assert(0);
409  return 0;
410 }
411 
412 /************************************************************************
413  * GetCreationModeFromSTGM
414  *
415  * This method will return a creation mode flag from a STGM value.
416  * The STGM value is assumed valid.
417  */
419 {
420  switch(STGM_CREATE_MODE(stgm))
421  {
422  case STGM_CREATE:
423  return CREATE_ALWAYS;
424  case STGM_CONVERT:
425  FIXME("STGM_CONVERT not implemented!\n");
426  return CREATE_NEW;
427  case STGM_FAILIFTHERE:
428  return CREATE_NEW;
429  }
430  ERR("Invalid create mode!\n");
431  assert(0);
432  return 0;
433 }
434 
435 
436 /************************************************************************
437  * IDirectWriterLock implementation
438  ***********************************************************************/
439 
441 {
442  return CONTAINING_RECORD(iface, StorageBaseImpl, IDirectWriterLock_iface);
443 }
444 
446 {
448  return IStorage_QueryInterface(&This->IStorage_iface, riid, obj);
449 }
450 
452 {
454  return IStorage_AddRef(&This->IStorage_iface);
455 }
456 
458 {
460  return IStorage_Release(&This->IStorage_iface);
461 }
462 
464 {
466  FIXME("(%p)->(%d): stub\n", This, timeout);
467  return E_NOTIMPL;
468 }
469 
471 {
473  FIXME("(%p): stub\n", This);
474  return E_NOTIMPL;
475 }
476 
478 {
480  FIXME("(%p): stub\n", This);
481  return E_NOTIMPL;
482 }
483 
484 static const IDirectWriterLockVtbl DirectWriterLockVtbl =
485 {
492 };
493 
494 
495 /************************************************************************
496  * StorageBaseImpl implementation : Tree helper functions
497  ***********************************************************************/
498 
499 /****************************************************************************
500  *
501  * Internal Method
502  *
503  * Case insensitive comparison of DirEntry.name by first considering
504  * their size.
505  *
506  * Returns <0 when name1 < name2
507  * >0 when name1 > name2
508  * 0 when name1 == name2
509  */
511  const OLECHAR *name1,
512  const OLECHAR *name2)
513 {
514  LONG diff = lstrlenW(name1) - lstrlenW(name2);
515 
516  while (diff == 0 && *name1 != 0)
517  {
518  /*
519  * We compare the string themselves only when they are of the same length
520  */
521  diff = towupper(*name1++) - towupper(*name2++);
522  }
523 
524  return diff;
525 }
526 
527 /****************************************************************************
528  *
529  * Internal Method
530  *
531  * Find and read the element of a storage with the given name.
532  */
533 static DirRef findElement(StorageBaseImpl *storage, DirRef storageEntry,
534  const OLECHAR *name, DirEntry *data)
535 {
536  DirRef currentEntry;
537 
538  /* Read the storage entry to find the root of the tree. */
539  StorageBaseImpl_ReadDirEntry(storage, storageEntry, data);
540 
541  currentEntry = data->dirRootEntry;
542 
543  while (currentEntry != DIRENTRY_NULL)
544  {
545  LONG cmp;
546 
547  StorageBaseImpl_ReadDirEntry(storage, currentEntry, data);
548 
550 
551  if (cmp == 0)
552  /* found it */
553  break;
554 
555  else if (cmp < 0)
556  currentEntry = data->leftChild;
557 
558  else if (cmp > 0)
559  currentEntry = data->rightChild;
560  }
561 
562  return currentEntry;
563 }
564 
565 /****************************************************************************
566  *
567  * Internal Method
568  *
569  * Find and read the binary tree parent of the element with the given name.
570  *
571  * If there is no such element, find a place where it could be inserted and
572  * return STG_E_FILENOTFOUND.
573  */
574 static HRESULT findTreeParent(StorageBaseImpl *storage, DirRef storageEntry,
575  const OLECHAR *childName, DirEntry *parentData, DirRef *parentEntry,
576  ULONG *relation)
577 {
578  DirRef childEntry;
579  DirEntry childData;
580 
581  /* Read the storage entry to find the root of the tree. */
582  StorageBaseImpl_ReadDirEntry(storage, storageEntry, parentData);
583 
584  *parentEntry = storageEntry;
585  *relation = DIRENTRY_RELATION_DIR;
586 
587  childEntry = parentData->dirRootEntry;
588 
589  while (childEntry != DIRENTRY_NULL)
590  {
591  LONG cmp;
592 
593  StorageBaseImpl_ReadDirEntry(storage, childEntry, &childData);
594 
595  cmp = entryNameCmp(childName, childData.name);
596 
597  if (cmp == 0)
598  /* found it */
599  break;
600 
601  else if (cmp < 0)
602  {
603  *parentData = childData;
604  *parentEntry = childEntry;
605  *relation = DIRENTRY_RELATION_PREVIOUS;
606 
607  childEntry = parentData->leftChild;
608  }
609 
610  else if (cmp > 0)
611  {
612  *parentData = childData;
613  *parentEntry = childEntry;
614  *relation = DIRENTRY_RELATION_NEXT;
615 
616  childEntry = parentData->rightChild;
617  }
618  }
619 
620  if (childEntry == DIRENTRY_NULL)
621  return STG_E_FILENOTFOUND;
622  else
623  return S_OK;
624 }
625 
626 static void setEntryLink(DirEntry *entry, ULONG relation, DirRef new_target)
627 {
628  switch (relation)
629  {
631  entry->leftChild = new_target;
632  break;
634  entry->rightChild = new_target;
635  break;
637  entry->dirRootEntry = new_target;
638  break;
639  default:
640  assert(0);
641  }
642 }
643 
644 /****************************************************************************
645  *
646  * Internal Method
647  *
648  * Add a directory entry to a storage
649  */
652  DirRef parentStorageIndex,
653  DirRef newEntryIndex)
654 {
655  DirEntry currentEntry;
656  DirEntry newEntry;
657 
658  /*
659  * Read the inserted entry
660  */
662  newEntryIndex,
663  &newEntry);
664 
665  /*
666  * Read the storage entry
667  */
669  parentStorageIndex,
670  &currentEntry);
671 
672  if (currentEntry.dirRootEntry != DIRENTRY_NULL)
673  {
674  /*
675  * The root storage contains some element, therefore, start the research
676  * for the appropriate location.
677  */
678  BOOL found = FALSE;
679  DirRef current, next, previous, currentEntryId;
680 
681  /*
682  * Keep a reference to the root of the storage's element tree
683  */
684  currentEntryId = currentEntry.dirRootEntry;
685 
686  /*
687  * Read
688  */
690  currentEntry.dirRootEntry,
691  &currentEntry);
692 
693  previous = currentEntry.leftChild;
694  next = currentEntry.rightChild;
695  current = currentEntryId;
696 
697  while (!found)
698  {
699  LONG diff = entryNameCmp( newEntry.name, currentEntry.name);
700 
701  if (diff < 0)
702  {
703  if (previous != DIRENTRY_NULL)
704  {
706  previous,
707  &currentEntry);
708  current = previous;
709  }
710  else
711  {
712  currentEntry.leftChild = newEntryIndex;
714  current,
715  &currentEntry);
716  found = TRUE;
717  }
718  }
719  else if (diff > 0)
720  {
721  if (next != DIRENTRY_NULL)
722  {
724  next,
725  &currentEntry);
726  current = next;
727  }
728  else
729  {
730  currentEntry.rightChild = newEntryIndex;
732  current,
733  &currentEntry);
734  found = TRUE;
735  }
736  }
737  else
738  {
739  /*
740  * Trying to insert an item with the same name in the
741  * subtree structure.
742  */
744  }
745 
746  previous = currentEntry.leftChild;
747  next = currentEntry.rightChild;
748  }
749  }
750  else
751  {
752  /*
753  * The storage is empty, make the new entry the root of its element tree
754  */
755  currentEntry.dirRootEntry = newEntryIndex;
757  parentStorageIndex,
758  &currentEntry);
759  }
760 
761  return S_OK;
762 }
763 
764 /*************************************************************************
765  *
766  * Internal Method
767  *
768  * This method removes a directory entry from its parent storage tree without
769  * freeing any resources attached to it.
770  */
773  DirRef parentStorageIndex,
774  DirRef deletedIndex)
775 {
776  DirEntry entryToDelete;
777  DirEntry parentEntry;
778  DirRef parentEntryRef;
779  ULONG typeOfRelation;
780  HRESULT hr;
781 
782  hr = StorageBaseImpl_ReadDirEntry(This, deletedIndex, &entryToDelete);
783 
784  if (hr != S_OK)
785  return hr;
786 
787  /*
788  * Find the element that links to the one we want to delete.
789  */
790  hr = findTreeParent(This, parentStorageIndex, entryToDelete.name,
791  &parentEntry, &parentEntryRef, &typeOfRelation);
792 
793  if (hr != S_OK)
794  return hr;
795 
796  if (entryToDelete.leftChild != DIRENTRY_NULL)
797  {
798  /*
799  * Replace the deleted entry with its left child
800  */
801  setEntryLink(&parentEntry, typeOfRelation, entryToDelete.leftChild);
802 
804  This,
805  parentEntryRef,
806  &parentEntry);
807  if(FAILED(hr))
808  {
809  return hr;
810  }
811 
812  if (entryToDelete.rightChild != DIRENTRY_NULL)
813  {
814  /*
815  * We need to reinsert the right child somewhere. We already know it and
816  * its children are greater than everything in the left tree, so we
817  * insert it at the rightmost point in the left tree.
818  */
819  DirRef newRightChildParent = entryToDelete.leftChild;
820  DirEntry newRightChildParentEntry;
821 
822  do
823  {
825  This,
826  newRightChildParent,
827  &newRightChildParentEntry);
828  if (FAILED(hr))
829  {
830  return hr;
831  }
832 
833  if (newRightChildParentEntry.rightChild != DIRENTRY_NULL)
834  newRightChildParent = newRightChildParentEntry.rightChild;
835  } while (newRightChildParentEntry.rightChild != DIRENTRY_NULL);
836 
837  newRightChildParentEntry.rightChild = entryToDelete.rightChild;
838 
840  This,
841  newRightChildParent,
842  &newRightChildParentEntry);
843  if (FAILED(hr))
844  {
845  return hr;
846  }
847  }
848  }
849  else
850  {
851  /*
852  * Replace the deleted entry with its right child
853  */
854  setEntryLink(&parentEntry, typeOfRelation, entryToDelete.rightChild);
855 
857  This,
858  parentEntryRef,
859  &parentEntry);
860  if(FAILED(hr))
861  {
862  return hr;
863  }
864  }
865 
866  return hr;
867 }
868 
869 
870 /************************************************************************
871  * IEnumSTATSTGImpl implementation for StorageBaseImpl_EnumElements
872  ***********************************************************************/
873 
874 /*
875  * IEnumSTATSTGImpl definitions.
876  *
877  * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
878  * This class allows iterating through the content of a storage and finding
879  * specific items inside it.
880  */
882 {
884 
885  LONG ref; /* Reference count */
886  StorageBaseImpl* parentStorage; /* Reference to the parent storage */
887  DirRef storageDirEntry; /* Directory entry of the storage to enumerate */
888 
889  WCHAR name[DIRENTRY_NAME_MAX_LEN]; /* The most recent name visited */
890 };
891 
893 {
894  return CONTAINING_RECORD(iface, IEnumSTATSTGImpl, IEnumSTATSTG_iface);
895 }
896 
898 {
899  IStorage_Release(&This->parentStorage->IStorage_iface);
901 }
902 
904  IEnumSTATSTG* iface,
905  REFIID riid,
906  void** ppvObject)
907 {
909 
910  TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), ppvObject);
911 
912  if (ppvObject==0)
913  return E_INVALIDARG;
914 
915  *ppvObject = 0;
916 
917  if (IsEqualGUID(&IID_IUnknown, riid) ||
919  {
920  *ppvObject = &This->IEnumSTATSTG_iface;
921  IEnumSTATSTG_AddRef(&This->IEnumSTATSTG_iface);
922  TRACE("<-- %p\n", *ppvObject);
923  return S_OK;
924  }
925 
926  TRACE("<-- E_NOINTERFACE\n");
927  return E_NOINTERFACE;
928 }
929 
931  IEnumSTATSTG* iface)
932 {
934  return InterlockedIncrement(&This->ref);
935 }
936 
938  IEnumSTATSTG* iface)
939 {
941 
942  ULONG newRef;
943 
944  newRef = InterlockedDecrement(&This->ref);
945 
946  if (newRef==0)
947  {
949  }
950 
951  return newRef;
952 }
953 
956  DirRef *ref)
957 {
959  DirRef searchNode;
960  DirEntry entry;
961  HRESULT hr;
962  WCHAR result_name[DIRENTRY_NAME_MAX_LEN];
963 
964  TRACE("%p,%p\n", This, ref);
965 
966  hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
967  This->parentStorage->storageDirEntry, &entry);
968  searchNode = entry.dirRootEntry;
969 
970  while (SUCCEEDED(hr) && searchNode != DIRENTRY_NULL)
971  {
972  hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, searchNode, &entry);
973 
974  if (SUCCEEDED(hr))
975  {
976  LONG diff = entryNameCmp( entry.name, This->name);
977 
978  if (diff <= 0)
979  {
980  searchNode = entry.rightChild;
981  }
982  else
983  {
984  result = searchNode;
985  memcpy(result_name, entry.name, sizeof(result_name));
986  searchNode = entry.leftChild;
987  }
988  }
989  }
990 
991  if (SUCCEEDED(hr))
992  {
993  *ref = result;
994  if (result != DIRENTRY_NULL)
995  memcpy(This->name, result_name, sizeof(result_name));
996  }
997 
998  TRACE("<-- %08x\n", hr);
999  return hr;
1000 }
1001 
1003  IEnumSTATSTG* iface,
1004  ULONG celt,
1005  STATSTG* rgelt,
1006  ULONG* pceltFetched)
1007 {
1009 
1010  DirEntry currentEntry;
1011  STATSTG* currentReturnStruct = rgelt;
1012  ULONG objectFetched = 0;
1013  DirRef currentSearchNode;
1014  HRESULT hr=S_OK;
1015 
1016  TRACE("%p,%u,%p,%p\n", iface, celt, rgelt, pceltFetched);
1017 
1018  if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
1019  return E_INVALIDARG;
1020 
1021  if (This->parentStorage->reverted)
1022  {
1023  TRACE("<-- STG_E_REVERTED\n");
1024  return STG_E_REVERTED;
1025  }
1026 
1027  /*
1028  * To avoid the special case, get another pointer to a ULONG value if
1029  * the caller didn't supply one.
1030  */
1031  if (pceltFetched==0)
1032  pceltFetched = &objectFetched;
1033 
1034  /*
1035  * Start the iteration, we will iterate until we hit the end of the
1036  * linked list or until we hit the number of items to iterate through
1037  */
1038  *pceltFetched = 0;
1039 
1040  while ( *pceltFetched < celt )
1041  {
1042  hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
1043 
1044  if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
1045  {
1046  memset(currentReturnStruct, 0, sizeof(*currentReturnStruct));
1047  break;
1048  }
1049 
1050  /*
1051  * Read the entry from the storage.
1052  */
1053  hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
1054  currentSearchNode,
1055  &currentEntry);
1056  if (FAILED(hr)) break;
1057 
1058  /*
1059  * Copy the information to the return buffer.
1060  */
1061  StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
1062  currentReturnStruct,
1063  &currentEntry,
1064  STATFLAG_DEFAULT);
1065 
1066  /*
1067  * Step to the next item in the iteration
1068  */
1069  (*pceltFetched)++;
1070  currentReturnStruct++;
1071  }
1072 
1073  if (SUCCEEDED(hr) && *pceltFetched != celt)
1074  hr = S_FALSE;
1075 
1076  TRACE("<-- %08x (asked %u, got %u)\n", hr, celt, *pceltFetched);
1077  return hr;
1078 }
1079 
1080 
1082  IEnumSTATSTG* iface,
1083  ULONG celt)
1084 {
1086 
1087  ULONG objectFetched = 0;
1088  DirRef currentSearchNode;
1089  HRESULT hr=S_OK;
1090 
1091  TRACE("%p,%u\n", iface, celt);
1092 
1093  if (This->parentStorage->reverted)
1094  {
1095  TRACE("<-- STG_E_REVERTED\n");
1096  return STG_E_REVERTED;
1097  }
1098 
1099  while ( (objectFetched < celt) )
1100  {
1101  hr = IEnumSTATSTGImpl_GetNextRef(This, &currentSearchNode);
1102 
1103  if (FAILED(hr) || currentSearchNode == DIRENTRY_NULL)
1104  break;
1105 
1106  objectFetched++;
1107  }
1108 
1109  if (SUCCEEDED(hr) && objectFetched != celt)
1110  return S_FALSE;
1111 
1112  TRACE("<-- %08x\n", hr);
1113  return hr;
1114 }
1115 
1117  IEnumSTATSTG* iface)
1118 {
1120 
1121  TRACE("%p\n", iface);
1122 
1123  if (This->parentStorage->reverted)
1124  {
1125  TRACE("<-- STG_E_REVERTED\n");
1126  return STG_E_REVERTED;
1127  }
1128 
1129  This->name[0] = 0;
1130 
1131  return S_OK;
1132 }
1133 
1135 
1137  IEnumSTATSTG* iface,
1138  IEnumSTATSTG** ppenum)
1139 {
1141  IEnumSTATSTGImpl* newClone;
1142 
1143  TRACE("%p,%p\n", iface, ppenum);
1144 
1145  if (This->parentStorage->reverted)
1146  {
1147  TRACE("<-- STG_E_REVERTED\n");
1148  return STG_E_REVERTED;
1149  }
1150 
1151  if (ppenum==0)
1152  return E_INVALIDARG;
1153 
1154  newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
1155  This->storageDirEntry);
1156  if (!newClone)
1157  {
1158  *ppenum = NULL;
1159  return E_OUTOFMEMORY;
1160  }
1161 
1162  /*
1163  * The new clone enumeration must point to the same current node as
1164  * the old one.
1165  */
1166  memcpy(newClone->name, This->name, sizeof(newClone->name));
1167 
1168  *ppenum = &newClone->IEnumSTATSTG_iface;
1169 
1170  return S_OK;
1171 }
1172 
1173 /*
1174  * Virtual function table for the IEnumSTATSTGImpl class.
1175  */
1176 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
1177 {
1185 };
1186 
1188  StorageBaseImpl* parentStorage,
1189  DirRef storageDirEntry)
1190 {
1191  IEnumSTATSTGImpl* newEnumeration;
1192 
1193  newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
1194 
1195  if (newEnumeration)
1196  {
1197  newEnumeration->IEnumSTATSTG_iface.lpVtbl = &IEnumSTATSTGImpl_Vtbl;
1198  newEnumeration->ref = 1;
1199  newEnumeration->name[0] = 0;
1200 
1201  /*
1202  * We want to nail-down the reference to the storage in case the
1203  * enumeration out-lives the storage in the client application.
1204  */
1205  newEnumeration->parentStorage = parentStorage;
1206  IStorage_AddRef(&newEnumeration->parentStorage->IStorage_iface);
1207 
1208  newEnumeration->storageDirEntry = storageDirEntry;
1209  }
1210 
1211  return newEnumeration;
1212 }
1213 
1214 
1215 /************************************************************************
1216  * StorageBaseImpl implementation
1217  ***********************************************************************/
1218 
1220 {
1221  return CONTAINING_RECORD(iface, StorageBaseImpl, IStorage_iface);
1222 }
1223 
1224 /************************************************************************
1225  * StorageBaseImpl_QueryInterface (IUnknown)
1226  *
1227  * This method implements the common QueryInterface for all IStorage
1228  * implementations contained in this file.
1229  *
1230  * See Windows documentation for more details on IUnknown methods.
1231  */
1233  IStorage* iface,
1234  REFIID riid,
1235  void** ppvObject)
1236 {
1238 
1239  TRACE("%p,%s,%p\n", iface, debugstr_guid(riid), ppvObject);
1240 
1241  if (!ppvObject)
1242  return E_INVALIDARG;
1243 
1244  *ppvObject = 0;
1245 
1246  if (IsEqualGUID(&IID_IUnknown, riid) ||
1247  IsEqualGUID(&IID_IStorage, riid))
1248  {
1249  *ppvObject = &This->IStorage_iface;
1250  }
1251  else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
1252  {
1253  *ppvObject = &This->IPropertySetStorage_iface;
1254  }
1255  /* locking interface is reported for writer only */
1256  else if (IsEqualGUID(&IID_IDirectWriterLock, riid) && This->lockingrole == SWMR_Writer)
1257  {
1258  *ppvObject = &This->IDirectWriterLock_iface;
1259  }
1260  else
1261  {
1262  TRACE("<-- E_NOINTERFACE\n");
1263  return E_NOINTERFACE;
1264  }
1265 
1266  IStorage_AddRef(iface);
1267  TRACE("<-- %p\n", *ppvObject);
1268  return S_OK;
1269 }
1270 
1271 /************************************************************************
1272  * StorageBaseImpl_AddRef (IUnknown)
1273  *
1274  * This method implements the common AddRef for all IStorage
1275  * implementations contained in this file.
1276  *
1277  * See Windows documentation for more details on IUnknown methods.
1278  */
1280  IStorage* iface)
1281 {
1284 
1285  TRACE("(%p) AddRef to %d\n", This, ref);
1286 
1287  return ref;
1288 }
1289 
1290 /************************************************************************
1291  * StorageBaseImpl_Release (IUnknown)
1292  *
1293  * This method implements the common Release for all IStorage
1294  * implementations contained in this file.
1295  *
1296  * See Windows documentation for more details on IUnknown methods.
1297  */
1299  IStorage* iface)
1300 {
1302 
1304 
1305  TRACE("(%p) ReleaseRef to %d\n", This, ref);
1306 
1307  if (ref == 0)
1308  {
1309  /*
1310  * Since we are using a system of base-classes, we want to call the
1311  * destructor of the appropriate derived class. To do this, we are
1312  * using virtual functions to implement the destructor.
1313  */
1315  }
1316 
1317  return ref;
1318 }
1319 
1321  DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1322  SNB snbExclude, IStorage *pstgDest);
1323 
1325  DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
1326  SNB snbExclude, IStorage *pstgDest)
1327 {
1328  DirEntry data;
1329  HRESULT hr;
1330  BOOL skip = FALSE;
1331  IStorage *pstgTmp;
1332  IStream *pstrChild, *pstrTmp;
1333  STATSTG strStat;
1334 
1335  if (srcEntry == DIRENTRY_NULL)
1336  return S_OK;
1337 
1338  hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
1339 
1340  if (FAILED(hr))
1341  return hr;
1342 
1343  if ( snbExclude )
1344  {
1345  WCHAR **snb = snbExclude;
1346 
1347  while ( *snb != NULL && !skip )
1348  {
1349  if ( wcscmp(data.name, *snb) == 0 )
1350  skip = TRUE;
1351  ++snb;
1352  }
1353  }
1354 
1355  if (!skip)
1356  {
1357  if (data.stgType == STGTY_STORAGE && !skip_storage)
1358  {
1359  /*
1360  * create a new storage in destination storage
1361  */
1362  hr = IStorage_CreateStorage( pstgDest, data.name,
1364  0, 0,
1365  &pstgTmp );
1366 
1367  /*
1368  * if it already exist, don't create a new one use this one
1369  */
1370  if (hr == STG_E_FILEALREADYEXISTS)
1371  {
1372  hr = IStorage_OpenStorage( pstgDest, data.name, NULL,
1374  NULL, 0, &pstgTmp );
1375  }
1376 
1377  if (SUCCEEDED(hr))
1378  {
1379  hr = StorageBaseImpl_CopyStorageEntryTo( This, srcEntry, skip_storage,
1380  skip_stream, NULL, pstgTmp );
1381 
1382  IStorage_Release(pstgTmp);
1383  }
1384  }
1385  else if (data.stgType == STGTY_STREAM && !skip_stream)
1386  {
1387  /*
1388  * create a new stream in destination storage. If the stream already
1389  * exist, it will be deleted and a new one will be created.
1390  */
1391  hr = IStorage_CreateStream( pstgDest, data.name,
1393  0, 0, &pstrTmp );
1394 
1395  /*
1396  * open child stream storage. This operation must succeed even if the
1397  * stream is already open, so we use internal functions to do it.
1398  */
1399  if (hr == S_OK)
1400  {
1402 
1403  if (streamimpl)
1404  {
1405  pstrChild = &streamimpl->IStream_iface;
1406  if (pstrChild)
1407  IStream_AddRef(pstrChild);
1408  }
1409  else
1410  {
1411  pstrChild = NULL;
1412  hr = E_OUTOFMEMORY;
1413  }
1414  }
1415 
1416  if (hr == S_OK)
1417  {
1418  /*
1419  * Get the size of the source stream
1420  */
1421  IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1422 
1423  /*
1424  * Set the size of the destination stream.
1425  */
1426  IStream_SetSize(pstrTmp, strStat.cbSize);
1427 
1428  /*
1429  * do the copy
1430  */
1431  hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1432  NULL, NULL );
1433 
1434  IStream_Release( pstrChild );
1435  }
1436 
1437  IStream_Release( pstrTmp );
1438  }
1439  }
1440 
1441  /* copy siblings */
1442  if (SUCCEEDED(hr))
1443  hr = StorageBaseImpl_CopyChildEntryTo( This, data.leftChild, skip_storage,
1444  skip_stream, snbExclude, pstgDest );
1445 
1446  if (SUCCEEDED(hr))
1447  hr = StorageBaseImpl_CopyChildEntryTo( This, data.rightChild, skip_storage,
1448  skip_stream, snbExclude, pstgDest );
1449 
1450  TRACE("<-- %08x\n", hr);
1451  return hr;
1452 }
1453 
1455 {
1456  StgStreamImpl *strm;
1457 
1458  TRACE("%p,%d\n", stg, streamEntry);
1459 
1460  LIST_FOR_EACH_ENTRY(strm, &stg->strmHead, StgStreamImpl, StrmListEntry)
1461  {
1462  if (strm->dirEntry == streamEntry)
1463  {
1464  return TRUE;
1465  }
1466  }
1467 
1468  return FALSE;
1469 }
1470 
1472 {
1473  StorageInternalImpl *childstg;
1474 
1475  TRACE("%p,%d\n", stg, storageEntry);
1476 
1477  LIST_FOR_EACH_ENTRY(childstg, &stg->storageHead, StorageInternalImpl, ParentListEntry)
1478  {
1479  if (childstg->base.storageDirEntry == storageEntry)
1480  {
1481  return TRUE;
1482  }
1483  }
1484 
1485  return FALSE;
1486 }
1487 
1488 /************************************************************************
1489  * StorageBaseImpl_OpenStream (IStorage)
1490  *
1491  * This method will open the specified stream object from the current storage.
1492  *
1493  * See Windows documentation for more details on IStorage methods.
1494  */
1496  IStorage* iface,
1497  const OLECHAR* pwcsName, /* [string][in] */
1498  void* reserved1, /* [unique][in] */
1499  DWORD grfMode, /* [in] */
1500  DWORD reserved2, /* [in] */
1501  IStream** ppstm) /* [out] */
1502 {
1504  StgStreamImpl* newStream;
1505  DirEntry currentEntry;
1506  DirRef streamEntryRef;
1508 
1509  TRACE("(%p, %s, %p, %x, %d, %p)\n",
1510  iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
1511 
1512  if ( (pwcsName==NULL) || (ppstm==0) )
1513  {
1514  res = E_INVALIDARG;
1515  goto end;
1516  }
1517 
1518  *ppstm = NULL;
1519 
1520  if ( FAILED( validateSTGM(grfMode) ) ||
1522  {
1524  goto end;
1525  }
1526 
1527  /*
1528  * As documented.
1529  */
1530  if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
1531  {
1533  goto end;
1534  }
1535 
1536  if (This->reverted)
1537  {
1538  res = STG_E_REVERTED;
1539  goto end;
1540  }
1541 
1542  /*
1543  * Check that we're compatible with the parent's storage mode, but
1544  * only if we are not in transacted mode
1545  */
1546  if(!(This->openFlags & STGM_TRANSACTED)) {
1547  if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1548  {
1550  goto end;
1551  }
1552  }
1553 
1554  /*
1555  * Search for the element with the given name
1556  */
1557  streamEntryRef = findElement(
1558  This,
1559  This->storageDirEntry,
1560  pwcsName,
1561  &currentEntry);
1562 
1563  /*
1564  * If it was found, construct the stream object and return a pointer to it.
1565  */
1566  if ( (streamEntryRef!=DIRENTRY_NULL) &&
1567  (currentEntry.stgType==STGTY_STREAM) )
1568  {
1569  if (StorageBaseImpl_IsStreamOpen(This, streamEntryRef))
1570  {
1571  /* A single stream cannot be opened a second time. */
1573  goto end;
1574  }
1575 
1576  newStream = StgStreamImpl_Construct(This, grfMode, streamEntryRef);
1577 
1578  if (newStream)
1579  {
1580  newStream->grfMode = grfMode;
1581  *ppstm = &newStream->IStream_iface;
1582 
1583  IStream_AddRef(*ppstm);
1584 
1585  res = S_OK;
1586  goto end;
1587  }
1588 
1589  res = E_OUTOFMEMORY;
1590  goto end;
1591  }
1592 
1594 
1595 end:
1596  if (res == S_OK)
1597  TRACE("<-- IStream %p\n", *ppstm);
1598  TRACE("<-- %08x\n", res);
1599  return res;
1600 }
1601 
1602 /************************************************************************
1603  * StorageBaseImpl_OpenStorage (IStorage)
1604  *
1605  * This method will open a new storage object from the current storage.
1606  *
1607  * See Windows documentation for more details on IStorage methods.
1608  */
1610  IStorage* iface,
1611  const OLECHAR* pwcsName, /* [string][unique][in] */
1612  IStorage* pstgPriority, /* [unique][in] */
1613  DWORD grfMode, /* [in] */
1614  SNB snbExclude, /* [unique][in] */
1615  DWORD reserved, /* [in] */
1616  IStorage** ppstg) /* [out] */
1617 {
1619  StorageInternalImpl* newStorage;
1620  StorageBaseImpl* newTransactedStorage;
1621  DirEntry currentEntry;
1622  DirRef storageEntryRef;
1624 
1625  TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
1626  iface, debugstr_w(pwcsName), pstgPriority,
1627  grfMode, snbExclude, reserved, ppstg);
1628 
1629  if ((pwcsName==NULL) || (ppstg==0) )
1630  {
1631  res = E_INVALIDARG;
1632  goto end;
1633  }
1634 
1635  if (This->openFlags & STGM_SIMPLE)
1636  {
1638  goto end;
1639  }
1640 
1641  /* as documented */
1642  if (snbExclude != NULL)
1643  {
1645  goto end;
1646  }
1647 
1648  if ( FAILED( validateSTGM(grfMode) ))
1649  {
1651  goto end;
1652  }
1653 
1654  /*
1655  * As documented.
1656  */
1657  if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
1658  (grfMode & STGM_DELETEONRELEASE) ||
1659  (grfMode & STGM_PRIORITY) )
1660  {
1662  goto end;
1663  }
1664 
1665  if (This->reverted)
1666  return STG_E_REVERTED;
1667 
1668  /*
1669  * Check that we're compatible with the parent's storage mode,
1670  * but only if we are not transacted
1671  */
1672  if(!(This->openFlags & STGM_TRANSACTED)) {
1673  if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1674  {
1676  goto end;
1677  }
1678  }
1679 
1680  *ppstg = NULL;
1681 
1682  storageEntryRef = findElement(
1683  This,
1684  This->storageDirEntry,
1685  pwcsName,
1686  &currentEntry);
1687 
1688  if ( (storageEntryRef!=DIRENTRY_NULL) &&
1689  (currentEntry.stgType==STGTY_STORAGE) )
1690  {
1691  if (StorageBaseImpl_IsStorageOpen(This, storageEntryRef))
1692  {
1693  /* A single storage cannot be opened a second time. */
1695  goto end;
1696  }
1697 
1698  newStorage = StorageInternalImpl_Construct(
1699  This,
1700  grfMode,
1701  storageEntryRef);
1702 
1703  if (newStorage != 0)
1704  {
1705  if (grfMode & STGM_TRANSACTED)
1706  {
1707  res = Storage_ConstructTransacted(&newStorage->base, FALSE, &newTransactedStorage);
1708 
1709  if (FAILED(res))
1710  {
1711  HeapFree(GetProcessHeap(), 0, newStorage);
1712  goto end;
1713  }
1714 
1715  *ppstg = &newTransactedStorage->IStorage_iface;
1716  }
1717  else
1718  {
1719  *ppstg = &newStorage->base.IStorage_iface;
1720  }
1721 
1722  list_add_tail(&This->storageHead, &newStorage->ParentListEntry);
1723 
1724  res = S_OK;
1725  goto end;
1726  }
1727 
1729  goto end;
1730  }
1731 
1733 
1734 end:
1735  TRACE("<-- %08x\n", res);
1736  return res;
1737 }
1738 
1739 /************************************************************************
1740  * StorageBaseImpl_EnumElements (IStorage)
1741  *
1742  * This method will create an enumerator object that can be used to
1743  * retrieve information about all the elements in the storage object.
1744  *
1745  * See Windows documentation for more details on IStorage methods.
1746  */
1748  IStorage* iface,
1749  DWORD reserved1, /* [in] */
1750  void* reserved2, /* [size_is][unique][in] */
1751  DWORD reserved3, /* [in] */
1752  IEnumSTATSTG** ppenum) /* [out] */
1753 {
1755  IEnumSTATSTGImpl* newEnum;
1756 
1757  TRACE("(%p, %d, %p, %d, %p)\n",
1758  iface, reserved1, reserved2, reserved3, ppenum);
1759 
1760  if (!ppenum)
1761  return E_INVALIDARG;
1762 
1763  if (This->reverted)
1764  return STG_E_REVERTED;
1765 
1766  newEnum = IEnumSTATSTGImpl_Construct(
1767  This,
1768  This->storageDirEntry);
1769 
1770  if (newEnum)
1771  {
1772  *ppenum = &newEnum->IEnumSTATSTG_iface;
1773  return S_OK;
1774  }
1775 
1776  return E_OUTOFMEMORY;
1777 }
1778 
1779 /************************************************************************
1780  * StorageBaseImpl_Stat (IStorage)
1781  *
1782  * This method will retrieve information about this storage object.
1783  *
1784  * See Windows documentation for more details on IStorage methods.
1785  */
1787  IStorage* iface,
1788  STATSTG* pstatstg, /* [out] */
1789  DWORD grfStatFlag) /* [in] */
1790 {
1792  DirEntry currentEntry;
1794 
1795  TRACE("(%p, %p, %x)\n",
1796  iface, pstatstg, grfStatFlag);
1797 
1798  if (!pstatstg)
1799  {
1800  res = E_INVALIDARG;
1801  goto end;
1802  }
1803 
1804  if (This->reverted)
1805  {
1806  res = STG_E_REVERTED;
1807  goto end;
1808  }
1809 
1811  This,
1812  This->storageDirEntry,
1813  &currentEntry);
1814 
1815  if (SUCCEEDED(res))
1816  {
1818  This,
1819  pstatstg,
1820  &currentEntry,
1821  grfStatFlag);
1822 
1823  pstatstg->grfMode = This->openFlags;
1824  pstatstg->grfStateBits = This->stateBits;
1825  }
1826 
1827 end:
1828  if (res == S_OK)
1829  {
1830  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);
1831  }
1832  TRACE("<-- %08x\n", res);
1833  return res;
1834 }
1835 
1836 /************************************************************************
1837  * StorageBaseImpl_RenameElement (IStorage)
1838  *
1839  * This method will rename the specified element.
1840  *
1841  * See Windows documentation for more details on IStorage methods.
1842  */
1844  IStorage* iface,
1845  const OLECHAR* pwcsOldName, /* [in] */
1846  const OLECHAR* pwcsNewName) /* [in] */
1847 {
1849  DirEntry currentEntry;
1850  DirRef currentEntryRef;
1851 
1852  TRACE("(%p, %s, %s)\n",
1853  iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
1854 
1855  if (This->reverted)
1856  return STG_E_REVERTED;
1857 
1858  currentEntryRef = findElement(This,
1859  This->storageDirEntry,
1860  pwcsNewName,
1861  &currentEntry);
1862 
1863  if (currentEntryRef != DIRENTRY_NULL)
1864  {
1865  /*
1866  * There is already an element with the new name
1867  */
1868  return STG_E_FILEALREADYEXISTS;
1869  }
1870 
1871  /*
1872  * Search for the old element name
1873  */
1874  currentEntryRef = findElement(This,
1875  This->storageDirEntry,
1876  pwcsOldName,
1877  &currentEntry);
1878 
1879  if (currentEntryRef != DIRENTRY_NULL)
1880  {
1881  if (StorageBaseImpl_IsStreamOpen(This, currentEntryRef) ||
1882  StorageBaseImpl_IsStorageOpen(This, currentEntryRef))
1883  {
1884  WARN("Element is already open; cannot rename.\n");
1885  return STG_E_ACCESSDENIED;
1886  }
1887 
1888  /* Remove the element from its current position in the tree */
1889  removeFromTree(This, This->storageDirEntry,
1890  currentEntryRef);
1891 
1892  /* Change the name of the element */
1893  lstrcpyW(currentEntry.name, pwcsNewName);
1894 
1895  /* Delete any sibling links */
1896  currentEntry.leftChild = DIRENTRY_NULL;
1897  currentEntry.rightChild = DIRENTRY_NULL;
1898 
1899  StorageBaseImpl_WriteDirEntry(This, currentEntryRef,
1900  &currentEntry);
1901 
1902  /* Insert the element in a new position in the tree */
1903  insertIntoTree(This, This->storageDirEntry,
1904  currentEntryRef);
1905  }
1906  else
1907  {
1908  /*
1909  * There is no element with the old name
1910  */
1911  return STG_E_FILENOTFOUND;
1912  }
1913 
1914  return StorageBaseImpl_Flush(This);
1915 }
1916 
1917 /************************************************************************
1918  * StorageBaseImpl_CreateStream (IStorage)
1919  *
1920  * This method will create a stream object within this storage
1921  *
1922  * See Windows documentation for more details on IStorage methods.
1923  */
1925  IStorage* iface,
1926  const OLECHAR* pwcsName, /* [string][in] */
1927  DWORD grfMode, /* [in] */
1928  DWORD reserved1, /* [in] */
1929  DWORD reserved2, /* [in] */
1930  IStream** ppstm) /* [out] */
1931 {
1933  StgStreamImpl* newStream;
1934  DirEntry currentEntry, newStreamEntry;
1935  DirRef currentEntryRef, newStreamEntryRef;
1936  HRESULT hr;
1937 
1938  TRACE("(%p, %s, %x, %d, %d, %p)\n",
1939  iface, debugstr_w(pwcsName), grfMode,
1940  reserved1, reserved2, ppstm);
1941 
1942  if (ppstm == 0)
1943  return STG_E_INVALIDPOINTER;
1944 
1945  if (pwcsName == 0)
1946  return STG_E_INVALIDNAME;
1947 
1948  if (reserved1 || reserved2)
1949  return STG_E_INVALIDPARAMETER;
1950 
1951  if ( FAILED( validateSTGM(grfMode) ))
1952  return STG_E_INVALIDFLAG;
1953 
1954  if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
1955  return STG_E_INVALIDFLAG;
1956 
1957  if (This->reverted)
1958  return STG_E_REVERTED;
1959 
1960  /*
1961  * As documented.
1962  */
1963  if ((grfMode & STGM_DELETEONRELEASE) ||
1964  (grfMode & STGM_TRANSACTED))
1965  return STG_E_INVALIDFUNCTION;
1966 
1967  /*
1968  * Don't worry about permissions in transacted mode, as we can always write
1969  * changes; we just can't always commit them.
1970  */
1971  if(!(This->openFlags & STGM_TRANSACTED)) {
1972  /* Can't create a stream on read-only storage */
1973  if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1974  return STG_E_ACCESSDENIED;
1975 
1976  /* Can't create a stream with greater access than the parent. */
1977  if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
1978  return STG_E_ACCESSDENIED;
1979  }
1980 
1981  if(This->openFlags & STGM_SIMPLE)
1982  if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
1983 
1984  *ppstm = 0;
1985 
1986  currentEntryRef = findElement(This,
1987  This->storageDirEntry,
1988  pwcsName,
1989  &currentEntry);
1990 
1991  if (currentEntryRef != DIRENTRY_NULL)
1992  {
1993  /*
1994  * An element with this name already exists
1995  */
1996  if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1997  {
1998  IStorage_DestroyElement(iface, pwcsName);
1999  }
2000  else
2001  return STG_E_FILEALREADYEXISTS;
2002  }
2003 
2004  /*
2005  * memset the empty entry
2006  */
2007  memset(&newStreamEntry, 0, sizeof(DirEntry));
2008 
2009  newStreamEntry.sizeOfNameString =
2010  ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
2011 
2012  if (newStreamEntry.sizeOfNameString > DIRENTRY_NAME_BUFFER_LEN)
2013  return STG_E_INVALIDNAME;
2014 
2015  lstrcpyW(newStreamEntry.name, pwcsName);
2016 
2017  newStreamEntry.stgType = STGTY_STREAM;
2018  newStreamEntry.startingBlock = BLOCK_END_OF_CHAIN;
2019  newStreamEntry.size.u.LowPart = 0;
2020  newStreamEntry.size.u.HighPart = 0;
2021 
2022  newStreamEntry.leftChild = DIRENTRY_NULL;
2023  newStreamEntry.rightChild = DIRENTRY_NULL;
2024  newStreamEntry.dirRootEntry = DIRENTRY_NULL;
2025 
2026  /* call CoFileTime to get the current time
2027  newStreamEntry.ctime
2028  newStreamEntry.mtime
2029  */
2030 
2031  /* newStreamEntry.clsid */
2032 
2033  /*
2034  * Create an entry with the new data
2035  */
2036  hr = StorageBaseImpl_CreateDirEntry(This, &newStreamEntry, &newStreamEntryRef);
2037  if (FAILED(hr))
2038  return hr;
2039 
2040  /*
2041  * Insert the new entry in the parent storage's tree.
2042  */
2043  hr = insertIntoTree(
2044  This,
2045  This->storageDirEntry,
2046  newStreamEntryRef);
2047  if (FAILED(hr))
2048  {
2049  StorageBaseImpl_DestroyDirEntry(This, newStreamEntryRef);
2050  return hr;
2051  }
2052 
2053  /*
2054  * Open the stream to return it.
2055  */
2056  newStream = StgStreamImpl_Construct(This, grfMode, newStreamEntryRef);
2057 
2058  if (newStream)
2059  {
2060  *ppstm = &newStream->IStream_iface;
2061  IStream_AddRef(*ppstm);
2062  }
2063  else
2064  {
2065  return STG_E_INSUFFICIENTMEMORY;
2066  }
2067 
2068  return StorageBaseImpl_Flush(This);
2069 }
2070 
2071 /************************************************************************
2072  * StorageBaseImpl_SetClass (IStorage)
2073  *
2074  * This method will write the specified CLSID in the directory entry of this
2075  * storage.
2076  *
2077  * See Windows documentation for more details on IStorage methods.
2078  */
2080  IStorage* iface,
2081  REFCLSID clsid) /* [in] */
2082 {
2084  HRESULT hRes;
2085  DirEntry currentEntry;
2086 
2087  TRACE("(%p, %s)\n", iface, wine_dbgstr_guid(clsid));
2088 
2089  if (This->reverted)
2090  return STG_E_REVERTED;
2091 
2093  This->storageDirEntry,
2094  &currentEntry);
2095  if (SUCCEEDED(hRes))
2096  {
2097  currentEntry.clsid = *clsid;
2098 
2100  This->storageDirEntry,
2101  &currentEntry);
2102  }
2103 
2104  if (SUCCEEDED(hRes))
2105  hRes = StorageBaseImpl_Flush(This);
2106 
2107  return hRes;
2108 }
2109 
2110 /************************************************************************
2111  * StorageBaseImpl_CreateStorage (IStorage)
2112  *
2113  * This method will create the storage object within the provided storage.
2114  *
2115  * See Windows documentation for more details on IStorage methods.
2116  */
2118  IStorage* iface,
2119  const OLECHAR *pwcsName, /* [string][in] */
2120  DWORD grfMode, /* [in] */
2121  DWORD reserved1, /* [in] */
2122  DWORD reserved2, /* [in] */
2123  IStorage **ppstg) /* [out] */
2124 {
2126 
2127  DirEntry currentEntry;
2128  DirEntry newEntry;
2129  DirRef currentEntryRef;
2130  DirRef newEntryRef;
2131  HRESULT hr;
2132 
2133  TRACE("(%p, %s, %x, %d, %d, %p)\n",
2134  iface, debugstr_w(pwcsName), grfMode,
2135  reserved1, reserved2, ppstg);
2136 
2137  if (ppstg == 0)
2138  return STG_E_INVALIDPOINTER;
2139 
2140  if (This->openFlags & STGM_SIMPLE)
2141  {
2142  return STG_E_INVALIDFUNCTION;
2143  }
2144 
2145  if (pwcsName == 0)
2146  return STG_E_INVALIDNAME;
2147 
2148  *ppstg = NULL;
2149 
2150  if ( FAILED( validateSTGM(grfMode) ) ||
2151  (grfMode & STGM_DELETEONRELEASE) )
2152  {
2153  WARN("bad grfMode: 0x%x\n", grfMode);
2154  return STG_E_INVALIDFLAG;
2155  }
2156 
2157  if (This->reverted)
2158  return STG_E_REVERTED;
2159 
2160  /*
2161  * Check that we're compatible with the parent's storage mode
2162  */
2163  if ( !(This->openFlags & STGM_TRANSACTED) &&
2164  STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
2165  {
2166  WARN("access denied\n");
2167  return STG_E_ACCESSDENIED;
2168  }
2169 
2170  currentEntryRef = findElement(This,
2171  This->storageDirEntry,
2172  pwcsName,
2173  &currentEntry);
2174 
2175  if (currentEntryRef != DIRENTRY_NULL)
2176  {
2177  /*
2178  * An element with this name already exists
2179  */
2180  if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
2181  ((This->openFlags & STGM_TRANSACTED) ||
2182  STGM_ACCESS_MODE(This->openFlags) != STGM_READ))
2183  {
2184  hr = IStorage_DestroyElement(iface, pwcsName);
2185  if (FAILED(hr))
2186  return hr;
2187  }
2188  else
2189  {
2190  WARN("file already exists\n");
2191  return STG_E_FILEALREADYEXISTS;
2192  }
2193  }
2194  else if (!(This->openFlags & STGM_TRANSACTED) &&
2195  STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
2196  {
2197  WARN("read-only storage\n");
2198  return STG_E_ACCESSDENIED;
2199  }
2200 
2201  memset(&newEntry, 0, sizeof(DirEntry));
2202 
2203  newEntry.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
2204 
2206  {
2207  FIXME("name too long\n");
2208  return STG_E_INVALIDNAME;
2209  }
2210 
2211  lstrcpyW(newEntry.name, pwcsName);
2212 
2213  newEntry.stgType = STGTY_STORAGE;
2214  newEntry.startingBlock = BLOCK_END_OF_CHAIN;
2215  newEntry.size.u.LowPart = 0;
2216  newEntry.size.u.HighPart = 0;
2217 
2218  newEntry.leftChild = DIRENTRY_NULL;
2219  newEntry.rightChild = DIRENTRY_NULL;
2220  newEntry.dirRootEntry = DIRENTRY_NULL;
2221 
2222  /* call CoFileTime to get the current time
2223  newEntry.ctime
2224  newEntry.mtime
2225  */
2226 
2227  /* newEntry.clsid */
2228 
2229  /*
2230  * Create a new directory entry for the storage
2231  */
2232  hr = StorageBaseImpl_CreateDirEntry(This, &newEntry, &newEntryRef);
2233  if (FAILED(hr))
2234  return hr;
2235 
2236  /*
2237  * Insert the new directory entry into the parent storage's tree
2238  */
2239  hr = insertIntoTree(
2240  This,
2241  This->storageDirEntry,
2242  newEntryRef);
2243  if (FAILED(hr))
2244  {
2245  StorageBaseImpl_DestroyDirEntry(This, newEntryRef);
2246  return hr;
2247  }
2248 
2249  /*
2250  * Open it to get a pointer to return.
2251  */
2252  hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
2253 
2254  if( (hr != S_OK) || (*ppstg == NULL))
2255  {
2256  return hr;
2257  }
2258 
2259  if (SUCCEEDED(hr))
2261 
2262  return S_OK;
2263 }
2264 
2266  DirRef srcEntry, BOOL skip_storage, BOOL skip_stream,
2267  SNB snbExclude, IStorage *pstgDest)
2268 {
2269  DirEntry data;
2270  HRESULT hr;
2271 
2272  hr = StorageBaseImpl_ReadDirEntry( This, srcEntry, &data );
2273 
2274  if (SUCCEEDED(hr))
2275  hr = IStorage_SetClass( pstgDest, &data.clsid );
2276 
2277  if (SUCCEEDED(hr))
2278  hr = StorageBaseImpl_CopyChildEntryTo( This, data.dirRootEntry, skip_storage,
2279  skip_stream, snbExclude, pstgDest );
2280 
2281  TRACE("<-- %08x\n", hr);
2282  return hr;
2283 }
2284 
2285 /*************************************************************************
2286  * CopyTo (IStorage)
2287  */
2289  IStorage* iface,
2290  DWORD ciidExclude, /* [in] */
2291  const IID* rgiidExclude, /* [size_is][unique][in] */
2292  SNB snbExclude, /* [unique][in] */
2293  IStorage* pstgDest) /* [unique][in] */
2294 {
2296 
2297  BOOL skip_storage = FALSE, skip_stream = FALSE;
2298  DWORD i;
2299 
2300  TRACE("(%p, %d, %p, %p, %p)\n",
2301  iface, ciidExclude, rgiidExclude,
2302  snbExclude, pstgDest);
2303 
2304  if ( pstgDest == 0 )
2305  return STG_E_INVALIDPOINTER;
2306 
2307  for(i = 0; i < ciidExclude; ++i)
2308  {
2309  if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
2310  skip_storage = TRUE;
2311  else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
2312  skip_stream = TRUE;
2313  else
2314  WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
2315  }
2316 
2317  if (!skip_storage)
2318  {
2319  /* Give up early if it looks like this would be infinitely recursive.
2320  * Oddly enough, this includes some cases that aren't really recursive, like
2321  * copying to a transacted child. */
2322  IStorage *pstgDestAncestor = pstgDest;
2323  IStorage *pstgDestAncestorChild = NULL;
2324 
2325  /* Go up the chain from the destination until we find the source storage. */
2326  while (pstgDestAncestor != iface) {
2327  pstgDestAncestorChild = pstgDest;
2328 
2329  if (pstgDestAncestor->lpVtbl == &TransactedSnapshotImpl_Vtbl)
2330  {
2332 
2333  pstgDestAncestor = &snapshot->transactedParent->IStorage_iface;
2334  }
2335  else if (pstgDestAncestor->lpVtbl == &StorageInternalImpl_Vtbl)
2336  {
2337  StorageInternalImpl *internal = (StorageInternalImpl*) pstgDestAncestor;
2338 
2339  pstgDestAncestor = &internal->parentStorage->IStorage_iface;
2340  }
2341  else
2342  break;
2343  }
2344 
2345  if (pstgDestAncestor == iface)
2346  {
2347  BOOL fail = TRUE;
2348 
2349  if (pstgDestAncestorChild && snbExclude)
2350  {
2351  StorageBaseImpl *ancestorChildBase = (StorageBaseImpl*)pstgDestAncestorChild;
2352  DirEntry data;
2353  WCHAR **snb = snbExclude;
2354 
2355  StorageBaseImpl_ReadDirEntry(ancestorChildBase, ancestorChildBase->storageDirEntry, &data);
2356 
2357  while ( *snb != NULL && fail )
2358  {
2359  if ( wcscmp(data.name, *snb) == 0 )
2360  fail = FALSE;
2361  ++snb;
2362  }
2363  }
2364 
2365  if (fail)
2366  return STG_E_ACCESSDENIED;
2367  }
2368  }
2369 
2370  return StorageBaseImpl_CopyStorageEntryTo( This, This->storageDirEntry,
2371  skip_storage, skip_stream, snbExclude, pstgDest );
2372 }
2373 
2374 /*************************************************************************
2375  * MoveElementTo (IStorage)
2376  */
2378  IStorage* iface,
2379  const OLECHAR *pwcsName, /* [string][in] */
2380  IStorage *pstgDest, /* [unique][in] */
2381  const OLECHAR *pwcsNewName,/* [string][in] */
2382  DWORD grfFlags) /* [in] */
2383 {
2384  FIXME("(%p %s %p %s %u): stub\n", iface,
2385  debugstr_w(pwcsName), pstgDest,
2386  debugstr_w(pwcsNewName), grfFlags);
2387  return E_NOTIMPL;
2388 }
2389 
2390 /*************************************************************************
2391  * Commit (IStorage)
2392  *
2393  * Ensures that any changes made to a storage object open in transacted mode
2394  * are reflected in the parent storage
2395  *
2396  * In a non-transacted mode, this ensures all cached writes are completed.
2397  */
2399  IStorage* iface,
2400  DWORD grfCommitFlags)/* [in] */
2401 {
2403  TRACE("(%p %d)\n", iface, grfCommitFlags);
2404  return StorageBaseImpl_Flush(This);
2405 }
2406 
2407 /*************************************************************************
2408  * Revert (IStorage)
2409  *
2410  * Discard all changes that have been made since the last commit operation
2411  */
2413  IStorage* iface)
2414 {
2415  TRACE("(%p)\n", iface);
2416  return S_OK;
2417 }
2418 
2419 /*********************************************************************
2420  *
2421  * Internal helper function for StorageBaseImpl_DestroyElement()
2422  *
2423  * Delete the contents of a storage entry.
2424  *
2425  */
2427  StorageBaseImpl *parentStorage,
2428  DirRef indexToDelete,
2429  DirEntry entryDataToDelete)
2430 {
2431  IEnumSTATSTG *elements = 0;
2432  IStorage *childStorage = 0;
2433  STATSTG currentElement;
2434  HRESULT hr;
2435  HRESULT destroyHr = S_OK;
2436  StorageInternalImpl *stg, *stg2;
2437 
2438  TRACE("%p,%d\n", parentStorage, indexToDelete);
2439 
2440  /* Invalidate any open storage objects. */
2441  LIST_FOR_EACH_ENTRY_SAFE(stg, stg2, &parentStorage->storageHead, StorageInternalImpl, ParentListEntry)
2442  {
2443  if (stg->base.storageDirEntry == indexToDelete)
2444  {
2446  }
2447  }
2448 
2449  /*
2450  * Open the storage and enumerate it
2451  */
2452  hr = IStorage_OpenStorage(
2453  &parentStorage->IStorage_iface,
2454  entryDataToDelete.name,
2455  0,
2457  0,
2458  0,
2459  &childStorage);
2460 
2461  if (hr != S_OK)
2462  {
2463  TRACE("<-- %08x\n", hr);
2464  return hr;
2465  }
2466 
2467  /*
2468  * Enumerate the elements
2469  */
2470  hr = IStorage_EnumElements(childStorage, 0, 0, 0, &elements);
2471  if (FAILED(hr))
2472  {
2473  IStorage_Release(childStorage);
2474  TRACE("<-- %08x\n", hr);
2475  return hr;
2476  }
2477 
2478  do
2479  {
2480  /*
2481  * Obtain the next element
2482  */
2483  hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2484  if (hr==S_OK)
2485  {
2486  destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
2487 
2488  CoTaskMemFree(currentElement.pwcsName);
2489  }
2490 
2491  /*
2492  * We need to Reset the enumeration every time because we delete elements
2493  * and the enumeration could be invalid
2494  */
2495  IEnumSTATSTG_Reset(elements);
2496 
2497  } while ((hr == S_OK) && (destroyHr == S_OK));
2498 
2499  IStorage_Release(childStorage);
2500  IEnumSTATSTG_Release(elements);
2501 
2502  TRACE("%08x\n", hr);
2503  return destroyHr;
2504 }
2505 
2506 /*********************************************************************
2507  *
2508  * Internal helper function for StorageBaseImpl_DestroyElement()
2509  *
2510  * Perform the deletion of a stream's data
2511  *
2512  */
2514  StorageBaseImpl *parentStorage,
2515  DirRef indexToDelete,
2516  DirEntry entryDataToDelete)
2517 {
2518  IStream *pis;
2519  HRESULT hr;
2521  StgStreamImpl *strm, *strm2;
2522 
2523  /* Invalidate any open stream objects. */
2524  LIST_FOR_EACH_ENTRY_SAFE(strm, strm2, &parentStorage->strmHead, StgStreamImpl, StrmListEntry)
2525  {
2526  if (strm->dirEntry == indexToDelete)
2527  {
2528  TRACE("Stream deleted %p\n", strm);
2529  strm->parentStorage = NULL;
2530  list_remove(&strm->StrmListEntry);
2531  }
2532  }
2533 
2534  size.u.HighPart = 0;
2535  size.u.LowPart = 0;
2536 
2537  hr = IStorage_OpenStream(&parentStorage->IStorage_iface,
2538  entryDataToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2539 
2540  if (hr!=S_OK)
2541  {
2542  TRACE("<-- %08x\n", hr);
2543  return(hr);
2544  }
2545 
2546  /*
2547  * Zap the stream
2548  */
2549  hr = IStream_SetSize(pis, size);
2550 
2551  if(hr != S_OK)
2552  {
2553  TRACE("<-- %08x\n", hr);
2554  return hr;
2555  }
2556 
2557  /*
2558  * Release the stream object.
2559  */
2560  IStream_Release(pis);
2561  TRACE("<-- %08x\n", hr);
2562  return S_OK;
2563 }
2564 
2565 /*************************************************************************
2566  * DestroyElement (IStorage)
2567  *
2568  * Strategy: This implementation is built this way for simplicity not for speed.
2569  * I always delete the topmost element of the enumeration and adjust
2570  * the deleted element pointer all the time. This takes longer to
2571  * do but allows reinvoking DestroyElement whenever we encounter a
2572  * storage object. The optimisation resides in the usage of another
2573  * enumeration strategy that would give all the leaves of a storage
2574  * first. (postfix order)
2575  */
2577  IStorage* iface,
2578  const OLECHAR *pwcsName)/* [string][in] */
2579 {
2581 
2582  HRESULT hr = S_OK;
2583  DirEntry entryToDelete;
2584  DirRef entryToDeleteRef;
2585 
2586  TRACE("(%p, %s)\n",
2587  iface, debugstr_w(pwcsName));
2588 
2589  if (pwcsName==NULL)
2590  return STG_E_INVALIDPOINTER;
2591 
2592  if (This->reverted)
2593  return STG_E_REVERTED;
2594 
2595  if ( !(This->openFlags & STGM_TRANSACTED) &&
2596  STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
2597  return STG_E_ACCESSDENIED;
2598 
2599  entryToDeleteRef = findElement(
2600  This,
2601  This->storageDirEntry,
2602  pwcsName,
2603  &entryToDelete);
2604 
2605  if ( entryToDeleteRef == DIRENTRY_NULL )
2606  {
2607  TRACE("<-- STG_E_FILENOTFOUND\n");
2608  return STG_E_FILENOTFOUND;
2609  }
2610 
2611  if ( entryToDelete.stgType == STGTY_STORAGE )
2612  {
2614  This,
2615  entryToDeleteRef,
2616  entryToDelete);
2617  }
2618  else if ( entryToDelete.stgType == STGTY_STREAM )
2619  {
2621  This,
2622  entryToDeleteRef,
2623  entryToDelete);
2624  }
2625 
2626  if (hr!=S_OK)
2627  {
2628  TRACE("<-- %08x\n", hr);
2629  return hr;
2630  }
2631 
2632  /*
2633  * Remove the entry from its parent storage
2634  */
2635  hr = removeFromTree(
2636  This,
2637  This->storageDirEntry,
2638  entryToDeleteRef);
2639 
2640  /*
2641  * Invalidate the entry
2642  */
2643  if (SUCCEEDED(hr))
2644  StorageBaseImpl_DestroyDirEntry(This, entryToDeleteRef);
2645 
2646  if (SUCCEEDED(hr))
2648 
2649  TRACE("<-- %08x\n", hr);
2650  return hr;
2651 }
2652 
2654 {
2655  struct list *cur, *cur2;
2656  StgStreamImpl *strm=NULL;
2657  StorageInternalImpl *childstg=NULL;
2658 
2659  LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
2660  strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
2661  TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
2662  strm->parentStorage = NULL;
2663  list_remove(cur);
2664  }
2665 
2666  LIST_FOR_EACH_SAFE(cur, cur2, &stg->storageHead) {
2667  childstg = LIST_ENTRY(cur,StorageInternalImpl,ParentListEntry);
2668  StorageBaseImpl_Invalidate( &childstg->base );
2669  }
2670 
2671  if (stg->transactedChild)
2672  {
2674 
2675  stg->transactedChild = NULL;
2676  }
2677 }
2678 
2679 /******************************************************************************
2680  * SetElementTimes (IStorage)
2681  */
2683  IStorage* iface,
2684  const OLECHAR *pwcsName,/* [string][in] */
2685  const FILETIME *pctime, /* [in] */
2686  const FILETIME *patime, /* [in] */
2687  const FILETIME *pmtime) /* [in] */
2688 {
2689  FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2690  return S_OK;
2691 }
2692 
2693 /******************************************************************************
2694  * SetStateBits (IStorage)
2695  */
2697  IStorage* iface,
2698  DWORD grfStateBits,/* [in] */
2699  DWORD grfMask) /* [in] */
2700 {
2702 
2703  if (This->reverted)
2704  return STG_E_REVERTED;
2705 
2706  This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2707  return S_OK;
2708 }
2709 
2710 /******************************************************************************
2711  * Internal stream list handlers
2712  */
2713 
2715 {
2716  TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
2717  list_add_tail(&stg->strmHead,&strm->StrmListEntry);
2718 }
2719 
2721 {
2722  TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
2723  list_remove(&(strm->StrmListEntry));
2724 }
2725 
2728  StorageBaseImpl *src, DirRef src_entry)
2729 {
2730  HRESULT hr;
2731  BYTE data[4096];
2732  DirEntry srcdata;
2733  ULARGE_INTEGER bytes_copied;
2734  ULONG bytestocopy, bytesread, byteswritten;
2735 
2736  hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
2737 
2738  if (SUCCEEDED(hr))
2739  {
2741 
2742  bytes_copied.QuadPart = 0;
2743  while (bytes_copied.QuadPart < srcdata.size.QuadPart && SUCCEEDED(hr))
2744  {
2745  bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
2746 
2747  hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
2748  data, &bytesread);
2749  if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
2750 
2751  if (SUCCEEDED(hr))
2752  hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
2753  data, &byteswritten);
2754  if (SUCCEEDED(hr))
2755  {
2756  if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
2757  bytes_copied.QuadPart += byteswritten;
2758  }
2759  }
2760  }
2761 
2762  return hr;
2763 }
2764 
2767  StorageBaseImpl *src, DirRef src_entry)
2768 {
2769  HRESULT hr;
2770  DirEntry data;
2771  BOOL has_stream=FALSE;
2772 
2773  if (src_entry == DIRENTRY_NULL)
2774  {
2776  return S_OK;
2777  }
2778 
2779  hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &data);
2780  if (SUCCEEDED(hr))
2781  {
2782  has_stream = (data.stgType == STGTY_STREAM && data.size.QuadPart != 0);
2783  data.startingBlock = BLOCK_END_OF_CHAIN;
2784  data.size.QuadPart = 0;
2785 
2786  hr = StorageBaseImpl_DupStorageTree(dst, &data.leftChild, src, data.leftChild);
2787  }
2788 
2789  if (SUCCEEDED(hr))
2790  hr = StorageBaseImpl_DupStorageTree(dst, &data.rightChild, src, data.rightChild);
2791 
2792  if (SUCCEEDED(hr))
2793  hr = StorageBaseImpl_DupStorageTree(dst, &data.dirRootEntry, src, data.dirRootEntry);
2794 
2795  if (SUCCEEDED(hr))
2797 
2798  if (SUCCEEDED(hr) && has_stream)
2799  hr = StorageBaseImpl_CopyStream(dst, *dst_entry, src, src_entry);
2800 
2801  return hr;
2802 }
2803 
2806  StorageBaseImpl *src, DirRef src_entry)
2807 {
2808  HRESULT hr;
2809  DirEntry src_data, dst_data;
2810  DirRef new_root_entry;
2811 
2812  hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &src_data);
2813 
2814  if (SUCCEEDED(hr))
2815  {
2816  hr = StorageBaseImpl_DupStorageTree(dst, &new_root_entry, src, src_data.dirRootEntry);
2817  }
2818 
2819  if (SUCCEEDED(hr))
2820  {
2822  dst_data.clsid = src_data.clsid;
2823  dst_data.ctime = src_data.ctime;
2824  dst_data.mtime = src_data.mtime;
2825  dst_data.dirRootEntry = new_root_entry;
2826  }
2827 
2828  if (SUCCEEDED(hr))
2830 
2831  return hr;
2832 }
2833 
2835 {
2836  HRESULT hr;
2837  DirEntry data;
2839 
2840  if (entry == DIRENTRY_NULL)
2841  return S_OK;
2842 
2843  zero.QuadPart = 0;
2844 
2846 
2847  if (SUCCEEDED(hr) && include_siblings)
2849 
2850  if (SUCCEEDED(hr) && include_siblings)
2852 
2853  if (SUCCEEDED(hr))
2855 
2856  if (SUCCEEDED(hr) && data.stgType == STGTY_STREAM)
2858 
2859  if (SUCCEEDED(hr))
2861 
2862  return hr;
2863 }
2864 
2865 
2866 /************************************************************************
2867  * StorageImpl implementation
2868  ***********************************************************************/
2869 
2872  void* buffer,
2873  ULONG size,
2874  ULONG* bytesRead)
2875 {
2876  return ILockBytes_ReadAt(This->lockBytes,offset,buffer,size,bytesRead);
2877 }
2878 
2881  const void* buffer,
2882  const ULONG size,
2884 {
2885  return ILockBytes_WriteAt(This->lockBytes,offset,buffer,size,bytesWritten);
2886 }
2887 
2888 /******************************************************************************
2889  * StorageImpl_LoadFileHeader
2890  *
2891  * This method will read in the file header
2892  */
2894  StorageImpl* This)
2895 {
2896  HRESULT hr;
2897  BYTE headerBigBlock[HEADER_SIZE];
2898  int index;
2900  DWORD bytes_read;
2901 
2902  TRACE("\n");
2903  /*
2904  * Get a pointer to the big block of data containing the header.
2905  */
2906  offset.u.HighPart = 0;
2907  offset.u.LowPart = 0;
2908  hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
2909  if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
2911 
2912  /*
2913  * Extract the information from the header.
2914  */
2915  if (SUCCEEDED(hr))
2916  {
2917  /*
2918  * Check for the "magic number" signature and return an error if it is not
2919  * found.
2920  */
2921  if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2922  {
2923  return STG_E_OLDFORMAT;
2924  }
2925 
2926  if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2927  {
2928  return STG_E_INVALIDHEADER;
2929  }
2930 
2932  headerBigBlock,
2934  &This->bigBlockSizeBits);
2935 
2937  headerBigBlock,
2939  &This->smallBlockSizeBits);
2940 
2942  headerBigBlock,
2944  &This->bigBlockDepotCount);
2945 
2947  headerBigBlock,
2949  &This->rootStartBlock);
2950 
2952  headerBigBlock,
2954  &This->transactionSig);
2955 
2957  headerBigBlock,
2959  &This->smallBlockLimit);
2960 
2962  headerBigBlock,
2964  &This->smallBlockDepotStart);
2965 
2967  headerBigBlock,
2969  &This->extBigBlockDepotStart);
2970 
2972  headerBigBlock,
2974  &This->extBigBlockDepotCount);
2975 
2976  for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2977  {
2979  headerBigBlock,
2980  OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2981  &(This->bigBlockDepotStart[index]));
2982  }
2983 
2984  /*
2985  * Make the bitwise arithmetic to get the size of the blocks in bytes.
2986  */
2987  This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2988  This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2989 
2990  /*
2991  * Right now, the code is making some assumptions about the size of the
2992  * blocks, just make sure they are what we're expecting.
2993  */
2994  if ((This->bigBlockSize != MIN_BIG_BLOCK_SIZE && This->bigBlockSize != MAX_BIG_BLOCK_SIZE) ||
2995  This->smallBlockSize != DEF_SMALL_BLOCK_SIZE ||
2996  This->smallBlockLimit != LIMIT_TO_USE_SMALL_BLOCK)
2997  {
2998  FIXME("Broken OLE storage file? bigblock=0x%x, smallblock=0x%x, sblimit=0x%x\n",
2999  This->bigBlockSize, This->smallBlockSize, This->smallBlockLimit);
3001  }
3002  else
3003  hr = S_OK;
3004  }
3005 
3006  return hr;
3007 }
3008 
3009 /******************************************************************************
3010  * StorageImpl_SaveFileHeader
3011  *
3012  * This method will save to the file the header
3013  */
3015  StorageImpl* This)
3016 {
3017  BYTE headerBigBlock[HEADER_SIZE];
3018  int index;
3019  HRESULT hr;
3021  DWORD bytes_read, bytes_written;
3022  DWORD major_version, dirsectorcount;
3023 
3024  /*
3025  * Get a pointer to the big block of data containing the header.
3026  */
3027  offset.u.HighPart = 0;
3028  offset.u.LowPart = 0;
3029  hr = StorageImpl_ReadAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_read);
3030  if (SUCCEEDED(hr) && bytes_read != HEADER_SIZE)
3032 
3033  if (This->bigBlockSizeBits == 0x9)
3034  major_version = 3;
3035  else if (This->bigBlockSizeBits == 0xc)
3036  major_version = 4;
3037  else
3038  {
3039  ERR("invalid big block shift 0x%x\n", This->bigBlockSizeBits);
3040  major_version = 4;
3041  }
3042 
3043  /*
3044  * If the block read failed, the file is probably new.
3045  */
3046  if (FAILED(hr))
3047  {
3048  /*
3049  * Initialize for all unknown fields.
3050  */
3051  memset(headerBigBlock, 0, HEADER_SIZE);
3052 
3053  /*
3054  * Initialize the magic number.
3055  */
3056  memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3057  }
3058 
3059  /*
3060  * Write the information to the header.
3061  */
3063  headerBigBlock,
3065  0x3e);
3066 
3068  headerBigBlock,
3070  major_version);
3071 
3073  headerBigBlock,
3075  (WORD)-2);
3076 
3078  headerBigBlock,
3080  This->bigBlockSizeBits);
3081 
3083  headerBigBlock,
3085  This->smallBlockSizeBits);
3086 
3087  if (major_version >= 4)
3088  {
3089  if (This->rootBlockChain)
3090  dirsectorcount = BlockChainStream_GetCount(This->rootBlockChain);
3091  else
3092  /* This file is being created, and it will start out with one block. */
3093  dirsectorcount = 1;
3094  }
3095  else
3096  /* This field must be 0 in versions older than 4 */
3097  dirsectorcount = 0;
3098 
3100  headerBigBlock,
3102  dirsectorcount);
3103 
3105  headerBigBlock,
3107  This->bigBlockDepotCount);
3108 
3110  headerBigBlock,
3112  This->rootStartBlock);
3113 
3115  headerBigBlock,
3117  This->transactionSig);
3118 
3120  headerBigBlock,
3122  This->smallBlockLimit);
3123 
3125  headerBigBlock,
3127  This->smallBlockDepotStart);
3128 
3130  headerBigBlock,
3132  This->smallBlockDepotChain ?
3133  BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3134 
3136  headerBigBlock,
3138  This->extBigBlockDepotStart);
3139 
3141  headerBigBlock,
3143  This->extBigBlockDepotCount);
3144 
3145  for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3146  {
3148  headerBigBlock,
3149  OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3150  (This->bigBlockDepotStart[index]));
3151  }
3152 
3153  /*
3154  * Write the big block back to the file.
3155  */
3156  StorageImpl_WriteAt(This, offset, headerBigBlock, HEADER_SIZE, &bytes_written);
3157 }
3158 
3159 
3160 /************************************************************************
3161  * StorageImpl implementation : DirEntry methods
3162  ***********************************************************************/
3163 
3164 /******************************************************************************
3165  * StorageImpl_ReadRawDirEntry
3166  *
3167  * This method will read the raw data from a directory entry in the file.
3168  *
3169  * buffer must be RAW_DIRENTRY_SIZE bytes long.
3170  */
3172 {
3174  HRESULT hr;
3175  ULONG bytesRead;
3176 
3177  offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3178 
3180  This->rootBlockChain,
3181  offset,
3183  buffer,
3184  &bytesRead);
3185 
3186  if (bytesRead != RAW_DIRENTRY_SIZE)
3187  return STG_E_READFAULT;
3188 
3189  return hr;
3190 }
3191 
3192 /******************************************************************************
3193  * StorageImpl_WriteRawDirEntry
3194  *
3195  * This method will write the raw data from a directory entry in the file.
3196  *
3197  * buffer must be RAW_DIRENTRY_SIZE bytes long.
3198  */
3200 {
3202  ULONG bytesRead;
3203 
3204  offset.QuadPart = (ULONGLONG)index * RAW_DIRENTRY_SIZE;
3205 
3206  return BlockChainStream_WriteAt(
3207  This->rootBlockChain,
3208  offset,
3210  buffer,
3211  &bytesRead);
3212 }
3213 
3214 /***************************************************************************
3215  *
3216  * Internal Method
3217  *
3218  * Mark a directory entry in the file as free.
3219  */
3222  DirRef index)
3223 {
3224  BYTE emptyData[RAW_DIRENTRY_SIZE];
3225  StorageImpl *storage = (StorageImpl*)base;
3226 
3227  memset(emptyData, 0, RAW_DIRENTRY_SIZE);
3228 
3229  return StorageImpl_WriteRawDirEntry(storage, index, emptyData);
3230 }
3231 
3232 /******************************************************************************
3233  * UpdateRawDirEntry
3234  *
3235  * Update raw directory entry data from the fields in newData.
3236  *
3237  * buffer must be RAW_DIRENTRY_SIZE bytes long.
3238  */
3239 static void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3240 {
3242 
3243  memcpy(
3245  newData->name,
3247 
3248  memcpy(buffer + OFFSET_PS_STGTYPE, &newData->stgType, 1);
3249 
3251  buffer,
3253  newData->sizeOfNameString);
3254 
3256  buffer,
3258  newData->leftChild);
3259 
3261  buffer,
3263  newData->rightChild);
3264 
3266  buffer,
3268  newData->dirRootEntry);
3269 
3271  buffer,
3273  &newData->clsid);
3274 
3276  buffer,
3278  newData->ctime.dwLowDateTime);
3279 
3281  buffer,
3283  newData->ctime.dwHighDateTime);
3284 
3286  buffer,
3288  newData->mtime.dwLowDateTime);
3289 
3291  buffer,
3293  newData->ctime.dwHighDateTime);
3294 
3296  buffer,
3298  newData->startingBlock);
3299 
3301  buffer,
3303  newData->size.u.LowPart);
3304 
3306  buffer,
3308  newData->size.u.HighPart);
3309 }
3310 
3311 /***************************************************************************
3312  *
3313  * Internal Method
3314  *
3315  * Reserve a directory entry in the file and initialize it.
3316  */
3319  const DirEntry *newData,
3320  DirRef *index)
3321 {
3322  StorageImpl *storage = (StorageImpl*)base;
3323  ULONG currentEntryIndex = 0;
3324  ULONG newEntryIndex = DIRENTRY_NULL;
3325  HRESULT hr = S_OK;
3326  BYTE currentData[RAW_DIRENTRY_SIZE];
3327  WORD sizeOfNameString;
3328 
3329  do
3330  {
3331  hr = StorageImpl_ReadRawDirEntry(storage,
3332  currentEntryIndex,
3333  currentData);
3334 
3335  if (SUCCEEDED(hr))
3336  {
3338  currentData,
3340  &sizeOfNameString);
3341 
3342  if (sizeOfNameString == 0)
3343  {
3344  /*
3345  * The entry exists and is available, we found it.
3346  */
3347  newEntryIndex = currentEntryIndex;
3348  }
3349  }
3350  else
3351  {
3352  /*
3353  * We exhausted the directory entries, we will create more space below
3354  */
3355  newEntryIndex = currentEntryIndex;
3356  }
3357  currentEntryIndex++;
3358 
3359  } while (newEntryIndex == DIRENTRY_NULL);
3360 
3361  /*
3362  * grow the directory stream
3363  */
3364  if (FAILED(hr))
3365  {
3366  BYTE emptyData[RAW_DIRENTRY_SIZE];
3367  ULARGE_INTEGER newSize;
3368  ULONG entryIndex;
3369  ULONG lastEntry = 0;
3370  ULONG blockCount = 0;
3371 
3372  /*
3373  * obtain the new count of blocks in the directory stream
3374  */
3375  blockCount = BlockChainStream_GetCount(
3376  storage->rootBlockChain)+1;
3377 
3378  /*
3379  * initialize the size used by the directory stream
3380  */
3381  newSize.QuadPart = (ULONGLONG)storage->bigBlockSize * blockCount;
3382 
3383  /*
3384  * add a block to the directory stream
3385  */
3386  BlockChainStream_SetSize(storage->rootBlockChain, newSize);
3387 
3388  /*
3389  * memset the empty entry in order to initialize the unused newly
3390  * created entries
3391  */
3392  memset(emptyData, 0, RAW_DIRENTRY_SIZE);
3393 
3394  /*
3395  * initialize them
3396  */
3397  lastEntry = storage->bigBlockSize / RAW_DIRENTRY_SIZE * blockCount;
3398 
3399  for(
3400  entryIndex = newEntryIndex + 1;
3401  entryIndex < lastEntry;
3402  entryIndex++)
3403  {
3405  storage,
3406  entryIndex,
3407  emptyData);
3408  }
3409 
3410  StorageImpl_SaveFileHeader(storage);
3411  }
3412 
3413  UpdateRawDirEntry(currentData, newData);
3414 
3415  hr = StorageImpl_WriteRawDirEntry(storage, newEntryIndex, currentData);
3416 
3417  if (SUCCEEDED(hr))
3418  *index = newEntryIndex;
3419 
3420  return hr;
3421 }
3422 
3423 /******************************************************************************
3424  * StorageImpl_ReadDirEntry
3425  *
3426  * This method will read the specified directory entry.
3427  */
3429  StorageImpl* This,
3430  DirRef index,
3431  DirEntry* buffer)
3432 {
3433  BYTE currentEntry[RAW_DIRENTRY_SIZE];
3434  HRESULT readRes;
3435 
3436  readRes = StorageImpl_ReadRawDirEntry(This, index, currentEntry);
3437 
3438  if (SUCCEEDED(readRes))
3439  {
3440  memset(buffer->name, 0, sizeof(buffer->name));
3441  memcpy(
3442  buffer->name,
3443  (WCHAR *)currentEntry+OFFSET_PS_NAME,
3445  TRACE("storage name: %s\n", debugstr_w(buffer->name));
3446 
3447  memcpy(&buffer->stgType, currentEntry + OFFSET_PS_STGTYPE, 1);
3448 
3450  currentEntry,
3452  &buffer->sizeOfNameString);
3453 
3455  currentEntry,
3457  &buffer->leftChild);
3458 
3460  currentEntry,
3462  &buffer->rightChild);
3463 
3465  currentEntry,
3467  &buffer->dirRootEntry);
3468 
3470  currentEntry,
3472  &buffer->clsid);
3473 
3475  currentEntry,
3477  &buffer->ctime.dwLowDateTime);
3478 
3480  currentEntry,
3482  &buffer->ctime.dwHighDateTime);
3483 
3485  currentEntry,
3487  &buffer->mtime.dwLowDateTime);
3488 
3490  currentEntry,
3492  &buffer->mtime.dwHighDateTime);
3493 
3495  currentEntry,
3497  &buffer->startingBlock);
3498 
3500  currentEntry,
3502  &buffer->size.u.LowPart);
3503 
3504  if (This->bigBlockSize < 4096)
3505  {
3506  /* Version 3 files may have junk in the high part of size. */
3507  buffer->size.u.HighPart = 0;
3508  }
3509  else
3510  {
3512  currentEntry,
3514  &buffer->size.u.HighPart);
3515  }
3516  }
3517 
3518  return readRes;
3519 }
3520 
3521 /*********************************************************************
3522  * Write the specified directory entry to the file
3523  */
3525  StorageImpl* This,
3526  DirRef index,
3527  const DirEntry* buffer)
3528 {
3529  BYTE currentEntry[RAW_DIRENTRY_SIZE];
3530 
3531  UpdateRawDirEntry(currentEntry, buffer);
3532 
3533  return StorageImpl_WriteRawDirEntry(This, index, currentEntry);
3534 }
3535 
3536 
3537 /************************************************************************
3538  * StorageImpl implementation : Block methods
3539  ***********************************************************************/
3540 
3542 {
3543  return (ULONGLONG)(index+1) * This->bigBlockSize;
3544 }
3545 
3547  StorageImpl* This,
3548  ULONG blockIndex,
3549  void* buffer,
3550  ULONG* out_read)
3551 {
3552  ULARGE_INTEGER ulOffset;
3553  DWORD read=0;
3554  HRESULT hr;
3555 
3556  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3557 
3558  hr = StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3559 
3560  if (SUCCEEDED(hr) && read < This->bigBlockSize)
3561  {
3562  /* File ends during this block; fill the rest with 0's. */
3563  memset((LPBYTE)buffer+read, 0, This->bigBlockSize-read);
3564  }
3565 
3566  if (out_read) *out_read = read;
3567 
3568  return hr;
3569 }
3570 
3572  StorageImpl* This,
3573  ULONG blockIndex,
3574  ULONG offset,
3575  DWORD* value)
3576 {
3577  ULARGE_INTEGER ulOffset;
3578  DWORD read;
3579  DWORD tmp;
3580 
3581  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3582  ulOffset.QuadPart += offset;
3583 
3584  StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3585  *value = lendian32toh(tmp);
3586  return (read == sizeof(DWORD));
3587 }
3588 
3590  StorageImpl* This,
3591  ULONG blockIndex,
3592  const void* buffer)
3593 {
3594  ULARGE_INTEGER ulOffset;
3595  DWORD wrote;
3596 
3597  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3598 
3599  StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3600  return (wrote == This->bigBlockSize);
3601 }
3602 
3604  StorageImpl* This,
3605  ULONG blockIndex,
3606  ULONG offset,
3607  DWORD value)
3608 {
3609  ULARGE_INTEGER ulOffset;
3610  DWORD wrote;
3611 
3612  ulOffset.QuadPart = StorageImpl_GetBigBlockOffset(This, blockIndex);
3613  ulOffset.QuadPart += offset;
3614 
3615  value = htole32(value);
3616  StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3617  return (wrote == sizeof(DWORD));
3618 }
3619 
3620 /******************************************************************************
3621  * Storage32Impl_SmallBlocksToBigBlocks
3622  *
3623  * This method will convert a small block chain to a big block chain.
3624  * The small block chain will be destroyed.
3625  */
3627  StorageImpl* This,
3628  SmallBlockChainStream** ppsbChain)
3629 {
3630  ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3632  ULONG cbRead, cbWritten;
3633  ULARGE_INTEGER cbTotalRead;
3634  DirRef streamEntryRef;
3635  HRESULT resWrite = S_OK;
3636  HRESULT resRead;
3637  DirEntry streamEntry;
3638  BYTE *buffer;
3639  BlockChainStream *bbTempChain = NULL;
3640  BlockChainStream *bigBlockChain = NULL;
3641 
3642  /*
3643  * Create a temporary big block chain that doesn't have
3644  * an associated directory entry. This temporary chain will be
3645  * used to copy data from small blocks to big blocks.
3646  */
3647  bbTempChain = BlockChainStream_Construct(This,
3648  &bbHeadOfChain,
3649  DIRENTRY_NULL);
3650  if(!bbTempChain) return NULL;
3651  /*
3652  * Grow the big block chain.
3653  */
3654  size = SmallBlockChainStream_GetSize(*ppsbChain);
3655  BlockChainStream_SetSize(bbTempChain, size);
3656 
3657  /*
3658  * Copy the contents of the small block chain to the big block chain
3659  * by small block size increments.
3660  */
3661  offset.u.LowPart = 0;
3662  offset.u.HighPart = 0;
3663  cbTotalRead.QuadPart = 0;
3664 
3666  do
3667  {
3668  resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3669  offset,
3670  min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3671  buffer,
3672  &cbRead);
3673  if (FAILED(resRead))
3674  break;
3675 
3676  if (cbRead > 0)
3677  {
3678  cbTotalRead.QuadPart += cbRead;
3679 
3680  resWrite = BlockChainStream_WriteAt(bbTempChain,
3681  offset,
3682  cbRead,
3683  buffer,
3684  &cbWritten);
3685 
3686  if (FAILED(resWrite))
3687  break;
3688 
3689  offset.u.LowPart += cbRead;
3690  }
3691  else
3692  {
3693  resRead = STG_E_READFAULT;
3694  break;
3695  }
3696  } while (cbTotalRead.QuadPart < size.QuadPart);
3698 
3699  size.u.HighPart = 0;
3700  size.u.LowPart = 0;
3701 
3702  if (FAILED(resRead) || FAILED(resWrite))
3703  {
3704  ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3705  BlockChainStream_SetSize(bbTempChain, size);
3706  BlockChainStream_Destroy(bbTempChain);
3707  return NULL;
3708  }
3709 
3710  /*
3711  * Destroy the small block chain.
3712  */
3713  streamEntryRef = (*ppsbChain)->ownerDirEntry;
3714  SmallBlockChainStream_SetSize(*ppsbChain, size);
3715  SmallBlockChainStream_Destroy(*ppsbChain);
3716  *ppsbChain = 0;
3717 
3718  /*
3719  * Change the directory entry. This chain is now a big block chain
3720  * and it doesn't reside in the small blocks chain anymore.
3721  */
3722  StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3723 
3724  streamEntry.startingBlock = bbHeadOfChain;
3725 
3726  StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3727 
3728  /*
3729  * Destroy the temporary entryless big block chain.
3730  * Create a new big block chain associated with this entry.
3731  */
3732  BlockChainStream_Destroy(bbTempChain);
3733  bigBlockChain = BlockChainStream_Construct(This,
3734  NULL,
3735  streamEntryRef);
3736 
3737  return bigBlockChain;
3738 }
3739 
3740 /******************************************************************************
3741  * Storage32Impl_BigBlocksToSmallBlocks
3742  *
3743  * This method will convert a big block chain to a small block chain.
3744  * The big block chain will be destroyed on success.
3745  */
3747  StorageImpl* This,
3748  BlockChainStream** ppbbChain,
3749  ULARGE_INTEGER newSize)
3750 {
3751  ULARGE_INTEGER size, offset, cbTotalRead;
3752  ULONG cbRead, cbWritten, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3753  DirRef streamEntryRef;
3754  HRESULT resWrite = S_OK, resRead = S_OK;
3755  DirEntry streamEntry;
3756  BYTE* buffer;
3757  SmallBlockChainStream* sbTempChain;
3758 
3759  TRACE("%p %p\n", This, ppbbChain);
3760 
3761  sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3762  DIRENTRY_NULL);
3763 
3764  if(!sbTempChain)
3765  return NULL;
3766 
3767  SmallBlockChainStream_SetSize(sbTempChain, newSize);
3768  size = BlockChainStream_GetSize(*ppbbChain);
3769  size.QuadPart = min(size.QuadPart, newSize.QuadPart);
3770 
3771  offset.u.HighPart = 0;
3772  offset.u.LowPart = 0;
3773  cbTotalRead.QuadPart = 0;
3774  buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3775  while(cbTotalRead.QuadPart < size.QuadPart)
3776  {
3777  resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3778  min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3779  buffer, &cbRead);
3780 
3781  if(FAILED(resRead))
3782  break;
3783 
3784  if(cbRead > 0)
3785  {
3786  cbTotalRead.QuadPart += cbRead;
3787 
3788  resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3789  cbRead, buffer, &cbWritten);
3790 
3791  if(FAILED(resWrite))
3792  break;
3793 
3794  offset.u.LowPart += cbRead;
3795  }
3796  else
3797  {
3798  resRead = STG_E_READFAULT;
3799  break;
3800  }
3801  }
3803 
3804  size.u.HighPart = 0;
3805  size.u.LowPart = 0;
3806 
3807  if(FAILED(resRead) || FAILED(resWrite))
3808  {
3809  ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3810  SmallBlockChainStream_SetSize(sbTempChain, size);
3811  SmallBlockChainStream_Destroy(sbTempChain);
3812  return NULL;
3813  }
3814 
3815  /* destroy the original big block chain */
3816  streamEntryRef = (*ppbbChain)->ownerDirEntry;
3817  BlockChainStream_SetSize(*ppbbChain, size);
3818  BlockChainStream_Destroy(*ppbbChain);
3819  *ppbbChain = NULL;
3820 
3821  StorageImpl_ReadDirEntry(This, streamEntryRef, &streamEntry);
3822  streamEntry.startingBlock = sbHeadOfChain;
3823  StorageImpl_WriteDirEntry(This, streamEntryRef, &streamEntry);
3824 
3825  SmallBlockChainStream_Destroy(sbTempChain);
3826  return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
3827 }
3828 
3829 /******************************************************************************
3830  * Storage32Impl_AddBlockDepot
3831  *
3832  * This will create a depot block, essentially it is a block initialized
3833  * to BLOCK_UNUSEDs.
3834  */
3835 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex, ULONG depotIndex)
3836 {
3837  BYTE blockBuffer[MAX_BIG_BLOCK_SIZE];
3838  ULONG rangeLockIndex = RANGELOCK_FIRST / This->bigBlockSize - 1;
3839  ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
3840  ULONG rangeLockDepot = rangeLockIndex / blocksPerDepot;
3841 
3842  /*
3843  * Initialize blocks as free
3844  */
3845  memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
3846 
3847  /* Reserve the range lock sector */
3848  if (depotIndex == rangeLockDepot)
3849  {
3850  ((ULONG*)blockBuffer)[rangeLockIndex % blocksPerDepot] = BLOCK_END_OF_CHAIN;
3851  }
3852 
3853  StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
3854 }
3855 
3856 /******************************************************************************
3857  * Storage32Impl_GetExtDepotBlock
3858  *
3859  * Returns the index of the block that corresponds to the specified depot
3860  * index. This method is only for depot indexes equal or greater than
3861  * COUNT_BBDEPOTINHEADER.
3862  */
3864 {
3865  ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3866  ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3867  ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3868  ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3869  ULONG blockIndex = BLOCK_UNUSED;
3870  ULONG extBlockIndex;
3871  BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3872  int index, num_blocks;
3873 
3874  assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3875 
3876  if (extBlockCount >= This->extBigBlockDepotCount)
3877  return BLOCK_UNUSED;
3878 
3879  if (This->indexExtBlockDepotCached != extBlockCount)
3880  {
3881  extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3882 
3883  StorageImpl_ReadBigBlock(This, extBlockIndex, depotBuffer, NULL);
3884 
3885  num_blocks = This->bigBlockSize / 4;
3886 
3887  for (index = 0; index < num_blocks; index++)
3888  {
3889  StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &blockIndex);
3890  This->extBlockDepotCached[index] = blockIndex;
3891  }
3892 
3893  This->indexExtBlockDepotCached = extBlockCount;
3894  }
3895 
3896  blockIndex = This->extBlockDepotCached[extBlockOffset];
3897 
3898  return blockIndex;
3899 }
3900 
3901 /******************************************************************************
3902  * Storage32Impl_SetExtDepotBlock
3903  *
3904  * Associates the specified block index to the specified depot index.
3905  * This method is only for depot indexes equal or greater than
3906  * COUNT_BBDEPOTINHEADER.
3907  */
3908 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
3909 {
3910  ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
3911  ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
3912  ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
3913  ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
3914  ULONG extBlockIndex;
3915 
3916  assert(depotIndex >= COUNT_BBDEPOTINHEADER);
3917 
3918  assert(extBlockCount < This->extBigBlockDepotCount);
3919 
3920  extBlockIndex = This->extBigBlockDepotLocations[extBlockCount];
3921 
3922  if (extBlockIndex != BLOCK_UNUSED)
3923  {
3924  StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
3925  extBlockOffset * sizeof(ULONG),
3926  blockIndex);
3927  }
3928 
3929  if (This->indexExtBlockDepotCached == extBlockCount)
3930  {
3931  This->extBlockDepotCached[extBlockOffset] = blockIndex;
3932  }
3933 }
3934 
3935 /******************************************************************************
3936  * Storage32Impl_AddExtBlockDepot
3937  *
3938  * Creates an extended depot block.
3939  */
3941 {
3942  ULONG numExtBlocks = This->extBigBlockDepotCount;
3943  ULONG nextExtBlock = This->extBigBlockDepotStart;
3944  BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
3946  ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
3947  ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
3948  ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
3949 
3950  index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
3951  blocksPerDepotBlock;
3952 
3953  if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
3954  {
3955  /*
3956  * The first extended block.
3957  */
3958  This->extBigBlockDepotStart = index;
3959  }
3960  else
3961  {
3962  /*
3963  * Find the last existing extended block.
3964  */
3965  nextExtBlock = This->extBigBlockDepotLocations[This->extBigBlockDepotCount-1];
3966 
3967  /*
3968  * Add the new extended block to the chain.
3969  */
3970  StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
3971  index);
3972  }
3973 
3974  /*
3975  * Initialize this block.
3976  */
3977  memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
3978  StorageImpl_WriteBigBlock(This, index, depotBuffer);
3979 
3980  /* Add the block to our cache. */
3981  if (This->extBigBlockDepotLocationsSize == numExtBlocks)
3982  {
3983  ULONG new_cache_size = (This->extBigBlockDepotLocationsSize+1)*2;
3984  ULONG *new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * new_cache_size);
3985 
3986  memcpy(new_cache, This->extBigBlockDepotLocations, sizeof(ULONG) * This->extBigBlockDepotLocationsSize);
3987  HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
3988 
3989  This->extBigBlockDepotLocations = new_cache;
3990  This->extBigBlockDepotLocationsSize = new_cache_size;
3991  }
3992  This->extBigBlockDepotLocations[numExtBlocks] = index;
3993 
3994  return index;
3995 }
3996 
3997 /************************************************************************
3998  * StorageImpl_GetNextBlockInChain
3999  *
4000  * This method will retrieve the block index of the next big block in
4001  * in the chain.
4002  *
4003  * Params: This - Pointer to the Storage object.
4004  * blockIndex - Index of the block to retrieve the chain
4005  * for.
4006  * nextBlockIndex - receives the return value.
4007  *
4008  * Returns: This method returns the index of the next block in the chain.
4009  * It will return the constants:
4010  * BLOCK_SPECIAL - If the block given was not part of a
4011  * chain.
4012  * BLOCK_END_OF_CHAIN - If the block given was the last in
4013  * a chain.
4014  * BLOCK_UNUSED - If the block given was not past of a chain
4015  * and is available.
4016  * BLOCK_EXTBBDEPOT - This block is part of the extended
4017  * big block depot.
4018  *
4019  * See Windows documentation for more details on IStorage methods.
4020  */
4022  StorageImpl* This,
4023  ULONG blockIndex,
4024  ULONG* nextBlockIndex)
4025 {
4026  ULONG offsetInDepot = blockIndex * sizeof (ULONG);
4027  ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
4028  ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
4029  BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
4030  ULONG read;
4031  ULONG depotBlockIndexPos;
4032  int index, num_blocks;
4033 
4034  *nextBlockIndex = BLOCK_SPECIAL;
4035 
4036  if(depotBlockCount >= This->bigBlockDepotCount)
4037  {
4038  WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
4039  This->bigBlockDepotCount);
4040  return STG_E_READFAULT;
4041  }
4042 
4043  /*
4044  * Cache the currently accessed depot block.
4045  */
4046  if (depotBlockCount != This->indexBlockDepotCached)
4047  {
4048  This->indexBlockDepotCached = depotBlockCount;
4049 
4050  if (depotBlockCount < COUNT_BBDEPOTINHEADER)
4051  {
4052  depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
4053  }
4054  else
4055  {
4056  /*
4057  * We have to look in the extended depot.
4058  */
4059  depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
4060  }
4061 
4062  StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
4063 
4064  if (!read)
4065  return STG_E_READFAULT;
4066 
4067  num_blocks = This->bigBlockSize / 4;
4068 
4069  for (index = 0; index < num_blocks; index++)
4070  {
4071  StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
4072  This->blockDepotCached[index] = *nextBlockIndex;
4073  }
4074  }
4075 
4076  *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
4077 
4078  return S_OK;
4079 }
4080 
4081 /******************************************************************************
4082  * Storage32Impl_GetNextExtendedBlock
4083  *
4084  * Given an extended block this method will return the next extended block.
4085  *
4086  * NOTES:
4087  * The last ULONG of an extended block is the block index of the next
4088  * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
4089  * depot.
4090  *
4091  * Return values:
4092  * - The index of the next extended block
4093  * - BLOCK_UNUSED: there is no next extended block.
4094  * - Any other return values denotes failure.
4095  */
4097 {
4098  ULONG nextBlockIndex = BLOCK_SPECIAL;
4099  ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
4100 
4101  StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
4102  &nextBlockIndex);
4103 
4104  return nextBlockIndex;
4105 }
4106 
4107 /******************************************************************************
4108  * StorageImpl_SetNextBlockInChain
4109  *
4110  * This method will write the index of the specified block's next block
4111  * in the big block depot.
4112  *
4113  * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
4114  * do the following
4115  *
4116  * StorageImpl_SetNextBlockInChain(This, 3, 1);
4117  * StorageImpl_SetNextBlockInChain(This, 1, 7);
4118  * StorageImpl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
4119  *
4120  */
4122  StorageImpl* This,
4123  ULONG blockIndex,
4124  ULONG nextBlock)
4125 {
4126  ULONG offsetInDepot = blockIndex * sizeof (ULONG);
4127  ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
4128  ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
4129  ULONG depotBlockIndexPos;
4130 
4131  assert(depotBlockCount < This->bigBlockDepotCount);
4132  assert(blockIndex != nextBlock);
4133 
4134  if (blockIndex == (RANGELOCK_FIRST / This->bigBlockSize) - 1)
4135  /* This should never happen (storage file format spec forbids it), but
4136  * older versions of Wine may have generated broken files. We don't want to
4137  * assert and potentially lose data, but we do want to know if this ever
4138  * happens in a newly-created file. */
4139  ERR("Using range lock page\n");
4140 
4141  if (depotBlockCount < COUNT_BBDEPOTINHEADER)
4142  {
4143  depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
4144  }
4145  else
4146  {
4147  /*
4148  * We have to look in the extended depot.
4149  */
4150  depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
4151  }
4152 
4153  StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
4154  nextBlock);
4155  /*
4156  * Update the cached block depot, if necessary.
4157  */
4158  if (depotBlockCount == This->indexBlockDepotCached)
4159  {
4160  This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
4161  }
4162 }
4163 
4164 /******************************************************************************
4165  * StorageImpl_GetNextFreeBigBlock
4166  *
4167  * Returns the index of the next free big block.
4168  * If the big block depot is filled, this method will enlarge it.
4169  *
4170  */
4172  StorageImpl* This)
4173 {
4174  ULONG depotBlockIndexPos;
4175  BYTE depotBuffer[MAX_BIG_BLOCK_SIZE];
4176  ULONG depotBlockOffset;
4177  ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
4178  ULONG nextBlockIndex = BLOCK_SPECIAL;
4179  int depotIndex = 0;
4180  ULONG freeBlock = BLOCK_UNUSED;
4181  ULONG read;
4182  ULARGE_INTEGER neededSize;
4183  STATSTG statstg;
4184 
4185  depotIndex = This->prevFreeBlock / blocksPerDepot;
4186  depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
4187 
4188  /*
4189  * Scan the entire big block depot until we find a block marked free
4190  */
4191  while (nextBlockIndex != BLOCK_UNUSED)
4192  {
4193  if (depotIndex < COUNT_BBDEPOTINHEADER)
4194  {
4195  depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
4196 
4197  /*
4198  * Grow the primary depot.
4199  */
4200  if (depotBlockIndexPos == BLOCK_UNUSED)
4201  {
4202  depotBlockIndexPos = depotIndex*blocksPerDepot;
4203 
4204  /*
4205  * Add a block depot.
4206  */
4207  Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
4208  This->bigBlockDepotCount++;
4209  This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
4210 
4211  /*
4212  * Flag it as a block depot.
4213  */
4215  depotBlockIndexPos,
4216  BLOCK_SPECIAL);
4217 
4218  /* Save new header information.
4219  */
4221  }
4222  }
4223  else
4224  {
4225  depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
4226 
4227  if (depotBlockIndexPos == BLOCK_UNUSED)
4228  {
4229  /*
4230  * Grow the extended depot.
4231  */
4232  ULONG extIndex = BLOCK_UNUSED;
4233  ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
4234  ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
4235 
4236  if (extBlockOffset == 0)
4237  {
4238  /* We need an extended block.
4239  */
4241  This->extBigBlockDepotCount++;
4242  depotBlockIndexPos = extIndex + 1;
4243  }
4244  else
4245  depotBlockIndexPos = depotIndex * blocksPerDepot;
4246 
4247  /*
4248  * Add a block depot and mark it in the extended block.
4249  */
4250  Storage32Impl_AddBlockDepot(This, depotBlockIndexPos, depotIndex);
4251  This->bigBlockDepotCount++;
4252  Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
4253 
4254  /* Flag the block depot.
4255  */
4257  depotBlockIndexPos,
4258  BLOCK_SPECIAL);
4259 
4260  /* If necessary, flag the extended depot block.
4261  */
4262  if (extIndex != BLOCK_UNUSED)
4264 
4265  /* Save header information.
4266  */
4268  }
4269  }
4270 
4271  StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer, &read);
4272 
4273  if (read)
4274  {
4275  while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
4276  ( nextBlockIndex != BLOCK_UNUSED))
4277  {
4278  StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
4279 
4280  if (nextBlockIndex == BLOCK_UNUSED)
4281  {
4282  freeBlock = (depotIndex * blocksPerDepot) +
4283  (depotBlockOffset/sizeof(ULONG));
4284  }
4285 
4286  depotBlockOffset += sizeof(ULONG);
4287  }
4288  }
4289 
4290  depotIndex++;
4291  depotBlockOffset = 0;
4292  }
4293 
4294  /*
4295  * make sure that the block physically exists before using it
4296  */
4297  neededSize.QuadPart = StorageImpl_GetBigBlockOffset(This, freeBlock)+This->bigBlockSize;
4298 
4299  ILockBytes_Stat(This->lockBytes, &statstg, STATFLAG_NONAME);
4300 
4301  if (neededSize.QuadPart > statstg.cbSize.QuadPart)
4302  ILockBytes_SetSize(This->lockBytes, neededSize);
4303 
4304  This->prevFreeBlock = freeBlock;
4305 
4306  return freeBlock;
4307 }
4308 
4309 /******************************************************************************
4310  * StorageImpl_FreeBigBlock
4311  *
4312  * This method will flag the specified block as free in the big block depot.
4313  */
4315  StorageImpl* This,
4316  ULONG blockIndex)
4317 {
4319 
4320  if (blockIndex < This->prevFreeBlock)
4321  This->prevFreeBlock = blockIndex;
4322 }
4323 
4324 
4326  DirRef index, const DirEntry *data)
4327 {
4330 }
4331 
4334 {
4337 }
4338 
4340 {
4341  int i;
4342 
4343  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4344  {
4345  if (!This->blockChainCache[i])
4346  {
4347  return &This->blockChainCache[i];
4348  }
4349  }
4350 
4351  i = This->blockChainToEvict;
4352 
4353  BlockChainStream_Destroy(This->blockChainCache[i]);
4354  This->blockChainCache[i] = NULL;
4355 
4356  This->blockChainToEvict++;
4357  if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
4358  This->blockChainToEvict = 0;
4359 
4360  return &This->blockChainCache[i];
4361 }
4362 
4364  DirRef index)
4365 {
4366  int i, free_index=-1;
4367 
4368  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4369  {
4370  if (!This->blockChainCache[i])
4371  {
4372  if (free_index == -1) free_index = i;
4373  }
4374  else if (This->blockChainCache[i]->ownerDirEntry == index)
4375  {
4376  return &This->blockChainCache[i];
4377  }
4378  }
4379 
4380  if (free_index == -1)
4381  {
4382  free_index = This->blockChainToEvict;
4383 
4384  BlockChainStream_Destroy(This->blockChainCache[free_index]);
4385  This->blockChainCache[free_index] = NULL;
4386 
4387  This->blockChainToEvict++;
4388  if (This->blockChainToEvict == BLOCKCHAIN_CACHE_SIZE)
4389  This->blockChainToEvict = 0;
4390  }
4391 
4392  This->blockChainCache[free_index] = BlockChainStream_Construct(This, NULL, index);
4393  return &This->blockChainCache[free_index];
4394 }
4395 
4397 {
4398  int i;
4399 
4400  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4401  {
4402  if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
4403  {
4404  BlockChainStream_Destroy(This->blockChainCache[i]);
4405  This->blockChainCache[i] = NULL;
4406  return;
4407  }
4408  }
4409 }
4410 
4412  ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
4413 {
4415  DirEntry data;
4416  HRESULT hr;
4417  ULONG bytesToRead;
4418 
4420  if (FAILED(hr)) return hr;
4421 
4422  if (data.size.QuadPart == 0)
4423  {
4424  *bytesRead = 0;
4425  return S_OK;
4426  }
4427 
4428  if (offset.QuadPart + size > data.size.QuadPart)
4429  {
4430  bytesToRead = data.size.QuadPart - offset.QuadPart;
4431  }
4432  else
4433  {
4434  bytesToRead = size;
4435  }
4436 
4437  if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4438  {
4440 
4442  if (!stream) return E_OUTOFMEMORY;
4443 
4444  hr = SmallBlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4445 
4447 
4448  return hr;
4449  }
4450  else
4451  {
4453 
4455  if (!stream) return E_OUTOFMEMORY;
4456 
4457  hr = BlockChainStream_ReadAt(stream, offset, bytesToRead, buffer, bytesRead);
4458 
4459  return hr;
4460  }
4461 }
4462 
4464  ULARGE_INTEGER newsize)
4465 {
4467  DirEntry data;
4468  HRESULT hr;
4469  SmallBlockChainStream *smallblock=NULL;
4470  BlockChainStream **pbigblock=NULL, *bigblock=NULL;
4471 
4473  if (FAILED(hr)) return hr;
4474 
4475  /* In simple mode keep the stream size above the small block limit */
4476  if (This->base.openFlags & STGM_SIMPLE)
4477  newsize.QuadPart = max(newsize.QuadPart, LIMIT_TO_USE_SMALL_BLOCK);
4478 
4479  if (data.size.QuadPart == newsize.QuadPart)
4480  return S_OK;
4481 
4482  /* Create a block chain object of the appropriate type */
4483  if (data.size.QuadPart == 0)
4484  {
4485  if (newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4486  {
4488  if (!smallblock) return E_OUTOFMEMORY;
4489  }
4490  else
4491  {
4493  bigblock = *pbigblock;
4494  if (!bigblock) return E_OUTOFMEMORY;
4495  }
4496  }
4497  else if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4498  {
4500  if (!smallblock) return E_OUTOFMEMORY;
4501  }
4502  else
4503  {
4505  bigblock = *pbigblock;
4506  if (!bigblock) return E_OUTOFMEMORY;
4507  }
4508 
4509  /* Change the block chain type if necessary. */
4510  if (smallblock && newsize.QuadPart >= LIMIT_TO_USE_SMALL_BLOCK)
4511  {
4512  bigblock = Storage32Impl_SmallBlocksToBigBlocks(This, &smallblock);
4513  if (!bigblock)
4514  {
4515  SmallBlockChainStream_Destroy(smallblock);
4516  return E_FAIL;
4517  }
4518 
4520  *pbigblock = bigblock;
4521  }
4522  else if (bigblock && newsize.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4523  {
4524  smallblock = Storage32Impl_BigBlocksToSmallBlocks(This, pbigblock, newsize);
4525  if (!smallblock)
4526  return E_FAIL;
4527  }
4528 
4529  /* Set the size of the block chain. */
4530  if (smallblock)
4531  {
4532  SmallBlockChainStream_SetSize(smallblock, newsize);
4533  SmallBlockChainStream_Destroy(smallblock);
4534  }
4535  else
4536  {
4537  BlockChainStream_SetSize(bigblock, newsize);
4538  }
4539 
4540  /* Set the size in the directory entry. */
4542  if (SUCCEEDED(hr))
4543  {
4544  data.size = newsize;
4545 
4547  }
4548  return hr;
4549 }
4550 
4553 {
4555  DirEntry data;
4556  HRESULT hr;
4557  ULARGE_INTEGER newSize;
4558 
4560  if (FAILED(hr)) return hr;
4561 
4562  /* Grow the stream if necessary */
4563  newSize.QuadPart = offset.QuadPart + size;
4564 
4565  if (newSize.QuadPart > data.size.QuadPart)
4566  {
4567  hr = StorageImpl_StreamSetSize(base, index, newSize);
4568  if (FAILED(hr))
4569  return hr;
4570 
4572  if (FAILED(hr)) return hr;
4573  }
4574 
4575  if (data.size.QuadPart < LIMIT_TO_USE_SMALL_BLOCK)
4576  {
4578 
4580  if (!stream) return E_OUTOFMEMORY;
4581 
4583 
4585 
4586  return hr;
4587  }
4588  else
4589  {
4591 
4593  if (!stream) return E_OUTOFMEMORY;
4594 
4596  }
4597 }
4598 
4600  DirRef src)
4601 {
4603  DirEntry dst_data, src_data;
4604  HRESULT hr;
4605 
4606  hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
4607 
4608  if (SUCCEEDED(hr))
4609  hr = StorageImpl_ReadDirEntry(This, src, &src_data);
4610 
4611  if (SUCCEEDED(hr))
4612  {
4614  dst_data.startingBlock = src_data.startingBlock;
4615  dst_data.size = src_data.size;
4616 
4617  hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
4618  }
4619 
4620  return hr;
4621 }
4622 
4624 {
4625  HRESULT hr=S_OK;
4626  DirEntry currentEntry;
4627  DirRef currentEntryRef;
4628  BlockChainStream *blockChainStream;
4629 
4630  if (create)
4631  {
4633  BYTE bigBlockBuffer[MAX_BIG_BLOCK_SIZE];
4634 
4635  /* Discard any existing data. */
4636  size.QuadPart = 0;
4637  ILockBytes_SetSize(This->lockBytes, size);
4638 
4639  /*
4640  * Initialize all header variables:
4641  * - The big block depot consists of one block and it is at block 0
4642  * - The directory table starts at block 1
4643  * - There is no small block depot
4644  */
4645  memset( This->bigBlockDepotStart,
4646  BLOCK_UNUSED,
4647  sizeof(This->bigBlockDepotStart));
4648 
4649  This->bigBlockDepotCount = 1;
4650  This->bigBlockDepotStart[0] = 0;
4651  This->rootStartBlock = 1;
4652  This->smallBlockLimit = LIMIT_TO_USE_SMALL_BLOCK;
4653  This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
4654  if (This->bigBlockSize == 4096)
4655  This->bigBlockSizeBits = MAX_BIG_BLOCK_SIZE_BITS;
4656  else
4657  This->bigBlockSizeBits = MIN_BIG_BLOCK_SIZE_BITS;
4658  This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
4659  This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
4660  This->extBigBlockDepotCount = 0;
4661 
4663 
4664  /*
4665  * Add one block for the big block depot and one block for the directory table
4666  */
4667  size.u.HighPart = 0;
4668  size.u.LowPart = This->bigBlockSize * 3;
4669  ILockBytes_SetSize(This->lockBytes, size);
4670 
4671  /*
4672  * Initialize the big block depot
4673  */
4674  memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
4675  StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
4676  StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
4677  StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
4678  }
4679  else
4680  {
4681  /*
4682  * Load the header for the file.
4683  */
4685 
4686  if (FAILED(hr))
4687  {
4688  return hr;
4689  }
4690  }
4691 
4692  /*
4693  * There is no block depot cached yet.
4694  */
4695  This->indexBlockDepotCached = 0xFFFFFFFF;
4696  This->indexExtBlockDepotCached = 0xFFFFFFFF;
4697 
4698  /*
4699  * Start searching for free blocks with block 0.
4700  */
4701  This->prevFreeBlock = 0;
4702 
4703  This->firstFreeSmallBlock = 0;
4704 
4705  /* Read the extended big block depot locations. */
4706  if (This->extBigBlockDepotCount != 0)
4707  {
4708  ULONG current_block = This->extBigBlockDepotStart;
4709  ULONG cache_size = This->extBigBlockDepotCount * 2;
4710  ULONG i;
4711 
4712  This->extBigBlockDepotLocations = HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * cache_size);
4713  if (!This->extBigBlockDepotLocations)
4714  {
4715  return E_OUTOFMEMORY;
4716  }
4717 
4718  This->extBigBlockDepotLocationsSize = cache_size;
4719 
4720  for (i=0; i<This->extBigBlockDepotCount; i++)
4721  {
4722  if (current_block == BLOCK_END_OF_CHAIN)
4723  {
4724  WARN("File has too few extended big block depot blocks.\n");
4725  return STG_E_DOCFILECORRUPT;
4726  }
4727  This->extBigBlockDepotLocations[i] = current_block;
4728  current_block = Storage32Impl_GetNextExtendedBlock(This, current_block);
4729  }
4730  }
4731  else
4732  {
4733  This->extBigBlockDepotLocations = NULL;
4734  This->extBigBlockDepotLocationsSize = 0;
4735  }
4736 
4737  /*
4738  * Create the block chain abstractions.
4739  */
4740  if(!(blockChainStream =
4741  BlockChainStream_Construct(This, &This->rootStartBlock, DIRENTRY_NULL)))
4742  {
4743  return STG_E_READFAULT;
4744  }
4745  if (!new_object)
4746  BlockChainStream_Destroy(This->rootBlockChain);
4747  This->rootBlockChain = blockChainStream;
4748 
4749  if(!(blockChainStream =
4750  BlockChainStream_Construct(This, &This->smallBlockDepotStart,
4751  DIRENTRY_NULL)))
4752  {
4753  return STG_E_READFAULT;
4754  }
4755  if (!new_object)
4756  BlockChainStream_Destroy(This->smallBlockDepotChain);
4757  This->smallBlockDepotChain = blockChainStream;
4758 
4759  /*
4760  * Write the root storage entry (memory only)
4761  */
4762  if (create)
4763  {
4764  static const WCHAR rootentryW[] = {'R','o','o','t',' ','E','n','t','r','y',0};
4765  DirEntry rootEntry;
4766  /*
4767  * Initialize the directory table
4768  */
4769  memset(&rootEntry, 0, sizeof(rootEntry));
4770  lstrcpyW(rootEntry.name, rootentryW);
4771  rootEntry.sizeOfNameString = sizeof(rootentryW);
4772  rootEntry.stgType = STGTY_ROOT;
4773  rootEntry.leftChild = DIRENTRY_NULL;
4774  rootEntry.rightChild = DIRENTRY_NULL;
4775  rootEntry.dirRootEntry = DIRENTRY_NULL;
4776  rootEntry.startingBlock = BLOCK_END_OF_CHAIN;
4777  rootEntry.size.u.HighPart = 0;
4778  rootEntry.size.u.LowPart = 0;
4779 
4780  StorageImpl_WriteDirEntry(This, 0, &rootEntry);
4781  }
4782 
4783  /*
4784  * Find the ID of the root storage.
4785  */
4786  currentEntryRef = 0;
4787 
4788  do
4789  {
4791  This,
4792  currentEntryRef,
4793  &currentEntry);
4794 
4795  if (SUCCEEDED(hr))
4796  {
4797  if ( (currentEntry.sizeOfNameString != 0 ) &&
4798  (currentEntry.stgType == STGTY_ROOT) )
4799  {
4800  This->base.storageDirEntry = currentEntryRef;
4801  }
4802  }
4803 
4804  currentEntryRef++;
4805 
4806  } while (SUCCEEDED(hr) && (This->base.storageDirEntry == DIRENTRY_NULL) );
4807 
4808  if (FAILED(hr))
4809  {
4810  return STG_E_READFAULT;
4811  }
4812 
4813  /*
4814  * Create the block chain abstraction for the small block root chain.
4815  */
4816  if(!(blockChainStream =
4817  BlockChainStream_Construct(This, NULL, This->base.storageDirEntry)))
4818  {
4819  return STG_E_READFAULT;
4820  }
4821  if (!new_object)
4822  BlockChainStream_Destroy(This->smallBlockRootChain);
4823  This->smallBlockRootChain = blockChainStream;
4824 
4825  if (!new_object)
4826  {
4827  int i;
4828  for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
4829  {
4830  BlockChainStream_Destroy(This->blockChainCache[i]);
4831  This->blockChainCache[i] = NULL;
4832  }
4833  }
4834 
4835  return hr;
4836 }
4837 
4839  ULONG* result, BOOL refresh)
4840 {
4842  HRESULT hr=S_OK;
4843  DWORD oldTransactionSig = This->transactionSig;
4844 
4845  if (refresh)
4846  {
4848  ULONG bytes_read;
4849  BYTE data[4];
4850 
4851  offset.u.HighPart = 0;
4852  offset.u.LowPart = OFFSET_TRANSACTIONSIG;
4853  hr = StorageImpl_ReadAt(This, offset, data, 4, &bytes_read);
4854 
4855  if (SUCCEEDED(hr))
4856  {
4857  StorageUtl_ReadDWord(data, 0, &This->transactionSig);
4858 
4859  if (oldTransactionSig != This->transactionSig)
4860  {
4861  /* Someone else wrote to this, so toss all cached information. */
4862  TRACE("signature changed\n");
4863 
4865  }
4866 
4867  if (FAILED(hr))
4868  This->transactionSig = oldTransactionSig;
4869  }
4870  }
4871 
4872  *result = This->transactionSig;
4873 
4874  return hr;
4875 }
4876 
4878  ULONG value)
4879 {
4881 
4882  This->transactionSig = value;
4884 
4885  return S_OK;
4886 }
4887 
4889  ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
4890 {
4891  if ((dwLockType & This->locks_supported) == 0)
4892  {
4893  if (supported) *supported = FALSE;
4894  return S_OK;
4895  }
4896 
4897  if (supported) *supported = TRUE;
4898  return ILockBytes_LockRegion(This->lockBytes, offset, cb, dwLockType);
4899 }
4900 
4902  ULARGE_INTEGER cb, DWORD dwLockType)
4903 {
4904  if ((dwLockType & This->locks_supported) == 0)
4905  return S_OK;
4906 
4907  return ILockBytes_UnlockRegion(This->lockBytes, offset, cb, dwLockType);
4908 }
4909 
4910 /* Internal function */
4912  ULARGE_INTEGER cb, DWORD dwLockType, BOOL *supported)
4913 {
4914  HRESULT hr;
4915  int delay = 0;
4916  DWORD start_time = GetTickCount();
4917  DWORD last_sanity_check = start_time;
4918  ULARGE_INTEGER sanity_offset, sanity_cb;
4919 
4920  sanity_offset.QuadPart = RANGELOCK_UNK1_FIRST;
4922 
4923  do
4924  {
4925  hr = StorageImpl_LockRegion(This, offset, cb, dwLockType, supported);
4926 
4928  {
4929  DWORD current_time = GetTickCount();
4930  if (current_time - start_time >= 20000)
4931  {
4932  /* timeout */
4933  break;
4934  }
4935  if (current_time - last_sanity_check >= 500)
4936  {
4937  /* Any storage implementation with the file open in a
4938  * shared mode should not lock these bytes for writing. However,
4939  * some programs (LibreOffice Writer) will keep ALL bytes locked
4940  * when opening in exclusive mode. We can use a read lock to
4941  * detect this case early, and not hang a full 20 seconds.
4942  *
4943  * This can collide with another attempt to open the file in
4944  * exclusive mode, but it's unlikely, and someone would fail anyway. */
4945  hr = StorageImpl_LockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ, NULL);
4947  break;
4948  if (SUCCEEDED(hr))
4949  {
4950  StorageImpl_UnlockRegion(This, sanity_offset, sanity_cb, WINE_LOCK_READ);
4952  }
4953 
4954  last_sanity_check = current_time;
4955  }
4956  Sleep(delay);
4957  if (delay < 150) delay++;
4958  }
4959  } while (hr == STG_E_ACCESSDENIED || hr == STG_E_LOCKVIOLATION);
4960 
4961  return hr;
4962 }
4963 
4965 {
4967  HRESULT hr;
4969 
4970  if (write)
4971  {
4972  /* Synchronous grab of second priority range, the commit lock, and the
4973  * lock-checking lock. */
4976  }
4977  else
4978  {
4979  offset.QuadPart = RANGELOCK_COMMIT;
4980  cb.QuadPart = 1;
4981  }
4982 
4983  hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, NULL);
4984 
4985  return hr;
4986 }
4987 
4989 {
4991  HRESULT hr;
4993 
4994  if (write)
4995  {
4998  }
4999  else
5000  {
5001  offset.QuadPart = RANGELOCK_COMMIT;
5002  cb.QuadPart = 1;
5003  }
5004 
5005  hr = StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5006 
5007  return hr;
5008 }
5009 
5011 {
5012  StorageImpl *This = (StorageImpl*) iface;
5013  STATSTG statstg;
5014  HRESULT hr;
5015 
5016  hr = ILockBytes_Stat(This->lockBytes, &statstg, 0);
5017 
5018  *result = statstg.pwcsName;
5019 
5020  return hr;
5021 }
5022 
5024  ULONG end, HRESULT fail_hr)
5025 {
5026  HRESULT hr;
5028 
5029  offset.QuadPart = start;
5030  cb.QuadPart = 1 + end - start;
5031 
5032  hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
5033  if (SUCCEEDED(hr)) StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5034 
5035  if (FAILED(hr))
5036  return fail_hr;
5037  else
5038  return S_OK;
5039 }
5040 
5042 {
5043  HRESULT hr=S_OK;
5044  int i, j;
5046 
5047  cb.QuadPart = 1;
5048 
5049  for (i=start; i<=end; i++)
5050  {
5051  offset.QuadPart = i;
5052  hr = StorageImpl_LockRegion(This, offset, cb, LOCK_ONLYONCE, NULL);
5054  break;
5055  }
5056 
5057  if (SUCCEEDED(hr))
5058  {
5059  for (j = 0; j < ARRAY_SIZE(This->locked_bytes); j++)
5060  {
5061  if (This->locked_bytes[j] == 0)
5062  {
5063  This->locked_bytes[j] = i;
5064  break;
5065  }
5066  }
5067  }
5068 
5069  return hr;
5070 }
5071 
5073 {
5074  HRESULT hr;
5077  DWORD share_mode = STGM_SHARE_MODE(openFlags);
5078  BOOL supported;
5079 
5080  if (openFlags & STGM_NOSNAPSHOT)
5081  {
5082  /* STGM_NOSNAPSHOT implies deny write */
5083  if (share_mode == STGM_SHARE_DENY_READ) share_mode = STGM_SHARE_EXCLUSIVE;
5084  else if (share_mode != STGM_SHARE_EXCLUSIVE) share_mode = STGM_SHARE_DENY_WRITE;
5085  }
5086 
5087  /* Wrap all other locking inside a single lock so we can check ranges safely */
5088  offset.QuadPart = RANGELOCK_CHECKLOCKS;
5089  cb.QuadPart = 1;
5090  hr = StorageImpl_LockRegionSync(This, offset, cb, LOCK_ONLYONCE, &supported);
5091 
5092  /* If the ILockBytes doesn't support locking that's ok. */
5093  if (!supported) return S_OK;
5094  else if (FAILED(hr)) return hr;
5095 
5096  hr = S_OK;
5097 
5098  /* First check for any conflicting locks. */
5099  if ((openFlags & STGM_PRIORITY) == STGM_PRIORITY)
5101 
5102  if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
5104 
5105  if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
5107 
5108  if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
5110 
5111  if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
5113 
5114  if (SUCCEEDED(hr) && STGM_ACCESS_MODE(openFlags) == STGM_READ && share_mode == STGM_SHARE_EXCLUSIVE)
5115  {
5117 
5118  if (SUCCEEDED(hr))
5120  }
5121 
5122  /* Then grab our locks. */
5123  if (SUCCEEDED(hr) && (openFlags & STGM_PRIORITY) == STGM_PRIORITY)
5124  {
5126  if (SUCCEEDED(hr))
5128  }
5129 
5130  if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_WRITE))
5132 
5133  if (SUCCEEDED(hr) && (STGM_ACCESS_MODE(openFlags) != STGM_READ))
5135 
5136  if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_READ || share_mode == STGM_SHARE_EXCLUSIVE))
5138 
5139  if (SUCCEEDED(hr) && (share_mode == STGM_SHARE_DENY_WRITE || share_mode == STGM_SHARE_EXCLUSIVE))
5141 
5142  if (SUCCEEDED(hr) && (openFlags & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT)
5144 
5145  offset.QuadPart = RANGELOCK_CHECKLOCKS;
5146  cb.QuadPart = 1;
5147  StorageImpl_UnlockRegion(This, offset, cb, LOCK_ONLYONCE);
5148 
5149  return hr;
5150 }
5151 
5153 {
5154  StorageImpl *This = (StorageImpl*)storage;
5155  int i;
5156  HRESULT hr;
5157  TRACE("(%p)\n", This);
5158 
5159  hr = BlockChainStream_Flush(This->smallBlockRootChain);
5160 
5161  if (SUCCEEDED(hr))
5162  hr = BlockChainStream_Flush(This->rootBlockChain);
5163 
5164  if (SUCCEEDED(hr))
5165  hr = BlockChainStream_Flush(This->smallBlockDepotChain);
5166 
5167  for (i=0; SUCCEEDED(hr) && i<BLOCKCHAIN_CACHE_SIZE; i++)
5168  if (This->blockChainCache[i])
5169  hr = BlockChainStream_Flush(This->blockChainCache[i]);
5170 
5171  if (SUCCEEDED(hr))
5172  hr = ILockBytes_Flush(This->lockBytes);
5173 
5174  return hr;
5175 }
5176 
5178 {
5179  StorageImpl *This = (StorageImpl*) iface;
5180 
5182 
5183  This->base.reverted = TRUE;
5184 }
5185 
5187 {
5188  StorageImpl *This = (StorageImpl*) iface;
5189  int i;
5190  TRACE("(%p)\n", This);
5191 
5192  StorageImpl_Flush(iface);
5193 
5194  StorageImpl_Invalidate(iface);
5195 
5196  HeapFree(GetProcessHeap(), 0, This->extBigBlockDepotLocations);
5197 
5198  BlockChainStream_Destroy(This->smallBlockRootChain);
5199  BlockChainStream_Destroy(This->rootBlockChain);
5200  BlockChainStream_Destroy(This->smallBlockDepotChain);
5201 
5202  for (i = 0; i < BLOCKCHAIN_CACHE_SIZE; i++)