VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestDirectoryImpl.cpp@ 79294

Last change on this file since 79294 was 79189, checked in by vboxsync, 5 years ago

Main/Guest*: Must always initalize rcGuest because experience inddicates that the code handling it cannot always be depended upon to set it. bugref:9320

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.2 KB
Line 
1/* $Id: GuestDirectoryImpl.cpp 79189 2019-06-17 16:04:55Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest directory handling.
4 */
5
6/*
7 * Copyright (C) 2012-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_MAIN_GUESTDIRECTORY
23#include "LoggingNew.h"
24
25#ifndef VBOX_WITH_GUEST_CONTROL
26# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
27#endif
28#include "GuestDirectoryImpl.h"
29#include "GuestSessionImpl.h"
30#include "GuestCtrlImplPrivate.h"
31
32#include "Global.h"
33#include "AutoCaller.h"
34
35#include <VBox/com/array.h>
36
37
38// constructor / destructor
39/////////////////////////////////////////////////////////////////////////////
40
41DEFINE_EMPTY_CTOR_DTOR(GuestDirectory)
42
43HRESULT GuestDirectory::FinalConstruct(void)
44{
45 LogFlowThisFunc(("\n"));
46 return BaseFinalConstruct();
47}
48
49void GuestDirectory::FinalRelease(void)
50{
51 LogFlowThisFuncEnter();
52 uninit();
53 BaseFinalRelease();
54 LogFlowThisFuncLeave();
55}
56
57// public initializer/uninitializer for internal purposes only
58/////////////////////////////////////////////////////////////////////////////
59
60int GuestDirectory::init(Console *pConsole, GuestSession *pSession, ULONG aObjectID, const GuestDirectoryOpenInfo &openInfo)
61{
62 LogFlowThisFunc(("pConsole=%p, pSession=%p, aObjectID=%RU32, strPath=%s, strFilter=%s, uFlags=%x\n",
63 pConsole, pSession, aObjectID, openInfo.mPath.c_str(), openInfo.mFilter.c_str(), openInfo.mFlags));
64
65 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
66 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
67
68 /* Enclose the state transition NotReady->InInit->Ready. */
69 AutoInitSpan autoInitSpan(this);
70 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
71
72 int vrc = bindToSession(pConsole, pSession, aObjectID);
73 if (RT_SUCCESS(vrc))
74 {
75 mSession = pSession;
76 mObjectID = aObjectID;
77
78 mData.mOpenInfo = openInfo;
79 }
80
81 if (RT_SUCCESS(vrc))
82 {
83 /* Start the directory process on the guest. */
84 GuestProcessStartupInfo procInfo;
85 procInfo.mName = Utf8StrFmt(tr("Reading directory \"%s\""), openInfo.mPath.c_str());
86 procInfo.mTimeoutMS = 5 * 60 * 1000; /* 5 minutes timeout. */
87 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
88 procInfo.mExecutable= Utf8Str(VBOXSERVICE_TOOL_LS);
89
90 procInfo.mArguments.push_back(procInfo.mExecutable);
91 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
92 /* We want the long output format which contains all the object details. */
93 procInfo.mArguments.push_back(Utf8Str("-l"));
94#if 0 /* Flags are not supported yet. */
95 if (uFlags & DirectoryOpenFlag_NoSymlinks)
96 procInfo.mArguments.push_back(Utf8Str("--nosymlinks")); /** @todo What does GNU here? */
97#endif
98 /** @todo Recursion support? */
99 procInfo.mArguments.push_back(openInfo.mPath); /* The directory we want to open. */
100
101 /*
102 * Start the process asynchronously and keep it around so that we can use
103 * it later in subsequent read() calls.
104 * Note: No guest rc available because operation is asynchronous.
105 */
106 vrc = mData.mProcessTool.init(mSession, procInfo, true /* Async */, NULL /* Guest rc */);
107
108/** @todo r=bird: IGuest::directoryOpen need to fail if the directory doesn't
109 * exist like it is documented to do. It seems this async approach or
110 * something is delaying such errors till GuestDirectory::read() is
111 * called, which is clearly messed up.
112 */
113 }
114
115 /* Confirm a successful initialization when it's the case. */
116 if (RT_SUCCESS(vrc))
117 autoInitSpan.setSucceeded();
118 else
119 autoInitSpan.setFailed();
120 return vrc;
121}
122
123/**
124 * Uninitializes the instance.
125 * Called from FinalRelease().
126 */
127void GuestDirectory::uninit(void)
128{
129 LogFlowThisFuncEnter();
130
131 /* Enclose the state transition Ready->InUninit->NotReady. */
132 AutoUninitSpan autoUninitSpan(this);
133 if (autoUninitSpan.uninitDone())
134 return;
135
136 LogFlowThisFuncLeave();
137}
138
139// implementation of private wrapped getters/setters for attributes
140/////////////////////////////////////////////////////////////////////////////
141
142HRESULT GuestDirectory::getDirectoryName(com::Utf8Str &aDirectoryName)
143{
144 LogFlowThisFuncEnter();
145
146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
147
148 aDirectoryName = mData.mOpenInfo.mPath;
149
150 return S_OK;
151}
152
153HRESULT GuestDirectory::getFilter(com::Utf8Str &aFilter)
154{
155 LogFlowThisFuncEnter();
156
157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
158
159 aFilter = mData.mOpenInfo.mFilter;
160
161 return S_OK;
162}
163
164// private methods
165/////////////////////////////////////////////////////////////////////////////
166
167int GuestDirectory::i_callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
168{
169 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
170 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
171
172 LogFlowThisFunc(("strPath=%s, uContextID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
173 mData.mOpenInfo.mPath.c_str(), pCbCtx->uContextID, pCbCtx->uMessage, pSvcCb));
174
175 int vrc;
176 switch (pCbCtx->uMessage)
177 {
178 case GUEST_MSG_DIR_NOTIFY:
179 {
180 int idx = 1; /* Current parameter index. */
181 CALLBACKDATA_DIR_NOTIFY dataCb;
182 /* pSvcCb->mpaParms[0] always contains the context ID. */
183 HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.uType);
184 HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.rc);
185
186 LogFlowFunc(("uType=%RU32, vrcGguest=%Rrc\n", dataCb.uType, (int)dataCb.rc));
187
188 switch (dataCb.uType)
189 {
190 /* Nothing here yet, nothing to dispatch further. */
191
192 default:
193 vrc = VERR_NOT_SUPPORTED;
194 break;
195 }
196 break;
197 }
198
199 default:
200 /* Silently ignore not implemented functions. */
201 vrc = VERR_NOT_SUPPORTED;
202 break;
203 }
204
205 LogFlowFuncLeaveRC(vrc);
206 return vrc;
207}
208
209/* static */
210Utf8Str GuestDirectory::i_guestErrorToString(int rcGuest)
211{
212 Utf8Str strError;
213
214 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
215 switch (rcGuest)
216 {
217 case VERR_CANT_CREATE:
218 strError += Utf8StrFmt("Access denied");
219 break;
220
221 case VERR_DIR_NOT_EMPTY:
222 strError += Utf8StrFmt("Not empty");
223 break;
224
225 default:
226 strError += Utf8StrFmt("%Rrc", rcGuest);
227 break;
228 }
229
230 return strError;
231}
232
233/**
234 * @copydoc GuestObject::i_onUnregister
235 */
236int GuestDirectory::i_onUnregister(void)
237{
238 LogFlowThisFuncEnter();
239
240 int vrc = VINF_SUCCESS;
241
242 LogFlowFuncLeaveRC(vrc);
243 return vrc;
244}
245
246/**
247 * @copydoc GuestObject::i_onSessionStatusChange
248 */
249int GuestDirectory::i_onSessionStatusChange(GuestSessionStatus_T enmSessionStatus)
250{
251 RT_NOREF(enmSessionStatus);
252
253 LogFlowThisFuncEnter();
254
255 int vrc = VINF_SUCCESS;
256
257 LogFlowFuncLeaveRC(vrc);
258 return vrc;
259}
260
261/**
262 * Closes this guest directory and removes it from the
263 * guest session's directory list.
264 *
265 * @return VBox status code.
266 * @param prcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
267 */
268int GuestDirectory::i_closeInternal(int *prcGuest)
269{
270 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
271
272 int rc = mData.mProcessTool.terminate(30 * 1000 /* 30s timeout */, prcGuest);
273 if (RT_FAILURE(rc))
274 return rc;
275
276 AssertPtr(mSession);
277 int rc2 = mSession->i_directoryUnregister(this);
278 if (RT_SUCCESS(rc))
279 rc = rc2;
280
281 LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
282 return rc;
283}
284
285/**
286 * Reads the next directory entry.
287 *
288 * @return VBox status code. Will return VERR_NO_MORE_FILES if no more entries are available.
289 * @param fsObjInfo Where to store the read directory entry.
290 * @param prcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
291 */
292int GuestDirectory::i_readInternal(ComObjPtr<GuestFsObjInfo> &fsObjInfo, int *prcGuest)
293{
294 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
295
296 /* Create the FS info object. */
297 HRESULT hr = fsObjInfo.createObject();
298 if (FAILED(hr))
299 return VERR_COM_UNEXPECTED;
300
301 GuestProcessStreamBlock curBlock;
302 int rc = mData.mProcessTool.waitEx(GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK, &curBlock, prcGuest);
303 if (RT_SUCCESS(rc))
304 {
305 /*
306 * Note: The guest process can still be around to serve the next
307 * upcoming stream block next time.
308 */
309 if (!mData.mProcessTool.isRunning())
310 rc = mData.mProcessTool.getTerminationStatus(); /* Tool process is not running (anymore). Check termination status. */
311
312 if (RT_SUCCESS(rc))
313 {
314 if (curBlock.GetCount()) /* Did we get content? */
315 {
316 GuestFsObjData objData;
317 rc = objData.FromLs(curBlock, true /* fLong */);
318 if (RT_SUCCESS(rc))
319 {
320 rc = fsObjInfo->init(objData);
321 }
322 else
323 rc = VERR_PATH_NOT_FOUND;
324 }
325 else
326 {
327 /* Nothing to read anymore. Tell the caller. */
328 rc = VERR_NO_MORE_FILES;
329 }
330 }
331 }
332
333 LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
334 return rc;
335}
336
337/* static */
338HRESULT GuestDirectory::i_setErrorExternal(VirtualBoxBase *pInterface, int rcGuest)
339{
340 AssertPtr(pInterface);
341 AssertMsg(RT_FAILURE(rcGuest), ("Guest rc does not indicate a failure when setting error\n"));
342
343 return pInterface->setError(VBOX_E_IPRT_ERROR, GuestDirectory::i_guestErrorToString(rcGuest).c_str());
344}
345
346// implementation of public methods
347/////////////////////////////////////////////////////////////////////////////
348HRESULT GuestDirectory::close()
349{
350 AutoCaller autoCaller(this);
351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
352
353 LogFlowThisFuncEnter();
354
355 HRESULT hr = S_OK;
356
357 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
358 int vrc = i_closeInternal(&rcGuest);
359 if (RT_FAILURE(vrc))
360 {
361 switch (vrc)
362 {
363 case VERR_GSTCTL_GUEST_ERROR:
364 hr = GuestDirectory::i_setErrorExternal(this, rcGuest);
365 break;
366
367 case VERR_NOT_SUPPORTED:
368 /* Silently skip old Guest Additions which do not support killing the
369 * the guest directory handling process. */
370 break;
371
372 default:
373 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
374 tr("Terminating open guest directory \"%s\" failed: %Rrc"), mData.mOpenInfo.mPath.c_str(), vrc);
375 break;
376 }
377 }
378
379 return hr;
380}
381
382HRESULT GuestDirectory::read(ComPtr<IFsObjInfo> &aObjInfo)
383{
384 AutoCaller autoCaller(this);
385 if (FAILED(autoCaller.rc())) return autoCaller.rc();
386
387 LogFlowThisFuncEnter();
388
389 HRESULT hr = S_OK;
390
391 ComObjPtr<GuestFsObjInfo> fsObjInfo;
392 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
393 int vrc = i_readInternal(fsObjInfo, &rcGuest);
394 if (RT_SUCCESS(vrc))
395 {
396 /* Return info object to the caller. */
397 hr = fsObjInfo.queryInterfaceTo(aObjInfo.asOutParam());
398 }
399 else
400 {
401 switch (vrc)
402 {
403 case VERR_GSTCTL_GUEST_ERROR:
404 hr = GuestDirectory::i_setErrorExternal(this, rcGuest);
405 break;
406
407 case VERR_GSTCTL_PROCESS_EXIT_CODE:
408 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading directory \"%s\" failed: %Rrc"),
409 mData.mOpenInfo.mPath.c_str(), mData.mProcessTool.getRc());
410 break;
411
412 case VERR_PATH_NOT_FOUND:
413 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading directory \"%s\" failed: Path not found"),
414 mData.mOpenInfo.mPath.c_str());
415 break;
416
417 case VERR_NO_MORE_FILES:
418 /* See SDK reference. */
419 hr = setErrorBoth(VBOX_E_OBJECT_NOT_FOUND, vrc, tr("Reading directory \"%s\" failed: No more entries"),
420 mData.mOpenInfo.mPath.c_str());
421 break;
422
423 default:
424 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading directory \"%s\" returned error: %Rrc\n"),
425 mData.mOpenInfo.mPath.c_str(), vrc);
426 break;
427 }
428 }
429
430 LogFlowThisFunc(("Returning hr=%Rhrc / vrc=%Rrc\n", hr, vrc));
431 return hr;
432}
433
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette