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
struct StorageBaseImpl base;
/*
* Changes are temporarily saved to the snapshot.
*/
StorageBaseImpl *snapshot;
/*
* Changes are committed to the transacted parent.
*/
StorageBaseImpl *transactedParent;
......@@ -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
......@@ -3861,12 +3906,139 @@ SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
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(
IStorage* iface,
DWORD grfCommitFlags) /* [in] */
{
FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
return S_OK;
TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
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(
......@@ -3896,6 +4068,8 @@ static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
IStorage_Release((IStorage*)This->transactedParent);
IStorage_Release((IStorage*)This->snapshot);
HeapFree(GetProcessHeap(), 0, This);
}
......@@ -3904,7 +4078,7 @@ static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
{
TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
return StorageBaseImpl_CreateDirEntry(This->transactedParent,
return StorageBaseImpl_CreateDirEntry(This->snapshot,
newData, index);
}
......@@ -3913,7 +4087,7 @@ static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
{
TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
return StorageBaseImpl_WriteDirEntry(This->transactedParent,
return StorageBaseImpl_WriteDirEntry(This->snapshot,
index, data);
}
......@@ -3922,7 +4096,7 @@ static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
{
TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
return StorageBaseImpl_ReadDirEntry(This->transactedParent,
return StorageBaseImpl_ReadDirEntry(This->snapshot,
index, data);
}
......@@ -3931,7 +4105,7 @@ static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
{
TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
return StorageBaseImpl_DestroyDirEntry(This->transactedParent,
return StorageBaseImpl_DestroyDirEntry(This->snapshot,
index);
}
......@@ -3940,7 +4114,7 @@ static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
{
TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
return StorageBaseImpl_StreamReadAt(This->transactedParent,
return StorageBaseImpl_StreamReadAt(This->snapshot,
index, offset, size, buffer, bytesRead);
}
......@@ -3949,7 +4123,7 @@ static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
{
TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
return StorageBaseImpl_StreamWriteAt(This->transactedParent,
return StorageBaseImpl_StreamWriteAt(This->snapshot,
index, offset, size, buffer, bytesWritten);
}
......@@ -3958,7 +4132,7 @@ static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
{
TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
return StorageBaseImpl_StreamSetSize(This->transactedParent,
return StorageBaseImpl_StreamSetSize(This->snapshot,
index, newsize);
}
......@@ -4000,6 +4174,8 @@ static const StorageBaseImplVtbl TransactedSnapshotImpl_BaseVtbl =
static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
TransactedSnapshotImpl** result)
{
HRESULT hr;
*result = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedSnapshotImpl));
if (*result)
{
......@@ -4016,18 +4192,26 @@ static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
(*result)->base.ref = 1;
(*result)->base.storageDirEntry = parentStorage->storageDirEntry;
(*result)->base.openFlags = parentStorage->openFlags;
(*result)->base.filename = parentStorage->filename;
/* parentStorage already has 1 reference, which we take over here. */
(*result)->transactedParent = parentStorage;
/* Create a new temporary storage to act as the snapshot */
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
return E_OUTOFMEMORY;
......
......@@ -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 );
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 );
ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream should fail %08x\n", r);
}
if (r == S_OK)
IStream_Release(stm);
todo_wine {
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);
}
if (r == S_OK)
IStorage_Release(stg2);
......@@ -1035,7 +1031,7 @@ static void test_transact(void)
IStorage_Release(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)
IStorage_Release(stg3);
......@@ -1423,7 +1419,7 @@ static void test_nonroot_transacted(void)
/* But changes cannot be committed. */
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);
}
......
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