From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Main/src-client/GuestDirectoryImpl.cpp | 501 ++++++++++++++++++++++++ 1 file changed, 501 insertions(+) create mode 100644 src/VBox/Main/src-client/GuestDirectoryImpl.cpp (limited to 'src/VBox/Main/src-client/GuestDirectoryImpl.cpp') diff --git a/src/VBox/Main/src-client/GuestDirectoryImpl.cpp b/src/VBox/Main/src-client/GuestDirectoryImpl.cpp new file mode 100644 index 00000000..8659c998 --- /dev/null +++ b/src/VBox/Main/src-client/GuestDirectoryImpl.cpp @@ -0,0 +1,501 @@ +/* $Id: GuestDirectoryImpl.cpp $ */ +/** @file + * VirtualBox Main - Guest directory handling. + */ + +/* + * Copyright (C) 2012-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_MAIN_GUESTDIRECTORY +#include "LoggingNew.h" + +#ifndef VBOX_WITH_GUEST_CONTROL +# error "VBOX_WITH_GUEST_CONTROL must defined in this file" +#endif +#include "GuestDirectoryImpl.h" +#include "GuestSessionImpl.h" +#include "GuestCtrlImplPrivate.h" + +#include "Global.h" +#include "AutoCaller.h" + +#include + + +// constructor / destructor +///////////////////////////////////////////////////////////////////////////// + +DEFINE_EMPTY_CTOR_DTOR(GuestDirectory) + +HRESULT GuestDirectory::FinalConstruct(void) +{ + LogFlowThisFunc(("\n")); + return BaseFinalConstruct(); +} + +void GuestDirectory::FinalRelease(void) +{ + LogFlowThisFuncEnter(); + uninit(); + BaseFinalRelease(); + LogFlowThisFuncLeave(); +} + +// public initializer/uninitializer for internal purposes only +///////////////////////////////////////////////////////////////////////////// + +int GuestDirectory::init(Console *pConsole, GuestSession *pSession, ULONG aObjectID, const GuestDirectoryOpenInfo &openInfo) +{ + LogFlowThisFunc(("pConsole=%p, pSession=%p, aObjectID=%RU32, strPath=%s, strFilter=%s, uFlags=%x\n", + pConsole, pSession, aObjectID, openInfo.mPath.c_str(), openInfo.mFilter.c_str(), openInfo.mFlags)); + + AssertPtrReturn(pConsole, VERR_INVALID_POINTER); + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + + /* Enclose the state transition NotReady->InInit->Ready. */ + AutoInitSpan autoInitSpan(this); + AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED); + + int vrc = bindToSession(pConsole, pSession, aObjectID); + if (RT_SUCCESS(vrc)) + { + mSession = pSession; + mObjectID = aObjectID; + + mData.mOpenInfo = openInfo; + } + + if (RT_SUCCESS(vrc)) + { + /* Start the directory process on the guest. */ + GuestProcessStartupInfo procInfo; + procInfo.mName.printf(tr("Opening directory \"%s\""), openInfo.mPath.c_str()); + procInfo.mTimeoutMS = 5 * 60 * 1000; /* 5 minutes timeout. */ + procInfo.mFlags = ProcessCreateFlag_WaitForStdOut; + procInfo.mExecutable= Utf8Str(VBOXSERVICE_TOOL_LS); + + procInfo.mArguments.push_back(procInfo.mExecutable); + procInfo.mArguments.push_back(Utf8Str("--machinereadable")); + /* We want the long output format which contains all the object details. */ + procInfo.mArguments.push_back(Utf8Str("-l")); +#if 0 /* Flags are not supported yet. */ + if (uFlags & DirectoryOpenFlag_NoSymlinks) + procInfo.mArguments.push_back(Utf8Str("--nosymlinks")); /** @todo What does GNU here? */ +#endif + /** @todo Recursion support? */ + procInfo.mArguments.push_back(openInfo.mPath); /* The directory we want to open. */ + + /* + * Start the process synchronously and keep it around so that we can use + * it later in subsequent read() calls. + */ + vrc = mData.mProcessTool.init(mSession, procInfo, false /*fAsync*/, NULL /*pvrcGuest*/); + if (RT_SUCCESS(vrc)) + { + /* As we need to know if the directory we were about to open exists and and is accessible, + * do the first read here in order to return a meaningful status here. */ + int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; + vrc = i_readInternal(mData.mObjData, &vrcGuest); + if (RT_FAILURE(vrc)) + { + /* + * We need to actively terminate our process tool in case of an error here, + * as this otherwise would be done on (directory) object destruction implicitly. + * This in turn then will run into a timeout, as the directory object won't be + * around anymore at that time. Ugly, but that's how it is for the moment. + */ + int vrcTerm = mData.mProcessTool.terminate(30 * RT_MS_1SEC, NULL /* prcGuest */); + AssertRC(vrcTerm); + + if (vrc == VERR_GSTCTL_GUEST_ERROR) + vrc = vrcGuest; + } + } + } + + /* Confirm a successful initialization when it's the case. */ + if (RT_SUCCESS(vrc)) + autoInitSpan.setSucceeded(); + else + autoInitSpan.setFailed(); + + LogFlowFuncLeaveRC(vrc); + return vrc; +} + +/** + * Uninitializes the instance. + * Called from FinalRelease(). + */ +void GuestDirectory::uninit(void) +{ + LogFlowThisFuncEnter(); + + /* Enclose the state transition Ready->InUninit->NotReady. */ + AutoUninitSpan autoUninitSpan(this); + if (autoUninitSpan.uninitDone()) + return; + + LogFlowThisFuncLeave(); +} + +// implementation of private wrapped getters/setters for attributes +///////////////////////////////////////////////////////////////////////////// + +HRESULT GuestDirectory::getDirectoryName(com::Utf8Str &aDirectoryName) +{ + LogFlowThisFuncEnter(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + aDirectoryName = mData.mOpenInfo.mPath; + + return S_OK; +} + +HRESULT GuestDirectory::getFilter(com::Utf8Str &aFilter) +{ + LogFlowThisFuncEnter(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + aFilter = mData.mOpenInfo.mFilter; + + return S_OK; +} + +// private methods +///////////////////////////////////////////////////////////////////////////// + +/** + * Entry point for guest side directory callbacks. + * + * @returns VBox status code. + * @param pCbCtx Host callback context. + * @param pSvcCb Host callback data. + */ +int GuestDirectory::i_callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb) +{ + AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER); + + LogFlowThisFunc(("strPath=%s, uContextID=%RU32, uFunction=%RU32, pSvcCb=%p\n", + mData.mOpenInfo.mPath.c_str(), pCbCtx->uContextID, pCbCtx->uMessage, pSvcCb)); + + int vrc; + switch (pCbCtx->uMessage) + { + case GUEST_MSG_DIR_NOTIFY: + { + int idx = 1; /* Current parameter index. */ + CALLBACKDATA_DIR_NOTIFY dataCb; + /* pSvcCb->mpaParms[0] always contains the context ID. */ + HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.uType); + HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.rc); + + LogFlowFunc(("uType=%RU32, vrcGguest=%Rrc\n", dataCb.uType, (int)dataCb.rc)); + + switch (dataCb.uType) + { + /* Nothing here yet, nothing to dispatch further. */ + + default: + vrc = VERR_NOT_SUPPORTED; + break; + } + break; + } + + default: + /* Silently ignore not implemented functions. */ + vrc = VERR_NOT_SUPPORTED; + break; + } + + LogFlowFuncLeaveRC(vrc); + return vrc; +} + +/** + * Converts a given guest directory error to a string. + * + * @returns Error string. + * @param vrcGuest Guest file error to return string for. + * @param pcszWhat Hint of what was involved when the error occurred. + */ +/* static */ +Utf8Str GuestDirectory::i_guestErrorToString(int vrcGuest, const char *pcszWhat) +{ + AssertPtrReturn(pcszWhat, ""); + + Utf8Str strErr; + switch (vrcGuest) + { +#define CASE_MSG(a_iRc, ...) \ + case a_iRc: strErr.printf(__VA_ARGS__); break; + CASE_MSG(VERR_CANT_CREATE , tr("Access to guest directory \"%s\" is denied"), pcszWhat); + CASE_MSG(VERR_DIR_NOT_EMPTY, tr("Guest directory \"%s\" is not empty"), pcszWhat); + default: + strErr.printf(tr("Error %Rrc for guest directory \"%s\" occurred\n"), vrcGuest, pcszWhat); + break; + } + +#undef CASE_MSG + + return strErr; +} + +/** + * @copydoc GuestObject::i_onUnregister + */ +int GuestDirectory::i_onUnregister(void) +{ + LogFlowThisFuncEnter(); + + int vrc = VINF_SUCCESS; + + LogFlowFuncLeaveRC(vrc); + return vrc; +} + +/** + * @copydoc GuestObject::i_onSessionStatusChange + */ +int GuestDirectory::i_onSessionStatusChange(GuestSessionStatus_T enmSessionStatus) +{ + RT_NOREF(enmSessionStatus); + + LogFlowThisFuncEnter(); + + int vrc = VINF_SUCCESS; + + LogFlowFuncLeaveRC(vrc); + return vrc; +} + +/** + * Closes this guest directory and removes it from the + * guest session's directory list. + * + * @return VBox status code. + * @param prcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned. + */ +int GuestDirectory::i_closeInternal(int *prcGuest) +{ + AssertPtrReturn(prcGuest, VERR_INVALID_POINTER); + + int vrc = mData.mProcessTool.terminate(30 * 1000 /* 30s timeout */, prcGuest); + if (RT_FAILURE(vrc)) + return vrc; + + AssertPtr(mSession); + int vrc2 = mSession->i_directoryUnregister(this); + if (RT_SUCCESS(vrc)) + vrc = vrc2; + + LogFlowThisFunc(("Returning vrc=%Rrc\n", vrc)); + return vrc; +} + +/** + * Reads the next directory entry, internal version. + * + * @return VBox status code. Will return VERR_NO_MORE_FILES if no more entries are available. + * @param objData Where to store the read directory entry as internal object data. + * @param prcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned. + */ +int GuestDirectory::i_readInternal(GuestFsObjData &objData, int *prcGuest) +{ + AssertPtrReturn(prcGuest, VERR_INVALID_POINTER); + + GuestProcessStreamBlock curBlock; + int vrc = mData.mProcessTool.waitEx(GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK, &curBlock, prcGuest); + if (RT_SUCCESS(vrc)) + { + /* + * Note: The guest process can still be around to serve the next + * upcoming stream block next time. + */ + if (!mData.mProcessTool.isRunning()) + vrc = mData.mProcessTool.getTerminationStatus(); /* Tool process is not running (anymore). Check termination status. */ + + if (RT_SUCCESS(vrc)) + { + if (curBlock.GetCount()) /* Did we get content? */ + { + if (curBlock.GetString("name")) + { + vrc = objData.FromLs(curBlock, true /* fLong */); + } + else + vrc = VERR_PATH_NOT_FOUND; + } + else + { + /* Nothing to read anymore. Tell the caller. */ + vrc = VERR_NO_MORE_FILES; + } + } + } + + LogFlowThisFunc(("Returning vrc=%Rrc\n", vrc)); + return vrc; +} + +/** + * Reads the next directory entry. + * + * @return VBox status code. Will return VERR_NO_MORE_FILES if no more entries are available. + * @param fsObjInfo Where to store the read directory entry. + * @param prcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned. + */ +int GuestDirectory::i_read(ComObjPtr &fsObjInfo, int *prcGuest) +{ + AssertPtrReturn(prcGuest, VERR_INVALID_POINTER); + + /* Create the FS info object. */ + HRESULT hr = fsObjInfo.createObject(); + if (FAILED(hr)) + return VERR_COM_UNEXPECTED; + + int vrc; + + /* If we have a valid object data cache, read from it. */ + if (mData.mObjData.mName.isNotEmpty()) + { + vrc = fsObjInfo->init(mData.mObjData); + if (RT_SUCCESS(vrc)) + { + mData.mObjData.mName = ""; /* Mark the object data as being empty (beacon). */ + } + } + else /* Otherwise ask the guest for the next object data (block). */ + { + + GuestFsObjData objData; + vrc = i_readInternal(objData, prcGuest); + if (RT_SUCCESS(vrc)) + vrc = fsObjInfo->init(objData); + } + + LogFlowThisFunc(("Returning vrc=%Rrc\n", vrc)); + return vrc; +} + +// implementation of public methods +///////////////////////////////////////////////////////////////////////////// +HRESULT GuestDirectory::close() +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.hrc())) return autoCaller.hrc(); + + LogFlowThisFuncEnter(); + + HRESULT hrc = S_OK; + + int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; + int vrc = i_closeInternal(&vrcGuest); + if (RT_FAILURE(vrc)) + { + switch (vrc) + { + case VERR_GSTCTL_GUEST_ERROR: + { + GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, mData.mOpenInfo.mPath.c_str()); + hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Closing guest directory failed: %s"), + GuestBase::getErrorAsString(ge).c_str()); + break; + } + case VERR_NOT_SUPPORTED: + /* Silently skip old Guest Additions which do not support killing the + * the guest directory handling process. */ + break; + + default: + hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, + tr("Closing guest directory \"%s\" failed: %Rrc"), mData.mOpenInfo.mPath.c_str(), vrc); + break; + } + } + + return hrc; +} + +HRESULT GuestDirectory::read(ComPtr &aObjInfo) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.hrc())) return autoCaller.hrc(); + + LogFlowThisFuncEnter(); + + HRESULT hrc = S_OK; + + ComObjPtr fsObjInfo; + int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; + int vrc = i_read(fsObjInfo, &vrcGuest); + if (RT_SUCCESS(vrc)) + { + /* Return info object to the caller. */ + hrc = fsObjInfo.queryInterfaceTo(aObjInfo.asOutParam()); + } + else + { + switch (vrc) + { + case VERR_GSTCTL_GUEST_ERROR: + { + GuestErrorInfo ge(GuestErrorInfo::Type_ToolLs, vrcGuest, mData.mOpenInfo.mPath.c_str()); + hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Reading guest directory failed: %s"), + GuestBase::getErrorAsString(ge).c_str()); + break; + } + case VERR_GSTCTL_PROCESS_EXIT_CODE: + hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" failed: %Rrc"), + mData.mOpenInfo.mPath.c_str(), mData.mProcessTool.getRc()); + break; + + case VERR_PATH_NOT_FOUND: + hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" failed: Path not found"), + mData.mOpenInfo.mPath.c_str()); + break; + + case VERR_NO_MORE_FILES: + /* See SDK reference. */ + hrc = setErrorBoth(VBOX_E_OBJECT_NOT_FOUND, vrc, tr("Reading guest directory \"%s\" failed: No more entries"), + mData.mOpenInfo.mPath.c_str()); + break; + + default: + hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" returned error: %Rrc\n"), + mData.mOpenInfo.mPath.c_str(), vrc); + break; + } + } + + LogFlowThisFunc(("Returning hrc=%Rhrc / vrc=%Rrc\n", hrc, vrc)); + return hrc; +} + -- cgit v1.2.3