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