VirtualBox

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

Last change on this file since 98526 was 98526, checked in by vboxsync, 22 months ago

Guest Control: Initial commit (work in progress, disabled by default). bugref:9783

IGuestDirectory:

Added new attributes id + status + an own event source. Also added for rewind support via rewind().

New event types for guest directory [un]registration, state changes and entry reads.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.3 KB
Line 
1/* $Id: GuestDirectoryImpl.cpp 98526 2023-02-10 15:10:50Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest directory handling.
4 */
5
6/*
7 * Copyright (C) 2012-2023 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 "GuestImpl.h"
39#include "GuestDirectoryImpl.h"
40#include "GuestSessionImpl.h"
41#include "GuestCtrlImplPrivate.h"
42#include "VirtualBoxErrorInfoImpl.h"
43
44#include "Global.h"
45#include "AutoCaller.h"
46#include "VBoxEvents.h"
47
48#include <VBox/com/array.h>
49#include <VBox/AssertGuest.h>
50
51
52// constructor / destructor
53/////////////////////////////////////////////////////////////////////////////
54
55DEFINE_EMPTY_CTOR_DTOR(GuestDirectory)
56
57HRESULT GuestDirectory::FinalConstruct(void)
58{
59 LogFlowThisFunc(("\n"));
60 return BaseFinalConstruct();
61}
62
63void GuestDirectory::FinalRelease(void)
64{
65 LogFlowThisFuncEnter();
66 uninit();
67 BaseFinalRelease();
68 LogFlowThisFuncLeave();
69}
70
71// public initializer/uninitializer for internal purposes only
72/////////////////////////////////////////////////////////////////////////////
73
74int GuestDirectory::init(Console *pConsole, GuestSession *pSession, ULONG aObjectID, const GuestDirectoryOpenInfo &openInfo)
75{
76 LogFlowThisFunc(("pConsole=%p, pSession=%p, aObjectID=%RU32, strPath=%s, enmFilter=%#x, fFlags=%x\n",
77 pConsole, pSession, aObjectID, openInfo.mPath.c_str(), openInfo.menmFilter, openInfo.mFlags));
78
79 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
80 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
81
82 /* Enclose the state transition NotReady->InInit->Ready. */
83 AutoInitSpan autoInitSpan(this);
84 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
85
86 int vrc = bindToSession(pConsole, pSession, aObjectID);
87 if (RT_SUCCESS(vrc))
88 {
89 mSession = pSession;
90 mObjectID = aObjectID;
91
92 mData.mOpenInfo = openInfo;
93 mData.mStatus = DirectoryStatus_Undefined;
94 mData.mLastError = VINF_SUCCESS;
95
96 unconst(mEventSource).createObject();
97 HRESULT hr = mEventSource->init();
98 if (FAILED(hr))
99 vrc = VERR_COM_UNEXPECTED;
100 }
101
102 /* Confirm a successful initialization when it's the case. */
103 if (RT_SUCCESS(vrc))
104 autoInitSpan.setSucceeded();
105 else
106 autoInitSpan.setFailed();
107
108 LogFlowFuncLeaveRC(vrc);
109 return vrc;
110}
111
112/**
113 * Uninitializes the instance.
114 * Called from FinalRelease().
115 */
116void GuestDirectory::uninit(void)
117{
118 LogFlowThisFuncEnter();
119
120 /* Enclose the state transition Ready->InUninit->NotReady. */
121 AutoUninitSpan autoUninitSpan(this);
122 if (autoUninitSpan.uninitDone())
123 return;
124
125 LogFlowThisFuncLeave();
126}
127
128// implementation of private wrapped getters/setters for attributes
129/////////////////////////////////////////////////////////////////////////////
130
131HRESULT GuestDirectory::getDirectoryName(com::Utf8Str &aDirectoryName)
132{
133 LogFlowThisFuncEnter();
134
135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
136
137 aDirectoryName = mData.mOpenInfo.mPath;
138
139 return S_OK;
140}
141
142HRESULT GuestDirectory::getEventSource(ComPtr<IEventSource> &aEventSource)
143{
144 /* No need to lock - lifetime constant. */
145 mEventSource.queryInterfaceTo(aEventSource.asOutParam());
146
147 return S_OK;
148}
149
150HRESULT GuestDirectory::getFilter(com::Utf8Str &aFilter)
151{
152 LogFlowThisFuncEnter();
153
154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
155
156 aFilter = mData.mOpenInfo.mFilter;
157
158 return S_OK;
159}
160
161HRESULT GuestDirectory::getId(ULONG *aId)
162{
163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
164
165 *aId = mObjectID;
166
167 return S_OK;
168}
169
170HRESULT GuestDirectory::getStatus(DirectoryStatus_T *aStatus)
171{
172 LogFlowThisFuncEnter();
173
174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
175
176 *aStatus = mData.mStatus;
177
178 return S_OK;
179}
180
181// private methods
182/////////////////////////////////////////////////////////////////////////////
183
184/**
185 * Entry point for guest side directory callbacks.
186 *
187 * @returns VBox status code.
188 * @param pCbCtx Host callback context.
189 * @param pSvcCb Host callback data.
190 */
191int GuestDirectory::i_callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
192{
193 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
194 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
195
196 LogFlowThisFunc(("strPath=%s, uContextID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
197 mData.mOpenInfo.mPath.c_str(), pCbCtx->uContextID, pCbCtx->uMessage, pSvcCb));
198
199 int vrc;
200 switch (pCbCtx->uMessage)
201 {
202 case GUEST_MSG_DISCONNECTED:
203 /** @todo vrc = i_onGuestDisconnected(pCbCtx, pSvcCb); */
204 vrc = VINF_SUCCESS; // TODO
205 break;
206
207 case GUEST_MSG_DIR_NOTIFY:
208 {
209 vrc = i_onDirNotify(pCbCtx, pSvcCb);
210 break;
211 }
212
213 default:
214 /* Silently ignore not implemented functions. */
215 vrc = VERR_NOT_SUPPORTED;
216 break;
217 }
218
219 LogFlowFuncLeaveRC(vrc);
220 return vrc;
221}
222
223/**
224 * Opens the directory on the guest side.
225 *
226 * @return VBox status code.
227 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
228 */
229int GuestDirectory::i_open(int *pvrcGuest)
230{
231 int vrc;
232#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
233 if ((mSession->i_getParent()->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS))
234 {
235 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
236
237 GuestWaitEvent *pEvent = NULL;
238 GuestEventTypes eventTypes;
239 try
240 {
241 eventTypes.push_back(VBoxEventType_OnGuestDirectoryStateChanged);
242
243 vrc = registerWaitEvent(eventTypes, &pEvent);
244 }
245 catch (std::bad_alloc &)
246 {
247 vrc = VERR_NO_MEMORY;
248 }
249
250 /* Prepare HGCM call. */
251 VBOXHGCMSVCPARM paParms[8];
252 int i = 0;
253 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
254 HGCMSvcSetPv(&paParms[i++], (void *)mData.mOpenInfo.mPath.c_str(), (ULONG)mData.mOpenInfo.mPath.length() + 1);
255 HGCMSvcSetPv(&paParms[i++], (void *)mData.mOpenInfo.mFilter.c_str(), (ULONG)mData.mOpenInfo.mFilter.length() + 1);
256 HGCMSvcSetU32(&paParms[i++], mData.mOpenInfo.menmFilter);
257 HGCMSvcSetU32(&paParms[i++], mData.mOpenInfo.mFlags);
258
259 alock.release(); /* Drop lock before sending. */
260
261 vrc = sendMessage(HOST_MSG_DIR_OPEN, i, paParms);
262 if (RT_SUCCESS(vrc))
263 {
264 vrc = pEvent->Wait(30 * 1000);
265 if (RT_SUCCESS(vrc))
266 {
267 }
268 }
269 }
270 else
271 {
272#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
273#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
274 vrc = i_openViaToolbox(pvrcGuest);
275#else
276 RT_NOREF(pvrcGuest);
277 vrc = VERR_NOT_SUPPORTED;
278#endif
279#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
280 }
281#endif
282
283 return vrc;
284}
285
286#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
287/**
288 * Opens the directory on the guest side (legacy version).
289 *
290 * @returns VBox status code.
291 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
292 *
293 * @note This uses an own guest process via the built-in toolbox in VBoxSerivce.
294 */
295int GuestDirectory::i_openViaToolbox(int *pvrcGuest)
296{
297 /* Start the directory process on the guest. */
298 GuestProcessStartupInfo procInfo;
299 procInfo.mName.printf(tr("Opening directory \"%s\""), mData.mOpenInfo.mPath.c_str());
300 procInfo.mTimeoutMS = 5 * 60 * 1000; /* 5 minutes timeout. */
301 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
302 procInfo.mExecutable= Utf8Str(VBOXSERVICE_TOOL_LS);
303
304 procInfo.mArguments.push_back(procInfo.mExecutable);
305 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
306 /* We want the long output format which contains all the object details. */
307 procInfo.mArguments.push_back(Utf8Str("-l"));
308# if 0 /* Flags are not supported yet. */
309 if (uFlags & DirectoryOpenFlag_NoSymlinks)
310 procInfo.mArguments.push_back(Utf8Str("--nosymlinks")); /** @todo What does GNU here? */
311# endif
312 /** @todo Recursion support? */
313 procInfo.mArguments.push_back(mData.mOpenInfo.mPath); /* The directory we want to open. */
314
315 /*
316 * Start the process synchronously and keep it around so that we can use
317 * it later in subsequent read() calls.
318 */
319 int vrc = mData.mProcessTool.init(mSession, procInfo, false /*fAsync*/, NULL /*pvrcGuest*/);
320 if (RT_SUCCESS(vrc))
321 {
322 /* As we need to know if the directory we were about to open exists and and is accessible,
323 * do the first read here in order to return a meaningful status here. */
324 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
325 vrc = i_readInternal(mData.mObjData, &vrcGuest);
326 if (RT_FAILURE(vrc))
327 {
328 /*
329 * We need to actively terminate our process tool in case of an error here,
330 * as this otherwise would be done on (directory) object destruction implicitly.
331 * This in turn then will run into a timeout, as the directory object won't be
332 * around anymore at that time. Ugly, but that's how it is for the moment.
333 */
334 /* ignore rc */ mData.mProcessTool.terminate(30 * RT_MS_1SEC, NULL /* pvrcGuest */);
335 }
336
337 if (pvrcGuest)
338 *pvrcGuest = vrcGuest;
339 }
340
341 return vrc;
342}
343#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
344
345/**
346 * Called when the guest side notifies the host of a directory event.
347 *
348 * @returns VBox status code.
349 * @param pCbCtx Host callback context.
350 * @param pSvcCbData Host callback data.
351 */
352int GuestDirectory::i_onDirNotify(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
353{
354#ifndef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
355 RT_NOREF(pCbCtx, pSvcCbData);
356 return VERR_NOT_SUPPORTED;
357#else
358 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
359 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
360
361 LogFlowThisFuncEnter();
362
363 if (pSvcCbData->mParms < 3)
364 return VERR_INVALID_PARAMETER;
365
366 int idx = 1; /* Current parameter index. */
367 CALLBACKDATA_DIR_NOTIFY dataCb;
368 RT_ZERO(dataCb);
369 /* pSvcCb->mpaParms[0] always contains the context ID. */
370 HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.uType);
371 HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.rc);
372
373 int vrcGuest = (int)dataCb.rc; /* uint32_t vs. int. */
374
375 LogFlowThisFunc(("uType=%RU32, vrcGuest=%Rrc\n", dataCb.uType, vrcGuest));
376
377 if (RT_FAILURE(vrcGuest))
378 {
379 /** @todo Set status? */
380
381 /* Ignore return code, as the event to signal might not be there (anymore). */
382 signalWaitEventInternal(pCbCtx, vrcGuest, NULL /* pPayload */);
383 return VINF_SUCCESS; /* Report to the guest. */
384 }
385
386 int vrc = VERR_NOT_SUPPORTED; /* Play safe by default. */
387
388 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
389 HRESULT hrc = errorInfo.createObject();
390 ComAssertComRCRet(hrc, VERR_COM_UNEXPECTED);
391 if (RT_FAILURE(vrcGuest))
392 {
393 hrc = errorInfo->initEx(VBOX_E_GSTCTL_GUEST_ERROR, vrcGuest,
394 COM_IIDOF(IGuestFile), getComponentName(),
395 i_guestErrorToString(vrcGuest, mData.mOpenInfo.mPath.c_str()));
396 ComAssertComRCRet(hrc, VERR_COM_UNEXPECTED);
397 }
398
399 switch (dataCb.uType)
400 {
401 case GUEST_DIR_NOTIFYTYPE_ERROR:
402 {
403 vrc = i_setStatus(DirectoryStatus_Error, vrcGuest);
404 break;
405 }
406
407 case GUEST_DIR_NOTIFYTYPE_OPEN:
408 {
409 vrc = i_setStatus(DirectoryStatus_Open, vrcGuest);
410 break;
411 }
412
413 case GUEST_DIR_NOTIFYTYPE_CLOSE:
414 {
415 vrc = i_setStatus(DirectoryStatus_Close, vrcGuest);
416 break;
417 }
418
419 case GUEST_DIR_NOTIFYTYPE_READ:
420 {
421 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mParms == 7, ("mParms=%u\n", pSvcCbData->mParms),
422 vrc = VERR_WRONG_PARAMETER_COUNT);
423 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mpaParms[idx].type == VBOX_HGCM_SVC_PARM_PTR,
424 ("type=%u\n", pSvcCbData->mpaParms[idx].type),
425 vrc = VERR_WRONG_PARAMETER_TYPE);
426
427 PGSTCTLFSOBJINFO pObjInfo;
428 uint32_t cbObjInfo;
429 vrc = HGCMSvcGetPv(&pSvcCbData->mpaParms[idx++], (void **)&pObjInfo, &cbObjInfo);
430 AssertRCBreak(vrc);
431 AssertBreakStmt(cbObjInfo == sizeof(GSTCTLFSOBJINFO), VERR_INVALID_PARAMETER);
432
433 GuestFsObjData fsObjData(mData.mOpenInfo.mPath);
434 vrc = fsObjData.FromGuestFsObjInfo(pObjInfo);
435 AssertRCBreak(vrc);
436 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
437 hrc = ptrFsObjInfo.createObject();
438 ComAssertComRCBreak(hrc, VERR_COM_UNEXPECTED);
439 vrc = ptrFsObjInfo->init(fsObjData);
440 AssertRCBreak(vrc);
441
442 ::FireGuestDirectoryReadEvent(mEventSource, mSession, this, ptrFsObjInfo);
443 break;
444 }
445
446 case GUEST_DIR_NOTIFYTYPE_REWIND:
447 {
448 /* Note: This does not change the overall status of the directory (i.e. open). */
449 ::FireGuestDirectoryStateChangedEvent(mEventSource, mSession, this, DirectoryStatus_Rewind, errorInfo);
450 break;
451 }
452
453 default:
454 AssertFailed();
455 break;
456 }
457
458 try
459 {
460 if (RT_SUCCESS(vrc))
461 {
462 GuestWaitEventPayload payload(dataCb.uType, &dataCb, sizeof(dataCb));
463
464 /* Ignore return code, as the event to signal might not be there (anymore). */
465 signalWaitEventInternal(pCbCtx, vrcGuest, &payload);
466 }
467 else /* OOM situation, wrong HGCM parameters or smth. not expected. */
468 {
469 /* Ignore return code, as the event to signal might not be there (anymore). */
470 signalWaitEventInternalEx(pCbCtx, vrc, 0 /* guestRc */, NULL /* pPayload */);
471 }
472 }
473 catch (int vrcEx) /* Thrown by GuestWaitEventPayload constructor. */
474 {
475 /* Also try to signal the waiter, to let it know of the OOM situation.
476 * Ignore return code, as the event to signal might not be there (anymore). */
477 signalWaitEventInternalEx(pCbCtx, vrcEx, 0 /* guestRc */, NULL /* pPayload */);
478 vrc = vrcEx;
479 }
480
481 LogFlowThisFunc(("uType=%RU32, rcGuest=%Rrc, vrc=%Rrc\n", dataCb.uType, vrcGuest, vrc));
482 return vrc;
483#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
484}
485
486/**
487 * Converts a given guest directory error to a string.
488 *
489 * @returns Error string.
490 * @param vrcGuest Guest file error to return string for.
491 * @param pcszWhat Hint of what was involved when the error occurred.
492 */
493/* static */
494Utf8Str GuestDirectory::i_guestErrorToString(int vrcGuest, const char *pcszWhat)
495{
496 AssertPtrReturn(pcszWhat, "");
497
498 Utf8Str strErr;
499 switch (vrcGuest)
500 {
501#define CASE_MSG(a_iRc, ...) \
502 case a_iRc: strErr.printf(__VA_ARGS__); break;
503 CASE_MSG(VERR_CANT_CREATE , tr("Access to guest directory \"%s\" is denied"), pcszWhat);
504 CASE_MSG(VERR_DIR_NOT_EMPTY, tr("Guest directory \"%s\" is not empty"), pcszWhat);
505 default:
506 strErr.printf(tr("Error %Rrc for guest directory \"%s\" occurred\n"), vrcGuest, pcszWhat);
507 break;
508 }
509
510#undef CASE_MSG
511
512 return strErr;
513}
514
515/**
516 * @copydoc GuestObject::i_onUnregister
517 */
518int GuestDirectory::i_onUnregister(void)
519{
520 LogFlowThisFuncEnter();
521
522 int vrc = VINF_SUCCESS;
523
524 LogFlowFuncLeaveRC(vrc);
525 return vrc;
526}
527
528/**
529 * @copydoc GuestObject::i_onSessionStatusChange
530 */
531int GuestDirectory::i_onSessionStatusChange(GuestSessionStatus_T enmSessionStatus)
532{
533 RT_NOREF(enmSessionStatus);
534
535 LogFlowThisFuncEnter();
536
537 int vrc = VINF_SUCCESS;
538
539 LogFlowFuncLeaveRC(vrc);
540 return vrc;
541}
542
543/**
544 * Closes this guest directory and removes it from the
545 * guest session's directory list.
546 *
547 * @return VBox status code.
548 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
549 */
550int GuestDirectory::i_close(int *pvrcGuest)
551{
552 int vrc;
553#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
554 if (mSession->i_getParent()->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS)
555 {
556 // TODO
557 vrc = VERR_NOT_IMPLEMENTED;
558 }
559 else
560 {
561#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
562#ifndef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
563 RT_NOREF(pvrcGuest);
564 vrc = VINF_SUCCESS; /* Nothing to do here. */
565#else
566 vrc = i_closeViaToolbox(pvrcGuest);
567#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
568#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
569 }
570#endif
571
572 AssertPtr(mSession);
573 int vrc2 = mSession->i_directoryUnregister(this);
574 if (RT_SUCCESS(vrc))
575 vrc = vrc2;
576
577 LogFlowThisFunc(("Returning vrc=%Rrc\n", vrc));
578 return vrc;
579}
580
581#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
582/**
583 * Closes this guest directory and removes it from the guest session's directory list (legacy version).
584 *
585 * @return VBox status code.
586 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
587 *
588 * @note This uses an own guest process via the built-in toolbox in VBoxSerivce.
589 */
590int GuestDirectory::i_closeViaToolbox(int *pvrcGuest)
591{
592 return mData.mProcessTool.terminate(30 * 1000 /* 30s timeout */, pvrcGuest);
593}
594#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
595
596/**
597 * Reads the next directory entry, internal version.
598 *
599 * @return VBox status code. Will return VERR_NO_MORE_FILES if no more entries are available.
600 * @param objData Where to store the read directory entry as internal object data.
601 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
602 */
603int GuestDirectory::i_readInternal(GuestFsObjData &objData, int *pvrcGuest)
604{
605 AssertPtrReturn(pvrcGuest, VERR_INVALID_POINTER);
606
607 int vrc;
608#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
609 if (mSession->i_getParent()->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS)
610 {
611 // TODO
612 RT_NOREF(objData, pvrcGuest);
613 vrc = 0;
614 }
615 else
616 {
617#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
618#ifndef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
619 RT_NOREF(objData);
620 vrc = VERR_NOT_SUPPORTED;
621#else /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
622 vrc = i_readInternalViaToolbox(objData, pvrcGuest);
623#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
624#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
625 }
626#endif
627
628 LogFlowThisFunc(("Returning vrc=%Rrc\n", vrc));
629 return vrc;
630}
631
632#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
633/**
634 * Reads the next directory entry, internal version (legacy version).
635 *
636 * @return VBox status code. Will return VERR_NO_MORE_FILES if no more entries are available.
637 * @param objData Where to store the read directory entry as internal object data.
638 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
639 *
640 * @note This uses an own guest process via the built-in toolbox in VBoxSerivce.
641 */
642int GuestDirectory::i_readInternalViaToolbox(GuestFsObjData &objData, int *pvrcGuest)
643{
644 GuestToolboxStreamBlock curBlock;
645 int vrc = mData.mProcessTool.waitEx(GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK, &curBlock, pvrcGuest);
646 if (RT_SUCCESS(vrc))
647 {
648 /*
649 * Note: The guest process can still be around to serve the next
650 * upcoming stream block next time.
651 */
652 if (!mData.mProcessTool.isRunning())
653 vrc = mData.mProcessTool.getTerminationStatus(); /* Tool process is not running (anymore). Check termination status. */
654
655 if (RT_SUCCESS(vrc))
656 {
657 if (curBlock.GetCount()) /* Did we get content? */
658 {
659 if (curBlock.GetString("name"))
660 {
661 vrc = objData.FromToolboxLs(curBlock, true /* fLong */);
662 }
663 else
664 vrc = VERR_PATH_NOT_FOUND;
665 }
666 else
667 {
668 /* Nothing to read anymore. Tell the caller. */
669 vrc = VERR_NO_MORE_FILES;
670 }
671 }
672 }
673
674 return vrc;
675}
676#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
677
678/**
679 * Reads the next directory entry.
680 *
681 * @return VBox status code. Will return VERR_NO_MORE_FILES if no more entries are available.
682 * @param fsObjInfo Where to store the read directory entry.
683 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
684 */
685int GuestDirectory::i_read(ComObjPtr<GuestFsObjInfo> &fsObjInfo, int *pvrcGuest)
686{
687 AssertPtrReturn(pvrcGuest, VERR_INVALID_POINTER);
688
689 /* Create the FS info object. */
690 HRESULT hr = fsObjInfo.createObject();
691 if (FAILED(hr))
692 return VERR_COM_UNEXPECTED;
693
694 int vrc;
695
696 /* If we have a valid object data cache, read from it. */
697 if (mData.mObjData.mName.isNotEmpty())
698 {
699 vrc = fsObjInfo->init(mData.mObjData);
700 if (RT_SUCCESS(vrc))
701 {
702 mData.mObjData.mName = ""; /* Mark the object data as being empty (beacon). */
703 }
704 }
705 else /* Otherwise ask the guest for the next object data. */
706 {
707
708 GuestFsObjData objData;
709 vrc = i_readInternal(objData, pvrcGuest);
710 if (RT_SUCCESS(vrc))
711 vrc = fsObjInfo->init(objData);
712 }
713
714 LogFlowThisFunc(("Returning vrc=%Rrc\n", vrc));
715 return vrc;
716}
717
718/**
719 * Rewinds the directory reading.
720 *
721 * @returns VBox status code.
722 * @retval VERR_GSTCTL_GUEST_ERROR when an error from the guest side has been received.
723 * @param uTimeoutMS Timeout (in ms) to wait.
724 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
725 */
726int GuestDirectory::i_rewind(uint32_t uTimeoutMS, int *pvrcGuest)
727{
728 RT_NOREF(pvrcGuest);
729#ifndef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
730 RT_NOREF(uTimeoutMS, pvrcGuest);
731#else
732 /* Only available for Guest Additions 7.1+. */
733 if (mSession->i_getParent()->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS)
734 {
735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
736
737 int vrc;
738
739 GuestWaitEvent *pEvent = NULL;
740 GuestEventTypes eventTypes;
741 try
742 {
743 eventTypes.push_back(VBoxEventType_OnGuestDirectoryStateChanged);
744 vrc = registerWaitEvent(eventTypes, &pEvent);
745 }
746 catch (std::bad_alloc &)
747 {
748 vrc = VERR_NO_MEMORY;
749 }
750
751 if (RT_FAILURE(vrc))
752 return vrc;
753
754 /* Prepare HGCM call. */
755 VBOXHGCMSVCPARM paParms[4];
756 int i = 0;
757 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
758 HGCMSvcSetU32(&paParms[i++], mObjectID /* Directory handle */);
759
760 alock.release(); /* Drop lock before sending. */
761
762 vrc = sendMessage(HOST_MSG_DIR_REWIND, i, paParms);
763 if (RT_SUCCESS(vrc))
764 {
765 VBoxEventType_T evtType;
766 ComPtr<IEvent> pIEvent;
767 vrc = waitForEvent(pEvent, uTimeoutMS, &evtType, pIEvent.asOutParam());
768 if (RT_SUCCESS(vrc))
769 {
770 if (evtType == VBoxEventType_OnGuestDirectoryStateChanged)
771 {
772 ComPtr<IGuestDirectoryStateChangedEvent> pEvt = pIEvent;
773 Assert(!pEvt.isNull());
774 }
775 else
776 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
777 }
778 else if (pEvent->HasGuestError()) /* Return guest vrc if available. */
779 vrc = pEvent->GetGuestError();
780 }
781
782 unregisterWaitEvent(pEvent);
783 return vrc;
784 }
785#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
786
787 return VERR_NOT_SUPPORTED;
788}
789
790/**
791 * Sets the current internal directory object status.
792 *
793 * @returns VBox status code.
794 * @param enmStatus New directory status to set.
795 * @param vrcDir New result code to set.
796 *
797 * @note Takes the write lock.
798 */
799int GuestDirectory::i_setStatus(DirectoryStatus_T enmStatus, int vrcDir)
800{
801 LogFlowThisFuncEnter();
802
803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
804
805 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, vrcDir=%Rrc\n", mData.mStatus, enmStatus, vrcDir));
806
807#ifdef VBOX_STRICT
808 if (enmStatus == DirectoryStatus_Error)
809 AssertMsg(RT_FAILURE(vrcDir), ("Guest vrc must be an error (%Rrc)\n", vrcDir));
810 else
811 AssertMsg(RT_SUCCESS(vrcDir), ("Guest vrc must not be an error (%Rrc)\n", vrcDir));
812#endif
813
814 if (mData.mStatus != enmStatus)
815 {
816 mData.mStatus = enmStatus;
817 mData.mLastError = vrcDir;
818
819 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
820 HRESULT hrc = errorInfo.createObject();
821 ComAssertComRCRet(hrc, VERR_COM_UNEXPECTED);
822 if (RT_FAILURE(vrcDir))
823 {
824 hrc = errorInfo->initEx(VBOX_E_GSTCTL_GUEST_ERROR, vrcDir,
825 COM_IIDOF(IGuestDirectory), getComponentName(),
826 i_guestErrorToString(vrcDir, mData.mOpenInfo.mPath.c_str()));
827 ComAssertComRCRet(hrc, VERR_COM_UNEXPECTED);
828 }
829 /* Note: On vrcDir success, errorInfo is set to S_OK and also sent via the event below. */
830
831 alock.release(); /* Release lock before firing off event. */
832
833 ::FireGuestDirectoryStateChangedEvent(mEventSource, mSession, this, enmStatus, errorInfo);
834 }
835
836 return VINF_SUCCESS;
837}
838
839// implementation of public methods
840/////////////////////////////////////////////////////////////////////////////
841HRESULT GuestDirectory::close()
842{
843 AutoCaller autoCaller(this);
844 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
845
846 LogFlowThisFuncEnter();
847
848 HRESULT hrc = S_OK;
849
850 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
851 int vrc = i_close(&vrcGuest);
852 if (RT_FAILURE(vrc))
853 {
854 switch (vrc)
855 {
856 case VERR_GSTCTL_GUEST_ERROR:
857 {
858 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, mData.mOpenInfo.mPath.c_str());
859 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Closing guest directory failed: %s"),
860 GuestBase::getErrorAsString(ge).c_str());
861 break;
862 }
863 case VERR_NOT_SUPPORTED:
864 /* Silently skip old Guest Additions which do not support killing the
865 * the guest directory handling process. */
866 break;
867
868 default:
869 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
870 tr("Closing guest directory \"%s\" failed: %Rrc"), mData.mOpenInfo.mPath.c_str(), vrc);
871 break;
872 }
873 }
874
875 return hrc;
876}
877
878HRESULT GuestDirectory::read(ComPtr<IFsObjInfo> &aObjInfo)
879{
880 AutoCaller autoCaller(this);
881 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
882
883 LogFlowThisFuncEnter();
884
885 HRESULT hrc = S_OK;
886
887 ComObjPtr<GuestFsObjInfo> fsObjInfo;
888 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
889 int vrc = i_read(fsObjInfo, &vrcGuest);
890 if (RT_SUCCESS(vrc))
891 {
892 /* Return info object to the caller. */
893 hrc = fsObjInfo.queryInterfaceTo(aObjInfo.asOutParam());
894 }
895 else
896 {
897 switch (vrc)
898 {
899 case VERR_GSTCTL_GUEST_ERROR:
900 {
901 GuestErrorInfo ge(
902#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
903 GuestErrorInfo::Type_ToolLs
904#else
905 GuestErrorInfo::Type_Fs
906#endif
907 , vrcGuest, mData.mOpenInfo.mPath.c_str());
908 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Reading guest directory failed: %s"),
909 GuestBase::getErrorAsString(ge).c_str());
910 break;
911 }
912
913#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
914 case VERR_GSTCTL_PROCESS_EXIT_CODE:
915 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" failed: %Rrc"),
916 mData.mOpenInfo.mPath.c_str(), mData.mProcessTool.getRc());
917 break;
918#endif
919 case VERR_PATH_NOT_FOUND:
920 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" failed: Path not found"),
921 mData.mOpenInfo.mPath.c_str());
922 break;
923
924 case VERR_NO_MORE_FILES:
925 /* See SDK reference. */
926 hrc = setErrorBoth(VBOX_E_OBJECT_NOT_FOUND, vrc, tr("Reading guest directory \"%s\" failed: No more entries"),
927 mData.mOpenInfo.mPath.c_str());
928 break;
929
930 default:
931 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" returned unhandled error: %Rrc\n"),
932 mData.mOpenInfo.mPath.c_str(), vrc);
933 break;
934 }
935 }
936
937 LogFlowThisFunc(("Returning hrc=%Rhrc / vrc=%Rrc\n", hrc, vrc));
938 return hrc;
939}
940
941HRESULT GuestDirectory::rewind(void)
942{
943 AutoCaller autoCaller(this);
944 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
945
946 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
947 int vrc = i_rewind(30 * 1000 /* Timeout in ms */, &vrcGuest);
948 if (RT_SUCCESS(vrc))
949 return S_OK;
950
951 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, mData.mOpenInfo.mPath.c_str());
952 return setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Rewinding guest directory failed: %s"),
953 GuestBase::getErrorAsString(ge).c_str());
954}
955
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