VirtualBox

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

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

Guest Control: Reduced #ifdefs. bugref:9783

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.8 KB
Line 
1/* $Id: GuestDirectoryImpl.cpp 98643 2023-02-20 11:01:13Z 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 To be implemented
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#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
272 {
273 vrc = i_openViaToolbox(pvrcGuest);
274 }
275
276 return vrc;
277}
278
279#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
280/**
281 * Opens the directory on the guest side (legacy version).
282 *
283 * @returns VBox status code.
284 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
285 *
286 * @note This uses an own guest process via the built-in toolbox in VBoxSerivce.
287 */
288int GuestDirectory::i_openViaToolbox(int *pvrcGuest)
289{
290 /* Start the directory process on the guest. */
291 GuestProcessStartupInfo procInfo;
292 procInfo.mName.printf(tr("Opening directory \"%s\""), mData.mOpenInfo.mPath.c_str());
293 procInfo.mTimeoutMS = 5 * 60 * 1000; /* 5 minutes timeout. */
294 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
295 procInfo.mExecutable= Utf8Str(VBOXSERVICE_TOOL_LS);
296
297 procInfo.mArguments.push_back(procInfo.mExecutable);
298 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
299 /* We want the long output format which contains all the object details. */
300 procInfo.mArguments.push_back(Utf8Str("-l"));
301# if 0 /* Flags are not supported yet. */
302 if (uFlags & DirectoryOpenFlag_NoSymlinks)
303 procInfo.mArguments.push_back(Utf8Str("--nosymlinks")); /** @todo What does GNU here? */
304# endif
305 /** @todo Recursion support? */
306 procInfo.mArguments.push_back(mData.mOpenInfo.mPath); /* The directory we want to open. */
307
308 /*
309 * Start the process synchronously and keep it around so that we can use
310 * it later in subsequent read() calls.
311 */
312 int vrc = mData.mProcessTool.init(mSession, procInfo, false /*fAsync*/, NULL /*pvrcGuest*/);
313 if (RT_SUCCESS(vrc))
314 {
315 /* As we need to know if the directory we were about to open exists and and is accessible,
316 * do the first read here in order to return a meaningful status here. */
317 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
318 vrc = i_readInternal(mData.mObjData, &vrcGuest);
319 if (RT_FAILURE(vrc))
320 {
321 /*
322 * We need to actively terminate our process tool in case of an error here,
323 * as this otherwise would be done on (directory) object destruction implicitly.
324 * This in turn then will run into a timeout, as the directory object won't be
325 * around anymore at that time. Ugly, but that's how it is for the moment.
326 */
327 /* ignore rc */ mData.mProcessTool.terminate(30 * RT_MS_1SEC, NULL /* pvrcGuest */);
328 }
329
330 if (pvrcGuest)
331 *pvrcGuest = vrcGuest;
332 }
333
334 return vrc;
335}
336#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
337
338/**
339 * Called when the guest side notifies the host of a directory event.
340 *
341 * @returns VBox status code.
342 * @param pCbCtx Host callback context.
343 * @param pSvcCbData Host callback data.
344 */
345int GuestDirectory::i_onDirNotify(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
346{
347#ifndef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
348 RT_NOREF(pCbCtx, pSvcCbData);
349 return VERR_NOT_SUPPORTED;
350#else
351 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
352 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
353
354 LogFlowThisFuncEnter();
355
356 if (pSvcCbData->mParms < 3)
357 return VERR_INVALID_PARAMETER;
358
359 int idx = 1; /* Current parameter index. */
360 CALLBACKDATA_DIR_NOTIFY dataCb;
361 RT_ZERO(dataCb);
362 /* pSvcCb->mpaParms[0] always contains the context ID. */
363 HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.uType);
364 HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.rc);
365
366 int vrcGuest = (int)dataCb.rc; /* uint32_t vs. int. */
367
368 LogFlowThisFunc(("uType=%RU32, vrcGuest=%Rrc\n", dataCb.uType, vrcGuest));
369
370 if (RT_FAILURE(vrcGuest))
371 {
372 /** @todo Set status? */
373
374 /* Ignore return code, as the event to signal might not be there (anymore). */
375 signalWaitEventInternal(pCbCtx, vrcGuest, NULL /* pPayload */);
376 return VINF_SUCCESS; /* Report to the guest. */
377 }
378
379 int vrc = VERR_NOT_SUPPORTED; /* Play safe by default. */
380
381 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
382 HRESULT hrc = errorInfo.createObject();
383 ComAssertComRCRet(hrc, VERR_COM_UNEXPECTED);
384 if (RT_FAILURE(vrcGuest))
385 {
386 hrc = errorInfo->initEx(VBOX_E_GSTCTL_GUEST_ERROR, vrcGuest,
387 COM_IIDOF(IGuestFile), getComponentName(),
388 i_guestErrorToString(vrcGuest, mData.mOpenInfo.mPath.c_str()));
389 ComAssertComRCRet(hrc, VERR_COM_UNEXPECTED);
390 }
391
392 switch (dataCb.uType)
393 {
394 case GUEST_DIR_NOTIFYTYPE_ERROR:
395 {
396 vrc = i_setStatus(DirectoryStatus_Error, vrcGuest);
397 break;
398 }
399
400 case GUEST_DIR_NOTIFYTYPE_OPEN:
401 {
402 vrc = i_setStatus(DirectoryStatus_Open, vrcGuest);
403 break;
404 }
405
406 case GUEST_DIR_NOTIFYTYPE_CLOSE:
407 {
408 vrc = i_setStatus(DirectoryStatus_Close, vrcGuest);
409 break;
410 }
411
412 case GUEST_DIR_NOTIFYTYPE_READ:
413 {
414 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mParms == 7, ("mParms=%u\n", pSvcCbData->mParms),
415 vrc = VERR_WRONG_PARAMETER_COUNT);
416 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mpaParms[idx].type == VBOX_HGCM_SVC_PARM_PTR,
417 ("type=%u\n", pSvcCbData->mpaParms[idx].type),
418 vrc = VERR_WRONG_PARAMETER_TYPE);
419
420 PGSTCTLFSOBJINFO pObjInfo;
421 uint32_t cbObjInfo;
422 vrc = HGCMSvcGetPv(&pSvcCbData->mpaParms[idx++], (void **)&pObjInfo, &cbObjInfo);
423 AssertRCBreak(vrc);
424 AssertBreakStmt(cbObjInfo == sizeof(GSTCTLFSOBJINFO), VERR_INVALID_PARAMETER);
425
426 GuestFsObjData fsObjData(mData.mOpenInfo.mPath);
427 vrc = fsObjData.FromGuestFsObjInfo(pObjInfo);
428 AssertRCBreak(vrc);
429 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
430 hrc = ptrFsObjInfo.createObject();
431 ComAssertComRCBreak(hrc, VERR_COM_UNEXPECTED);
432 vrc = ptrFsObjInfo->init(fsObjData);
433 AssertRCBreak(vrc);
434
435 ::FireGuestDirectoryReadEvent(mEventSource, mSession, this, ptrFsObjInfo);
436 break;
437 }
438
439 case GUEST_DIR_NOTIFYTYPE_REWIND:
440 {
441 /* Note: This does not change the overall status of the directory (i.e. open). */
442 ::FireGuestDirectoryStateChangedEvent(mEventSource, mSession, this, DirectoryStatus_Rewind, errorInfo);
443 break;
444 }
445
446 default:
447 AssertFailed();
448 break;
449 }
450
451 try
452 {
453 if (RT_SUCCESS(vrc))
454 {
455 GuestWaitEventPayload payload(dataCb.uType, &dataCb, sizeof(dataCb));
456
457 /* Ignore return code, as the event to signal might not be there (anymore). */
458 signalWaitEventInternal(pCbCtx, vrcGuest, &payload);
459 }
460 else /* OOM situation, wrong HGCM parameters or smth. not expected. */
461 {
462 /* Ignore return code, as the event to signal might not be there (anymore). */
463 signalWaitEventInternalEx(pCbCtx, vrc, 0 /* guestRc */, NULL /* pPayload */);
464 }
465 }
466 catch (int vrcEx) /* Thrown by GuestWaitEventPayload constructor. */
467 {
468 /* Also try to signal the waiter, to let it know of the OOM situation.
469 * Ignore return code, as the event to signal might not be there (anymore). */
470 signalWaitEventInternalEx(pCbCtx, vrcEx, 0 /* guestRc */, NULL /* pPayload */);
471 vrc = vrcEx;
472 }
473
474 LogFlowThisFunc(("uType=%RU32, rcGuest=%Rrc, vrc=%Rrc\n", dataCb.uType, vrcGuest, vrc));
475 return vrc;
476#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
477}
478
479/**
480 * Converts a given guest directory error to a string.
481 *
482 * @returns Error string.
483 * @param vrcGuest Guest file error to return string for.
484 * @param pcszWhat Hint of what was involved when the error occurred.
485 */
486/* static */
487Utf8Str GuestDirectory::i_guestErrorToString(int vrcGuest, const char *pcszWhat)
488{
489 AssertPtrReturn(pcszWhat, "");
490
491 Utf8Str strErr;
492 switch (vrcGuest)
493 {
494#define CASE_MSG(a_iRc, ...) \
495 case a_iRc: strErr.printf(__VA_ARGS__); break;
496 CASE_MSG(VERR_CANT_CREATE , tr("Access to guest directory \"%s\" is denied"), pcszWhat);
497 CASE_MSG(VERR_DIR_NOT_EMPTY, tr("Guest directory \"%s\" is not empty"), pcszWhat);
498 default:
499 strErr.printf(tr("Error %Rrc for guest directory \"%s\" occurred\n"), vrcGuest, pcszWhat);
500 break;
501 }
502
503#undef CASE_MSG
504
505 return strErr;
506}
507
508/**
509 * @copydoc GuestObject::i_onUnregister
510 */
511int GuestDirectory::i_onUnregister(void)
512{
513 LogFlowThisFuncEnter();
514
515 int vrc = VINF_SUCCESS;
516
517 LogFlowFuncLeaveRC(vrc);
518 return vrc;
519}
520
521/**
522 * @copydoc GuestObject::i_onSessionStatusChange
523 */
524int GuestDirectory::i_onSessionStatusChange(GuestSessionStatus_T enmSessionStatus)
525{
526 RT_NOREF(enmSessionStatus);
527
528 LogFlowThisFuncEnter();
529
530 int vrc = VINF_SUCCESS;
531
532 LogFlowFuncLeaveRC(vrc);
533 return vrc;
534}
535
536/**
537 * Closes this guest directory and removes it from the
538 * guest session's directory list.
539 *
540 * @return VBox status code.
541 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
542 */
543int GuestDirectory::i_close(int *pvrcGuest)
544{
545 int vrc;
546#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
547 if (mSession->i_getParent()->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS)
548 {
549 /// @todo To be implemented
550 vrc = VERR_NOT_IMPLEMENTED;
551 }
552 else
553#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
554 {
555 vrc = i_closeViaToolbox(pvrcGuest);
556 }
557
558 AssertPtr(mSession);
559 int vrc2 = mSession->i_directoryUnregister(this);
560 if (RT_SUCCESS(vrc))
561 vrc = vrc2;
562
563 LogFlowThisFunc(("Returning vrc=%Rrc\n", vrc));
564 return vrc;
565}
566
567#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
568/**
569 * Closes this guest directory and removes it from the guest session's directory list (legacy version).
570 *
571 * @return VBox status code.
572 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
573 *
574 * @note This uses an own guest process via the built-in toolbox in VBoxSerivce.
575 */
576int GuestDirectory::i_closeViaToolbox(int *pvrcGuest)
577{
578 return mData.mProcessTool.terminate(30 * 1000 /* 30s timeout */, pvrcGuest);
579}
580#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
581
582/**
583 * Reads the next directory entry, internal version.
584 *
585 * @return VBox status code. Will return VERR_NO_MORE_FILES if no more entries are available.
586 * @param objData Where to store the read directory entry as internal object data.
587 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
588 */
589int GuestDirectory::i_readInternal(GuestFsObjData &objData, int *pvrcGuest)
590{
591 AssertPtrReturn(pvrcGuest, VERR_INVALID_POINTER);
592
593 int vrc;
594#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
595 if (mSession->i_getParent()->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS)
596 {
597 /// @todo To be implemented
598 RT_NOREF(objData, pvrcGuest);
599 vrc = 0;
600 }
601 else
602#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
603 {
604 vrc = i_readInternalViaToolbox(objData, pvrcGuest);
605 }
606
607 LogFlowThisFunc(("Returning vrc=%Rrc\n", vrc));
608 return vrc;
609}
610
611#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
612/**
613 * Reads the next directory entry, internal version (legacy version).
614 *
615 * @return VBox status code. Will return VERR_NO_MORE_FILES if no more entries are available.
616 * @param objData Where to store the read directory entry as internal object data.
617 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
618 *
619 * @note This uses an own guest process via the built-in toolbox in VBoxSerivce.
620 */
621int GuestDirectory::i_readInternalViaToolbox(GuestFsObjData &objData, int *pvrcGuest)
622{
623 GuestToolboxStreamBlock curBlock;
624 int vrc = mData.mProcessTool.waitEx(GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK, &curBlock, pvrcGuest);
625 if (RT_SUCCESS(vrc))
626 {
627 /*
628 * Note: The guest process can still be around to serve the next
629 * upcoming stream block next time.
630 */
631 if (!mData.mProcessTool.isRunning())
632 vrc = mData.mProcessTool.getTerminationStatus(); /* Tool process is not running (anymore). Check termination status. */
633
634 if (RT_SUCCESS(vrc))
635 {
636 if (curBlock.GetCount()) /* Did we get content? */
637 {
638 if (curBlock.GetString("name"))
639 {
640 vrc = objData.FromToolboxLs(curBlock, true /* fLong */);
641 }
642 else
643 vrc = VERR_PATH_NOT_FOUND;
644 }
645 else
646 {
647 /* Nothing to read anymore. Tell the caller. */
648 vrc = VERR_NO_MORE_FILES;
649 }
650 }
651 }
652
653 return vrc;
654}
655#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
656
657/**
658 * Reads the next directory entry.
659 *
660 * @return VBox status code. Will return VERR_NO_MORE_FILES if no more entries are available.
661 * @param fsObjInfo Where to store the read directory entry.
662 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
663 */
664int GuestDirectory::i_read(ComObjPtr<GuestFsObjInfo> &fsObjInfo, int *pvrcGuest)
665{
666 AssertPtrReturn(pvrcGuest, VERR_INVALID_POINTER);
667
668 /* Create the FS info object. */
669 HRESULT hr = fsObjInfo.createObject();
670 if (FAILED(hr))
671 return VERR_COM_UNEXPECTED;
672
673 int vrc;
674
675 /* If we have a valid object data cache, read from it. */
676 if (mData.mObjData.mName.isNotEmpty())
677 {
678 vrc = fsObjInfo->init(mData.mObjData);
679 if (RT_SUCCESS(vrc))
680 {
681 mData.mObjData.mName = ""; /* Mark the object data as being empty (beacon). */
682 }
683 }
684 else /* Otherwise ask the guest for the next object data. */
685 {
686
687 GuestFsObjData objData;
688 vrc = i_readInternal(objData, pvrcGuest);
689 if (RT_SUCCESS(vrc))
690 vrc = fsObjInfo->init(objData);
691 }
692
693 LogFlowThisFunc(("Returning vrc=%Rrc\n", vrc));
694 return vrc;
695}
696
697/**
698 * Rewinds the directory reading.
699 *
700 * @returns VBox status code.
701 * @retval VERR_GSTCTL_GUEST_ERROR when an error from the guest side has been received.
702 * @param uTimeoutMS Timeout (in ms) to wait.
703 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
704 */
705int GuestDirectory::i_rewind(uint32_t uTimeoutMS, int *pvrcGuest)
706{
707 RT_NOREF(pvrcGuest);
708#ifndef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
709 RT_NOREF(uTimeoutMS, pvrcGuest);
710#else
711 /* Only available for Guest Additions 7.1+. */
712 if (mSession->i_getParent()->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS)
713 {
714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
715
716 int vrc;
717
718 GuestWaitEvent *pEvent = NULL;
719 GuestEventTypes eventTypes;
720 try
721 {
722 eventTypes.push_back(VBoxEventType_OnGuestDirectoryStateChanged);
723 vrc = registerWaitEvent(eventTypes, &pEvent);
724 }
725 catch (std::bad_alloc &)
726 {
727 vrc = VERR_NO_MEMORY;
728 }
729
730 if (RT_FAILURE(vrc))
731 return vrc;
732
733 /* Prepare HGCM call. */
734 VBOXHGCMSVCPARM paParms[4];
735 int i = 0;
736 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
737 HGCMSvcSetU32(&paParms[i++], mObjectID /* Directory handle */);
738
739 alock.release(); /* Drop lock before sending. */
740
741 vrc = sendMessage(HOST_MSG_DIR_REWIND, i, paParms);
742 if (RT_SUCCESS(vrc))
743 {
744 VBoxEventType_T evtType;
745 ComPtr<IEvent> pIEvent;
746 vrc = waitForEvent(pEvent, uTimeoutMS, &evtType, pIEvent.asOutParam());
747 if (RT_SUCCESS(vrc))
748 {
749 if (evtType == VBoxEventType_OnGuestDirectoryStateChanged)
750 {
751 ComPtr<IGuestDirectoryStateChangedEvent> pEvt = pIEvent;
752 Assert(!pEvt.isNull());
753 }
754 else
755 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
756 }
757 else if (pEvent->HasGuestError()) /* Return guest vrc if available. */
758 vrc = pEvent->GetGuestError();
759 }
760
761 unregisterWaitEvent(pEvent);
762 return vrc;
763 }
764#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
765
766 return VERR_NOT_SUPPORTED;
767}
768
769/**
770 * Sets the current internal directory object status.
771 *
772 * @returns VBox status code.
773 * @param enmStatus New directory status to set.
774 * @param vrcDir New result code to set.
775 *
776 * @note Takes the write lock.
777 */
778int GuestDirectory::i_setStatus(DirectoryStatus_T enmStatus, int vrcDir)
779{
780 LogFlowThisFuncEnter();
781
782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
783
784 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, vrcDir=%Rrc\n", mData.mStatus, enmStatus, vrcDir));
785
786#ifdef VBOX_STRICT
787 if (enmStatus == DirectoryStatus_Error)
788 AssertMsg(RT_FAILURE(vrcDir), ("Guest vrc must be an error (%Rrc)\n", vrcDir));
789 else
790 AssertMsg(RT_SUCCESS(vrcDir), ("Guest vrc must not be an error (%Rrc)\n", vrcDir));
791#endif
792
793 if (mData.mStatus != enmStatus)
794 {
795 mData.mStatus = enmStatus;
796 mData.mLastError = vrcDir;
797
798 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
799 HRESULT hrc = errorInfo.createObject();
800 ComAssertComRCRet(hrc, VERR_COM_UNEXPECTED);
801 if (RT_FAILURE(vrcDir))
802 {
803 hrc = errorInfo->initEx(VBOX_E_GSTCTL_GUEST_ERROR, vrcDir,
804 COM_IIDOF(IGuestDirectory), getComponentName(),
805 i_guestErrorToString(vrcDir, mData.mOpenInfo.mPath.c_str()));
806 ComAssertComRCRet(hrc, VERR_COM_UNEXPECTED);
807 }
808 /* Note: On vrcDir success, errorInfo is set to S_OK and also sent via the event below. */
809
810 alock.release(); /* Release lock before firing off event. */
811
812 ::FireGuestDirectoryStateChangedEvent(mEventSource, mSession, this, enmStatus, errorInfo);
813 }
814
815 return VINF_SUCCESS;
816}
817
818// implementation of public methods
819/////////////////////////////////////////////////////////////////////////////
820HRESULT GuestDirectory::close()
821{
822 AutoCaller autoCaller(this);
823 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
824
825 LogFlowThisFuncEnter();
826
827 HRESULT hrc = S_OK;
828
829 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
830 int vrc = i_close(&vrcGuest);
831 if (RT_FAILURE(vrc))
832 {
833 switch (vrc)
834 {
835 case VERR_GSTCTL_GUEST_ERROR:
836 {
837 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, mData.mOpenInfo.mPath.c_str());
838 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Closing guest directory failed: %s"),
839 GuestBase::getErrorAsString(ge).c_str());
840 break;
841 }
842 case VERR_NOT_SUPPORTED:
843 /* Silently skip old Guest Additions which do not support killing the
844 * the guest directory handling process. */
845 break;
846
847 default:
848 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
849 tr("Closing guest directory \"%s\" failed: %Rrc"), mData.mOpenInfo.mPath.c_str(), vrc);
850 break;
851 }
852 }
853
854 return hrc;
855}
856
857HRESULT GuestDirectory::read(ComPtr<IFsObjInfo> &aObjInfo)
858{
859 AutoCaller autoCaller(this);
860 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
861
862 LogFlowThisFuncEnter();
863
864 HRESULT hrc = S_OK;
865
866 ComObjPtr<GuestFsObjInfo> fsObjInfo;
867 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
868 int vrc = i_read(fsObjInfo, &vrcGuest);
869 if (RT_SUCCESS(vrc))
870 {
871 /* Return info object to the caller. */
872 hrc = fsObjInfo.queryInterfaceTo(aObjInfo.asOutParam());
873 }
874 else
875 {
876 switch (vrc)
877 {
878 case VERR_GSTCTL_GUEST_ERROR:
879 {
880 GuestErrorInfo ge(
881#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
882 GuestErrorInfo::Type_ToolLs
883#else
884 GuestErrorInfo::Type_Fs
885#endif
886 , vrcGuest, mData.mOpenInfo.mPath.c_str());
887 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Reading guest directory failed: %s"),
888 GuestBase::getErrorAsString(ge).c_str());
889 break;
890 }
891
892#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
893 case VERR_GSTCTL_PROCESS_EXIT_CODE:
894 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" failed: %Rrc"),
895 mData.mOpenInfo.mPath.c_str(), mData.mProcessTool.getRc());
896 break;
897#endif
898 case VERR_PATH_NOT_FOUND:
899 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" failed: Path not found"),
900 mData.mOpenInfo.mPath.c_str());
901 break;
902
903 case VERR_NO_MORE_FILES:
904 /* See SDK reference. */
905 hrc = setErrorBoth(VBOX_E_OBJECT_NOT_FOUND, vrc, tr("Reading guest directory \"%s\" failed: No more entries"),
906 mData.mOpenInfo.mPath.c_str());
907 break;
908
909 default:
910 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" returned unhandled error: %Rrc\n"),
911 mData.mOpenInfo.mPath.c_str(), vrc);
912 break;
913 }
914 }
915
916 LogFlowThisFunc(("Returning hrc=%Rhrc / vrc=%Rrc\n", hrc, vrc));
917 return hrc;
918}
919
920HRESULT GuestDirectory::rewind(void)
921{
922 AutoCaller autoCaller(this);
923 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
924
925 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
926 int vrc = i_rewind(30 * 1000 /* Timeout in ms */, &vrcGuest);
927 if (RT_SUCCESS(vrc))
928 return S_OK;
929
930 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, mData.mOpenInfo.mPath.c_str());
931 return setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Rewinding guest directory failed: %s"),
932 GuestBase::getErrorAsString(ge).c_str());
933}
934
Note: See TracBrowser for help on using the repository browser.

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