Commit 226363f5 authored by Vincent Povirk's avatar Vincent Povirk Committed by Alexandre Julliard

ole32: Implement transacted storage.

parent d799f7e1
...@@ -126,6 +126,11 @@ typedef struct TransactedSnapshotImpl ...@@ -126,6 +126,11 @@ typedef struct TransactedSnapshotImpl
struct StorageBaseImpl base; struct StorageBaseImpl base;
/* /*
* Changes are temporarily saved to the snapshot.
*/
StorageBaseImpl *snapshot;
/*
* Changes are committed to the transacted parent. * Changes are committed to the transacted parent.
*/ */
StorageBaseImpl *transactedParent; StorageBaseImpl *transactedParent;
...@@ -1288,6 +1293,46 @@ static HRESULT StorageImpl_DestroyDirEntry( ...@@ -1288,6 +1293,46 @@ static HRESULT StorageImpl_DestroyDirEntry(
} }
/***************************************************************************
*
* Internal Method
*
* Destroy an entry, its attached data, and all entries reachable from it.
*/
static HRESULT DestroyReachableEntries(
StorageBaseImpl *base,
DirRef index)
{
HRESULT hr = S_OK;
DirEntry data;
ULARGE_INTEGER zero;
zero.QuadPart = 0;
if (index != DIRENTRY_NULL)
{
hr = StorageBaseImpl_ReadDirEntry(base, index, &data);
if (SUCCEEDED(hr))
hr = DestroyReachableEntries(base, data.dirRootEntry);
if (SUCCEEDED(hr))
hr = DestroyReachableEntries(base, data.leftChild);
if (SUCCEEDED(hr))
hr = DestroyReachableEntries(base, data.rightChild);
if (SUCCEEDED(hr))
hr = StorageBaseImpl_StreamSetSize(base, index, zero);
if (SUCCEEDED(hr))
hr = StorageBaseImpl_DestroyDirEntry(base, index);
}
return hr;
}
/**************************************************************************** /****************************************************************************
* *
* Internal Method * Internal Method
...@@ -3861,12 +3906,139 @@ SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks( ...@@ -3861,12 +3906,139 @@ SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
return SmallBlockChainStream_Construct(This, NULL, streamEntryRef); return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
} }
static HRESULT CreateSnapshotFile(StorageBaseImpl* original, StorageBaseImpl **snapshot)
{
HRESULT hr;
DirEntry parentData, snapshotData;
hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_DELETEONRELEASE,
0, (IStorage**)snapshot);
if (SUCCEEDED(hr))
{
hr = StorageBaseImpl_ReadDirEntry(original,
original->storageDirEntry, &parentData);
if (SUCCEEDED(hr))
hr = StorageBaseImpl_ReadDirEntry((*snapshot),
(*snapshot)->storageDirEntry, &snapshotData);
if (SUCCEEDED(hr))
{
memcpy(snapshotData.name, parentData.name, sizeof(snapshotData.name));
snapshotData.sizeOfNameString = parentData.sizeOfNameString;
snapshotData.stgType = parentData.stgType;
snapshotData.clsid = parentData.clsid;
snapshotData.ctime = parentData.ctime;
snapshotData.mtime = parentData.mtime;
hr = StorageBaseImpl_WriteDirEntry((*snapshot),
(*snapshot)->storageDirEntry, &snapshotData);
}
if (SUCCEEDED(hr))
hr = IStorage_CopyTo((IStorage*)original, 0, NULL, NULL,
(IStorage*)(*snapshot));
if (FAILED(hr)) IStorage_Release((IStorage*)(*snapshot));
}
return hr;
}
static HRESULT WINAPI TransactedSnapshotImpl_Commit( static HRESULT WINAPI TransactedSnapshotImpl_Commit(
IStorage* iface, IStorage* iface,
DWORD grfCommitFlags) /* [in] */ DWORD grfCommitFlags) /* [in] */
{ {
FIXME("(%p,%x): stub\n", iface, grfCommitFlags); TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
return S_OK; HRESULT hr;
DirEntry data, tempStorageData, snapshotRootData;
DirRef tempStorageEntry, oldDirRoot;
StorageInternalImpl *tempStorage;
TRACE("(%p,%x)\n", iface, grfCommitFlags);
/* Cannot commit a read-only transacted storage */
if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
return STG_E_ACCESSDENIED;
/* To prevent data loss, we create the new structure in the file before we
* delete the old one, so that in case of errors the old data is intact. We
* shouldn't do this if STGC_OVERWRITE is set, but that flag should only be
* needed in the rare situation where we have just enough free disk space to
* overwrite the existing data. */
/* Create an orphaned storage in the parent for the new directory structure. */
memset(&data, 0, sizeof(data));
data.name[0] = 'D';
data.sizeOfNameString = 1;
data.stgType = STGTY_STORAGE;
data.leftChild = DIRENTRY_NULL;
data.rightChild = DIRENTRY_NULL;
data.dirRootEntry = DIRENTRY_NULL;
hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &data, &tempStorageEntry);
if (FAILED(hr)) return hr;
tempStorage = StorageInternalImpl_Construct(This->transactedParent,
STGM_READWRITE|STGM_SHARE_EXCLUSIVE, tempStorageEntry);
if (tempStorage)
{
hr = IStorage_CopyTo((IStorage*)This->snapshot, 0, NULL, NULL,
(IStorage*)tempStorage);
list_init(&tempStorage->ParentListEntry);
IStorage_Release((IStorage*) tempStorage);
}
else
hr = E_OUTOFMEMORY;
if (FAILED(hr))
{
DestroyReachableEntries(This->transactedParent, tempStorageEntry);
return hr;
}
/* Update the storage to use the new data in one step. */
hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
This->transactedParent->storageDirEntry, &data);
if (SUCCEEDED(hr))
{
hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
tempStorageEntry, &tempStorageData);
}
if (SUCCEEDED(hr))
{
hr = StorageBaseImpl_ReadDirEntry(This->snapshot,
This->snapshot->storageDirEntry, &snapshotRootData);
}
if (SUCCEEDED(hr))
{
oldDirRoot = data.dirRootEntry;
data.dirRootEntry = tempStorageData.dirRootEntry;
data.clsid = snapshotRootData.clsid;
data.ctime = snapshotRootData.ctime;
data.mtime = snapshotRootData.mtime;
hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
This->transactedParent->storageDirEntry, &data);
}
if (SUCCEEDED(hr))
{
/* Destroy the old now-orphaned data. */
DestroyReachableEntries(This->transactedParent, oldDirRoot);
StorageBaseImpl_DestroyDirEntry(This->transactedParent, tempStorageEntry);
}
else
{
DestroyReachableEntries(This->transactedParent, tempStorageEntry);
}
return hr;
} }
static HRESULT WINAPI TransactedSnapshotImpl_Revert( static HRESULT WINAPI TransactedSnapshotImpl_Revert(
...@@ -3896,6 +4068,8 @@ static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface) ...@@ -3896,6 +4068,8 @@ static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
IStorage_Release((IStorage*)This->transactedParent); IStorage_Release((IStorage*)This->transactedParent);
IStorage_Release((IStorage*)This->snapshot);
HeapFree(GetProcessHeap(), 0, This); HeapFree(GetProcessHeap(), 0, This);
} }
...@@ -3904,7 +4078,7 @@ static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base, ...@@ -3904,7 +4078,7 @@ static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
{ {
TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
return StorageBaseImpl_CreateDirEntry(This->transactedParent, return StorageBaseImpl_CreateDirEntry(This->snapshot,
newData, index); newData, index);
} }
...@@ -3913,7 +4087,7 @@ static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base, ...@@ -3913,7 +4087,7 @@ static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
{ {
TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
return StorageBaseImpl_WriteDirEntry(This->transactedParent, return StorageBaseImpl_WriteDirEntry(This->snapshot,
index, data); index, data);
} }
...@@ -3922,7 +4096,7 @@ static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base, ...@@ -3922,7 +4096,7 @@ static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
{ {
TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
return StorageBaseImpl_ReadDirEntry(This->transactedParent, return StorageBaseImpl_ReadDirEntry(This->snapshot,
index, data); index, data);
} }
...@@ -3931,7 +4105,7 @@ static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base, ...@@ -3931,7 +4105,7 @@ static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
{ {
TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
return StorageBaseImpl_DestroyDirEntry(This->transactedParent, return StorageBaseImpl_DestroyDirEntry(This->snapshot,
index); index);
} }
...@@ -3940,7 +4114,7 @@ static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base, ...@@ -3940,7 +4114,7 @@ static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
{ {
TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
return StorageBaseImpl_StreamReadAt(This->transactedParent, return StorageBaseImpl_StreamReadAt(This->snapshot,
index, offset, size, buffer, bytesRead); index, offset, size, buffer, bytesRead);
} }
...@@ -3949,7 +4123,7 @@ static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base, ...@@ -3949,7 +4123,7 @@ static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
{ {
TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
return StorageBaseImpl_StreamWriteAt(This->transactedParent, return StorageBaseImpl_StreamWriteAt(This->snapshot,
index, offset, size, buffer, bytesWritten); index, offset, size, buffer, bytesWritten);
} }
...@@ -3958,7 +4132,7 @@ static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base, ...@@ -3958,7 +4132,7 @@ static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
{ {
TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base; TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
return StorageBaseImpl_StreamSetSize(This->transactedParent, return StorageBaseImpl_StreamSetSize(This->snapshot,
index, newsize); index, newsize);
} }
...@@ -4000,6 +4174,8 @@ static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl = ...@@ -4000,6 +4174,8 @@ static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage, static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
TransactedSnapshotImpl** result) TransactedSnapshotImpl** result)
{ {
HRESULT hr;
*result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl)); *result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
if (*result) if (*result)
{ {
...@@ -4016,18 +4192,26 @@ static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage, ...@@ -4016,18 +4192,26 @@ static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
(*result)->base.ref = 1; (*result)->base.ref = 1;
(*result)->base.storageDirEntry = parentStorage->storageDirEntry;
(*result)->base.openFlags = parentStorage->openFlags; (*result)->base.openFlags = parentStorage->openFlags;
(*result)->base.filename = parentStorage->filename; (*result)->base.filename = parentStorage->filename;
/* parentStorage already has 1 reference, which we take over here. */ /* Create a new temporary storage to act as the snapshot */
(*result)->transactedParent = parentStorage; hr = CreateSnapshotFile(parentStorage, &(*result)->snapshot);
parentStorage->transactedChild = (StorageBaseImpl*)*result; if (SUCCEEDED(hr))
{
(*result)->base.storageDirEntry = (*result)->snapshot->storageDirEntry;
return S_OK; /* parentStorage already has 1 reference, which we take over here. */
(*result)->transactedParent = parentStorage;
parentStorage->transactedChild = (StorageBaseImpl*)*result;
}
if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
return hr;
} }
else else
return E_OUTOFMEMORY; return E_OUTOFMEMORY;
......
...@@ -1003,17 +1003,13 @@ static void test_transact(void) ...@@ -1003,17 +1003,13 @@ static void test_transact(void)
r = IStorage_OpenStorage(stg, stmname, NULL, STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 ); r = IStorage_OpenStorage(stg, stmname, NULL, STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 );
ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream failed %08x\n", r); ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream failed %08x\n", r);
todo_wine {
r = IStorage_OpenStream(stg, stmname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm ); r = IStorage_OpenStream(stg, stmname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm );
ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream should fail %08x\n", r); ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream should fail %08x\n", r);
}
if (r == S_OK) if (r == S_OK)
IStream_Release(stm); IStream_Release(stm);
todo_wine {
r = IStorage_OpenStorage(stg, stgname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 ); r = IStorage_OpenStorage(stg, stgname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 );
ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStorage should fail %08x\n", r); ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStorage should fail %08x\n", r);
}
if (r == S_OK) if (r == S_OK)
IStorage_Release(stg2); IStorage_Release(stg2);
...@@ -1035,7 +1031,7 @@ static void test_transact(void) ...@@ -1035,7 +1031,7 @@ static void test_transact(void)
IStorage_Release(stg3); IStorage_Release(stg3);
r = IStorage_OpenStorage(stg2, stgname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg3 ); r = IStorage_OpenStorage(stg2, stgname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg3 );
todo_wine ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStorage should fail %08x\n", r); ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStorage should fail %08x\n", r);
if (r == S_OK) if (r == S_OK)
IStorage_Release(stg3); IStorage_Release(stg3);
...@@ -1423,7 +1419,7 @@ static void test_nonroot_transacted(void) ...@@ -1423,7 +1419,7 @@ static void test_nonroot_transacted(void)
/* But changes cannot be committed. */ /* But changes cannot be committed. */
r = IStorage_Commit(stg2, 0); r = IStorage_Commit(stg2, 0);
todo_wine ok(r==STG_E_ACCESSDENIED, "IStorage->Commit should fail, hr=%08x\n", r); ok(r==STG_E_ACCESSDENIED, "IStorage->Commit should fail, hr=%08x\n", r);
IStorage_Release(stg2); IStorage_Release(stg2);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment