* These are static/global variables and internal data structures that the
* OLE module uses to maintain it's state.
*/
-typedef struct tagDropTargetNode
-{
- HWND hwndTarget;
- IDropTarget* dropTarget;
- struct list entry;
-} DropTargetNode;
-
typedef struct tagTrackerWindowInfo
{
IDataObject* dataObject;
/*
* Name of our registered window class.
*/
-static const char OLEDD_DRAGTRACKERCLASS[] = "WineDragDropTracker32";
+static const WCHAR OLEDD_DRAGTRACKERCLASS[] =
+ {'W','i','n','e','D','r','a','g','D','r','o','p','T','r','a','c','k','e','r','3','2',0};
/*
- * This is the head of the Drop target container.
+ * Name of menu descriptor property.
*/
-static struct list targetListHead = LIST_INIT(targetListHead);
+static const WCHAR prop_olemenuW[] =
+ {'P','R','O','P','_','O','L','E','M','e','n','u','D','e','s','c','r','i','p','t','o','r',0};
+
+/* property to store IDropTarget pointer */
+static const WCHAR prop_oledroptarget[] =
+ {'O','l','e','D','r','o','p','T','a','r','g','e','t','I','n','t','e','r','f','a','c','e',0};
+
+static const WCHAR clsidfmtW[] =
+ {'C','L','S','I','D','\\','{','%','0','8','x','-','%','0','4','x','-','%','0','4','x','-',
+ '%','0','2','x','%','0','2','x','-','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x',
+ '%','0','2','x','%','0','2','x','}','\\',0};
+
+static const WCHAR emptyW[] = { 0 };
/******************************************************************************
* These are the prototypes of miscellaneous utility methods
/******************************************************************************
* These are the prototypes of the utility methods used for OLE Drag n Drop
*/
-static void OLEDD_Initialize(void);
-static DropTargetNode* OLEDD_FindDropTarget(
- HWND hwndOfTarget);
-static void OLEDD_FreeDropTarget(DropTargetNode*, BOOL);
-static LRESULT WINAPI OLEDD_DragTrackerWindowProc(
- HWND hwnd,
- UINT uMsg,
- WPARAM wParam,
- LPARAM lParam);
-static void OLEDD_TrackMouseMove(
- TrackerWindowInfo* trackerInfo);
-static void OLEDD_TrackStateChange(
- TrackerWindowInfo* trackerInfo);
+static void OLEDD_Initialize(void);
+static LRESULT WINAPI OLEDD_DragTrackerWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+static void OLEDD_TrackMouseMove(TrackerWindowInfo* trackerInfo);
+static void OLEDD_TrackStateChange(TrackerWindowInfo* trackerInfo);
static DWORD OLEDD_GetButtonState(void);
-
/******************************************************************************
* OleBuildVersion [OLE32.@]
*/
return 0;
}
+/***
+ * OLEDD_FindDropTarget()
+ *
+ * Returns IDropTarget pointer registered for this window.
+ */
+static inline IDropTarget* OLEDD_FindDropTarget(HWND hwnd)
+{
+ return GetPropW(hwnd, prop_oledroptarget);
+}
+
/***********************************************************************
* RegisterDragDrop (OLE32.@)
*/
-HRESULT WINAPI RegisterDragDrop(
- HWND hwnd,
- LPDROPTARGET pDropTarget)
+HRESULT WINAPI RegisterDragDrop(HWND hwnd, LPDROPTARGET pDropTarget)
{
- DropTargetNode* dropTargetInfo;
+ DWORD pid = 0;
TRACE("(%p,%p)\n", hwnd, pDropTarget);
return DRAGDROP_E_INVALIDHWND;
}
- /*
- * First, check if the window is already registered.
- */
- dropTargetInfo = OLEDD_FindDropTarget(hwnd);
+ /* block register for other processes windows */
+ GetWindowThreadProcessId(hwnd, &pid);
+ if (pid != GetCurrentProcessId())
+ {
+ FIXME("register for another process windows is disabled\n");
+ return DRAGDROP_E_INVALIDHWND;
+ }
- if (dropTargetInfo!=NULL)
+ /* check if the window is already registered */
+ if (OLEDD_FindDropTarget(hwnd))
return DRAGDROP_E_ALREADYREGISTERED;
- /*
- * If it's not there, we can add it. We first create a node for it.
- */
- dropTargetInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(DropTargetNode));
-
- if (dropTargetInfo==NULL)
- return E_OUTOFMEMORY;
-
- dropTargetInfo->hwndTarget = hwnd;
-
- /*
- * Don't forget that this is an interface pointer, need to nail it down since
- * we keep a copy of it.
- */
IDropTarget_AddRef(pDropTarget);
- dropTargetInfo->dropTarget = pDropTarget;
-
- list_add_tail(&targetListHead, &dropTargetInfo->entry);
+ SetPropW(hwnd, prop_oledroptarget, pDropTarget);
return S_OK;
}
/***********************************************************************
* RevokeDragDrop (OLE32.@)
*/
-HRESULT WINAPI RevokeDragDrop(
- HWND hwnd)
+HRESULT WINAPI RevokeDragDrop(HWND hwnd)
{
- DropTargetNode* dropTargetInfo;
+ IDropTarget* droptarget;
TRACE("(%p)\n", hwnd);
return DRAGDROP_E_INVALIDHWND;
}
- /*
- * First, check if the window is already registered.
- */
- dropTargetInfo = OLEDD_FindDropTarget(hwnd);
-
- /*
- * If it ain't in there, it's an error.
- */
- if (dropTargetInfo==NULL)
+ /* no registration data */
+ if (!(droptarget = OLEDD_FindDropTarget(hwnd)))
return DRAGDROP_E_NOTREGISTERED;
- OLEDD_FreeDropTarget(dropTargetInfo, TRUE);
+ IDropTarget_Release(droptarget);
+ RemovePropW(hwnd, prop_oledroptarget);
return S_OK;
}
DWORD dwFormOfType,
LPOLESTR* pszUserType)
{
- char keyName[60];
+ WCHAR keyName[60];
DWORD dwKeyType;
DWORD cbData;
HKEY clsidKey;
LONG hres;
- LPSTR buffer;
- HRESULT retVal;
+
/*
* Initialize the out parameter.
*/
/*
* Build the key name we're looking for
*/
- sprintf( keyName, "CLSID\\{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\\",
- clsid->Data1, clsid->Data2, clsid->Data3,
- clsid->Data4[0], clsid->Data4[1], clsid->Data4[2], clsid->Data4[3],
- clsid->Data4[4], clsid->Data4[5], clsid->Data4[6], clsid->Data4[7] );
+ sprintfW( keyName, clsidfmtW,
+ clsid->Data1, clsid->Data2, clsid->Data3,
+ clsid->Data4[0], clsid->Data4[1], clsid->Data4[2], clsid->Data4[3],
+ clsid->Data4[4], clsid->Data4[5], clsid->Data4[6], clsid->Data4[7] );
- TRACE("(%s, %d, %p)\n", keyName, dwFormOfType, pszUserType);
+ TRACE("(%s, %d, %p)\n", debugstr_w(keyName), dwFormOfType, pszUserType);
/*
* Open the class id Key
*/
- hres = RegOpenKeyA(HKEY_CLASSES_ROOT,
+ hres = RegOpenKeyW(HKEY_CLASSES_ROOT,
keyName,
&clsidKey);
*/
cbData = 0;
- hres = RegQueryValueExA(clsidKey,
- "",
+ hres = RegQueryValueExW(clsidKey,
+ emptyW,
NULL,
&dwKeyType,
NULL,
/*
* Allocate a buffer for the registry value.
*/
- *pszUserType = CoTaskMemAlloc(cbData*2);
+ *pszUserType = CoTaskMemAlloc(cbData);
if (*pszUserType==NULL)
{
return E_OUTOFMEMORY;
}
- buffer = HeapAlloc(GetProcessHeap(), 0, cbData);
-
- if (buffer == NULL)
- {
- RegCloseKey(clsidKey);
- CoTaskMemFree(*pszUserType);
- *pszUserType=NULL;
- return E_OUTOFMEMORY;
- }
-
- hres = RegQueryValueExA(clsidKey,
- "",
+ hres = RegQueryValueExW(clsidKey,
+ emptyW,
NULL,
&dwKeyType,
- (LPBYTE) buffer,
+ (LPBYTE) *pszUserType,
&cbData);
RegCloseKey(clsidKey);
-
- if (hres!=ERROR_SUCCESS)
+ if (hres != ERROR_SUCCESS)
{
CoTaskMemFree(*pszUserType);
- *pszUserType=NULL;
+ *pszUserType = NULL;
- retVal = REGDB_E_READREGDB;
- }
- else
- {
- MultiByteToWideChar( CP_ACP, 0, buffer, -1, *pszUserType, cbData /*FIXME*/ );
- retVal = S_OK;
+ return REGDB_E_READREGDB;
}
- HeapFree(GetProcessHeap(), 0, buffer);
- return retVal;
+ return S_OK;
}
/***********************************************************************
DWORD dwOKEffect, /* [in] effects allowed by the source */
DWORD *pdwEffect) /* [out] ptr to effects of the source */
{
+ static const WCHAR trackerW[] = {'T','r','a','c','k','e','r','W','i','n','d','o','w',0};
TrackerWindowInfo trackerInfo;
HWND hwndTrackWindow;
MSG msg;
- TRACE("(DataObject %p, DropSource %p)\n", pDataObject, pDropSource);
+ TRACE("(%p, %p, %d, %p)\n", pDataObject, pDropSource, dwOKEffect, pdwEffect);
+
+ if (!pDataObject || !pDropSource || !pdwEffect)
+ return E_INVALIDARG;
/*
* Setup the drag n drop tracking window.
*/
- if (!IsValidInterface((LPUNKNOWN)pDropSource))
- return E_INVALIDARG;
trackerInfo.dataObject = pDataObject;
trackerInfo.dropSource = pDropSource;
trackerInfo.curTargetHWND = 0;
trackerInfo.curDragTarget = 0;
- hwndTrackWindow = CreateWindowA(OLEDD_DRAGTRACKERCLASS, "TrackerWindow",
+ hwndTrackWindow = CreateWindowW(OLEDD_DRAGTRACKERCLASS, trackerW,
WS_POPUP, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, 0,
&trackerInfo);
- if (hwndTrackWindow!=0)
+ if (hwndTrackWindow)
{
/*
* Capture the mouse input
/*
* Pump messages. All mouse input should go to the capture window.
*/
- while (!trackerInfo.trackingDone && GetMessageA(&msg, 0, 0, 0) )
+ while (!trackerInfo.trackingDone && GetMessageW(&msg, 0, 0, 0) )
{
trackerInfo.curMousePos.x = msg.pt.x;
trackerInfo.curMousePos.y = msg.pt.y;
/*
* Dispatch the messages only when it's not a keyboard message.
*/
- DispatchMessageA(&msg);
+ DispatchMessageW(&msg);
}
}
DWORD dwAspect,
DWORD* pdwStatus)
{
- char keyName[60];
+ static const WCHAR miscstatusW[] = {'M','i','s','c','S','t','a','t','u','s',0};
+ static const WCHAR dfmtW[] = {'%','d',0};
+ WCHAR keyName[60];
HKEY clsidKey;
HKEY miscStatusKey;
HKEY aspectKey;
/*
* Build the key name we're looking for
*/
- sprintf( keyName, "CLSID\\{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\\",
- clsid->Data1, clsid->Data2, clsid->Data3,
- clsid->Data4[0], clsid->Data4[1], clsid->Data4[2], clsid->Data4[3],
- clsid->Data4[4], clsid->Data4[5], clsid->Data4[6], clsid->Data4[7] );
+ sprintfW( keyName, clsidfmtW,
+ clsid->Data1, clsid->Data2, clsid->Data3,
+ clsid->Data4[0], clsid->Data4[1], clsid->Data4[2], clsid->Data4[3],
+ clsid->Data4[4], clsid->Data4[5], clsid->Data4[6], clsid->Data4[7] );
- TRACE("(%s, %d, %p)\n", keyName, dwAspect, pdwStatus);
+ TRACE("(%s, %d, %p)\n", debugstr_w(keyName), dwAspect, pdwStatus);
/*
* Open the class id Key
*/
- result = RegOpenKeyA(HKEY_CLASSES_ROOT,
+ result = RegOpenKeyW(HKEY_CLASSES_ROOT,
keyName,
&clsidKey);
/*
* Get the MiscStatus
*/
- result = RegOpenKeyA(clsidKey,
- "MiscStatus",
+ result = RegOpenKeyW(clsidKey,
+ miscstatusW,
&miscStatusKey);
/*
* Open the key specific to the requested aspect.
*/
- sprintf(keyName, "%d", dwAspect);
+ sprintfW(keyName, dfmtW, dwAspect);
- result = RegOpenKeyA(miscStatusKey,
+ result = RegOpenKeyW(miscStatusKey,
keyName,
&aspectKey);
*/
static BOOL OLEMenu_InstallHooks( DWORD tid )
{
- OleMenuHookItem *pHookItem = NULL;
+ OleMenuHookItem *pHookItem;
/* Create an entry for the hook table */
if ( !(pHookItem = HeapAlloc(GetProcessHeap(), 0,
pHookItem->tid = tid;
pHookItem->hHeap = GetProcessHeap();
+ pHookItem->CallWndProc_hHook = NULL;
/* Install a thread scope message hook for WH_GETMESSAGE */
- pHookItem->GetMsg_hHook = SetWindowsHookExA( WH_GETMESSAGE, OLEMenu_GetMsgProc,
+ pHookItem->GetMsg_hHook = SetWindowsHookExW( WH_GETMESSAGE, OLEMenu_GetMsgProc,
0, GetCurrentThreadId() );
if ( !pHookItem->GetMsg_hHook )
goto CLEANUP;
/* Install a thread scope message hook for WH_CALLWNDPROC */
- pHookItem->CallWndProc_hHook = SetWindowsHookExA( WH_CALLWNDPROC, OLEMenu_CallWndProc,
+ pHookItem->CallWndProc_hHook = SetWindowsHookExW( WH_CALLWNDPROC, OLEMenu_CallWndProc,
0, GetCurrentThreadId() );
if ( !pHookItem->CallWndProc_hHook )
goto CLEANUP;
*/
static OleMenuHookItem * OLEMenu_IsHookInstalled( DWORD tid )
{
- OleMenuHookItem *pHookItem = NULL;
+ OleMenuHookItem *pHookItem;
/* Do a simple linear search for an entry whose tid matches ours.
* We really need a map but efficiency is not a concern here. */
*/
static LRESULT CALLBACK OLEMenu_CallWndProc(INT code, WPARAM wParam, LPARAM lParam)
{
- LPCWPSTRUCT pMsg = NULL;
+ LPCWPSTRUCT pMsg;
HOLEMENU hOleMenu = 0;
OleMenuDescriptor *pOleMenuDescriptor = NULL;
OleMenuHookItem *pHookItem = NULL;
* If the window has an OLEMenu property we may need to dispatch
* the menu message to its active objects window instead. */
- hOleMenu = GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" );
+ hOleMenu = GetPropW( pMsg->hwnd, prop_olemenuW );
if ( !hOleMenu )
goto NEXTHOOK;
pOleMenuDescriptor->bIsServerItem = FALSE;
/* Send this message to the server as well */
- SendMessageA( pOleMenuDescriptor->hwndActiveObject,
+ SendMessageW( pOleMenuDescriptor->hwndActiveObject,
pMsg->message, pMsg->wParam, pMsg->lParam );
goto NEXTHOOK;
}
/* If the message was for the server dispatch it accordingly */
if ( pOleMenuDescriptor->bIsServerItem )
{
- SendMessageA( pOleMenuDescriptor->hwndActiveObject,
+ SendMessageW( pOleMenuDescriptor->hwndActiveObject,
pMsg->message, pMsg->wParam, pMsg->lParam );
}
*/
static LRESULT CALLBACK OLEMenu_GetMsgProc(INT code, WPARAM wParam, LPARAM lParam)
{
- LPMSG pMsg = NULL;
+ LPMSG pMsg;
HOLEMENU hOleMenu = 0;
OleMenuDescriptor *pOleMenuDescriptor = NULL;
OleMenuHookItem *pHookItem = NULL;
* If the window has an OLEMenu property we may need to dispatch
* the menu message to its active objects window instead. */
- hOleMenu = GetPropA( pMsg->hwnd, "PROP_OLEMenuDescriptor" );
+ hOleMenu = GetPropW( pMsg->hwnd, prop_olemenuW );
if ( !hOleMenu )
goto NEXTHOOK;
pOleMenuDescriptor = NULL;
/* Add a menu descriptor windows property to the frame window */
- SetPropA( hwndFrame, "PROP_OLEMenuDescriptor", hOleMenu );
+ SetPropW( hwndFrame, prop_olemenuW, hOleMenu );
/* Install thread scope message hooks for WH_GETMESSAGE and WH_CALLWNDPROC */
if ( !OLEMenu_InstallHooks( GetCurrentThreadId() ) )
return E_FAIL;
/* Remove the menu descriptor property from the frame window */
- RemovePropA( hwndFrame, "PROP_OLEMenuDescriptor" );
+ RemovePropW( hwndFrame, prop_olemenuW );
}
return S_OK;
*/
static void OLEDD_Initialize(void)
{
- WNDCLASSA wndClass;
+ WNDCLASSW wndClass;
- ZeroMemory (&wndClass, sizeof(WNDCLASSA));
+ ZeroMemory (&wndClass, sizeof(WNDCLASSW));
wndClass.style = CS_GLOBALCLASS;
wndClass.lpfnWndProc = OLEDD_DragTrackerWindowProc;
wndClass.cbClsExtra = 0;
wndClass.hbrBackground = 0;
wndClass.lpszClassName = OLEDD_DRAGTRACKERCLASS;
- RegisterClassA (&wndClass);
-}
-
-/***
- * OLEDD_FreeDropTarget()
- *
- * Frees the drag and drop data structure
- */
-static void OLEDD_FreeDropTarget(DropTargetNode *dropTargetInfo, BOOL release_drop_target)
-{
- list_remove(&dropTargetInfo->entry);
- if (release_drop_target) IDropTarget_Release(dropTargetInfo->dropTarget);
- HeapFree(GetProcessHeap(), 0, dropTargetInfo);
-}
-
-/***
- * OLEDD_UnInitialize()
- *
- * Releases the OLE drag and drop data structures.
- */
-void OLEDD_UnInitialize(void)
-{
- /*
- * Simply empty the list.
- */
- while (!list_empty(&targetListHead))
- {
- DropTargetNode* curNode = LIST_ENTRY(list_head(&targetListHead), DropTargetNode, entry);
- OLEDD_FreeDropTarget(curNode, FALSE);
- }
-}
-
-/***
- * OLEDD_FindDropTarget()
- *
- * Finds information about the drop target.
- */
-static DropTargetNode* OLEDD_FindDropTarget(HWND hwndOfTarget)
-{
- DropTargetNode* curNode;
-
- /*
- * Iterate the list to find the HWND value.
- */
- LIST_FOR_EACH_ENTRY(curNode, &targetListHead, DropTargetNode, entry)
- if (hwndOfTarget==curNode->hwndTarget)
- return curNode;
-
- /*
- * If we get here, the item is not in the list
- */
- return NULL;
+ RegisterClassW (&wndClass);
}
/***
{
LPCREATESTRUCTA createStruct = (LPCREATESTRUCTA)lParam;
- SetWindowLongPtrA(hwnd, 0, (LONG_PTR)createStruct->lpCreateParams);
+ SetWindowLongPtrW(hwnd, 0, (LONG_PTR)createStruct->lpCreateParams);
SetTimer(hwnd, DRAG_TIMER_ID, 50, NULL);
break;
/*
* This is a window proc after all. Let's call the default.
*/
- return DefWindowProcA (hwnd, uMsg, wParam, lParam);
+ return DefWindowProcW (hwnd, uMsg, wParam, lParam);
}
/***
}
else
{
- DropTargetNode* newDropTargetNode = 0;
-
/*
* If we changed window, we have to notify our old target and check for
* the new one.
*/
- if (trackerInfo->curDragTarget!=0)
- {
+ if (trackerInfo->curDragTarget)
IDropTarget_DragLeave(trackerInfo->curDragTarget);
- }
/*
* Make sure we're hovering over a window.
*/
- if (hwndNewTarget!=0)
+ if (hwndNewTarget)
{
/*
* Find-out if there is a drag target under the mouse
*/
- HWND nexttar = hwndNewTarget;
+ HWND next_target_wnd = hwndNewTarget;
+ IDropTarget *new_target;
+ DWORD pid;
+
trackerInfo->curTargetHWND = hwndNewTarget;
do {
- newDropTargetNode = OLEDD_FindDropTarget(nexttar);
- } while (!newDropTargetNode && (nexttar = GetParent(nexttar)) != 0);
- if(nexttar) hwndNewTarget = nexttar;
+ new_target = OLEDD_FindDropTarget(next_target_wnd);
+ } while (!new_target && (next_target_wnd = GetParent(next_target_wnd)));
+
+ if (next_target_wnd) hwndNewTarget = next_target_wnd;
- trackerInfo->curDragTargetHWND = hwndNewTarget;
- trackerInfo->curDragTarget = newDropTargetNode ? newDropTargetNode->dropTarget : 0;
+ GetWindowThreadProcessId(hwndNewTarget, &pid);
+ if (pid != GetCurrentProcessId())
+ {
+ FIXME("drop to another process window is unsupported\n");
+ trackerInfo->curDragTargetHWND = 0;
+ trackerInfo->curTargetHWND = 0;
+ trackerInfo->curDragTarget = 0;
+ }
+ else
+ {
+ trackerInfo->curDragTargetHWND = hwndNewTarget;
+ trackerInfo->curDragTarget = new_target;
+ }
/*
* If there is, notify it that we just dragged-in
*/
- if (trackerInfo->curDragTarget!=0)
+ if (trackerInfo->curDragTarget)
{
- IDropTarget_DragEnter(trackerInfo->curDragTarget,
- trackerInfo->dataObject,
- trackerInfo->dwKeyState,
- trackerInfo->curMousePos,
- trackerInfo->pdwEffect);
+ hr = IDropTarget_DragEnter(trackerInfo->curDragTarget,
+ trackerInfo->dataObject,
+ trackerInfo->dwKeyState,
+ trackerInfo->curMousePos,
+ trackerInfo->pdwEffect);
+
+ /* failed DragEnter() means invalid target */
+ if (hr != S_OK)
+ {
+ trackerInfo->curDragTargetHWND = 0;
+ trackerInfo->curTargetHWND = 0;
+ trackerInfo->curDragTarget = 0;
+ }
}
}
else
* when that's the case, we must display the standard drag and drop
* cursors.
*/
- if (hr==DRAGDROP_S_USEDEFAULTCURSORS)
+ if (hr == DRAGDROP_S_USEDEFAULTCURSORS)
{
+ HCURSOR hCur;
+
if (*trackerInfo->pdwEffect & DROPEFFECT_MOVE)
{
- SetCursor(LoadCursorA(hProxyDll, MAKEINTRESOURCEA(1)));
+ hCur = LoadCursorW(hProxyDll, MAKEINTRESOURCEW(1));
}
else if (*trackerInfo->pdwEffect & DROPEFFECT_COPY)
{
- SetCursor(LoadCursorA(hProxyDll, MAKEINTRESOURCEA(2)));
+ hCur = LoadCursorW(hProxyDll, MAKEINTRESOURCEW(2));
}
else if (*trackerInfo->pdwEffect & DROPEFFECT_LINK)
{
- SetCursor(LoadCursorA(hProxyDll, MAKEINTRESOURCEA(3)));
+ hCur = LoadCursorW(hProxyDll, MAKEINTRESOURCEW(3));
}
else
{
- SetCursor(LoadCursorA(hProxyDll, MAKEINTRESOURCEA(0)));
+ hCur = LoadCursorW(hProxyDll, MAKEINTRESOURCEW(0));
}
+
+ SetCursor(hCur);
}
}
* If we end-up over a target, drop the object in the target or
* inform the target that the operation was cancelled.
*/
- if (trackerInfo->curDragTarget!=0)
+ if (trackerInfo->curDragTarget)
{
switch (trackerInfo->returnValue)
{
* the drop target that we just dropped the object in it.
*/
case DRAGDROP_S_DROP:
- {
- IDropTarget_Drop(trackerInfo->curDragTarget,
- trackerInfo->dataObject,
- trackerInfo->dwKeyState,
- trackerInfo->curMousePos,
- trackerInfo->pdwEffect);
- break;
- }
+ if (*trackerInfo->pdwEffect != DROPEFFECT_NONE)
+ IDropTarget_Drop(trackerInfo->curDragTarget,
+ trackerInfo->dataObject,
+ trackerInfo->dwKeyState,
+ trackerInfo->curMousePos,
+ trackerInfo->pdwEffect);
+ else
+ IDropTarget_DragLeave(trackerInfo->curDragTarget);
+ break;
+
/*
* If the source told us that we should cancel, fool the drop
* target by telling it that the mouse left it's window.
HKEY regKey,
DWORD* pdwValue)
{
- char buffer[20];
+ WCHAR buffer[20];
+ DWORD cbData = sizeof(buffer);
DWORD dwKeyType;
- DWORD cbData = 20;
LONG lres;
- lres = RegQueryValueExA(regKey,
- "",
+ lres = RegQueryValueExW(regKey,
+ emptyW,
NULL,
&dwKeyType,
(LPBYTE)buffer,
case REG_EXPAND_SZ:
case REG_MULTI_SZ:
case REG_SZ:
- *pdwValue = (DWORD)strtoul(buffer, NULL, 10);
+ *pdwValue = (DWORD)strtoulW(buffer, NULL, 10);
break;
}
}
static BOOL StorageBaseImpl_IsStreamOpen(StorageBaseImpl * stg, DirRef streamEntry);
static BOOL StorageBaseImpl_IsStorageOpen(StorageBaseImpl * stg, DirRef storageEntry);
+typedef struct TransactedDirEntry
+{
+ /* If applicable, a reference to the original DirEntry in the transacted
+ * parent. If this is a newly-created entry, DIRENTRY_NULL. */
+ DirRef transactedParentEntry;
+
+ /* True if this entry is being used. */
+ int inuse;
+
+ /* True if data is up to date. */
+ int read;
+
+ /* True if this entry has been modified. */
+ int dirty;
+
+ /* True if this entry's stream has been modified. */
+ int stream_dirty;
+
+ /* True if this entry has been deleted in the transacted storage, but the
+ * delete has not yet been committed. */
+ int deleted;
+
+ /* If this entry's stream has been modified, a reference to where the stream
+ * is stored in the snapshot file. */
+ DirRef stream_entry;
+
+ /* This directory entry's data, including any changes that have been made. */
+ DirEntry data;
+
+ /* A reference to the parent of this node. This is only valid while we are
+ * committing changes. */
+ DirRef parent;
+
+ /* A reference to a newly-created entry in the transacted parent. This is
+ * always equal to transactedParentEntry except when committing changes. */
+ DirRef newTransactedParentEntry;
+} TransactedDirEntry;
+
/****************************************************************************
- * Transacted storage object that reads/writes a snapshot file.
+ * Transacted storage object.
*/
typedef struct TransactedSnapshotImpl
{
struct StorageBaseImpl base;
/*
- * Changes are temporarily saved to the snapshot.
+ * Modified streams are temporarily saved to the scratch file.
*/
- StorageBaseImpl *snapshot;
+ StorageBaseImpl *scratch;
+
+ /* The directory structure is kept here, so that we can track how these
+ * entries relate to those in the parent storage. */
+ TransactedDirEntry *entries;
+ ULONG entries_size;
+ ULONG firstFreeEntry;
/*
* Changes are committed to the transacted parent.
}
-/***************************************************************************
- *
- * 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
return &This->blockChainCache[free_index];
}
+static void StorageImpl_DeleteCachedBlockChainStream(StorageImpl *This, DirRef index)
+{
+ int i;
+
+ for (i=0; i<BLOCKCHAIN_CACHE_SIZE; i++)
+ {
+ if (This->blockChainCache[i] && This->blockChainCache[i]->ownerDirEntry == index)
+ {
+ BlockChainStream_Destroy(This->blockChainCache[i]);
+ This->blockChainCache[i] = NULL;
+ return;
+ }
+ }
+}
+
static HRESULT StorageImpl_StreamReadAt(StorageBaseImpl *base, DirRef index,
ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
{
}
}
+static HRESULT StorageImpl_StreamLink(StorageBaseImpl *base, DirRef dst,
+ DirRef src)
+{
+ StorageImpl *This = (StorageImpl*)base;
+ DirEntry dst_data, src_data;
+ HRESULT hr;
+
+ hr = StorageImpl_ReadDirEntry(This, dst, &dst_data);
+
+ if (SUCCEEDED(hr))
+ hr = StorageImpl_ReadDirEntry(This, src, &src_data);
+
+ if (SUCCEEDED(hr))
+ {
+ StorageImpl_DeleteCachedBlockChainStream(This, src);
+ dst_data.startingBlock = src_data.startingBlock;
+ dst_data.size = src_data.size;
+
+ hr = StorageImpl_WriteDirEntry(This, dst, &dst_data);
+ }
+
+ return hr;
+}
+
/*
* Virtual function table for the IStorage32Impl class.
*/
StorageImpl_DestroyDirEntry,
StorageImpl_StreamReadAt,
StorageImpl_StreamWriteAt,
- StorageImpl_StreamSetSize
+ StorageImpl_StreamSetSize,
+ StorageImpl_StreamLink
};
static HRESULT StorageImpl_Construct(
buffer,
&bytesRead);
+ if (bytesRead != RAW_DIRENTRY_SIZE)
+ return STG_E_READFAULT;
+
return hr;
}
offset.u.LowPart += cbRead;
}
+ else
+ {
+ resRead = STG_E_READFAULT;
+ break;
+ }
} while (cbTotalRead.QuadPart < size.QuadPart);
HeapFree(GetProcessHeap(),0,buffer);
offset.u.LowPart += cbRead;
}
+ else
+ {
+ resRead = STG_E_READFAULT;
+ break;
+ }
}while(cbTotalRead.QuadPart < size.QuadPart);
HeapFree(GetProcessHeap(), 0, buffer);
return SmallBlockChainStream_Construct(This, NULL, streamEntryRef);
}
-static HRESULT CreateSnapshotFile(StorageBaseImpl* original, StorageBaseImpl **snapshot)
+static HRESULT StorageBaseImpl_CopyStream(
+ StorageBaseImpl *dst, DirRef dst_entry,
+ StorageBaseImpl *src, DirRef src_entry)
{
HRESULT hr;
- DirEntry parentData, snapshotData;
+ BYTE data[4096];
+ DirEntry srcdata;
+ ULARGE_INTEGER bytes_copied;
+ ULONG bytestocopy, bytesread, byteswritten;
- hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_DELETEONRELEASE,
- 0, (IStorage**)snapshot);
+ hr = StorageBaseImpl_ReadDirEntry(src, src_entry, &srcdata);
if (SUCCEEDED(hr))
{
- hr = StorageBaseImpl_ReadDirEntry(original,
- original->storageDirEntry, &parentData);
-
- if (SUCCEEDED(hr))
- hr = StorageBaseImpl_ReadDirEntry((*snapshot),
- (*snapshot)->storageDirEntry, &snapshotData);
+ hr = StorageBaseImpl_StreamSetSize(dst, dst_entry, srcdata.size);
- if (SUCCEEDED(hr))
+ bytes_copied.QuadPart = 0;
+ while (bytes_copied.QuadPart < srcdata.size.QuadPart && 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);
- }
+ bytestocopy = min(4096, srcdata.size.QuadPart - bytes_copied.QuadPart);
- if (SUCCEEDED(hr))
- hr = IStorage_CopyTo((IStorage*)original, 0, NULL, NULL,
- (IStorage*)(*snapshot));
+ hr = StorageBaseImpl_StreamReadAt(src, src_entry, bytes_copied, bytestocopy,
+ data, &bytesread);
+ if (SUCCEEDED(hr) && bytesread != bytestocopy) hr = STG_E_READFAULT;
- if (FAILED(hr)) IStorage_Release((IStorage*)(*snapshot));
+ if (SUCCEEDED(hr))
+ hr = StorageBaseImpl_StreamWriteAt(dst, dst_entry, bytes_copied, bytestocopy,
+ data, &byteswritten);
+ if (SUCCEEDED(hr))
+ {
+ if (byteswritten != bytestocopy) hr = STG_E_WRITEFAULT;
+ bytes_copied.QuadPart += byteswritten;
+ }
+ }
}
return hr;
}
-static HRESULT WINAPI TransactedSnapshotImpl_Commit(
- IStorage* iface,
- DWORD grfCommitFlags) /* [in] */
+static DirRef TransactedSnapshotImpl_FindFreeEntry(TransactedSnapshotImpl *This)
{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
- HRESULT hr;
- DirEntry data, tempStorageData, snapshotRootData;
- DirRef tempStorageEntry, oldDirRoot;
- StorageInternalImpl *tempStorage;
+ DirRef result=This->firstFreeEntry;
- TRACE("(%p,%x)\n", iface, grfCommitFlags);
+ while (result < This->entries_size && This->entries[result].inuse)
+ result++;
- /* Cannot commit a read-only transacted storage */
- if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
- return STG_E_ACCESSDENIED;
+ if (result == This->entries_size)
+ {
+ ULONG new_size = This->entries_size * 2;
+ TransactedDirEntry *new_entries;
- /* 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. */
+ new_entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * new_size);
+ if (!new_entries) return DIRENTRY_NULL;
- /* 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);
+ memcpy(new_entries, This->entries, sizeof(TransactedDirEntry) * This->entries_size);
+ HeapFree(GetProcessHeap(), 0, This->entries);
- if (FAILED(hr)) return hr;
+ This->entries = new_entries;
+ This->entries_size = new_size;
+ }
- tempStorage = StorageInternalImpl_Construct(This->transactedParent,
- STGM_READWRITE|STGM_SHARE_EXCLUSIVE, tempStorageEntry);
- if (tempStorage)
- {
- hr = IStorage_CopyTo((IStorage*)This->snapshot, 0, NULL, NULL,
- (IStorage*)tempStorage);
+ This->entries[result].inuse = 1;
- list_init(&tempStorage->ParentListEntry);
+ This->firstFreeEntry = result+1;
- IStorage_Release((IStorage*) tempStorage);
- }
- else
- hr = E_OUTOFMEMORY;
+ return result;
+}
- if (FAILED(hr))
+static DirRef TransactedSnapshotImpl_CreateStubEntry(
+ TransactedSnapshotImpl *This, DirRef parentEntryRef)
+{
+ DirRef stubEntryRef;
+ TransactedDirEntry *entry;
+
+ stubEntryRef = TransactedSnapshotImpl_FindFreeEntry(This);
+
+ if (stubEntryRef != DIRENTRY_NULL)
{
- DestroyReachableEntries(This->transactedParent, tempStorageEntry);
- return hr;
+ entry = &This->entries[stubEntryRef];
+
+ entry->newTransactedParentEntry = entry->transactedParentEntry = parentEntryRef;
+
+ entry->read = 0;
}
- /* Update the storage to use the new data in one step. */
- hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
- This->transactedParent->storageDirEntry, &data);
+ return stubEntryRef;
+}
- if (SUCCEEDED(hr))
+static HRESULT TransactedSnapshotImpl_EnsureReadEntry(
+ TransactedSnapshotImpl *This, DirRef entry)
+{
+ HRESULT hr=S_OK;
+ DirEntry data;
+
+ if (!This->entries[entry].read)
{
hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
- tempStorageEntry, &tempStorageData);
- }
+ This->entries[entry].transactedParentEntry,
+ &data);
- if (SUCCEEDED(hr))
- {
- hr = StorageBaseImpl_ReadDirEntry(This->snapshot,
- This->snapshot->storageDirEntry, &snapshotRootData);
- }
+ if (SUCCEEDED(hr) && data.leftChild != DIRENTRY_NULL)
+ {
+ data.leftChild = TransactedSnapshotImpl_CreateStubEntry(This, data.leftChild);
- if (SUCCEEDED(hr))
- {
- oldDirRoot = data.dirRootEntry;
- data.dirRootEntry = tempStorageData.dirRootEntry;
- data.clsid = snapshotRootData.clsid;
- data.ctime = snapshotRootData.ctime;
- data.mtime = snapshotRootData.mtime;
+ if (data.leftChild == DIRENTRY_NULL)
+ hr = E_OUTOFMEMORY;
+ }
- hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
- This->transactedParent->storageDirEntry, &data);
- }
+ if (SUCCEEDED(hr) && data.rightChild != DIRENTRY_NULL)
+ {
+ data.rightChild = TransactedSnapshotImpl_CreateStubEntry(This, data.rightChild);
- if (SUCCEEDED(hr))
- {
- /* Destroy the old now-orphaned data. */
- DestroyReachableEntries(This->transactedParent, oldDirRoot);
- StorageBaseImpl_DestroyDirEntry(This->transactedParent, tempStorageEntry);
- }
- else
- {
- DestroyReachableEntries(This->transactedParent, tempStorageEntry);
+ if (data.rightChild == DIRENTRY_NULL)
+ hr = E_OUTOFMEMORY;
+ }
+
+ if (SUCCEEDED(hr) && data.dirRootEntry != DIRENTRY_NULL)
+ {
+ data.dirRootEntry = TransactedSnapshotImpl_CreateStubEntry(This, data.dirRootEntry);
+
+ if (data.dirRootEntry == DIRENTRY_NULL)
+ hr = E_OUTOFMEMORY;
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ memcpy(&This->entries[entry].data, &data, sizeof(DirEntry));
+ This->entries[entry].read = 1;
+ }
}
return hr;
}
-static HRESULT WINAPI TransactedSnapshotImpl_Revert(
- IStorage* iface)
+static HRESULT TransactedSnapshotImpl_MakeStreamDirty(
+ TransactedSnapshotImpl *This, DirRef entry)
{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
- StorageBaseImpl *newSnapshot;
- HRESULT hr;
+ HRESULT hr = S_OK;
- TRACE("(%p)\n", iface);
+ if (!This->entries[entry].stream_dirty)
+ {
+ DirEntry new_entrydata;
- /* Create a new copy of the parent data. */
- hr = CreateSnapshotFile(This->transactedParent, &newSnapshot);
- if (FAILED(hr)) return hr;
+ memset(&new_entrydata, 0, sizeof(DirEntry));
+ new_entrydata.name[0] = 'S';
+ new_entrydata.sizeOfNameString = 1;
+ new_entrydata.stgType = STGTY_STREAM;
+ new_entrydata.startingBlock = BLOCK_END_OF_CHAIN;
+ new_entrydata.leftChild = DIRENTRY_NULL;
+ new_entrydata.rightChild = DIRENTRY_NULL;
+ new_entrydata.dirRootEntry = DIRENTRY_NULL;
- /* Destroy the open objects. */
- StorageBaseImpl_DeleteAll(&This->base);
+ hr = StorageBaseImpl_CreateDirEntry(This->scratch, &new_entrydata,
+ &This->entries[entry].stream_entry);
- /* Replace our current snapshot. */
- IStorage_Release((IStorage*)This->snapshot);
- This->snapshot = newSnapshot;
+ if (SUCCEEDED(hr) && This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
+ {
+ hr = StorageBaseImpl_CopyStream(
+ This->scratch, This->entries[entry].stream_entry,
+ This->transactedParent, This->entries[entry].transactedParentEntry);
- return S_OK;
+ if (FAILED(hr))
+ StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[entry].stream_entry);
+ }
+
+ if (SUCCEEDED(hr))
+ This->entries[entry].stream_dirty = 1;
+
+ if (This->entries[entry].transactedParentEntry != DIRENTRY_NULL)
+ {
+ /* Since this entry is modified, and we aren't using its stream data, we
+ * no longer care about the original entry. */
+ DirRef delete_ref;
+ delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[entry].transactedParentEntry);
+
+ if (delete_ref != DIRENTRY_NULL)
+ This->entries[delete_ref].deleted = 1;
+
+ This->entries[entry].transactedParentEntry = This->entries[entry].newTransactedParentEntry = DIRENTRY_NULL;
+ }
+ }
+
+ return hr;
}
-static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
+/* Find the first entry in a depth-first traversal. */
+static DirRef TransactedSnapshotImpl_FindFirstChild(
+ TransactedSnapshotImpl* This, DirRef parent)
{
- if (!This->reverted)
- {
- TRACE("Storage invalidated (stg=%p)\n", This);
+ DirRef cursor, prev;
+ TransactedDirEntry *entry;
- This->reverted = 1;
-
- StorageBaseImpl_DeleteAll(This);
+ cursor = parent;
+ entry = &This->entries[cursor];
+ while (entry->read)
+ {
+ if (entry->data.leftChild != DIRENTRY_NULL)
+ {
+ prev = cursor;
+ cursor = entry->data.leftChild;
+ entry = &This->entries[cursor];
+ entry->parent = prev;
+ }
+ else if (entry->data.rightChild != DIRENTRY_NULL)
+ {
+ prev = cursor;
+ cursor = entry->data.rightChild;
+ entry = &This->entries[cursor];
+ entry->parent = prev;
+ }
+ else if (entry->data.dirRootEntry != DIRENTRY_NULL)
+ {
+ prev = cursor;
+ cursor = entry->data.dirRootEntry;
+ entry = &This->entries[cursor];
+ entry->parent = prev;
+ }
+ else
+ break;
}
+
+ return cursor;
}
-static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
+/* Find the next entry in a depth-first traversal. */
+static DirRef TransactedSnapshotImpl_FindNextChild(
+ TransactedSnapshotImpl* This, DirRef current)
{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
+ DirRef parent;
+ TransactedDirEntry *parent_entry;
- TransactedSnapshotImpl_Invalidate(iface);
+ parent = This->entries[current].parent;
+ parent_entry = &This->entries[parent];
- IStorage_Release((IStorage*)This->transactedParent);
+ if (parent != DIRENTRY_NULL && parent_entry->data.dirRootEntry != current)
+ {
+ if (parent_entry->data.rightChild != current && parent_entry->data.rightChild != DIRENTRY_NULL)
+ {
+ This->entries[parent_entry->data.rightChild].parent = parent;
+ return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.rightChild);
+ }
- IStorage_Release((IStorage*)This->snapshot);
+ if (parent_entry->data.dirRootEntry != DIRENTRY_NULL)
+ {
+ This->entries[parent_entry->data.dirRootEntry].parent = parent;
+ return TransactedSnapshotImpl_FindFirstChild(This, parent_entry->data.dirRootEntry);
+ }
+ }
- HeapFree(GetProcessHeap(), 0, This);
+ return parent;
}
-static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
- const DirEntry *newData, DirRef *index)
+/* Return TRUE if we've made a copy of this entry for committing to the parent. */
+static inline BOOL TransactedSnapshotImpl_MadeCopy(
+ TransactedSnapshotImpl* This, DirRef entry)
{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
-
- return StorageBaseImpl_CreateDirEntry(This->snapshot,
- newData, index);
+ return entry != DIRENTRY_NULL &&
+ This->entries[entry].newTransactedParentEntry != This->entries[entry].transactedParentEntry;
}
-static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
- DirRef index, const DirEntry *data)
+/* Destroy the entries created by CopyTree. */
+static void TransactedSnapshotImpl_DestroyTemporaryCopy(
+ TransactedSnapshotImpl* This, DirRef stop)
{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ DirRef cursor;
+ TransactedDirEntry *entry;
+ ULARGE_INTEGER zero;
- return StorageBaseImpl_WriteDirEntry(This->snapshot,
- index, data);
-}
+ zero.QuadPart = 0;
-static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
- DirRef index, DirEntry *data)
-{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ if (!This->entries[This->base.storageDirEntry].read)
+ return;
- return StorageBaseImpl_ReadDirEntry(This->snapshot,
- index, data);
-}
+ cursor = This->entries[This->base.storageDirEntry].data.dirRootEntry;
-static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
- DirRef index)
-{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ if (cursor == DIRENTRY_NULL)
+ return;
- return StorageBaseImpl_DestroyDirEntry(This->snapshot,
- index);
-}
+ cursor = TransactedSnapshotImpl_FindFirstChild(This, cursor);
-static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
- DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
-{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ while (cursor != DIRENTRY_NULL && cursor != stop)
+ {
+ if (TransactedSnapshotImpl_MadeCopy(This, cursor))
+ {
+ entry = &This->entries[cursor];
- return StorageBaseImpl_StreamReadAt(This->snapshot,
- index, offset, size, buffer, bytesRead);
-}
+ if (entry->stream_dirty)
+ StorageBaseImpl_StreamSetSize(This->transactedParent,
+ entry->newTransactedParentEntry, zero);
-static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
- DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
-{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ StorageBaseImpl_DestroyDirEntry(This->transactedParent,
+ entry->newTransactedParentEntry);
- return StorageBaseImpl_StreamWriteAt(This->snapshot,
- index, offset, size, buffer, bytesWritten);
+ entry->newTransactedParentEntry = entry->transactedParentEntry;
+ }
+
+ cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
+ }
}
-static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
- DirRef index, ULARGE_INTEGER newsize)
+/* Make a copy of our edited tree that we can use in the parent. */
+static HRESULT TransactedSnapshotImpl_CopyTree(TransactedSnapshotImpl* This)
{
- TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ DirRef cursor;
+ TransactedDirEntry *entry;
+ HRESULT hr = S_OK;
- return StorageBaseImpl_StreamSetSize(This->snapshot,
- index, newsize);
-}
+ cursor = This->base.storageDirEntry;
+ entry = &This->entries[cursor];
+ entry->parent = DIRENTRY_NULL;
+ entry->newTransactedParentEntry = entry->transactedParentEntry;
-static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
-{
- StorageBaseImpl_QueryInterface,
- StorageBaseImpl_AddRef,
- StorageBaseImpl_Release,
- StorageBaseImpl_CreateStream,
- StorageBaseImpl_OpenStream,
- StorageBaseImpl_CreateStorage,
- StorageBaseImpl_OpenStorage,
- StorageBaseImpl_CopyTo,
- StorageBaseImpl_MoveElementTo,
+ if (entry->data.dirRootEntry == DIRENTRY_NULL)
+ return S_OK;
+
+ This->entries[entry->data.dirRootEntry].parent = DIRENTRY_NULL;
+
+ cursor = TransactedSnapshotImpl_FindFirstChild(This, entry->data.dirRootEntry);
+ entry = &This->entries[cursor];
+
+ while (cursor != DIRENTRY_NULL)
+ {
+ /* Make a copy of this entry in the transacted parent. */
+ if (!entry->read ||
+ (!entry->dirty && !entry->stream_dirty &&
+ !TransactedSnapshotImpl_MadeCopy(This, entry->data.leftChild) &&
+ !TransactedSnapshotImpl_MadeCopy(This, entry->data.rightChild) &&
+ !TransactedSnapshotImpl_MadeCopy(This, entry->data.dirRootEntry)))
+ entry->newTransactedParentEntry = entry->transactedParentEntry;
+ else
+ {
+ DirEntry newData;
+
+ memcpy(&newData, &entry->data, sizeof(DirEntry));
+
+ newData.size.QuadPart = 0;
+ newData.startingBlock = BLOCK_END_OF_CHAIN;
+
+ if (newData.leftChild != DIRENTRY_NULL)
+ newData.leftChild = This->entries[newData.leftChild].newTransactedParentEntry;
+
+ if (newData.rightChild != DIRENTRY_NULL)
+ newData.rightChild = This->entries[newData.rightChild].newTransactedParentEntry;
+
+ if (newData.dirRootEntry != DIRENTRY_NULL)
+ newData.dirRootEntry = This->entries[newData.dirRootEntry].newTransactedParentEntry;
+
+ hr = StorageBaseImpl_CreateDirEntry(This->transactedParent, &newData,
+ &entry->newTransactedParentEntry);
+ if (FAILED(hr))
+ {
+ TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
+ return hr;
+ }
+
+ if (entry->stream_dirty)
+ {
+ hr = StorageBaseImpl_CopyStream(
+ This->transactedParent, entry->newTransactedParentEntry,
+ This->scratch, entry->stream_entry);
+ }
+ else if (entry->data.size.QuadPart)
+ {
+ hr = StorageBaseImpl_StreamLink(
+ This->transactedParent, entry->newTransactedParentEntry,
+ entry->transactedParentEntry);
+ }
+
+ if (FAILED(hr))
+ {
+ cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
+ TransactedSnapshotImpl_DestroyTemporaryCopy(This, cursor);
+ return hr;
+ }
+ }
+
+ cursor = TransactedSnapshotImpl_FindNextChild(This, cursor);
+ entry = &This->entries[cursor];
+ }
+
+ return hr;
+}
+
+static HRESULT WINAPI TransactedSnapshotImpl_Commit(
+ IStorage* iface,
+ DWORD grfCommitFlags) /* [in] */
+{
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
+ TransactedDirEntry *root_entry;
+ DirRef i, dir_root_ref;
+ DirEntry data;
+ ULARGE_INTEGER zero;
+ HRESULT hr;
+
+ zero.QuadPart = 0;
+
+ 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. */
+
+ root_entry = &This->entries[This->base.storageDirEntry];
+
+ if (!root_entry->read)
+ return S_OK;
+
+ hr = TransactedSnapshotImpl_CopyTree(This);
+ if (FAILED(hr)) return hr;
+
+ if (root_entry->data.dirRootEntry == DIRENTRY_NULL)
+ dir_root_ref = DIRENTRY_NULL;
+ else
+ dir_root_ref = This->entries[root_entry->data.dirRootEntry].newTransactedParentEntry;
+
+ /* Update the storage to use the new data in one step. */
+ hr = StorageBaseImpl_ReadDirEntry(This->transactedParent,
+ root_entry->transactedParentEntry, &data);
+
+ if (SUCCEEDED(hr))
+ {
+ data.dirRootEntry = dir_root_ref;
+ data.clsid = root_entry->data.clsid;
+ data.ctime = root_entry->data.ctime;
+ data.mtime = root_entry->data.mtime;
+
+ hr = StorageBaseImpl_WriteDirEntry(This->transactedParent,
+ root_entry->transactedParentEntry, &data);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ /* Destroy the old now-orphaned data. */
+ for (i=0; i<This->entries_size; i++)
+ {
+ TransactedDirEntry *entry = &This->entries[i];
+ if (entry->inuse)
+ {
+ if (entry->deleted)
+ {
+ StorageBaseImpl_StreamSetSize(This->transactedParent,
+ entry->transactedParentEntry, zero);
+ StorageBaseImpl_DestroyDirEntry(This->transactedParent,
+ entry->transactedParentEntry);
+ memset(entry, 0, sizeof(TransactedDirEntry));
+ This->firstFreeEntry = min(i, This->firstFreeEntry);
+ }
+ else if (entry->read && entry->transactedParentEntry != entry->newTransactedParentEntry)
+ {
+ if (entry->transactedParentEntry != DIRENTRY_NULL)
+ StorageBaseImpl_DestroyDirEntry(This->transactedParent,
+ entry->transactedParentEntry);
+ if (entry->stream_dirty)
+ {
+ StorageBaseImpl_StreamSetSize(This->scratch, entry->stream_entry, zero);
+ StorageBaseImpl_DestroyDirEntry(This->scratch, entry->stream_entry);
+ entry->stream_dirty = 0;
+ }
+ entry->dirty = 0;
+ entry->transactedParentEntry = entry->newTransactedParentEntry;
+ }
+ }
+ }
+ }
+ else
+ {
+ TransactedSnapshotImpl_DestroyTemporaryCopy(This, DIRENTRY_NULL);
+ }
+
+ return hr;
+}
+
+static HRESULT WINAPI TransactedSnapshotImpl_Revert(
+ IStorage* iface)
+{
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
+ ULARGE_INTEGER zero;
+ ULONG i;
+
+ TRACE("(%p)\n", iface);
+
+ /* Destroy the open objects. */
+ StorageBaseImpl_DeleteAll(&This->base);
+
+ /* Clear out the scratch file. */
+ zero.QuadPart = 0;
+ for (i=0; i<This->entries_size; i++)
+ {
+ if (This->entries[i].stream_dirty)
+ {
+ StorageBaseImpl_StreamSetSize(This->scratch, This->entries[i].stream_entry,
+ zero);
+
+ StorageBaseImpl_DestroyDirEntry(This->scratch, This->entries[i].stream_entry);
+ }
+ }
+
+ memset(This->entries, 0, sizeof(TransactedDirEntry) * This->entries_size);
+
+ This->firstFreeEntry = 0;
+ This->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(This, This->transactedParent->storageDirEntry);
+
+ return S_OK;
+}
+
+static void TransactedSnapshotImpl_Invalidate(StorageBaseImpl* This)
+{
+ if (!This->reverted)
+ {
+ TRACE("Storage invalidated (stg=%p)\n", This);
+
+ This->reverted = 1;
+
+ StorageBaseImpl_DeleteAll(This);
+ }
+}
+
+static void TransactedSnapshotImpl_Destroy( StorageBaseImpl *iface)
+{
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) iface;
+
+ TransactedSnapshotImpl_Revert((IStorage*)iface);
+
+ IStorage_Release((IStorage*)This->transactedParent);
+
+ IStorage_Release((IStorage*)This->scratch);
+
+ HeapFree(GetProcessHeap(), 0, This->entries);
+
+ HeapFree(GetProcessHeap(), 0, This);
+}
+
+static HRESULT TransactedSnapshotImpl_CreateDirEntry(StorageBaseImpl *base,
+ const DirEntry *newData, DirRef *index)
+{
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ DirRef new_ref;
+ TransactedDirEntry *new_entry;
+
+ new_ref = TransactedSnapshotImpl_FindFreeEntry(This);
+ if (new_ref == DIRENTRY_NULL)
+ return E_OUTOFMEMORY;
+
+ new_entry = &This->entries[new_ref];
+
+ new_entry->newTransactedParentEntry = new_entry->transactedParentEntry = DIRENTRY_NULL;
+ new_entry->read = 1;
+ new_entry->dirty = 1;
+ memcpy(&new_entry->data, newData, sizeof(DirEntry));
+
+ *index = new_ref;
+
+ TRACE("%s l=%x r=%x d=%x <-- %x\n", debugstr_w(newData->name), newData->leftChild, newData->rightChild, newData->dirRootEntry, *index);
+
+ return S_OK;
+}
+
+static HRESULT TransactedSnapshotImpl_WriteDirEntry(StorageBaseImpl *base,
+ DirRef index, const DirEntry *data)
+{
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ HRESULT hr;
+
+ TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
+
+ hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
+ if (FAILED(hr)) return hr;
+
+ memcpy(&This->entries[index].data, data, sizeof(DirEntry));
+
+ if (index != This->base.storageDirEntry)
+ {
+ This->entries[index].dirty = 1;
+
+ if (data->size.QuadPart == 0 &&
+ This->entries[index].transactedParentEntry != DIRENTRY_NULL)
+ {
+ /* Since this entry is modified, and we aren't using its stream data, we
+ * no longer care about the original entry. */
+ DirRef delete_ref;
+ delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
+
+ if (delete_ref != DIRENTRY_NULL)
+ This->entries[delete_ref].deleted = 1;
+
+ This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
+ }
+ }
+
+ return S_OK;
+}
+
+static HRESULT TransactedSnapshotImpl_ReadDirEntry(StorageBaseImpl *base,
+ DirRef index, DirEntry *data)
+{
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ HRESULT hr;
+
+ hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
+ if (FAILED(hr)) return hr;
+
+ memcpy(data, &This->entries[index].data, sizeof(DirEntry));
+
+ TRACE("%x %s l=%x r=%x d=%x\n", index, debugstr_w(data->name), data->leftChild, data->rightChild, data->dirRootEntry);
+
+ return S_OK;
+}
+
+static HRESULT TransactedSnapshotImpl_DestroyDirEntry(StorageBaseImpl *base,
+ DirRef index)
+{
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+
+ if (This->entries[index].transactedParentEntry == DIRENTRY_NULL ||
+ This->entries[index].data.size.QuadPart != 0)
+ {
+ /* If we deleted this entry while it has stream data. We must have left the
+ * data because some other entry is using it, and we need to leave the
+ * original entry alone. */
+ memset(&This->entries[index], 0, sizeof(TransactedDirEntry));
+ This->firstFreeEntry = min(index, This->firstFreeEntry);
+ }
+ else
+ {
+ This->entries[index].deleted = 1;
+ }
+
+ return S_OK;
+}
+
+static HRESULT TransactedSnapshotImpl_StreamReadAt(StorageBaseImpl *base,
+ DirRef index, ULARGE_INTEGER offset, ULONG size, void *buffer, ULONG *bytesRead)
+{
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+
+ if (This->entries[index].stream_dirty)
+ {
+ return StorageBaseImpl_StreamReadAt(This->scratch,
+ This->entries[index].stream_entry, offset, size, buffer, bytesRead);
+ }
+ else if (This->entries[index].transactedParentEntry == DIRENTRY_NULL)
+ {
+ /* This stream doesn't live in the parent, and we haven't allocated storage
+ * for it yet */
+ *bytesRead = 0;
+ return S_OK;
+ }
+ else
+ {
+ return StorageBaseImpl_StreamReadAt(This->transactedParent,
+ This->entries[index].transactedParentEntry, offset, size, buffer, bytesRead);
+ }
+}
+
+static HRESULT TransactedSnapshotImpl_StreamWriteAt(StorageBaseImpl *base,
+ DirRef index, ULARGE_INTEGER offset, ULONG size, const void *buffer, ULONG *bytesWritten)
+{
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ HRESULT hr;
+
+ hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
+ if (FAILED(hr)) return hr;
+
+ hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
+ if (FAILED(hr)) return hr;
+
+ hr = StorageBaseImpl_StreamWriteAt(This->scratch,
+ This->entries[index].stream_entry, offset, size, buffer, bytesWritten);
+
+ if (SUCCEEDED(hr) && size != 0)
+ This->entries[index].data.size.QuadPart = max(
+ This->entries[index].data.size.QuadPart,
+ offset.QuadPart + size);
+
+ return hr;
+}
+
+static HRESULT TransactedSnapshotImpl_StreamSetSize(StorageBaseImpl *base,
+ DirRef index, ULARGE_INTEGER newsize)
+{
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ HRESULT hr;
+
+ hr = TransactedSnapshotImpl_EnsureReadEntry(This, index);
+ if (FAILED(hr)) return hr;
+
+ if (This->entries[index].data.size.QuadPart == newsize.QuadPart)
+ return S_OK;
+
+ if (newsize.QuadPart == 0)
+ {
+ /* Destroy any parent references or entries in the scratch file. */
+ if (This->entries[index].stream_dirty)
+ {
+ ULARGE_INTEGER zero;
+ zero.QuadPart = 0;
+ StorageBaseImpl_StreamSetSize(This->scratch,
+ This->entries[index].stream_entry, zero);
+ StorageBaseImpl_DestroyDirEntry(This->scratch,
+ This->entries[index].stream_entry);
+ This->entries[index].stream_dirty = 0;
+ }
+ else if (This->entries[index].transactedParentEntry != DIRENTRY_NULL)
+ {
+ DirRef delete_ref;
+ delete_ref = TransactedSnapshotImpl_CreateStubEntry(This, This->entries[index].transactedParentEntry);
+
+ if (delete_ref != DIRENTRY_NULL)
+ This->entries[delete_ref].deleted = 1;
+
+ This->entries[index].transactedParentEntry = This->entries[index].newTransactedParentEntry = DIRENTRY_NULL;
+ }
+ }
+ else
+ {
+ hr = TransactedSnapshotImpl_MakeStreamDirty(This, index);
+ if (FAILED(hr)) return hr;
+
+ hr = StorageBaseImpl_StreamSetSize(This->scratch,
+ This->entries[index].stream_entry, newsize);
+ }
+
+ if (SUCCEEDED(hr))
+ This->entries[index].data.size = newsize;
+
+ return hr;
+}
+
+static HRESULT TransactedSnapshotImpl_StreamLink(StorageBaseImpl *base,
+ DirRef dst, DirRef src)
+{
+ TransactedSnapshotImpl* This = (TransactedSnapshotImpl*) base;
+ HRESULT hr;
+ TransactedDirEntry *dst_entry, *src_entry;
+
+ hr = TransactedSnapshotImpl_EnsureReadEntry(This, src);
+ if (FAILED(hr)) return hr;
+
+ hr = TransactedSnapshotImpl_EnsureReadEntry(This, dst);
+ if (FAILED(hr)) return hr;
+
+ dst_entry = &This->entries[dst];
+ src_entry = &This->entries[src];
+
+ dst_entry->stream_dirty = src_entry->stream_dirty;
+ dst_entry->stream_entry = src_entry->stream_entry;
+ dst_entry->transactedParentEntry = src_entry->transactedParentEntry;
+ dst_entry->newTransactedParentEntry = src_entry->newTransactedParentEntry;
+ dst_entry->data.size = src_entry->data.size;
+
+ return S_OK;
+}
+
+static const IStorageVtbl TransactedSnapshotImpl_Vtbl =
+{
+ StorageBaseImpl_QueryInterface,
+ StorageBaseImpl_AddRef,
+ StorageBaseImpl_Release,
+ StorageBaseImpl_CreateStream,
+ StorageBaseImpl_OpenStream,
+ StorageBaseImpl_CreateStorage,
+ StorageBaseImpl_OpenStorage,
+ StorageBaseImpl_CopyTo,
+ StorageBaseImpl_MoveElementTo,
TransactedSnapshotImpl_Commit,
TransactedSnapshotImpl_Revert,
StorageBaseImpl_EnumElements,
TransactedSnapshotImpl_DestroyDirEntry,
TransactedSnapshotImpl_StreamReadAt,
TransactedSnapshotImpl_StreamWriteAt,
- TransactedSnapshotImpl_StreamSetSize
+ TransactedSnapshotImpl_StreamSetSize,
+ TransactedSnapshotImpl_StreamLink
};
static HRESULT TransactedSnapshotImpl_Construct(StorageBaseImpl *parentStorage,
(*result)->base.filename = parentStorage->filename;
- /* Create a new temporary storage to act as the snapshot */
- hr = CreateSnapshotFile(parentStorage, &(*result)->snapshot);
+ /* Create a new temporary storage to act as the scratch file. */
+ hr = StgCreateDocfile(NULL, STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE,
+ 0, (IStorage**)&(*result)->scratch);
if (SUCCEEDED(hr))
{
- (*result)->base.storageDirEntry = (*result)->snapshot->storageDirEntry;
+ ULONG num_entries = 20;
- /* parentStorage already has 1 reference, which we take over here. */
- (*result)->transactedParent = parentStorage;
+ (*result)->entries = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TransactedDirEntry) * num_entries);
- parentStorage->transactedChild = (StorageBaseImpl*)*result;
+ (*result)->entries_size = num_entries;
+
+ (*result)->firstFreeEntry = 0;
+
+ if ((*result)->entries)
+ {
+ /* parentStorage already has 1 reference, which we take over here. */
+ (*result)->transactedParent = parentStorage;
+
+ parentStorage->transactedChild = (StorageBaseImpl*)*result;
+
+ (*result)->base.storageDirEntry = TransactedSnapshotImpl_CreateStubEntry(*result, parentStorage->storageDirEntry);
+ }
+ else
+ {
+ IStorage_Release((IStorage*)(*result)->scratch);
+
+ hr = E_OUTOFMEMORY;
+ }
}
if (FAILED(hr)) HeapFree(GetProcessHeap(), 0, (*result));
index, newsize);
}
+static HRESULT StorageInternalImpl_StreamLink(StorageBaseImpl *base,
+ DirRef dst, DirRef src)
+{
+ StorageInternalImpl* This = (StorageInternalImpl*) base;
+
+ return StorageBaseImpl_StreamLink(This->parentStorage,
+ dst, src);
+}
+
/******************************************************************************
**
** Storage32InternalImpl_Commit
StorageInternalImpl_DestroyDirEntry,
StorageInternalImpl_StreamReadAt,
StorageInternalImpl_StreamWriteAt,
- StorageInternalImpl_StreamSetSize
+ StorageInternalImpl_StreamSetSize,
+ StorageInternalImpl_StreamLink
};
/******************************************************************************
** BlockChainStream implementation
*/
+/* Read and save the index of all blocks in this stream. */
+HRESULT BlockChainStream_UpdateIndexCache(BlockChainStream* This)
+{
+ ULONG next_sector, next_offset;
+ HRESULT hr;
+ struct BlockChainRun *last_run;
+
+ if (This->indexCacheLen == 0)
+ {
+ last_run = NULL;
+ next_offset = 0;
+ next_sector = BlockChainStream_GetHeadOfChain(This);
+ }
+ else
+ {
+ last_run = &This->indexCache[This->indexCacheLen-1];
+ next_offset = last_run->lastOffset+1;
+ hr = StorageImpl_GetNextBlockInChain(This->parentStorage,
+ last_run->firstSector + last_run->lastOffset - last_run->firstOffset,
+ &next_sector);
+ if (FAILED(hr)) return hr;
+ }
+
+ while (next_sector != BLOCK_END_OF_CHAIN)
+ {
+ if (!last_run || next_sector != last_run->firstSector + next_offset - last_run->firstOffset)
+ {
+ /* Add the current block to the cache. */
+ if (This->indexCacheSize == 0)
+ {
+ This->indexCache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*16);
+ if (!This->indexCache) return E_OUTOFMEMORY;
+ This->indexCacheSize = 16;
+ }
+ else if (This->indexCacheSize == This->indexCacheLen)
+ {
+ struct BlockChainRun *new_cache;
+ ULONG new_size;
+
+ new_size = This->indexCacheSize * 2;
+ new_cache = HeapAlloc(GetProcessHeap(), 0, sizeof(struct BlockChainRun)*new_size);
+ if (!new_cache) return E_OUTOFMEMORY;
+ memcpy(new_cache, This->indexCache, sizeof(struct BlockChainRun)*This->indexCacheLen);
+
+ HeapFree(GetProcessHeap(), 0, This->indexCache);
+ This->indexCache = new_cache;
+ This->indexCacheSize = new_size;
+ }
+
+ This->indexCacheLen++;
+ last_run = &This->indexCache[This->indexCacheLen-1];
+ last_run->firstSector = next_sector;
+ last_run->firstOffset = next_offset;
+ }
+
+ last_run->lastOffset = next_offset;
+
+ /* Find the next block. */
+ next_offset++;
+ hr = StorageImpl_GetNextBlockInChain(This->parentStorage, next_sector, &next_sector);
+ if (FAILED(hr)) return hr;
+ }
+
+ if (This->indexCacheLen)
+ {
+ This->tailIndex = last_run->firstSector + last_run->lastOffset - last_run->firstOffset;
+ This->numBlocks = last_run->lastOffset+1;
+ }
+ else
+ {
+ This->tailIndex = BLOCK_END_OF_CHAIN;
+ This->numBlocks = 0;
+ }
+
+ return S_OK;
+}
+
+/* Locate the nth block in this stream. */
+ULONG BlockChainStream_GetSectorOfOffset(BlockChainStream *This, ULONG offset)
+{
+ ULONG min_offset = 0, max_offset = This->numBlocks-1;
+ ULONG min_run = 0, max_run = This->indexCacheLen-1;
+
+ if (offset >= This->numBlocks)
+ return BLOCK_END_OF_CHAIN;
+
+ while (min_run < max_run)
+ {
+ ULONG run_to_check = min_run + (offset - min_offset) * (max_run - min_run) / (max_offset - min_offset);
+ if (offset < This->indexCache[run_to_check].firstOffset)
+ {
+ max_offset = This->indexCache[run_to_check].firstOffset-1;
+ max_run = run_to_check-1;
+ }
+ else if (offset > This->indexCache[run_to_check].lastOffset)
+ {
+ min_offset = This->indexCache[run_to_check].lastOffset+1;
+ min_run = run_to_check+1;
+ }
+ else
+ /* Block is in this run. */
+ min_run = max_run = run_to_check;
+ }
+
+ return This->indexCache[min_run].firstSector + offset - This->indexCache[min_run].firstOffset;
+}
+
BlockChainStream* BlockChainStream_Construct(
StorageImpl* parentStorage,
ULONG* headOfStreamPlaceHolder,
DirRef dirEntry)
{
BlockChainStream* newStream;
- ULONG blockIndex;
newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
newStream->parentStorage = parentStorage;
newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
newStream->ownerDirEntry = dirEntry;
- newStream->lastBlockNoInSequence = 0xFFFFFFFF;
- newStream->tailIndex = BLOCK_END_OF_CHAIN;
- newStream->numBlocks = 0;
-
- blockIndex = BlockChainStream_GetHeadOfChain(newStream);
+ newStream->indexCache = NULL;
+ newStream->indexCacheLen = 0;
+ newStream->indexCacheSize = 0;
- while (blockIndex != BLOCK_END_OF_CHAIN)
+ if (FAILED(BlockChainStream_UpdateIndexCache(newStream)))
{
- newStream->numBlocks++;
- newStream->tailIndex = blockIndex;
-
- if(FAILED(StorageImpl_GetNextBlockInChain(
- parentStorage,
- blockIndex,
- &blockIndex)))
- {
- HeapFree(GetProcessHeap(), 0, newStream);
- return NULL;
- }
+ HeapFree(GetProcessHeap(), 0, newStream->indexCache);
+ HeapFree(GetProcessHeap(), 0, newStream);
+ return NULL;
}
return newStream;
void BlockChainStream_Destroy(BlockChainStream* This)
{
+ if (This)
+ HeapFree(GetProcessHeap(), 0, This->indexCache);
HeapFree(GetProcessHeap(), 0, This);
}
*
* Returns the number of blocks that comprises this chain.
* This is not the size of the stream as the last block may not be full!
- *
*/
static ULONG BlockChainStream_GetCount(BlockChainStream* This)
{
- ULONG blockIndex;
- ULONG count = 0;
-
- blockIndex = BlockChainStream_GetHeadOfChain(This);
-
- while (blockIndex != BLOCK_END_OF_CHAIN)
- {
- count++;
-
- if(FAILED(StorageImpl_GetNextBlockInChain(
- This->parentStorage,
- blockIndex,
- &blockIndex)))
- return 0;
- }
-
- return count;
+ return This->numBlocks;
}
/******************************************************************************
ULONG bytesToReadInBuffer;
ULONG blockIndex;
BYTE* bufferWalker;
+ ULARGE_INTEGER stream_size;
TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
/*
* Find the first block in the stream that contains part of the buffer.
*/
- if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
- (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
- (blockNoInSequence < This->lastBlockNoInSequence) )
- {
- blockIndex = BlockChainStream_GetHeadOfChain(This);
- This->lastBlockNoInSequence = blockNoInSequence;
- }
- else
- {
- ULONG temp = blockNoInSequence;
-
- blockIndex = This->lastBlockNoInSequenceIndex;
- blockNoInSequence -= This->lastBlockNoInSequence;
- This->lastBlockNoInSequence = temp;
- }
-
- while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
- {
- if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
- return STG_E_DOCFILECORRUPT;
- blockNoInSequence--;
- }
+ blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
- if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
- return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
+ *bytesRead = 0;
- This->lastBlockNoInSequenceIndex = blockIndex;
+ stream_size = BlockChainStream_GetSize(This);
+ if (stream_size.QuadPart > offset.QuadPart)
+ size = min(stream_size.QuadPart - offset.QuadPart, size);
+ else
+ return S_OK;
/*
* Start reading the buffer.
*/
- *bytesRead = 0;
bufferWalker = buffer;
while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
break;
}
- return (size == 0) ? S_OK : STG_E_READFAULT;
+ return S_OK;
}
/******************************************************************************
/*
* Find the first block in the stream that contains part of the buffer.
*/
- if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
- (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
- (blockNoInSequence < This->lastBlockNoInSequence) )
- {
- blockIndex = BlockChainStream_GetHeadOfChain(This);
- This->lastBlockNoInSequence = blockNoInSequence;
- }
- else
- {
- ULONG temp = blockNoInSequence;
-
- blockIndex = This->lastBlockNoInSequenceIndex;
- blockNoInSequence -= This->lastBlockNoInSequence;
- This->lastBlockNoInSequence = temp;
- }
-
- while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
- {
- if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
- &blockIndex)))
- return STG_E_DOCFILECORRUPT;
- blockNoInSequence--;
- }
-
- This->lastBlockNoInSequenceIndex = blockIndex;
+ blockIndex = BlockChainStream_GetSectorOfOffset(This, blockNoInSequence);
/* BlockChainStream_SetSize should have already been called to ensure we have
* enough blocks in the chain to write into */
static BOOL BlockChainStream_Shrink(BlockChainStream* This,
ULARGE_INTEGER newSize)
{
- ULONG blockIndex, extraBlock;
+ ULONG blockIndex;
ULONG numBlocks;
- ULONG count = 1;
-
- /*
- * Reset the last accessed block cache.
- */
- This->lastBlockNoInSequence = 0xFFFFFFFF;
- This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
/*
* Figure out how many blocks are needed to contain the new size
if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
numBlocks++;
- blockIndex = BlockChainStream_GetHeadOfChain(This);
-
- /*
- * Go to the new end of chain
- */
- while (count < numBlocks)
+ if (numBlocks)
{
- if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
- &blockIndex)))
- return FALSE;
- count++;
+ /*
+ * Go to the new end of chain
+ */
+ blockIndex = BlockChainStream_GetSectorOfOffset(This, numBlocks-1);
+
+ /* Mark the new end of chain */
+ StorageImpl_SetNextBlockInChain(
+ This->parentStorage,
+ blockIndex,
+ BLOCK_END_OF_CHAIN);
+
+ This->tailIndex = blockIndex;
}
+ else
+ {
+ if (This->headOfStreamPlaceHolder != 0)
+ {
+ *This->headOfStreamPlaceHolder = BLOCK_END_OF_CHAIN;
+ }
+ else
+ {
+ DirEntry chainEntry;
+ assert(This->ownerDirEntry != DIRENTRY_NULL);
- /* Get the next block before marking the new end */
- if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
- &extraBlock)))
- return FALSE;
+ StorageImpl_ReadDirEntry(
+ This->parentStorage,
+ This->ownerDirEntry,
+ &chainEntry);
- /* Mark the new end of chain */
- StorageImpl_SetNextBlockInChain(
- This->parentStorage,
- blockIndex,
- BLOCK_END_OF_CHAIN);
+ chainEntry.startingBlock = BLOCK_END_OF_CHAIN;
+
+ StorageImpl_WriteDirEntry(
+ This->parentStorage,
+ This->ownerDirEntry,
+ &chainEntry);
+ }
+
+ This->tailIndex = BLOCK_END_OF_CHAIN;
+ }
- This->tailIndex = blockIndex;
This->numBlocks = numBlocks;
/*
* Mark the extra blocks as free
*/
- while (extraBlock != BLOCK_END_OF_CHAIN)
+ while (This->indexCacheLen && This->indexCache[This->indexCacheLen-1].lastOffset >= numBlocks)
{
- if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
- &blockIndex)))
- return FALSE;
- StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
- extraBlock = blockIndex;
+ struct BlockChainRun *last_run = &This->indexCache[This->indexCacheLen-1];
+ StorageImpl_FreeBigBlock(This->parentStorage,
+ last_run->firstSector + last_run->lastOffset - last_run->firstOffset);
+ if (last_run->lastOffset == last_run->firstOffset)
+ This->indexCacheLen--;
+ else
+ last_run->lastOffset--;
}
return TRUE;
This->numBlocks = newNumBlocks;
}
+ if (FAILED(BlockChainStream_UpdateIndexCache(This)))
+ return FALSE;
+
return TRUE;
}
&buffer,
&bytesRead);
+ if (SUCCEEDED(res) && bytesRead != sizeof(DWORD))
+ res = STG_E_READFAULT;
+
if (SUCCEEDED(res))
{
StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
HRESULT res = S_OK;
ULONG smallBlocksPerBigBlock;
+ DirEntry rootEntry;
+ ULONG blocksRequired;
+ ULARGE_INTEGER old_size, size_required;
offsetOfBlockInDepot.u.HighPart = 0;
/*
* If we run out of space for the small block depot, enlarge it
*/
- if (SUCCEEDED(res))
+ if (SUCCEEDED(res) && bytesRead == sizeof(DWORD))
{
StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
ULONG count =
BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
- ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
- ULONG nextBlock, newsbdIndex;
BYTE smallBlockDepot[MAX_BIG_BLOCK_SIZE];
+ ULARGE_INTEGER newSize, offset;
+ ULONG bytesWritten;
- nextBlock = sbdIndex;
- while (nextBlock != BLOCK_END_OF_CHAIN)
- {
- sbdIndex = nextBlock;
- StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
- }
-
- newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
- if (sbdIndex != BLOCK_END_OF_CHAIN)
- StorageImpl_SetNextBlockInChain(
- This->parentStorage,
- sbdIndex,
- newsbdIndex);
-
- StorageImpl_SetNextBlockInChain(
- This->parentStorage,
- newsbdIndex,
- BLOCK_END_OF_CHAIN);
+ newSize.QuadPart = (count + 1) * This->parentStorage->bigBlockSize;
+ BlockChainStream_Enlarge(This->parentStorage->smallBlockDepotChain, newSize);
/*
* Initialize all the small blocks to free
*/
memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
- StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
+ offset.QuadPart = count * This->parentStorage->bigBlockSize;
+ BlockChainStream_WriteAt(This->parentStorage->smallBlockDepotChain,
+ offset, This->parentStorage->bigBlockSize, smallBlockDepot, &bytesWritten);
- if (count == 0)
- {
- /*
- * We have just created the small block depot.
- */
- DirEntry rootEntry;
- ULONG sbStartIndex;
-
- /*
- * Save it in the header
- */
- This->parentStorage->smallBlockDepotStart = newsbdIndex;
- StorageImpl_SaveFileHeader(This->parentStorage);
-
- /*
- * And allocate the first big block that will contain small blocks
- */
- sbStartIndex =
- StorageImpl_GetNextFreeBigBlock(This->parentStorage);
-
- StorageImpl_SetNextBlockInChain(
- This->parentStorage,
- sbStartIndex,
- BLOCK_END_OF_CHAIN);
-
- StorageImpl_ReadDirEntry(
- This->parentStorage,
- This->parentStorage->base.storageDirEntry,
- &rootEntry);
-
- rootEntry.startingBlock = sbStartIndex;
- rootEntry.size.u.HighPart = 0;
- rootEntry.size.u.LowPart = This->parentStorage->bigBlockSize;
-
- StorageImpl_WriteDirEntry(
- This->parentStorage,
- This->parentStorage->base.storageDirEntry,
- &rootEntry);
- }
- else
- StorageImpl_SaveFileHeader(This->parentStorage);
+ StorageImpl_SaveFileHeader(This->parentStorage);
}
}
/*
* Verify if we have to allocate big blocks to contain small blocks
*/
- if (blockIndex % smallBlocksPerBigBlock == 0)
+ blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
+
+ size_required.QuadPart = blocksRequired * This->parentStorage->bigBlockSize;
+
+ old_size = BlockChainStream_GetSize(This->parentStorage->smallBlockRootChain);
+
+ if (size_required.QuadPart > old_size.QuadPart)
{
- DirEntry rootEntry;
- ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
+ BlockChainStream_SetSize(
+ This->parentStorage->smallBlockRootChain,
+ size_required);
StorageImpl_ReadDirEntry(
This->parentStorage,
This->parentStorage->base.storageDirEntry,
&rootEntry);
- if (rootEntry.size.u.LowPart <
- (blocksRequired * This->parentStorage->bigBlockSize))
- {
- rootEntry.size.u.LowPart += This->parentStorage->bigBlockSize;
-
- BlockChainStream_SetSize(
- This->parentStorage->smallBlockRootChain,
- rootEntry.size);
+ rootEntry.size = size_required;
- StorageImpl_WriteDirEntry(
- This->parentStorage,
- This->parentStorage->base.storageDirEntry,
- &rootEntry);
- }
+ StorageImpl_WriteDirEntry(
+ This->parentStorage,
+ This->parentStorage->base.storageDirEntry,
+ &rootEntry);
}
return blockIndex;
ULONG blockIndex;
ULONG bytesReadFromBigBlockFile;
BYTE* bufferWalker;
+ ULARGE_INTEGER stream_size;
/*
* This should never happen on a small block file.
*/
assert(offset.u.HighPart==0);
+ *bytesRead = 0;
+
+ stream_size = SmallBlockChainStream_GetSize(This);
+ if (stream_size.QuadPart > offset.QuadPart)
+ size = min(stream_size.QuadPart - offset.QuadPart, size);
+ else
+ return S_OK;
+
/*
* Find the first block in the stream that contains part of the buffer.
*/
/*
* Start reading the buffer.
*/
- *bytesRead = 0;
bufferWalker = buffer;
while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
if (FAILED(rc))
return rc;
+ if (!bytesReadFromBigBlockFile)
+ return STG_E_DOCFILECORRUPT;
+
/*
* Step to the next big block.
*/
offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
}
- return (size == 0) ? S_OK : STG_E_READFAULT;
+ return S_OK;
}
/******************************************************************************