/* $Id: GuestDirectoryImpl.cpp 42693 2012-08-08 22:37:51Z vboxsync $ */ /** @file * VirtualBox Main - XXX. */ /* * Copyright (C) 2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ /******************************************************************************* * Header Files * *******************************************************************************/ #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(GuestSession *aSession, const Utf8Str &strPath, const Utf8Str &strFilter /*= ""*/, uint32_t uFlags /*= 0*/) { LogFlowThisFunc(("strPath=%s, strFilter=%s, uFlags=%x\n", strPath.c_str(), strFilter.c_str(), uFlags)); /* Enclose the state transition NotReady->InInit->Ready. */ AutoInitSpan autoInitSpan(this); AssertReturn(autoInitSpan.isOk(), E_FAIL); mData.mParent = aSession; mData.mName = strPath; mData.mFilter = strFilter; mData.mFlags = uFlags; /* Start the directory process on the guest. */ GuestProcessStartupInfo procInfo; procInfo.mName = Utf8StrFmt(tr("Reading directory \"%s\"", strPath.c_str())); procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_LS); procInfo.mTimeoutMS = 0; /* No timeout. */ procInfo.mFlags = ProcessCreateFlag_Hidden | ProcessCreateFlag_WaitForStdOut; 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(strPath); /* The directory we want to open. */ /* * Start the process asynchronously and keep it around so that we can use * it later in subsequent read() calls. */ int rc = mData.mParent->processCreateExInteral(procInfo, mData.mProcess); if (RT_SUCCESS(rc)) rc = mData.mProcess->startProcessAsync(); LogFlowThisFunc(("rc=%Rrc\n", rc)); if (RT_SUCCESS(rc)) { /* Confirm a successful initialization when it's the case. */ autoInitSpan.setSucceeded(); return rc; } autoInitSpan.setFailed(); return rc; } /** * Uninitializes the instance. * Called from FinalRelease(). */ void GuestDirectory::uninit(void) { LogFlowThisFunc(("\n")); /* Enclose the state transition Ready->InUninit->NotReady. */ AutoUninitSpan autoUninitSpan(this); if (autoUninitSpan.uninitDone()) return; if (!mData.mProcess.isNull()) mData.mProcess->uninit(); } // implementation of public getters/setters for attributes ///////////////////////////////////////////////////////////////////////////// STDMETHODIMP GuestDirectory::COMGETTER(DirectoryName)(BSTR *aName) { LogFlowThisFuncEnter(); CheckComArgOutPointerValid(aName); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); mData.mName.cloneTo(aName); return S_OK; } STDMETHODIMP GuestDirectory::COMGETTER(Filter)(BSTR *aFilter) { LogFlowThisFuncEnter(); CheckComArgOutPointerValid(aFilter); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); mData.mFilter.cloneTo(aFilter); return S_OK; } // private methods ///////////////////////////////////////////////////////////////////////////// int GuestDirectory::parseData(GuestProcessStreamBlock &streamBlock) { LogFlowThisFunc(("cbStream=%RU32\n", mData.mStream.GetSize())); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); int rc; do { /* Try parsing the data to see if the current block is complete. */ rc = mData.mStream.ParseBlock(streamBlock); if (streamBlock.GetCount()) break; } while (RT_SUCCESS(rc)); LogFlowFuncLeaveRC(rc); return rc; } // implementation of public methods ///////////////////////////////////////////////////////////////////////////// STDMETHODIMP GuestDirectory::Close(void) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); uninit(); return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } STDMETHODIMP GuestDirectory::Read(IFsObjInfo **aInfo) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); ComObjPtr pProcess = mData.mProcess; Assert(!pProcess.isNull()); GuestProcessStreamBlock streamBlock; GuestFsObjData objData; int rc = parseData(streamBlock); if ( RT_FAILURE(rc) || streamBlock.IsEmpty()) /* More data needed. */ { rc = pProcess->waitForStart(30 * 1000 /* 30s timeout */); } if (RT_SUCCESS(rc)) { BYTE byBuf[_64K]; size_t cbRead = 0; /** @todo Merge with GuestSession::queryFileInfoInternal. */ for (;RT_SUCCESS(rc);) { GuestProcessWaitResult waitRes; rc = pProcess->waitFor( ProcessWaitForFlag_Terminate | ProcessWaitForFlag_StdOut, 30 * 1000 /* Timeout */, waitRes); if ( RT_FAILURE(rc) || waitRes.mResult == ProcessWaitResult_Terminate || waitRes.mResult == ProcessWaitResult_Error || waitRes.mResult == ProcessWaitResult_Timeout) { rc = waitRes.mRC; break; } rc = pProcess->readData(OUTPUT_HANDLE_ID_STDOUT, sizeof(byBuf), 30 * 1000 /* Timeout */, byBuf, sizeof(byBuf), &cbRead); if (RT_FAILURE(rc)) break; if (cbRead) { AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); rc = mData.mStream.AddData(byBuf, cbRead); if (RT_FAILURE(rc)) break; LogFlowThisFunc(("rc=%Rrc, cbRead=%RU64, cbStreamOut=%RU32\n", rc, cbRead, mData.mStream.GetSize())); rc = parseData(streamBlock); if (RT_SUCCESS(rc)) { /* Parsing the current stream block succeeded so * we don't need more at the moment. */ break; } } } LogFlowThisFunc(("Reading done with rc=%Rrc, cbRead=%RU64, cbStream=%RU32\n", rc, cbRead, mData.mStream.GetSize())); if (RT_SUCCESS(rc)) { rc = parseData(streamBlock); if (rc == VERR_NO_DATA) /* Since this is the last parsing call, this is ok. */ rc = VINF_SUCCESS; } /* * Note: The guest process can still be around to serve the next * upcoming stream block next time. */ if (RT_SUCCESS(rc)) { /** @todo Move into common function. */ ProcessStatus_T procStatus = ProcessStatus_Undefined; LONG exitCode = 0; HRESULT hr2 = pProcess->COMGETTER(Status(&procStatus)); ComAssertComRC(hr2); hr2 = pProcess->COMGETTER(ExitCode(&exitCode)); ComAssertComRC(hr2); if ( ( procStatus != ProcessStatus_Started && procStatus != ProcessStatus_Paused && procStatus != ProcessStatus_Terminating ) && exitCode != 0) { rc = VERR_ACCESS_DENIED; } } } if (RT_SUCCESS(rc)) { if (streamBlock.GetCount()) /* Did we get content? */ { rc = objData.FromLs(streamBlock); if (RT_FAILURE(rc)) rc = VERR_PATH_NOT_FOUND; if (RT_SUCCESS(rc)) { /* Create the object. */ ComObjPtr pFsObjInfo; HRESULT hr2 = pFsObjInfo.createObject(); if (FAILED(hr2)) rc = VERR_COM_UNEXPECTED; if (RT_SUCCESS(rc)) rc = pFsObjInfo->init(objData); if (RT_SUCCESS(rc)) { /* Return info object to the caller. */ hr2 = pFsObjInfo.queryInterfaceTo(aInfo); if (FAILED(hr2)) rc = VERR_COM_UNEXPECTED; } } } else { /* Nothing to read anymore. Tell the caller. */ rc = VERR_NO_MORE_FILES; } } HRESULT hr = S_OK; if (RT_FAILURE(rc)) /** @todo Add more errors here. */ { switch (rc) { case VERR_ACCESS_DENIED: hr = setError(VBOX_E_IPRT_ERROR, tr("Reading directory \"%s\" failed: Unable to read / access denied"), mData.mName.c_str()); break; case VERR_PATH_NOT_FOUND: hr = setError(VBOX_E_IPRT_ERROR, tr("Reading directory \"%s\" failed: Path not found"), mData.mName.c_str()); break; case VERR_NO_MORE_FILES: hr = setError(VBOX_E_OBJECT_NOT_FOUND, tr("No more entries for directory \"%s\""), mData.mName.c_str()); break; default: hr = setError(VBOX_E_IPRT_ERROR, tr("Error while reading directory \"%s\": %Rrc\n"), mData.mName.c_str(), rc); break; } } LogFlowFuncLeaveRC(rc); return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ }