VirtualBox

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

Last change on this file since 98100 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.9 KB
Line 
1/* $Id: GuestDirectoryImpl.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest directory handling.
4 */
5
6/*
7 * Copyright (C) 2012-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_MAIN_GUESTDIRECTORY
33#include "LoggingNew.h"
34
35#ifndef VBOX_WITH_GUEST_CONTROL
36# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
37#endif
38#include "GuestDirectoryImpl.h"
39#include "GuestSessionImpl.h"
40#include "GuestCtrlImplPrivate.h"
41
42#include "Global.h"
43#include "AutoCaller.h"
44
45#include <VBox/com/array.h>
46
47
48// constructor / destructor
49/////////////////////////////////////////////////////////////////////////////
50
51DEFINE_EMPTY_CTOR_DTOR(GuestDirectory)
52
53HRESULT GuestDirectory::FinalConstruct(void)
54{
55 LogFlowThisFunc(("\n"));
56 return BaseFinalConstruct();
57}
58
59void GuestDirectory::FinalRelease(void)
60{
61 LogFlowThisFuncEnter();
62 uninit();
63 BaseFinalRelease();
64 LogFlowThisFuncLeave();
65}
66
67// public initializer/uninitializer for internal purposes only
68/////////////////////////////////////////////////////////////////////////////
69
70int GuestDirectory::init(Console *pConsole, GuestSession *pSession, ULONG aObjectID, const GuestDirectoryOpenInfo &openInfo)
71{
72 LogFlowThisFunc(("pConsole=%p, pSession=%p, aObjectID=%RU32, strPath=%s, strFilter=%s, uFlags=%x\n",
73 pConsole, pSession, aObjectID, openInfo.mPath.c_str(), openInfo.mFilter.c_str(), openInfo.mFlags));
74
75 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
76 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
77
78 /* Enclose the state transition NotReady->InInit->Ready. */
79 AutoInitSpan autoInitSpan(this);
80 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
81
82 int vrc = bindToSession(pConsole, pSession, aObjectID);
83 if (RT_SUCCESS(vrc))
84 {
85 mSession = pSession;
86 mObjectID = aObjectID;
87
88 mData.mOpenInfo = openInfo;
89 }
90
91 if (RT_SUCCESS(vrc))
92 {
93 /* Start the directory process on the guest. */
94 GuestProcessStartupInfo procInfo;
95 procInfo.mName.printf(tr("Opening directory \"%s\""), openInfo.mPath.c_str());
96 procInfo.mTimeoutMS = 5 * 60 * 1000; /* 5 minutes timeout. */
97 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
98 procInfo.mExecutable= Utf8Str(VBOXSERVICE_TOOL_LS);
99
100 procInfo.mArguments.push_back(procInfo.mExecutable);
101 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
102 /* We want the long output format which contains all the object details. */
103 procInfo.mArguments.push_back(Utf8Str("-l"));
104#if 0 /* Flags are not supported yet. */
105 if (uFlags & DirectoryOpenFlag_NoSymlinks)
106 procInfo.mArguments.push_back(Utf8Str("--nosymlinks")); /** @todo What does GNU here? */
107#endif
108 /** @todo Recursion support? */
109 procInfo.mArguments.push_back(openInfo.mPath); /* The directory we want to open. */
110
111 /*
112 * Start the process synchronously and keep it around so that we can use
113 * it later in subsequent read() calls.
114 */
115 vrc = mData.mProcessTool.init(mSession, procInfo, false /* Async */, NULL /* Guest rc */);
116 if (RT_SUCCESS(vrc))
117 {
118 /* As we need to know if the directory we were about to open exists and and is accessible,
119 * do the first read here in order to return a meaningful status here. */
120 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
121 vrc = i_readInternal(mData.mObjData, &vrcGuest);
122 if (RT_FAILURE(vrc))
123 {
124 /*
125 * We need to actively terminate our process tool in case of an error here,
126 * as this otherwise would be done on (directory) object destruction implicitly.
127 * This in turn then will run into a timeout, as the directory object won't be
128 * around anymore at that time. Ugly, but that's how it is for the moment.
129 */
130 int vrcTerm = mData.mProcessTool.terminate(30 * RT_MS_1SEC, NULL /* prcGuest */);
131 AssertRC(vrcTerm);
132
133 if (vrc == VERR_GSTCTL_GUEST_ERROR)
134 vrc = vrcGuest;
135 }
136 }
137 }
138
139 /* Confirm a successful initialization when it's the case. */
140 if (RT_SUCCESS(vrc))
141 autoInitSpan.setSucceeded();
142 else
143 autoInitSpan.setFailed();
144
145 LogFlowFuncLeaveRC(vrc);
146 return vrc;
147}
148
149/**
150 * Uninitializes the instance.
151 * Called from FinalRelease().
152 */
153void GuestDirectory::uninit(void)
154{
155 LogFlowThisFuncEnter();
156
157 /* Enclose the state transition Ready->InUninit->NotReady. */
158 AutoUninitSpan autoUninitSpan(this);
159 if (autoUninitSpan.uninitDone())
160 return;
161
162 LogFlowThisFuncLeave();
163}
164
165// implementation of private wrapped getters/setters for attributes
166/////////////////////////////////////////////////////////////////////////////
167
168HRESULT GuestDirectory::getDirectoryName(com::Utf8Str &aDirectoryName)
169{
170 LogFlowThisFuncEnter();
171
172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
173
174 aDirectoryName = mData.mOpenInfo.mPath;
175
176 return S_OK;
177}
178
179HRESULT GuestDirectory::getFilter(com::Utf8Str &aFilter)
180{
181 LogFlowThisFuncEnter();
182
183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
184
185 aFilter = mData.mOpenInfo.mFilter;
186
187 return S_OK;
188}
189
190// private methods
191/////////////////////////////////////////////////////////////////////////////
192
193/**
194 * Entry point for guest side directory callbacks.
195 *
196 * @returns VBox status code.
197 * @param pCbCtx Host callback context.
198 * @param pSvcCb Host callback data.
199 */
200int GuestDirectory::i_callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
201{
202 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
203 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
204
205 LogFlowThisFunc(("strPath=%s, uContextID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
206 mData.mOpenInfo.mPath.c_str(), pCbCtx->uContextID, pCbCtx->uMessage, pSvcCb));
207
208 int vrc;
209 switch (pCbCtx->uMessage)
210 {
211 case GUEST_MSG_DIR_NOTIFY:
212 {
213 int idx = 1; /* Current parameter index. */
214 CALLBACKDATA_DIR_NOTIFY dataCb;
215 /* pSvcCb->mpaParms[0] always contains the context ID. */
216 HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.uType);
217 HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.rc);
218
219 LogFlowFunc(("uType=%RU32, vrcGguest=%Rrc\n", dataCb.uType, (int)dataCb.rc));
220
221 switch (dataCb.uType)
222 {
223 /* Nothing here yet, nothing to dispatch further. */
224
225 default:
226 vrc = VERR_NOT_SUPPORTED;
227 break;
228 }
229 break;
230 }
231
232 default:
233 /* Silently ignore not implemented functions. */
234 vrc = VERR_NOT_SUPPORTED;
235 break;
236 }
237
238 LogFlowFuncLeaveRC(vrc);
239 return vrc;
240}
241
242/**
243 * Converts a given guest directory error to a string.
244 *
245 * @returns Error string.
246 * @param rcGuest Guest file error to return string for.
247 * @param pcszWhat Hint of what was involved when the error occurred.
248 */
249/* static */
250Utf8Str GuestDirectory::i_guestErrorToString(int rcGuest, const char *pcszWhat)
251{
252 AssertPtrReturn(pcszWhat, "");
253
254 Utf8Str strErr;
255 switch (rcGuest)
256 {
257#define CASE_MSG(a_iRc, ...) \
258 case a_iRc: strErr.printf(__VA_ARGS__); break;
259 CASE_MSG(VERR_CANT_CREATE , tr("Access to guest directory \"%s\" is denied"), pcszWhat);
260 CASE_MSG(VERR_DIR_NOT_EMPTY, tr("Guest directory \"%s\" is not empty"), pcszWhat);
261 default:
262 strErr.printf(tr("Error %Rrc for guest directory \"%s\" occurred\n"), rcGuest, pcszWhat);
263 break;
264 }
265
266#undef CASE_MSG
267
268 return strErr;
269}
270
271/**
272 * @copydoc GuestObject::i_onUnregister
273 */
274int GuestDirectory::i_onUnregister(void)
275{
276 LogFlowThisFuncEnter();
277
278 int vrc = VINF_SUCCESS;
279
280 LogFlowFuncLeaveRC(vrc);
281 return vrc;
282}
283
284/**
285 * @copydoc GuestObject::i_onSessionStatusChange
286 */
287int GuestDirectory::i_onSessionStatusChange(GuestSessionStatus_T enmSessionStatus)
288{
289 RT_NOREF(enmSessionStatus);
290
291 LogFlowThisFuncEnter();
292
293 int vrc = VINF_SUCCESS;
294
295 LogFlowFuncLeaveRC(vrc);
296 return vrc;
297}
298
299/**
300 * Closes this guest directory and removes it from the
301 * guest session's directory list.
302 *
303 * @return VBox status code.
304 * @param prcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
305 */
306int GuestDirectory::i_closeInternal(int *prcGuest)
307{
308 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
309
310 int vrc = mData.mProcessTool.terminate(30 * 1000 /* 30s timeout */, prcGuest);
311 if (RT_FAILURE(vrc))
312 return vrc;
313
314 AssertPtr(mSession);
315 int vrc2 = mSession->i_directoryUnregister(this);
316 if (RT_SUCCESS(vrc))
317 vrc = vrc2;
318
319 LogFlowThisFunc(("Returning vrc=%Rrc\n", vrc));
320 return vrc;
321}
322
323/**
324 * Reads the next directory entry, internal version.
325 *
326 * @return VBox status code. Will return VERR_NO_MORE_FILES if no more entries are available.
327 * @param objData Where to store the read directory entry as internal object data.
328 * @param prcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
329 */
330int GuestDirectory::i_readInternal(GuestFsObjData &objData, int *prcGuest)
331{
332 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
333
334 GuestProcessStreamBlock curBlock;
335 int vrc = mData.mProcessTool.waitEx(GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK, &curBlock, prcGuest);
336 if (RT_SUCCESS(vrc))
337 {
338 /*
339 * Note: The guest process can still be around to serve the next
340 * upcoming stream block next time.
341 */
342 if (!mData.mProcessTool.isRunning())
343 vrc = mData.mProcessTool.getTerminationStatus(); /* Tool process is not running (anymore). Check termination status. */
344
345 if (RT_SUCCESS(vrc))
346 {
347 if (curBlock.GetCount()) /* Did we get content? */
348 {
349 if (curBlock.GetString("name"))
350 {
351 vrc = objData.FromLs(curBlock, true /* fLong */);
352 }
353 else
354 vrc = VERR_PATH_NOT_FOUND;
355 }
356 else
357 {
358 /* Nothing to read anymore. Tell the caller. */
359 vrc = VERR_NO_MORE_FILES;
360 }
361 }
362 }
363
364 LogFlowThisFunc(("Returning vrc=%Rrc\n", vrc));
365 return vrc;
366}
367
368/**
369 * Reads the next directory entry.
370 *
371 * @return VBox status code. Will return VERR_NO_MORE_FILES if no more entries are available.
372 * @param fsObjInfo Where to store the read directory entry.
373 * @param prcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
374 */
375int GuestDirectory::i_read(ComObjPtr<GuestFsObjInfo> &fsObjInfo, int *prcGuest)
376{
377 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
378
379 /* Create the FS info object. */
380 HRESULT hr = fsObjInfo.createObject();
381 if (FAILED(hr))
382 return VERR_COM_UNEXPECTED;
383
384 int vrc;
385
386 /* If we have a valid object data cache, read from it. */
387 if (mData.mObjData.mName.isNotEmpty())
388 {
389 vrc = fsObjInfo->init(mData.mObjData);
390 if (RT_SUCCESS(vrc))
391 {
392 mData.mObjData.mName = ""; /* Mark the object data as being empty (beacon). */
393 }
394 }
395 else /* Otherwise ask the guest for the next object data (block). */
396 {
397
398 GuestFsObjData objData;
399 vrc = i_readInternal(objData, prcGuest);
400 if (RT_SUCCESS(vrc))
401 vrc = fsObjInfo->init(objData);
402 }
403
404 LogFlowThisFunc(("Returning vrc=%Rrc\n", vrc));
405 return vrc;
406}
407
408// implementation of public methods
409/////////////////////////////////////////////////////////////////////////////
410HRESULT GuestDirectory::close()
411{
412 AutoCaller autoCaller(this);
413 if (FAILED(autoCaller.rc())) return autoCaller.rc();
414
415 LogFlowThisFuncEnter();
416
417 HRESULT hrc = S_OK;
418
419 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
420 int vrc = i_closeInternal(&vrcGuest);
421 if (RT_FAILURE(vrc))
422 {
423 switch (vrc)
424 {
425 case VERR_GSTCTL_GUEST_ERROR:
426 {
427 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, mData.mOpenInfo.mPath.c_str());
428 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Closing guest directory failed: %s"),
429 GuestBase::getErrorAsString(ge).c_str());
430 break;
431 }
432 case VERR_NOT_SUPPORTED:
433 /* Silently skip old Guest Additions which do not support killing the
434 * the guest directory handling process. */
435 break;
436
437 default:
438 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
439 tr("Closing guest directory \"%s\" failed: %Rrc"), mData.mOpenInfo.mPath.c_str(), vrc);
440 break;
441 }
442 }
443
444 return hrc;
445}
446
447HRESULT GuestDirectory::read(ComPtr<IFsObjInfo> &aObjInfo)
448{
449 AutoCaller autoCaller(this);
450 if (FAILED(autoCaller.rc())) return autoCaller.rc();
451
452 LogFlowThisFuncEnter();
453
454 HRESULT hrc = S_OK;
455
456 ComObjPtr<GuestFsObjInfo> fsObjInfo;
457 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
458 int vrc = i_read(fsObjInfo, &vrcGuest);
459 if (RT_SUCCESS(vrc))
460 {
461 /* Return info object to the caller. */
462 hrc = fsObjInfo.queryInterfaceTo(aObjInfo.asOutParam());
463 }
464 else
465 {
466 switch (vrc)
467 {
468 case VERR_GSTCTL_GUEST_ERROR:
469 {
470 GuestErrorInfo ge(GuestErrorInfo::Type_ToolLs, vrcGuest, mData.mOpenInfo.mPath.c_str());
471 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Reading guest directory failed: %s"),
472 GuestBase::getErrorAsString(ge).c_str());
473 break;
474 }
475 case VERR_GSTCTL_PROCESS_EXIT_CODE:
476 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" failed: %Rrc"),
477 mData.mOpenInfo.mPath.c_str(), mData.mProcessTool.getRc());
478 break;
479
480 case VERR_PATH_NOT_FOUND:
481 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" failed: Path not found"),
482 mData.mOpenInfo.mPath.c_str());
483 break;
484
485 case VERR_NO_MORE_FILES:
486 /* See SDK reference. */
487 hrc = setErrorBoth(VBOX_E_OBJECT_NOT_FOUND, vrc, tr("Reading guest directory \"%s\" failed: No more entries"),
488 mData.mOpenInfo.mPath.c_str());
489 break;
490
491 default:
492 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" returned error: %Rrc\n"),
493 mData.mOpenInfo.mPath.c_str(), vrc);
494 break;
495 }
496 }
497
498 LogFlowThisFunc(("Returning hrc=%Rhrc / vrc=%Rrc\n", hrc, vrc));
499 return hrc;
500}
501
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