diff options
Diffstat (limited to 'src/VBox/Main/src-client/GuestFileImpl.cpp')
-rw-r--r-- | src/VBox/Main/src-client/GuestFileImpl.cpp | 1287 |
1 files changed, 1240 insertions, 47 deletions
diff --git a/src/VBox/Main/src-client/GuestFileImpl.cpp b/src/VBox/Main/src-client/GuestFileImpl.cpp index 19afbcc2..8a0ce53e 100644 --- a/src/VBox/Main/src-client/GuestFileImpl.cpp +++ b/src/VBox/Main/src-client/GuestFileImpl.cpp @@ -1,11 +1,10 @@ - /* $Id: GuestFileImpl.cpp $ */ /** @file - * VirtualBox Main - XXX. + * VirtualBox Main - Guest file handling. */ /* - * Copyright (C) 2012 Oracle Corporation + * Copyright (C) 2012-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -23,11 +22,18 @@ #include "GuestFileImpl.h" #include "GuestSessionImpl.h" #include "GuestCtrlImplPrivate.h" +#include "ConsoleImpl.h" +#include "VirtualBoxErrorInfoImpl.h" #include "Global.h" #include "AutoCaller.h" +#include "VBoxEvents.h" + +#include <iprt/cpp/utils.h> /* For unconst(). */ +#include <iprt/file.h> #include <VBox/com/array.h> +#include <VBox/com/listeners.h> #ifdef LOG_GROUP #undef LOG_GROUP @@ -36,6 +42,64 @@ #include <VBox/log.h> +/** + * Internal listener class to serve events in an + * active manner, e.g. without polling delays. + */ +class GuestFileListener +{ +public: + + GuestFileListener(void) + { + } + + HRESULT init(GuestFile *pFile) + { + AssertPtrReturn(pFile, E_POINTER); + mFile = pFile; + return S_OK; + } + + void uninit(void) + { + mFile = NULL; + } + + STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent) + { + switch (aType) + { + case VBoxEventType_OnGuestFileStateChanged: + case VBoxEventType_OnGuestFileOffsetChanged: + case VBoxEventType_OnGuestFileRead: + case VBoxEventType_OnGuestFileWrite: + { + AssertPtrReturn(mFile, E_POINTER); + int rc2 = mFile->signalWaitEvent(aType, aEvent); +#ifdef DEBUG_andy + LogFlowFunc(("Signalling events of type=%RU32, file=%p resulted in rc=%Rrc\n", + aType, mFile, rc2)); +#endif + break; + } + + default: + AssertMsgFailed(("Unhandled event %RU32\n", aType)); + break; + } + + return S_OK; + } + +private: + + GuestFile *mFile; +}; +typedef ListenerImpl<GuestFileListener, GuestFile*> GuestFileListenerImpl; + +VBOX_LISTENER_DECLARE(GuestFileListenerImpl) + // constructor / destructor ///////////////////////////////////////////////////////////////////////////// @@ -43,7 +107,7 @@ DEFINE_EMPTY_CTOR_DTOR(GuestFile) HRESULT GuestFile::FinalConstruct(void) { - LogFlowThisFunc(("\n")); + LogFlowThisFuncEnter(); return BaseFinalConstruct(); } @@ -58,29 +122,100 @@ void GuestFile::FinalRelease(void) // public initializer/uninitializer for internal purposes only ///////////////////////////////////////////////////////////////////////////// -int GuestFile::init(GuestSession *pSession, const Utf8Str &strPath, - const Utf8Str &strOpenMode, const Utf8Str &strDisposition, uint32_t uCreationMode, - int64_t iOffset, int *pGuestRc) +/** + * Initializes a file object but does *not* open the file on the guest + * yet. This is done in the dedidcated openFile call. + * + * @return IPRT status code. + * @param pConsole Pointer to console object. + * @param pSession Pointer to session object. + * @param uFileID Host-based file ID (part of the context ID). + * @param openInfo File opening information. + */ +int GuestFile::init(Console *pConsole, GuestSession *pSession, + ULONG uFileID, const GuestFileOpenInfo &openInfo) { - /* Enclose the state transition NotReady->InInit->Ready. */ - AutoInitSpan autoInitSpan(this); - AssertReturn(autoInitSpan.isOk(), E_FAIL); + LogFlowThisFunc(("pConsole=%p, pSession=%p, uFileID=%RU32, strPath=%s\n", + pConsole, pSession, uFileID, openInfo.mFileName.c_str())); - mData.mSession = pSession; - mData.mCreationMode = uCreationMode; - mData.mDisposition = GuestFile::getDispositionFromString(strDisposition); - mData.mFileName = strPath; - mData.mInitialSize = 0; - mData.mOpenMode = GuestFile::getOpenModeFromString(strOpenMode); - mData.mOffset = iOffset; + AssertPtrReturn(pConsole, VERR_INVALID_POINTER); + AssertPtrReturn(pSession, VERR_INVALID_POINTER); - /** @todo Validate parameters! */ - /** @todo Implement guest side file handling! */ + /* Enclose the state transition NotReady->InInit->Ready. */ + AutoInitSpan autoInitSpan(this); + AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED); - /* Confirm a successful initialization when it's the case. */ +#ifndef VBOX_WITH_GUEST_CONTROL autoInitSpan.setSucceeded(); - return VINF_SUCCESS; +#else + int vrc = bindToSession(pConsole, pSession, uFileID /* Object ID */); + if (RT_SUCCESS(vrc)) + { + mSession = pSession; + + mData.mID = uFileID; + mData.mInitialSize = 0; + mData.mStatus = FileStatus_Undefined; + mData.mOpenInfo = openInfo; + + unconst(mEventSource).createObject(); + HRESULT hr = mEventSource->init(); + if (FAILED(hr)) + vrc = VERR_COM_UNEXPECTED; + } + + if (RT_SUCCESS(vrc)) + { + try + { + GuestFileListener *pListener = new GuestFileListener(); + ComObjPtr<GuestFileListenerImpl> thisListener; + HRESULT hr = thisListener.createObject(); + if (SUCCEEDED(hr)) + hr = thisListener->init(pListener, this); + + if (SUCCEEDED(hr)) + { + com::SafeArray <VBoxEventType_T> eventTypes; + eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged); + eventTypes.push_back(VBoxEventType_OnGuestFileOffsetChanged); + eventTypes.push_back(VBoxEventType_OnGuestFileRead); + eventTypes.push_back(VBoxEventType_OnGuestFileWrite); + hr = mEventSource->RegisterListener(thisListener, + ComSafeArrayAsInParam(eventTypes), + TRUE /* Active listener */); + if (SUCCEEDED(hr)) + { + vrc = baseInit(); + if (RT_SUCCESS(vrc)) + { + mLocalListener = thisListener; + } + } + else + vrc = VERR_COM_UNEXPECTED; + } + else + vrc = VERR_COM_UNEXPECTED; + } + catch(std::bad_alloc &) + { + vrc = VERR_NO_MEMORY; + } + } + + if (RT_SUCCESS(vrc)) + { + /* Confirm a successful initialization when it's the case. */ + autoInitSpan.setSucceeded(); + } + else + autoInitSpan.setFailed(); + + LogFlowFuncLeaveRC(vrc); + return vrc; +#endif /* VBOX_WITH_GUEST_CONTROL */ } /** @@ -89,13 +224,16 @@ int GuestFile::init(GuestSession *pSession, const Utf8Str &strPath, */ void GuestFile::uninit(void) { - LogFlowThisFunc(("\n")); - /* Enclose the state transition Ready->InUninit->NotReady. */ AutoUninitSpan autoUninitSpan(this); if (autoUninitSpan.uninitDone()) return; + LogFlowThisFuncEnter(); + +#ifdef VBOX_WITH_GUEST_CONTROL + baseUninit(); +#endif LogFlowThisFuncLeave(); } @@ -114,13 +252,13 @@ STDMETHODIMP GuestFile::COMGETTER(CreationMode)(ULONG *aCreationMode) AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - *aCreationMode = mData.mCreationMode; + *aCreationMode = mData.mOpenInfo.mCreationMode; return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } -STDMETHODIMP GuestFile::COMGETTER(Disposition)(ULONG *aDisposition) +STDMETHODIMP GuestFile::COMGETTER(Disposition)(BSTR *aDisposition) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); @@ -132,7 +270,24 @@ STDMETHODIMP GuestFile::COMGETTER(Disposition)(ULONG *aDisposition) AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - *aDisposition = mData.mDisposition; + mData.mOpenInfo.mDisposition.cloneTo(aDisposition); + + return S_OK; +#endif /* VBOX_WITH_GUEST_CONTROL */ +} + +STDMETHODIMP GuestFile::COMGETTER(EventSource)(IEventSource ** aEventSource) +{ +#ifndef VBOX_WITH_GUEST_CONTROL + ReturnComNotImplemented(); +#else + CheckComArgOutPointerValid(aEventSource); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + /* No need to lock - lifetime constant. */ + mEventSource.queryInterfaceTo(aEventSource); return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ @@ -150,7 +305,25 @@ STDMETHODIMP GuestFile::COMGETTER(FileName)(BSTR *aFileName) AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - mData.mFileName.cloneTo(aFileName); + mData.mOpenInfo.mFileName.cloneTo(aFileName); + + return S_OK; +#endif /* VBOX_WITH_GUEST_CONTROL */ +} + +STDMETHODIMP GuestFile::COMGETTER(Id)(ULONG *aID) +{ +#ifndef VBOX_WITH_GUEST_CONTROL + ReturnComNotImplemented(); +#else + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + CheckComArgOutPointerValid(aID); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + *aID = mData.mID; return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ @@ -186,13 +359,13 @@ STDMETHODIMP GuestFile::COMGETTER(Offset)(LONG64 *aOffset) AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - *aOffset = mData.mOffset; + *aOffset = mData.mOffCurrent; return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } -STDMETHODIMP GuestFile::COMGETTER(OpenMode)(ULONG *aOpenMode) +STDMETHODIMP GuestFile::COMGETTER(OpenMode)(BSTR *aOpenMode) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); @@ -204,7 +377,25 @@ STDMETHODIMP GuestFile::COMGETTER(OpenMode)(ULONG *aOpenMode) AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - *aOpenMode = mData.mOpenMode; + mData.mOpenInfo.mOpenMode.cloneTo(aOpenMode); + + return S_OK; +#endif /* VBOX_WITH_GUEST_CONTROL */ +} + +STDMETHODIMP GuestFile::COMGETTER(Status)(FileStatus_T *aStatus) +{ +#ifndef VBOX_WITH_GUEST_CONTROL + ReturnComNotImplemented(); +#else + LogFlowThisFuncEnter(); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + *aStatus = mData.mStatus; return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ @@ -213,16 +404,865 @@ STDMETHODIMP GuestFile::COMGETTER(OpenMode)(ULONG *aOpenMode) // private methods ///////////////////////////////////////////////////////////////////////////// +int GuestFile::callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb) +{ + AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER); + + LogFlowThisFunc(("strName=%s, uContextID=%RU32, uFunction=%RU32, pSvcCb=%p\n", + mData.mOpenInfo.mFileName.c_str(), pCbCtx->uContextID, pCbCtx->uFunction, pSvcCb)); + + int vrc; + switch (pCbCtx->uFunction) + { + case GUEST_DISCONNECTED: + vrc = onGuestDisconnected(pCbCtx, pSvcCb); + break; + + case GUEST_FILE_NOTIFY: + vrc = onFileNotify(pCbCtx, pSvcCb); + break; + + default: + /* Silently ignore not implemented functions. */ + vrc = VERR_NOT_SUPPORTED; + break; + } + +#ifdef DEBUG + LogFlowFuncLeaveRC(vrc); +#endif + return vrc; +} + +int GuestFile::closeFile(int *pGuestRc) +{ + LogFlowThisFunc(("strFile=%s\n", mData.mOpenInfo.mFileName.c_str())); + + int vrc; + + GuestWaitEvent *pEvent = NULL; + GuestEventTypes eventTypes; + try + { + eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged); + + vrc = registerWaitEvent(eventTypes, &pEvent); + } + catch (std::bad_alloc) + { + vrc = VERR_NO_MEMORY; + } + + if (RT_FAILURE(vrc)) + return vrc; + + /* Prepare HGCM call. */ + VBOXHGCMSVCPARM paParms[4]; + int i = 0; + paParms[i++].setUInt32(pEvent->ContextID()); + paParms[i++].setUInt32(mData.mID /* Guest file ID */); + + vrc = sendCommand(HOST_FILE_CLOSE, i, paParms); + if (RT_SUCCESS(vrc)) + vrc = waitForStatusChange(pEvent, 30 * 1000 /* Timeout in ms */, + NULL /* FileStatus */, pGuestRc); + unregisterWaitEvent(pEvent); + + LogFlowFuncLeaveRC(vrc); + return vrc; +} + /* static */ -uint32_t GuestFile::getDispositionFromString(const Utf8Str &strDisposition) +Utf8Str GuestFile::guestErrorToString(int guestRc) { - return 0; /** @todo Implement me! */ + Utf8Str strError; + + /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */ + switch (guestRc) + { + case VERR_ALREADY_EXISTS: + strError += Utf8StrFmt(tr("File already exists")); + break; + + case VERR_FILE_NOT_FOUND: + strError += Utf8StrFmt(tr("File not found")); + break; + + case VERR_NET_HOST_NOT_FOUND: + strError += Utf8StrFmt(tr("Host name not found")); + break; + + case VERR_SHARING_VIOLATION: + strError += Utf8StrFmt(tr("Sharing violation")); + break; + + default: + strError += Utf8StrFmt("%Rrc", guestRc); + break; + } + + return strError; +} + +int GuestFile::onFileNotify(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData) +{ + AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER); + + LogFlowThisFuncEnter(); + + if (pSvcCbData->mParms < 3) + return VERR_INVALID_PARAMETER; + + int vrc = VINF_SUCCESS; + + int idx = 1; /* Current parameter index. */ + CALLBACKDATA_FILE_NOTIFY dataCb; + /* pSvcCb->mpaParms[0] always contains the context ID. */ + pSvcCbData->mpaParms[idx++].getUInt32(&dataCb.uType); + pSvcCbData->mpaParms[idx++].getUInt32(&dataCb.rc); + + FileStatus_T fileStatus = FileStatus_Undefined; + int guestRc = (int)dataCb.rc; /* uint32_t vs. int. */ + + LogFlowFunc(("uType=%RU32, guestRc=%Rrc\n", + dataCb.uType, guestRc)); + + if (RT_FAILURE(guestRc)) + { + int rc2 = setFileStatus(FileStatus_Error, guestRc); + AssertRC(rc2); + + rc2 = signalWaitEventInternal(pCbCtx, + guestRc, NULL /* pPayload */); + AssertRC(rc2); + + return VINF_SUCCESS; /* Report to the guest. */ + } + + switch (dataCb.uType) + { + case GUEST_FILE_NOTIFYTYPE_ERROR: + { + int rc2 = setFileStatus(FileStatus_Error, guestRc); + AssertRC(rc2); + + break; + } + + case GUEST_FILE_NOTIFYTYPE_OPEN: + { + if (pSvcCbData->mParms == 4) + { + pSvcCbData->mpaParms[idx++].getUInt32(&dataCb.u.open.uHandle); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + AssertMsg(mData.mID == VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCbCtx->uContextID), + ("File ID %RU32 does not match context ID %RU32\n", mData.mID, + VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCbCtx->uContextID))); + + /* Set the initial offset. On the guest the whole opening operation + * would fail if an initial seek isn't possible. */ + mData.mOffCurrent = mData.mOpenInfo.mInitialOffset; + } + + /* Set the process status. */ + int rc2 = setFileStatus(FileStatus_Open, guestRc); + AssertRC(rc2); + } + else + vrc = VERR_NOT_SUPPORTED; + + break; + } + + case GUEST_FILE_NOTIFYTYPE_CLOSE: + { + int rc2 = setFileStatus(FileStatus_Closed, guestRc); + AssertRC(rc2); + + break; + } + + case GUEST_FILE_NOTIFYTYPE_READ: + { + if (pSvcCbData->mParms == 4) + { + pSvcCbData->mpaParms[idx++].getPointer(&dataCb.u.read.pvData, + &dataCb.u.read.cbData); + uint32_t cbRead = dataCb.u.read.cbData; + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + mData.mOffCurrent += cbRead; + + alock.release(); + + com::SafeArray<BYTE> data((size_t)cbRead); + data.initFrom((BYTE*)dataCb.u.read.pvData, cbRead); + + fireGuestFileReadEvent(mEventSource, mSession, this, mData.mOffCurrent, + cbRead, ComSafeArrayAsInParam(data)); + } + else + vrc = VERR_NOT_SUPPORTED; + break; + } + + case GUEST_FILE_NOTIFYTYPE_WRITE: + { + if (pSvcCbData->mParms == 4) + { + pSvcCbData->mpaParms[idx++].getUInt32(&dataCb.u.write.cbWritten); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + mData.mOffCurrent += dataCb.u.write.cbWritten; + uint64_t uOffCurrent = mData.mOffCurrent; + + alock.release(); + + fireGuestFileWriteEvent(mEventSource, mSession, this, uOffCurrent, + dataCb.u.write.cbWritten); + } + else + vrc = VERR_NOT_SUPPORTED; + break; + } + + case GUEST_FILE_NOTIFYTYPE_SEEK: + { + if (pSvcCbData->mParms == 4) + { + pSvcCbData->mpaParms[idx++].getUInt64(&dataCb.u.seek.uOffActual); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + mData.mOffCurrent = dataCb.u.seek.uOffActual; + + alock.release(); + + fireGuestFileOffsetChangedEvent(mEventSource, mSession, this, + dataCb.u.seek.uOffActual, 0 /* Processed */); + } + else + vrc = VERR_NOT_SUPPORTED; + break; + } + + case GUEST_FILE_NOTIFYTYPE_TELL: + { + if (pSvcCbData->mParms == 4) + { + pSvcCbData->mpaParms[idx++].getUInt64(&dataCb.u.tell.uOffActual); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + mData.mOffCurrent = dataCb.u.tell.uOffActual; + + alock.release(); + + fireGuestFileOffsetChangedEvent(mEventSource, mSession, this, + dataCb.u.tell.uOffActual, 0 /* Processed */); + } + else + vrc = VERR_NOT_SUPPORTED; + break; + } + + default: + vrc = VERR_NOT_SUPPORTED; + break; + } + + if (RT_SUCCESS(vrc)) + { + GuestWaitEventPayload payload(dataCb.uType, &dataCb, sizeof(dataCb)); + int rc2 = signalWaitEventInternal(pCbCtx, guestRc, &payload); + AssertRC(rc2); + } + + LogFlowThisFunc(("uType=%RU32, guestRc=%Rrc\n", + dataCb.uType, dataCb.rc)); + + LogFlowFuncLeaveRC(vrc); + return vrc; +} + +int GuestFile::onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData) +{ + AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER); + + int vrc = setFileStatus(FileStatus_Down, VINF_SUCCESS); + + LogFlowFuncLeaveRC(vrc); + return vrc; +} + +/** + * Called by IGuestSession right before this file gets removed + * from the public file list. + */ +int GuestFile::onRemove(void) +{ + LogFlowThisFuncEnter(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + int vrc = VINF_SUCCESS; + + /* + * Note: The event source stuff holds references to this object, + * so make sure that this is cleaned up *before* calling uninit(). + */ + if (!mEventSource.isNull()) + { + mEventSource->UnregisterListener(mLocalListener); + + mLocalListener.setNull(); + unconst(mEventSource).setNull(); + } + + LogFlowFuncLeaveRC(vrc); + return vrc; +} + +int GuestFile::openFile(uint32_t uTimeoutMS, int *pGuestRc) +{ + LogFlowThisFuncEnter(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + LogFlowThisFunc(("strFile=%s, strOpenMode=%s, strDisposition=%s, uCreationMode=%RU32, uOffset=%RU64\n", + mData.mOpenInfo.mFileName.c_str(), mData.mOpenInfo.mOpenMode.c_str(), + mData.mOpenInfo.mDisposition.c_str(), mData.mOpenInfo.mCreationMode, mData.mOpenInfo.mInitialOffset)); + int vrc; + + GuestWaitEvent *pEvent = NULL; + GuestEventTypes eventTypes; + try + { + eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged); + + vrc = registerWaitEvent(eventTypes, &pEvent); + } + catch (std::bad_alloc) + { + vrc = VERR_NO_MEMORY; + } + + if (RT_FAILURE(vrc)) + return vrc; + + /* Prepare HGCM call. */ + VBOXHGCMSVCPARM paParms[8]; + int i = 0; + paParms[i++].setUInt32(pEvent->ContextID()); + paParms[i++].setPointer((void*)mData.mOpenInfo.mFileName.c_str(), + (ULONG)mData.mOpenInfo.mFileName.length() + 1); + paParms[i++].setPointer((void*)mData.mOpenInfo.mOpenMode.c_str(), + (ULONG)mData.mOpenInfo.mOpenMode.length() + 1); + paParms[i++].setPointer((void*)mData.mOpenInfo.mDisposition.c_str(), + (ULONG)mData.mOpenInfo.mDisposition.length() + 1); + paParms[i++].setPointer((void*)mData.mOpenInfo.mSharingMode.c_str(), + (ULONG)mData.mOpenInfo.mSharingMode.length() + 1); + paParms[i++].setUInt32(mData.mOpenInfo.mCreationMode); + paParms[i++].setUInt64(mData.mOpenInfo.mInitialOffset); + + alock.release(); /* Drop write lock before sending. */ + + vrc = sendCommand(HOST_FILE_OPEN, i, paParms); + if (RT_SUCCESS(vrc)) + vrc = waitForStatusChange(pEvent, uTimeoutMS, + NULL /* FileStatus */, pGuestRc); + + unregisterWaitEvent(pEvent); + + LogFlowFuncLeaveRC(vrc); + return vrc; +} + +int GuestFile::readData(uint32_t uSize, uint32_t uTimeoutMS, + void* pvData, uint32_t cbData, uint32_t* pcbRead) +{ + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + + LogFlowThisFunc(("uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n", + uSize, uTimeoutMS, pvData, cbData)); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + int vrc; + + GuestWaitEvent *pEvent = NULL; + GuestEventTypes eventTypes; + try + { + eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged); + eventTypes.push_back(VBoxEventType_OnGuestFileRead); + + vrc = registerWaitEvent(eventTypes, &pEvent); + } + catch (std::bad_alloc) + { + vrc = VERR_NO_MEMORY; + } + + if (RT_FAILURE(vrc)) + return vrc; + + /* Prepare HGCM call. */ + VBOXHGCMSVCPARM paParms[4]; + int i = 0; + paParms[i++].setUInt32(pEvent->ContextID()); + paParms[i++].setUInt32(mData.mID /* File handle */); + paParms[i++].setUInt32(uSize /* Size (in bytes) to read */); + + alock.release(); /* Drop write lock before sending. */ + + uint32_t cbRead; + vrc = sendCommand(HOST_FILE_READ, i, paParms); + if (RT_SUCCESS(vrc)) + vrc = waitForRead(pEvent, uTimeoutMS, pvData, cbData, &cbRead); + + if (RT_SUCCESS(vrc)) + { + LogFlowThisFunc(("cbRead=%RU32\n", cbRead)); + + if (pcbRead) + *pcbRead = cbRead; + } + + unregisterWaitEvent(pEvent); + + LogFlowFuncLeaveRC(vrc); + return vrc; +} + +int GuestFile::readDataAt(uint64_t uOffset, uint32_t uSize, uint32_t uTimeoutMS, + void* pvData, size_t cbData, size_t* pcbRead) +{ + LogFlowThisFunc(("uOffset=%RU64, uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n", + uOffset, uSize, uTimeoutMS, pvData, cbData)); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + int vrc; + + GuestWaitEvent *pEvent = NULL; + GuestEventTypes eventTypes; + try + { + eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged); + eventTypes.push_back(VBoxEventType_OnGuestFileRead); + + vrc = registerWaitEvent(eventTypes, &pEvent); + } + catch (std::bad_alloc) + { + vrc = VERR_NO_MEMORY; + } + + if (RT_FAILURE(vrc)) + return vrc; + + /* Prepare HGCM call. */ + VBOXHGCMSVCPARM paParms[4]; + int i = 0; + paParms[i++].setUInt32(pEvent->ContextID()); + paParms[i++].setUInt32(mData.mID /* File handle */); + paParms[i++].setUInt64(uOffset /* Offset (in bytes) to start reading */); + paParms[i++].setUInt32(uSize /* Size (in bytes) to read */); + + alock.release(); /* Drop write lock before sending. */ + + uint32_t cbRead; + vrc = sendCommand(HOST_FILE_READ_AT, i, paParms); + if (RT_SUCCESS(vrc)) + vrc = waitForRead(pEvent, uTimeoutMS, pvData, cbData, &cbRead); + + if (RT_SUCCESS(vrc)) + { + LogFlowThisFunc(("cbRead=%RU32\n", cbRead)); + + if (pcbRead) + *pcbRead = cbRead; + } + + unregisterWaitEvent(pEvent); + + LogFlowFuncLeaveRC(vrc); + return vrc; +} + +int GuestFile::seekAt(int64_t iOffset, GUEST_FILE_SEEKTYPE eSeekType, + uint32_t uTimeoutMS, uint64_t *puOffset) +{ + LogFlowThisFunc(("iOffset=%RI64, uTimeoutMS=%RU32\n", + iOffset, uTimeoutMS)); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + int vrc; + + GuestWaitEvent *pEvent = NULL; + GuestEventTypes eventTypes; + try + { + eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged); + eventTypes.push_back(VBoxEventType_OnGuestFileOffsetChanged); + + vrc = registerWaitEvent(eventTypes, &pEvent); + } + catch (std::bad_alloc) + { + vrc = VERR_NO_MEMORY; + } + + if (RT_FAILURE(vrc)) + return vrc; + + /* Prepare HGCM call. */ + VBOXHGCMSVCPARM paParms[4]; + int i = 0; + paParms[i++].setUInt32(pEvent->ContextID()); + paParms[i++].setUInt32(mData.mID /* File handle */); + paParms[i++].setUInt32(eSeekType /* Seek method */); + /** @todo uint64_t vs. int64_t! */ + paParms[i++].setUInt64((uint64_t)iOffset /* Offset (in bytes) to start reading */); + + alock.release(); /* Drop write lock before sending. */ + + vrc = sendCommand(HOST_FILE_SEEK, i, paParms); + if (RT_SUCCESS(vrc)) + vrc = waitForOffsetChange(pEvent, uTimeoutMS, puOffset); + + unregisterWaitEvent(pEvent); + + LogFlowFuncLeaveRC(vrc); + return vrc; } /* static */ -uint32_t GuestFile::getOpenModeFromString(const Utf8Str &strOpenMode) +HRESULT GuestFile::setErrorExternal(VirtualBoxBase *pInterface, int guestRc) +{ + AssertPtr(pInterface); + AssertMsg(RT_FAILURE(guestRc), ("Guest rc does not indicate a failure when setting error\n")); + + return pInterface->setError(VBOX_E_IPRT_ERROR, GuestFile::guestErrorToString(guestRc).c_str()); +} + +int GuestFile::setFileStatus(FileStatus_T fileStatus, int fileRc) +{ + LogFlowThisFuncEnter(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, fileRc=%Rrc\n", + mData.mStatus, fileStatus, fileRc)); + +#ifdef VBOX_STRICT + if (fileStatus == FileStatus_Error) + { + AssertMsg(RT_FAILURE(fileRc), ("Guest rc must be an error (%Rrc)\n", fileRc)); + } + else + AssertMsg(RT_SUCCESS(fileRc), ("Guest rc must not be an error (%Rrc)\n", fileRc)); +#endif + + if (mData.mStatus != fileStatus) + { + mData.mStatus = fileStatus; + mData.mLastError = fileRc; + + ComObjPtr<VirtualBoxErrorInfo> errorInfo; + HRESULT hr = errorInfo.createObject(); + ComAssertComRC(hr); + if (RT_FAILURE(fileRc)) + { + hr = errorInfo->initEx(VBOX_E_IPRT_ERROR, fileRc, + COM_IIDOF(IGuestFile), getComponentName(), + guestErrorToString(fileRc)); + ComAssertComRC(hr); + } + + alock.release(); /* Release lock before firing off event. */ + + fireGuestFileStateChangedEvent(mEventSource, mSession, + this, fileStatus, errorInfo); + } + + return VINF_SUCCESS; +} + +int GuestFile::waitForOffsetChange(GuestWaitEvent *pEvent, + uint32_t uTimeoutMS, uint64_t *puOffset) +{ + AssertPtrReturn(pEvent, VERR_INVALID_POINTER); + + VBoxEventType_T evtType; + ComPtr<IEvent> pIEvent; + int vrc = waitForEvent(pEvent, uTimeoutMS, + &evtType, pIEvent.asOutParam()); + if (RT_SUCCESS(vrc)) + { + if (evtType == VBoxEventType_OnGuestFileOffsetChanged) + { + if (puOffset) + { + ComPtr<IGuestFileOffsetChangedEvent> pFileEvent = pIEvent; + Assert(!pFileEvent.isNull()); + + HRESULT hr = pFileEvent->COMGETTER(Offset)((LONG64*)puOffset); + ComAssertComRC(hr); + } + } + else + vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED; + } + + return vrc; +} + +int GuestFile::waitForRead(GuestWaitEvent *pEvent, + uint32_t uTimeoutMS, + void *pvData, size_t cbData, uint32_t *pcbRead) +{ + AssertPtrReturn(pEvent, VERR_INVALID_POINTER); + + VBoxEventType_T evtType; + ComPtr<IEvent> pIEvent; + int vrc = waitForEvent(pEvent, uTimeoutMS, + &evtType, pIEvent.asOutParam()); + if (RT_SUCCESS(vrc)) + { + if (evtType == VBoxEventType_OnGuestFileRead) + { + ComPtr<IGuestFileReadEvent> pFileEvent = pIEvent; + Assert(!pFileEvent.isNull()); + + HRESULT hr; + if (pvData) + { + com::SafeArray <BYTE> data; + hr = pFileEvent->COMGETTER(Data)(ComSafeArrayAsOutParam(data)); + ComAssertComRC(hr); + size_t cbRead = data.size(); + if ( cbRead + && cbRead <= cbData) + { + memcpy(pvData, data.raw(), data.size()); + } + else + vrc = VERR_BUFFER_OVERFLOW; + } + if (pcbRead) + { + hr = pFileEvent->COMGETTER(Processed)((ULONG*)pcbRead); + ComAssertComRC(hr); + } + } + else + vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED; + } + + return vrc; +} + +int GuestFile::waitForStatusChange(GuestWaitEvent *pEvent, uint32_t uTimeoutMS, + FileStatus_T *pFileStatus, int *pGuestRc) +{ + AssertPtrReturn(pEvent, VERR_INVALID_POINTER); + /* pFileStatus is optional. */ + + VBoxEventType_T evtType; + ComPtr<IEvent> pIEvent; + int vrc = waitForEvent(pEvent, uTimeoutMS, + &evtType, pIEvent.asOutParam()); + if (RT_SUCCESS(vrc)) + { + Assert(evtType == VBoxEventType_OnGuestFileStateChanged); + ComPtr<IGuestFileStateChangedEvent> pFileEvent = pIEvent; + Assert(!pFileEvent.isNull()); + + HRESULT hr; + if (pFileStatus) + { + hr = pFileEvent->COMGETTER(Status)(pFileStatus); + ComAssertComRC(hr); + } + + ComPtr<IVirtualBoxErrorInfo> errorInfo; + hr = pFileEvent->COMGETTER(Error)(errorInfo.asOutParam()); + ComAssertComRC(hr); + + LONG lGuestRc; + hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc); + ComAssertComRC(hr); + + LogFlowThisFunc(("resultDetail=%RI32 (%Rrc)\n", + lGuestRc, lGuestRc)); + + if (RT_FAILURE((int)lGuestRc)) + vrc = VERR_GSTCTL_GUEST_ERROR; + + if (pGuestRc) + *pGuestRc = (int)lGuestRc; + } + + return vrc; +} + +int GuestFile::waitForWrite(GuestWaitEvent *pEvent, + uint32_t uTimeoutMS, uint32_t *pcbWritten) { - return 0; /** @todo Implement me! */ + AssertPtrReturn(pEvent, VERR_INVALID_POINTER); + + VBoxEventType_T evtType; + ComPtr<IEvent> pIEvent; + int vrc = waitForEvent(pEvent, uTimeoutMS, + &evtType, pIEvent.asOutParam()); + if (RT_SUCCESS(vrc)) + { + if (evtType == VBoxEventType_OnGuestFileWrite) + { + if (pcbWritten) + { + ComPtr<IGuestFileWriteEvent> pFileEvent = pIEvent; + Assert(!pFileEvent.isNull()); + + HRESULT hr = pFileEvent->COMGETTER(Processed)((ULONG*)pcbWritten); + ComAssertComRC(hr); + } + } + else + vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED; + } + + return vrc; +} + +int GuestFile::writeData(uint32_t uTimeoutMS, void *pvData, uint32_t cbData, + uint32_t *pcbWritten) +{ + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + + LogFlowThisFunc(("uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n", + uTimeoutMS, pvData, cbData)); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + int vrc; + + GuestWaitEvent *pEvent = NULL; + GuestEventTypes eventTypes; + try + { + eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged); + eventTypes.push_back(VBoxEventType_OnGuestFileWrite); + + vrc = registerWaitEvent(eventTypes, &pEvent); + } + catch (std::bad_alloc) + { + vrc = VERR_NO_MEMORY; + } + + if (RT_FAILURE(vrc)) + return vrc; + + /* Prepare HGCM call. */ + VBOXHGCMSVCPARM paParms[8]; + int i = 0; + paParms[i++].setUInt32(pEvent->ContextID()); + paParms[i++].setUInt32(mData.mID /* File handle */); + paParms[i++].setUInt32(cbData /* Size (in bytes) to write */); + paParms[i++].setPointer(pvData, cbData); + + alock.release(); /* Drop write lock before sending. */ + + uint32_t cbWritten; + vrc = sendCommand(HOST_FILE_WRITE, i, paParms); + if (RT_SUCCESS(vrc)) + vrc = waitForWrite(pEvent, uTimeoutMS, &cbWritten); + + if (RT_SUCCESS(vrc)) + { + LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten)); + + if (cbWritten) + *pcbWritten = cbWritten; + } + + unregisterWaitEvent(pEvent); + + LogFlowFuncLeaveRC(vrc); + return vrc; +} + +int GuestFile::writeDataAt(uint64_t uOffset, uint32_t uTimeoutMS, + void *pvData, uint32_t cbData, uint32_t *pcbWritten) +{ + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + + LogFlowThisFunc(("uOffset=%RU64, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n", + uOffset, uTimeoutMS, pvData, cbData)); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + int vrc; + + GuestWaitEvent *pEvent = NULL; + GuestEventTypes eventTypes; + try + { + eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged); + eventTypes.push_back(VBoxEventType_OnGuestFileWrite); + + vrc = registerWaitEvent(eventTypes, &pEvent); + } + catch (std::bad_alloc) + { + vrc = VERR_NO_MEMORY; + } + + if (RT_FAILURE(vrc)) + return vrc; + + /* Prepare HGCM call. */ + VBOXHGCMSVCPARM paParms[8]; + int i = 0; + paParms[i++].setUInt32(pEvent->ContextID()); + paParms[i++].setUInt32(mData.mID /* File handle */); + paParms[i++].setUInt64(uOffset /* Offset where to starting writing */); + paParms[i++].setUInt32(cbData /* Size (in bytes) to write */); + paParms[i++].setPointer(pvData, cbData); + + alock.release(); /* Drop write lock before sending. */ + + uint32_t cbWritten; + vrc = sendCommand(HOST_FILE_WRITE_AT, i, paParms); + if (RT_SUCCESS(vrc)) + vrc = waitForWrite(pEvent, uTimeoutMS, &cbWritten); + + if (RT_SUCCESS(vrc)) + { + LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten)); + + if (cbWritten) + *pcbWritten = cbWritten; + } + + unregisterWaitEvent(pEvent); + + LogFlowFuncLeaveRC(vrc); + return vrc; } // implementation of public methods @@ -238,17 +1278,27 @@ STDMETHODIMP GuestFile::Close(void) AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - AssertPtr(mData.mSession); - int rc = mData.mSession->fileRemoveFromList(this); + /* Close file on guest. */ + int guestRc; + int rc = closeFile(&guestRc); + /* On failure don't return here, instead do all the cleanup + * work first and then return an error. */ - /* - * Release autocaller before calling uninit. - */ - autoCaller.release(); + AssertPtr(mSession); + int rc2 = mSession->fileRemoveFromList(this); + if (RT_SUCCESS(rc)) + rc = rc2; - uninit(); + if (RT_FAILURE(rc)) + { + if (rc == VERR_GSTCTL_GUEST_ERROR) + return GuestFile::setErrorExternal(this, guestRc); + + return setError(VBOX_E_IPRT_ERROR, + tr("Closing guest file failed with %Rrc\n"), rc); + } - LogFlowFuncLeaveRC(rc); + LogFlowThisFunc(("Returning rc=%Rrc\n", rc)); return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } @@ -270,10 +1320,41 @@ STDMETHODIMP GuestFile::Read(ULONG aToRead, ULONG aTimeoutMS, ComSafeArrayOut(BY #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else + if (aToRead == 0) + return setError(E_INVALIDARG, tr("The size to read is zero")); + CheckComArgOutSafeArrayPointerValid(aData); + AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - ReturnComNotImplemented(); + com::SafeArray<BYTE> data((size_t)aToRead); + Assert(data.size() >= aToRead); + + HRESULT hr = S_OK; + + uint32_t cbRead; + int vrc = readData(aToRead, aTimeoutMS, + data.raw(), aToRead, &cbRead); + if (RT_SUCCESS(vrc)) + { + if (data.size() != cbRead) + data.resize(cbRead); + data.detachTo(ComSafeArrayOutArg(aData)); + } + else + { + switch (vrc) + { + default: + hr = setError(VBOX_E_IPRT_ERROR, + tr("Reading from file \"%s\" failed: %Rrc"), + mData.mOpenInfo.mFileName.c_str(), vrc); + break; + } + } + + LogFlowFuncLeaveRC(vrc); + return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } @@ -282,10 +1363,41 @@ STDMETHODIMP GuestFile::ReadAt(LONG64 aOffset, ULONG aToRead, ULONG aTimeoutMS, #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else + if (aToRead == 0) + return setError(E_INVALIDARG, tr("The size to read is zero")); + CheckComArgOutSafeArrayPointerValid(aData); + AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - ReturnComNotImplemented(); + com::SafeArray<BYTE> data((size_t)aToRead); + Assert(data.size() >= aToRead); + + HRESULT hr = S_OK; + + size_t cbRead; + int vrc = readDataAt(aOffset, aToRead, aTimeoutMS, + data.raw(), aToRead, &cbRead); + if (RT_SUCCESS(vrc)) + { + if (data.size() != cbRead) + data.resize(cbRead); + data.detachTo(ComSafeArrayOutArg(aData)); + } + else + { + switch (vrc) + { + default: + hr = setError(VBOX_E_IPRT_ERROR, + tr("Reading from file \"%s\" (at offset %RU64) failed: %Rrc"), + mData.mOpenInfo.mFileName.c_str(), aOffset, vrc); + break; + } + } + + LogFlowFuncLeaveRC(vrc); + return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } @@ -294,10 +1406,45 @@ STDMETHODIMP GuestFile::Seek(LONG64 aOffset, FileSeekType_T aType) #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else + LogFlowThisFuncEnter(); + AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - ReturnComNotImplemented(); + HRESULT hr = S_OK; + + GUEST_FILE_SEEKTYPE eSeekType; + switch (aType) + { + case FileSeekType_Set: + eSeekType = GUEST_FILE_SEEKTYPE_BEGIN; + break; + + case FileSeekType_Current: + eSeekType = GUEST_FILE_SEEKTYPE_CURRENT; + break; + + default: + return setError(E_INVALIDARG, tr("Invalid seek type specified")); + break; /* Never reached. */ + } + + int vrc = seekAt(aOffset, eSeekType, + 30 * 1000 /* 30s timeout */, NULL /* puOffset */); + if (RT_FAILURE(vrc)) + { + switch (vrc) + { + default: + hr = setError(VBOX_E_IPRT_ERROR, + tr("Seeking file \"%s\" (to offset %RI64) failed: %Rrc"), + mData.mOpenInfo.mFileName.c_str(), aOffset, vrc); + break; + } + } + + LogFlowFuncLeaveRC(vrc); + return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } @@ -318,10 +1465,33 @@ STDMETHODIMP GuestFile::Write(ComSafeArrayIn(BYTE, aData), ULONG aTimeoutMS, ULO #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else + LogFlowThisFuncEnter(); + + CheckComArgSafeArrayNotNull(aData); + CheckComArgOutPointerValid(aWritten); + AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - ReturnComNotImplemented(); + HRESULT hr = S_OK; + + com::SafeArray<BYTE> data(ComSafeArrayInArg(aData)); + int vrc = writeData(aTimeoutMS, data.raw(), (uint32_t)data.size(), + (uint32_t*)aWritten); + if (RT_FAILURE(vrc)) + { + switch (vrc) + { + default: + hr = setError(VBOX_E_IPRT_ERROR, + tr("Writing %zubytes to file \"%s\" failed: %Rrc"), + data.size(), mData.mOpenInfo.mFileName.c_str(), vrc); + break; + } + } + + LogFlowFuncLeaveRC(vrc); + return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } @@ -330,10 +1500,33 @@ STDMETHODIMP GuestFile::WriteAt(LONG64 aOffset, ComSafeArrayIn(BYTE, aData), ULO #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else + LogFlowThisFuncEnter(); + + CheckComArgSafeArrayNotNull(aData); + CheckComArgOutPointerValid(aWritten); + AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - ReturnComNotImplemented(); + HRESULT hr = S_OK; + + com::SafeArray<BYTE> data(ComSafeArrayInArg(aData)); + int vrc = writeData(aTimeoutMS, data.raw(), (uint32_t)data.size(), + (uint32_t*)aWritten); + if (RT_FAILURE(vrc)) + { + switch (vrc) + { + default: + hr = setError(VBOX_E_IPRT_ERROR, + tr("Writing %zubytes to file \"%s\" (at offset %RU64) failed: %Rrc"), + data.size(), mData.mOpenInfo.mFileName.c_str(), aOffset, vrc); + break; + } + } + + LogFlowFuncLeaveRC(vrc); + return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } |