VirtualBox

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

Last change on this file was 104178, checked in by vboxsync, 6 weeks ago

Guest Control:

  • Factored out most of the guest process stream handling of GuestToolboxStream (deprecated) into a new generic class GuestProcessOutputStream. That way we can make use of most of that code for other, non-toolbox related functionality.
  • Factoredd out most of the guest process wrapping functionality from GuestProcessToolbox into a new generic class GuestProcessWrapper. Ditto (see above).
  • Make more use of VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT to compile a lot less code if not defined. Toolbox handling is required for supporting older Guest Additions (< 7.1) though (so enabled by default).
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 55.2 KB
RevLine 
[42084]1/* $Id: GuestDirectoryImpl.cpp 104178 2024-04-05 12:23:48Z vboxsync $ */
2/** @file
[47469]3 * VirtualBox Main - Guest directory handling.
[42084]4 */
5
6/*
[98103]7 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
[42084]8 *
[96407]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
[42084]26 */
27
28
[57358]29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
[71173]32#define LOG_GROUP LOG_GROUP_MAIN_GUESTDIRECTORY
[67914]33#include "LoggingNew.h"
34
[55645]35#ifndef VBOX_WITH_GUEST_CONTROL
36# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
37#endif
[98526]38#include "GuestImpl.h"
[42084]39#include "GuestDirectoryImpl.h"
[42673]40#include "GuestSessionImpl.h"
[42478]41#include "GuestCtrlImplPrivate.h"
[98526]42#include "VirtualBoxErrorInfoImpl.h"
[42084]43
44#include "Global.h"
45#include "AutoCaller.h"
[98526]46#include "VBoxEvents.h"
[42084]47
48#include <VBox/com/array.h>
[98709]49#include <VBox/com/listeners.h>
[98526]50#include <VBox/AssertGuest.h>
[42084]51
52
[98709]53/**
54 * Internal listener class to serve events in an
55 * active manner, e.g. without polling delays.
56 */
57class GuestDirectoryListener
58{
59public:
60
61 GuestDirectoryListener(void)
62 {
63 }
64
65 virtual ~GuestDirectoryListener()
66 {
67 }
68
69 HRESULT init(GuestDirectory *pDir)
70 {
71 AssertPtrReturn(pDir, E_POINTER);
72 mDir = pDir;
73 return S_OK;
74 }
75
76 void uninit(void)
77 {
78 mDir = NULL;
79 }
80
81 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
82 {
83 switch (aType)
84 {
85 case VBoxEventType_OnGuestDirectoryStateChanged:
86 RT_FALL_THROUGH();
87 case VBoxEventType_OnGuestDirectoryRead:
88 {
89 AssertPtrReturn(mDir, E_POINTER);
90 int vrc2 = mDir->signalWaitEvent(aType, aEvent);
91 RT_NOREF(vrc2);
92#ifdef DEBUG_andy
93 LogFlowFunc(("Signalling events of type=%RU32, dir=%p resulted in vrc=%Rrc\n",
94 aType, mDir, vrc2));
95#endif
96 break;
97 }
98
99 default:
100 AssertMsgFailed(("Unhandled event %RU32\n", aType));
101 break;
102 }
103
104 return S_OK;
105 }
106
107private:
108
109 /** Weak pointer to the guest directory object to listen for. */
110 GuestDirectory *mDir;
111};
112typedef ListenerImpl<GuestDirectoryListener, GuestDirectory *> GuestDirectoryListenerImpl;
113
114VBOX_LISTENER_DECLARE(GuestDirectoryListenerImpl)
115
[42084]116// constructor / destructor
117/////////////////////////////////////////////////////////////////////////////
118
119DEFINE_EMPTY_CTOR_DTOR(GuestDirectory)
120
[42095]121HRESULT GuestDirectory::FinalConstruct(void)
[42084]122{
123 LogFlowThisFunc(("\n"));
124 return BaseFinalConstruct();
125}
126
127void GuestDirectory::FinalRelease(void)
128{
129 LogFlowThisFuncEnter();
130 uninit();
131 BaseFinalRelease();
132 LogFlowThisFuncLeave();
133}
134
135// public initializer/uninitializer for internal purposes only
136/////////////////////////////////////////////////////////////////////////////
137
[71406]138int GuestDirectory::init(Console *pConsole, GuestSession *pSession, ULONG aObjectID, const GuestDirectoryOpenInfo &openInfo)
[42084]139{
[98526]140 LogFlowThisFunc(("pConsole=%p, pSession=%p, aObjectID=%RU32, strPath=%s, enmFilter=%#x, fFlags=%x\n",
141 pConsole, pSession, aObjectID, openInfo.mPath.c_str(), openInfo.menmFilter, openInfo.mFlags));
[42673]142
[49349]143 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
144 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
145
[42084]146 /* Enclose the state transition NotReady->InInit->Ready. */
147 AutoInitSpan autoInitSpan(this);
[71566]148 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
[42084]149
[71406]150 int vrc = bindToSession(pConsole, pSession, aObjectID);
[49349]151 if (RT_SUCCESS(vrc))
152 {
[71406]153 mSession = pSession;
154 mObjectID = aObjectID;
[42525]155
[98526]156 mData.mOpenInfo = openInfo;
157 mData.mStatus = DirectoryStatus_Undefined;
158 mData.mLastError = VINF_SUCCESS;
[42084]159
[98526]160 unconst(mEventSource).createObject();
161 HRESULT hr = mEventSource->init();
162 if (FAILED(hr))
163 vrc = VERR_COM_UNEXPECTED;
[49349]164 }
165
[98709]166 if (RT_SUCCESS(vrc))
167 {
168 try
169 {
170 GuestDirectoryListener *pListener = new GuestDirectoryListener();
171 ComObjPtr<GuestDirectoryListenerImpl> thisListener;
172 HRESULT hr = thisListener.createObject();
173 if (SUCCEEDED(hr))
174 hr = thisListener->init(pListener, this);
175
176 if (SUCCEEDED(hr))
177 {
178 com::SafeArray <VBoxEventType_T> eventTypes;
179 eventTypes.push_back(VBoxEventType_OnGuestDirectoryStateChanged);
180 eventTypes.push_back(VBoxEventType_OnGuestDirectoryRead);
181 hr = mEventSource->RegisterListener(thisListener,
182 ComSafeArrayAsInParam(eventTypes),
183 TRUE /* Active listener */);
184 if (SUCCEEDED(hr))
185 {
186 vrc = baseInit();
187 if (RT_SUCCESS(vrc))
188 {
189 mLocalListener = thisListener;
190 }
191 }
192 else
193 vrc = VERR_COM_UNEXPECTED;
194 }
195 else
196 vrc = VERR_COM_UNEXPECTED;
197 }
198 catch(std::bad_alloc &)
199 {
200 vrc = VERR_NO_MEMORY;
201 }
202 }
203
[79133]204 /* Confirm a successful initialization when it's the case. */
[49349]205 if (RT_SUCCESS(vrc))
[42673]206 autoInitSpan.setSucceeded();
[49349]207 else
208 autoInitSpan.setFailed();
[83489]209
210 LogFlowFuncLeaveRC(vrc);
[49349]211 return vrc;
[42084]212}
213
214/**
215 * Uninitializes the instance.
216 * Called from FinalRelease().
217 */
218void GuestDirectory::uninit(void)
219{
[49504]220 LogFlowThisFuncEnter();
[42084]221
222 /* Enclose the state transition Ready->InUninit->NotReady. */
223 AutoUninitSpan autoUninitSpan(this);
224 if (autoUninitSpan.uninitDone())
225 return;
[42673]226
[42897]227 LogFlowThisFuncLeave();
[42084]228}
229
[50559]230// implementation of private wrapped getters/setters for attributes
[42084]231/////////////////////////////////////////////////////////////////////////////
232
[50559]233HRESULT GuestDirectory::getDirectoryName(com::Utf8Str &aDirectoryName)
[42084]234{
235 LogFlowThisFuncEnter();
236
237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
238
[50559]239 aDirectoryName = mData.mOpenInfo.mPath;
[42084]240
241 return S_OK;
242}
243
[98526]244HRESULT GuestDirectory::getEventSource(ComPtr<IEventSource> &aEventSource)
245{
246 /* No need to lock - lifetime constant. */
247 mEventSource.queryInterfaceTo(aEventSource.asOutParam());
248
249 return S_OK;
250}
251
[50559]252HRESULT GuestDirectory::getFilter(com::Utf8Str &aFilter)
[42673]253{
254 LogFlowThisFuncEnter();
255
256 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
257
[50559]258 aFilter = mData.mOpenInfo.mFilter;
[42673]259
260 return S_OK;
261}
262
[98526]263HRESULT GuestDirectory::getId(ULONG *aId)
264{
265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
266
267 *aId = mObjectID;
268
269 return S_OK;
270}
271
272HRESULT GuestDirectory::getStatus(DirectoryStatus_T *aStatus)
273{
274 LogFlowThisFuncEnter();
275
276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
277
278 *aStatus = mData.mStatus;
279
280 return S_OK;
281}
282
[42673]283// private methods
284/////////////////////////////////////////////////////////////////////////////
285
[92897]286/**
287 * Entry point for guest side directory callbacks.
288 *
289 * @returns VBox status code.
290 * @param pCbCtx Host callback context.
291 * @param pSvcCb Host callback data.
292 */
[50618]293int GuestDirectory::i_callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
[49349]294{
295 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
296 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
297
[98709]298 LogFlowThisFunc(("strPath=%s, uContextID=%RU32, uMessage=%RU32, pSvcCb=%p\n",
[76958]299 mData.mOpenInfo.mPath.c_str(), pCbCtx->uContextID, pCbCtx->uMessage, pSvcCb));
[49349]300
301 int vrc;
[76958]302 switch (pCbCtx->uMessage)
[49349]303 {
[98526]304 case GUEST_MSG_DISCONNECTED:
305 /** @todo vrc = i_onGuestDisconnected(pCbCtx, pSvcCb); */
[98528]306 vrc = VINF_SUCCESS; /// @todo To be implemented
[98526]307 break;
308
[76958]309 case GUEST_MSG_DIR_NOTIFY:
[49349]310 {
[98526]311 vrc = i_onDirNotify(pCbCtx, pSvcCb);
312 break;
313 }
[49349]314
[98526]315 default:
316 /* Silently ignore not implemented functions. */
317 vrc = VERR_NOT_SUPPORTED;
318 break;
319 }
[49349]320
[98526]321 LogFlowFuncLeaveRC(vrc);
322 return vrc;
323}
324
325/**
326 * Opens the directory on the guest side.
327 *
328 * @return VBox status code.
329 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
330 */
331int GuestDirectory::i_open(int *pvrcGuest)
332{
333 int vrc;
334#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
335 if ((mSession->i_getParent()->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS))
336 {
337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
338
339 GuestWaitEvent *pEvent = NULL;
340 GuestEventTypes eventTypes;
341 try
342 {
343 eventTypes.push_back(VBoxEventType_OnGuestDirectoryStateChanged);
344
345 vrc = registerWaitEvent(eventTypes, &pEvent);
346 }
347 catch (std::bad_alloc &)
348 {
349 vrc = VERR_NO_MEMORY;
350 }
351
[98709]352 if (RT_FAILURE(vrc))
353 return vrc;
354
[98526]355 /* Prepare HGCM call. */
356 VBOXHGCMSVCPARM paParms[8];
357 int i = 0;
358 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
[98709]359 HGCMSvcSetStr(&paParms[i++], mData.mOpenInfo.mPath.c_str());
[98526]360 HGCMSvcSetU32(&paParms[i++], mData.mOpenInfo.menmFilter);
361 HGCMSvcSetU32(&paParms[i++], mData.mOpenInfo.mFlags);
[98818]362 HGCMSvcSetU32(&paParms[i++], GSTCTLFSOBJATTRADD_UNIX /* Implicit */);
363 HGCMSvcSetU32(&paParms[i++], GSTCTL_PATH_F_ON_LINK /* Ditto */ );
[98526]364
365 alock.release(); /* Drop lock before sending. */
366
367 vrc = sendMessage(HOST_MSG_DIR_OPEN, i, paParms);
368 if (RT_SUCCESS(vrc))
[104003]369 vrc = i_waitForStatusChange(pEvent, GSTCTL_DEFAULT_TIMEOUT_MS, NULL /* FileStatus */, pvrcGuest);
[98526]370 }
371 else
[98643]372#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
[98526]373 {
[104178]374#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
[98526]375 vrc = i_openViaToolbox(pvrcGuest);
[104178]376#else
377 RT_NOREF(pvrcGuest);
378 vrc = VERR_NOT_SUPPORTED;
379#endif
[98526]380 }
[49349]381
[98526]382 return vrc;
383}
384
385#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
386/**
387 * Opens the directory on the guest side (legacy version).
388 *
389 * @returns VBox status code.
390 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
391 *
392 * @note This uses an own guest process via the built-in toolbox in VBoxSerivce.
393 */
394int GuestDirectory::i_openViaToolbox(int *pvrcGuest)
395{
396 /* Start the directory process on the guest. */
397 GuestProcessStartupInfo procInfo;
398 procInfo.mName.printf(tr("Opening directory \"%s\""), mData.mOpenInfo.mPath.c_str());
399 procInfo.mTimeoutMS = 5 * 60 * 1000; /* 5 minutes timeout. */
400 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
401 procInfo.mExecutable= Utf8Str(VBOXSERVICE_TOOL_LS);
402
403 procInfo.mArguments.push_back(procInfo.mExecutable);
404 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
405 /* We want the long output format which contains all the object details. */
406 procInfo.mArguments.push_back(Utf8Str("-l"));
[99073]407 /* Always dereference symlinks by default when opening directories, as we want to show its
408 * contents rather than working directly on the link.
409 *
410 * Newer Linux distros such as Ubuntu 22.10 symlink /bin to /usr/bin, for example. */
411 if (!(mData.mOpenInfo.mFlags & DirectoryOpenFlag_NoSymlinks)) /* Check if the caller explicitly forbids this. */
412 procInfo.mArguments.push_back(Utf8Str("--dereference"));
[98526]413 /** @todo Recursion support? */
414 procInfo.mArguments.push_back(mData.mOpenInfo.mPath); /* The directory we want to open. */
415
416 /*
417 * Start the process synchronously and keep it around so that we can use
418 * it later in subsequent read() calls.
419 */
420 int vrc = mData.mProcessTool.init(mSession, procInfo, false /*fAsync*/, NULL /*pvrcGuest*/);
421 if (RT_SUCCESS(vrc))
422 {
423 /* As we need to know if the directory we were about to open exists and and is accessible,
424 * do the first read here in order to return a meaningful status here. */
425 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
426 vrc = i_readInternal(mData.mObjData, &vrcGuest);
427 if (RT_FAILURE(vrc))
428 {
429 /*
430 * We need to actively terminate our process tool in case of an error here,
431 * as this otherwise would be done on (directory) object destruction implicitly.
432 * This in turn then will run into a timeout, as the directory object won't be
433 * around anymore at that time. Ugly, but that's how it is for the moment.
434 */
435 /* ignore rc */ mData.mProcessTool.terminate(30 * RT_MS_1SEC, NULL /* pvrcGuest */);
436 }
437
438 if (pvrcGuest)
439 *pvrcGuest = vrcGuest;
440 }
441
442 return vrc;
443}
444#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
445
446/**
447 * Called when the guest side notifies the host of a directory event.
448 *
449 * @returns VBox status code.
450 * @param pCbCtx Host callback context.
451 * @param pSvcCbData Host callback data.
452 */
453int GuestDirectory::i_onDirNotify(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
454{
455#ifndef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
456 RT_NOREF(pCbCtx, pSvcCbData);
457 return VERR_NOT_SUPPORTED;
458#else
459 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
460 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
461
462 LogFlowThisFuncEnter();
463
464 if (pSvcCbData->mParms < 3)
465 return VERR_INVALID_PARAMETER;
466
467 int idx = 1; /* Current parameter index. */
468 CALLBACKDATA_DIR_NOTIFY dataCb;
469 RT_ZERO(dataCb);
470 /* pSvcCb->mpaParms[0] always contains the context ID. */
471 HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.uType);
472 HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.rc);
473
[99085]474 int const vrcGuest = (int)dataCb.rc; /* uint32_t vs. int. */
[98526]475
476 LogFlowThisFunc(("uType=%RU32, vrcGuest=%Rrc\n", dataCb.uType, vrcGuest));
477
478 if (RT_FAILURE(vrcGuest))
479 {
480 /** @todo Set status? */
481
482 /* Ignore return code, as the event to signal might not be there (anymore). */
483 signalWaitEventInternal(pCbCtx, vrcGuest, NULL /* pPayload */);
484 return VINF_SUCCESS; /* Report to the guest. */
485 }
486
487 int vrc = VERR_NOT_SUPPORTED; /* Play safe by default. */
488
489 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
490 HRESULT hrc = errorInfo.createObject();
491 ComAssertComRCRet(hrc, VERR_COM_UNEXPECTED);
492 if (RT_FAILURE(vrcGuest))
493 {
494 hrc = errorInfo->initEx(VBOX_E_GSTCTL_GUEST_ERROR, vrcGuest,
[98709]495 COM_IIDOF(IGuestDirectory), getComponentName(),
[98526]496 i_guestErrorToString(vrcGuest, mData.mOpenInfo.mPath.c_str()));
497 ComAssertComRCRet(hrc, VERR_COM_UNEXPECTED);
498 }
499
500 switch (dataCb.uType)
501 {
502 case GUEST_DIR_NOTIFYTYPE_ERROR:
503 {
504 vrc = i_setStatus(DirectoryStatus_Error, vrcGuest);
[49349]505 break;
506 }
507
[98526]508 case GUEST_DIR_NOTIFYTYPE_OPEN:
509 {
[98709]510 AssertBreakStmt(pSvcCbData->mParms >= 4, vrc = VERR_INVALID_PARAMETER);
511 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.u.open.uHandle /* Guest native file handle */);
512 AssertRCBreak(vrc);
[98526]513 vrc = i_setStatus(DirectoryStatus_Open, vrcGuest);
514 break;
515 }
516
517 case GUEST_DIR_NOTIFYTYPE_CLOSE:
518 {
519 vrc = i_setStatus(DirectoryStatus_Close, vrcGuest);
520 break;
521 }
522
523 case GUEST_DIR_NOTIFYTYPE_READ:
524 {
[98824]525 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mParms == 6, ("mParms=%u\n", pSvcCbData->mParms),
[98526]526 vrc = VERR_WRONG_PARAMETER_COUNT);
527 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mpaParms[idx].type == VBOX_HGCM_SVC_PARM_PTR,
528 ("type=%u\n", pSvcCbData->mpaParms[idx].type),
529 vrc = VERR_WRONG_PARAMETER_TYPE);
[99085]530 PGSTCTLDIRENTRYEX pDirEntryEx;
531 uint32_t cbDirEntryEx;
532 vrc = HGCMSvcGetPv(&pSvcCbData->mpaParms[idx++], (void **)&pDirEntryEx, &cbDirEntryEx);
[98709]533 AssertRCBreak(vrc);
[99085]534 AssertBreakStmt( cbDirEntryEx >= RT_UOFFSETOF(GSTCTLDIRENTRYEX, szName[2])
535 && cbDirEntryEx <= GSTCTL_DIRENTRY_MAX_SIZE, vrc = VERR_INVALID_PARAMETER);
536 dataCb.u.read.Entry.pDirEntryEx = (PGSTCTLDIRENTRYEX)RTMemDup(pDirEntryEx, cbDirEntryEx);
537 AssertPtrBreakStmt(dataCb.u.read.Entry.pDirEntryEx, vrc = VERR_NO_MEMORY);
538 dataCb.u.read.Entry.cbDirEntryEx = cbDirEntryEx;
[98526]539
[98709]540 char *pszUser;
541 uint32_t cbUser;
542 vrc = HGCMSvcGetStr(&pSvcCbData->mpaParms[idx++], &pszUser, &cbUser);
[98526]543 AssertRCBreak(vrc);
[99085]544 AssertBreakStmt(cbUser <= GSTCTL_DIRENTRY_MAX_USER_NAME, vrc = VERR_TOO_MUCH_DATA);
545 dataCb.u.read.Entry.pszUser = RTStrDup(pszUser);
546 AssertPtrBreakStmt(dataCb.u.read.Entry.pszUser, vrc = VERR_NO_MEMORY);
547 dataCb.u.read.Entry.cbUser = cbUser;
[98526]548
[98709]549 char *pszGroups;
550 uint32_t cbGroups;
551 vrc = HGCMSvcGetStr(&pSvcCbData->mpaParms[idx++], &pszGroups, &cbGroups);
[98526]552 AssertRCBreak(vrc);
[99085]553 AssertBreakStmt(cbGroups <= GSTCTL_DIRENTRY_MAX_USER_GROUPS, vrc = VERR_TOO_MUCH_DATA);
554 dataCb.u.read.Entry.pszGroups = RTStrDup(pszGroups);
555 AssertPtrBreakStmt(dataCb.u.read.Entry.pszGroups, vrc = VERR_NO_MEMORY);
556 dataCb.u.read.Entry.cbGroups = cbGroups;
[98709]557
558 /** @todo ACLs not implemented yet. */
559
[99085]560 GuestFsObjData fsObjData(dataCb.u.read.Entry.pDirEntryEx->szName);
561 vrc = fsObjData.FromGuestFsObjInfo(&dataCb.u.read.Entry.pDirEntryEx->Info);
[98709]562 AssertRCBreak(vrc);
[98526]563 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
564 hrc = ptrFsObjInfo.createObject();
[98709]565 ComAssertComRCBreak(hrc, vrc = VERR_COM_UNEXPECTED);
[98526]566 vrc = ptrFsObjInfo->init(fsObjData);
567 AssertRCBreak(vrc);
568
[98709]569 ::FireGuestDirectoryReadEvent(mEventSource, mSession, this,
[99085]570 dataCb.u.read.Entry.pDirEntryEx->szName, ptrFsObjInfo,
571 dataCb.u.read.Entry.pszUser, dataCb.u.read.Entry.pszGroups);
[98526]572 break;
573 }
574
575 case GUEST_DIR_NOTIFYTYPE_REWIND:
576 {
577 /* Note: This does not change the overall status of the directory (i.e. open). */
578 ::FireGuestDirectoryStateChangedEvent(mEventSource, mSession, this, DirectoryStatus_Rewind, errorInfo);
579 break;
580 }
581
[99085]582 case GUEST_DIR_NOTIFYTYPE_LIST:
583 {
584 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mParms >= 4, ("mParms=%u\n", pSvcCbData->mParms),
585 vrc = VERR_WRONG_PARAMETER_COUNT);
586 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mpaParms[idx].type == VBOX_HGCM_SVC_PARM_32BIT,
587 ("type=%u\n", pSvcCbData->mpaParms[idx].type),
588 vrc = VERR_WRONG_PARAMETER_TYPE);
589
590 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.u.list.cEntries);
591 AssertRCBreak(vrc);
592 /* We limit this for now to 64K entries max per call.
593 * The guest does not do this check, so we could de/increase this limit in the future. */
594 AssertBreakStmt(dataCb.u.list.cEntries <= _64K, vrc = VERR_TOO_MUCH_DATA);
595
596 if (!dataCb.u.list.cEntries) /* No entries sent? Bail out early. */
597 break;
598
599 /*
600 * Fetch buffer with packed directory entries.
601 */
602 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mParms == 5, ("mParms=%u\n", pSvcCbData->mParms),
603 vrc = VERR_WRONG_PARAMETER_COUNT);
604 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mpaParms[idx].type == VBOX_HGCM_SVC_PARM_PTR,
605 ("type=%u\n", pSvcCbData->mpaParms[idx].type),
606 vrc = VERR_WRONG_PARAMETER_TYPE);
607 void *pvBuf;
608 uint32_t cbBuf;
609 vrc = HGCMSvcGetPv(&pSvcCbData->mpaParms[idx], &pvBuf, &cbBuf);
610 AssertRCBreak(vrc);
611 AssertBreakStmt(cbBuf >= sizeof(GSTCTLDIRENTRYEX), vrc = VERR_INVALID_PARAMETER);
612 dataCb.u.list.paEntries = (PCALLBACKDATA_DIR_ENTRY *)RTMemAllocZ(dataCb.u.list.cEntries * sizeof(PCALLBACKDATA_DIR_ENTRY));
613 AssertPtrBreakStmt(dataCb.u.list.paEntries, vrc = VERR_NO_MEMORY);
614
615 /*
616 * Unpack directory entries.
617 */
618 size_t offBuf = 0;
619 for (uint32_t i = 0; i < dataCb.u.list.cEntries; i++)
620 {
621 dataCb.u.list.paEntries[i] = (PCALLBACKDATA_DIR_ENTRY)RTMemAlloc(sizeof(CALLBACKDATA_DIR_ENTRY));
622 AssertPtrBreakStmt(dataCb.u.list.paEntries[i], vrc = VERR_NO_MEMORY);
623
624 PCALLBACKDATA_DIR_ENTRY pEntry = dataCb.u.list.paEntries[i];
625
626 PGSTCTLDIRENTRYLISTHDR const pHdr = (PGSTCTLDIRENTRYLISTHDR)((uint8_t *)pvBuf + offBuf);
627 AssertBreakStmt(pHdr->cbDirEntryEx <= GSTCTL_DIRENTRY_MAX_SIZE, vrc = VERR_TOO_MUCH_DATA);
628 AssertBreakStmt(pHdr->cbUser <= GSTCTL_DIRENTRY_MAX_USER_NAME, vrc = VERR_TOO_MUCH_DATA);
629 AssertBreakStmt(pHdr->cbGroups <= GSTCTL_DIRENTRY_MAX_USER_GROUPS, vrc = VERR_TOO_MUCH_DATA);
630 offBuf += sizeof(GSTCTLDIRENTRYLISTHDR);
631
632 AssertBreakStmt( pHdr->cbDirEntryEx >= RT_UOFFSETOF(GSTCTLDIRENTRYEX, szName[2])
633 && pHdr->cbDirEntryEx <= GSTCTL_DIRENTRY_MAX_SIZE, vrc = VERR_INVALID_PARAMETER);
634 pEntry->pDirEntryEx = (PGSTCTLDIRENTRYEX)RTMemDup((uint8_t *)pvBuf + offBuf, pHdr->cbDirEntryEx);
635 AssertPtrBreakStmt(pEntry->pDirEntryEx, vrc = VERR_NO_MEMORY);
636 pEntry->cbDirEntryEx = pHdr->cbDirEntryEx;
637 offBuf += pHdr->cbDirEntryEx;
638
639 if (pHdr->cbUser)
640 {
641 pEntry->pszUser = (char *)RTMemDup((uint8_t *)pvBuf + offBuf, pHdr->cbUser);
642 AssertPtrBreakStmt(pEntry->pszUser, vrc = VERR_NO_MEMORY);
643 pEntry->cbUser = pHdr->cbUser;
644 offBuf += pHdr->cbUser;
645 }
646
647 if (pHdr->cbGroups)
648 {
649 pEntry->pszGroups = (char *)RTMemDup((uint8_t *)pvBuf + offBuf, pHdr->cbGroups);
650 AssertPtrBreakStmt(pEntry->pszGroups, vrc = VERR_NO_MEMORY);
651 pEntry->cbGroups = pHdr->cbGroups;
652 offBuf += pHdr->cbGroups;
653 }
654
655#ifdef DEBUG
656 GuestFsObjData obj;
657 AssertRC(obj.FromGuestFsObjInfo(&pEntry->pDirEntryEx->Info, pEntry->pszUser, pEntry->pszGroups));
658 AssertRC(RTStrValidateEncodingEx(pEntry->pszUser, pHdr->cbUser, RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED));
659 AssertRC(RTStrValidateEncodingEx(pEntry->pszGroups, pHdr->cbGroups, RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED));
660#endif
661 }
662
663 if (RT_SUCCESS(vrc))
664 {
665 Assert(offBuf == cbBuf);
666 }
667 else /* Roll back on error. */
668 {
669 GuestDirectory::i_dirNotifyDataDestroy(&dataCb);
670 }
671 break;
672 }
673
[49349]674 default:
[98526]675 AssertFailed();
[49349]676 break;
677 }
678
[98526]679 try
680 {
681 if (RT_SUCCESS(vrc))
682 {
683 GuestWaitEventPayload payload(dataCb.uType, &dataCb, sizeof(dataCb));
684
685 /* Ignore return code, as the event to signal might not be there (anymore). */
686 signalWaitEventInternal(pCbCtx, vrcGuest, &payload);
687 }
688 else /* OOM situation, wrong HGCM parameters or smth. not expected. */
689 {
690 /* Ignore return code, as the event to signal might not be there (anymore). */
691 signalWaitEventInternalEx(pCbCtx, vrc, 0 /* guestRc */, NULL /* pPayload */);
692 }
693 }
694 catch (int vrcEx) /* Thrown by GuestWaitEventPayload constructor. */
695 {
696 /* Also try to signal the waiter, to let it know of the OOM situation.
697 * Ignore return code, as the event to signal might not be there (anymore). */
698 signalWaitEventInternalEx(pCbCtx, vrcEx, 0 /* guestRc */, NULL /* pPayload */);
699 vrc = vrcEx;
700 }
701
702 LogFlowThisFunc(("uType=%RU32, rcGuest=%Rrc, vrc=%Rrc\n", dataCb.uType, vrcGuest, vrc));
[49349]703 return vrc;
[98526]704#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
[49349]705}
706
[84648]707/**
[99085]708 * Static helper function to convert a given guest directory error to a string.
[84648]709 *
710 * @returns Error string.
[98709]711 * @param vrcGuest Guest directory error to return string for.
[84648]712 * @param pcszWhat Hint of what was involved when the error occurred.
713 */
[49349]714/* static */
[98272]715Utf8Str GuestDirectory::i_guestErrorToString(int vrcGuest, const char *pcszWhat)
[49349]716{
[84648]717 AssertPtrReturn(pcszWhat, "");
[49349]718
[98667]719#define CASE_MSG(a_iRc, ...) \
720 case a_iRc: strErr.printf(__VA_ARGS__); break;
721
[84648]722 Utf8Str strErr;
[98272]723 switch (vrcGuest)
[49349]724 {
[98667]725 CASE_MSG(VERR_ACCESS_DENIED, tr("Access to guest directory \"%s\" is denied"), pcszWhat);
726 CASE_MSG(VERR_ALREADY_EXISTS, tr("Guest directory \"%s\" already exists"), pcszWhat);
727 CASE_MSG(VERR_CANT_CREATE, tr("Guest directory \"%s\" cannot be created"), pcszWhat);
728 CASE_MSG(VERR_DIR_NOT_EMPTY, tr("Guest directory \"%s\" is not empty"), pcszWhat);
[99085]729 CASE_MSG(VERR_NO_MORE_FILES, tr("Guest directory \"%s\" has no more entries"), pcszWhat);
730 CASE_MSG(VERR_PATH_NOT_FOUND, tr("Path of guest directory \"%s\" not found"), pcszWhat);
[49349]731 default:
[98272]732 strErr.printf(tr("Error %Rrc for guest directory \"%s\" occurred\n"), vrcGuest, pcszWhat);
[49349]733 break;
734 }
735
[84648]736#undef CASE_MSG
737
738 return strErr;
[49349]739}
740
[49504]741/**
[99085]742 * Static helper function to destroy direction notification callback data.
743 *
744 * @param pDirNotify Pointer to direction notification callback data to destroy.
745 */
746/* static */
747void GuestDirectory::i_dirNotifyDataDestroy(PCALLBACKDATA_DIR_NOTIFY pDirNotify)
748{
749 if (!pDirNotify)
750 return;
751
752 switch (pDirNotify->uType)
753 {
754 case GUEST_DIR_NOTIFYTYPE_LIST:
755 {
756 for (uint32_t i = 0; i < pDirNotify->u.list.cEntries; i++)
757 {
758 PCALLBACKDATA_DIR_ENTRY const pEntry = pDirNotify->u.list.paEntries[i];
759 AssertPtrBreak(pEntry);
760 RTStrFree(pEntry->pszUser);
761 RTStrFree(pEntry->pszGroups);
762 RTMemFree(pEntry->pDirEntryEx);
763 RTMemFree(pEntry);
764 }
765 RTMemFree(pDirNotify->u.list.paEntries);
766 pDirNotify->u.list.paEntries = NULL;
767 pDirNotify->u.list.cEntries = 0;
768 break;
769 }
770
771 default:
772 break;
773 }
774}
775
776/**
[77587]777 * @copydoc GuestObject::i_onUnregister
[49504]778 */
[77587]779int GuestDirectory::i_onUnregister(void)
[49504]780{
781 LogFlowThisFuncEnter();
782
783 int vrc = VINF_SUCCESS;
[50213]784
[49504]785 LogFlowFuncLeaveRC(vrc);
786 return vrc;
787}
788
[71250]789/**
[77587]790 * @copydoc GuestObject::i_onSessionStatusChange
791 */
792int GuestDirectory::i_onSessionStatusChange(GuestSessionStatus_T enmSessionStatus)
793{
794 RT_NOREF(enmSessionStatus);
795
796 LogFlowThisFuncEnter();
797
798 int vrc = VINF_SUCCESS;
799
800 LogFlowFuncLeaveRC(vrc);
801 return vrc;
802}
803
804/**
[71250]805 * Closes this guest directory and removes it from the
806 * guest session's directory list.
807 *
808 * @return VBox status code.
[98526]809 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
[71250]810 */
[98526]811int GuestDirectory::i_close(int *pvrcGuest)
[71250]812{
[98526]813 int vrc;
814#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
815 if (mSession->i_getParent()->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS)
816 {
[98709]817 GuestWaitEvent *pEvent = NULL;
818 GuestEventTypes eventTypes;
819 try
820 {
821 eventTypes.push_back(VBoxEventType_OnGuestDirectoryStateChanged);
822
823 vrc = registerWaitEvent(eventTypes, &pEvent);
824 }
825 catch (std::bad_alloc &)
826 {
827 vrc = VERR_NO_MEMORY;
828 }
829
830 if (RT_FAILURE(vrc))
831 return vrc;
832
833 /* Prepare HGCM call. */
834 VBOXHGCMSVCPARM paParms[2];
835 int i = 0;
836 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
837 HGCMSvcSetU32(&paParms[i++], mObjectID /* Guest directory handle */);
838
839 vrc = sendMessage(HOST_MSG_DIR_CLOSE, i, paParms);
840 if (RT_SUCCESS(vrc))
841 {
[104003]842 vrc = pEvent->Wait(GSTCTL_DEFAULT_TIMEOUT_MS);
[98709]843 if (RT_SUCCESS(vrc))
844 {
845 // Nothing to do here.
846 }
847 else if (pEvent->HasGuestError() && pvrcGuest)
848 *pvrcGuest = pEvent->GuestResult();
849 }
[98526]850 }
851 else
[98643]852#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
[98526]853 {
[104178]854#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
[98526]855 vrc = i_closeViaToolbox(pvrcGuest);
[104178]856#else
857 RT_NOREF(pvrcGuest);
858 vrc = VERR_NOT_SUPPORTED;
859#endif
[98526]860 }
[71250]861
862 AssertPtr(mSession);
[94958]863 int vrc2 = mSession->i_directoryUnregister(this);
864 if (RT_SUCCESS(vrc))
865 vrc = vrc2;
[71250]866
[94958]867 LogFlowThisFunc(("Returning vrc=%Rrc\n", vrc));
868 return vrc;
[71250]869}
870
[98526]871#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
[71250]872/**
[98526]873 * Closes this guest directory and removes it from the guest session's directory list (legacy version).
874 *
875 * @return VBox status code.
876 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
877 *
878 * @note This uses an own guest process via the built-in toolbox in VBoxSerivce.
879 */
880int GuestDirectory::i_closeViaToolbox(int *pvrcGuest)
881{
[104003]882 return mData.mProcessTool.terminate(GSTCTL_DEFAULT_TIMEOUT_MS, pvrcGuest);
[98526]883}
884#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
885
886/**
[83489]887 * Reads the next directory entry, internal version.
[71250]888 *
[99085]889 * @return VBox status code.
890 * @retval VERR_GSTCTL_GUEST_ERROR / VERR_NO_MORE_FILES if no more entries are available.
[83489]891 * @param objData Where to store the read directory entry as internal object data.
[98526]892 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
[71250]893 */
[98526]894int GuestDirectory::i_readInternal(GuestFsObjData &objData, int *pvrcGuest)
[71250]895{
[98526]896 AssertPtrReturn(pvrcGuest, VERR_INVALID_POINTER);
[71250]897
[98526]898 int vrc;
[98709]899
[98526]900#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
901 if (mSession->i_getParent()->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS)
902 {
[98709]903 GuestWaitEvent *pEvent = NULL;
904 GuestEventTypes eventTypes;
905 try
906 {
907 vrc = registerWaitEvent(eventTypes, &pEvent);
908 }
909 catch (std::bad_alloc &)
910 {
911 vrc = VERR_NO_MEMORY;
912 }
913
914 if (RT_FAILURE(vrc))
915 return vrc;
916
917 /* Prepare HGCM call. */
[98818]918 VBOXHGCMSVCPARM paParms[4];
[98709]919 int i = 0;
920 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
921 HGCMSvcSetU32(&paParms[i++], mObjectID /* Guest directory handle */);
922
923 vrc = sendMessage(HOST_MSG_DIR_READ, i, paParms);
924 if (RT_SUCCESS(vrc))
925 {
[104003]926 vrc = pEvent->Wait(GSTCTL_DEFAULT_TIMEOUT_MS);
[98709]927 if (RT_SUCCESS(vrc))
928 {
929 PCALLBACKDATA_DIR_NOTIFY const pDirNotify = (PCALLBACKDATA_DIR_NOTIFY)pEvent->Payload().Raw();
930 AssertPtrReturn(pDirNotify, VERR_INVALID_POINTER);
[98714]931 int vrcGuest = (int)pDirNotify->rc;
[98709]932 if (RT_SUCCESS(vrcGuest))
933 {
934 AssertReturn(pDirNotify->uType == GUEST_DIR_NOTIFYTYPE_READ, VERR_INVALID_PARAMETER);
[99085]935 AssertPtrReturn(pDirNotify->u.read.Entry.pDirEntryEx, VERR_INVALID_POINTER);
936 objData.Init(pDirNotify->u.read.Entry.pDirEntryEx->szName);
937 vrc = objData.FromGuestFsObjInfo(&pDirNotify->u.read.Entry.pDirEntryEx->Info,
938 pDirNotify->u.read.Entry.pszUser, pDirNotify->u.read.Entry.pszGroups);
939 RTMemFree(pDirNotify->u.read.Entry.pDirEntryEx);
940 RTStrFree(pDirNotify->u.read.Entry.pszUser);
941 RTStrFree(pDirNotify->u.read.Entry.pszGroups);
[98709]942 }
943 else
944 {
[99085]945 *pvrcGuest = vrcGuest;
[98709]946 vrc = VERR_GSTCTL_GUEST_ERROR;
947 }
948 }
949 else if (pEvent->HasGuestError() && pvrcGuest)
950 *pvrcGuest = pEvent->GuestResult();
951 }
[98526]952 }
953 else
[98643]954#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
[98526]955 {
[104178]956#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
[98526]957 vrc = i_readInternalViaToolbox(objData, pvrcGuest);
[104178]958#else
959 RT_NOREF(objData, pvrcGuest);
960 vrc = VERR_NOT_SUPPORTED;
961#endif
[98526]962 }
963
964 LogFlowThisFunc(("Returning vrc=%Rrc\n", vrc));
965 return vrc;
966}
967
968#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
969/**
970 * Reads the next directory entry, internal version (legacy version).
971 *
972 * @return VBox status code. Will return VERR_NO_MORE_FILES if no more entries are available.
973 * @param objData Where to store the read directory entry as internal object data.
974 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
975 *
976 * @note This uses an own guest process via the built-in toolbox in VBoxSerivce.
977 */
978int GuestDirectory::i_readInternalViaToolbox(GuestFsObjData &objData, int *pvrcGuest)
979{
980 GuestToolboxStreamBlock curBlock;
981 int vrc = mData.mProcessTool.waitEx(GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK, &curBlock, pvrcGuest);
[94958]982 if (RT_SUCCESS(vrc))
[71250]983 {
984 /*
985 * Note: The guest process can still be around to serve the next
986 * upcoming stream block next time.
987 */
[73063]988 if (!mData.mProcessTool.isRunning())
[94958]989 vrc = mData.mProcessTool.getTerminationStatus(); /* Tool process is not running (anymore). Check termination status. */
[73063]990
[94958]991 if (RT_SUCCESS(vrc))
[71250]992 {
993 if (curBlock.GetCount()) /* Did we get content? */
994 {
[83489]995 if (curBlock.GetString("name"))
[71250]996 {
[98526]997 vrc = objData.FromToolboxLs(curBlock, true /* fLong */);
[71250]998 }
999 else
[99085]1000 {
1001#ifdef DEBUG
1002 curBlock.DumpToLog();
1003#endif
[94958]1004 vrc = VERR_PATH_NOT_FOUND;
[99085]1005 }
[71250]1006 }
1007 else
1008 {
1009 /* Nothing to read anymore. Tell the caller. */
[94958]1010 vrc = VERR_NO_MORE_FILES;
[71250]1011 }
1012 }
1013 }
1014
[94958]1015 return vrc;
[71250]1016}
[98526]1017#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
[71250]1018
[83489]1019/**
[99085]1020 * Worker for listing the next directory entries.
1021 *
1022 * @return VBox status code.
1023 * @retval VERR_GSTCTL_GUEST_ERROR / VERR_NO_MORE_FILES if no more entries are available after this iteration.
1024 * \a vecObjData will contain the rest of the entries then (if any).
[99295]1025 * @retval VERR_NOT_SUPPORTED if the installed Guest Additions do not support this method.
[99085]1026 * @param cMaxEntries How many directory entries to read at max.
1027 * @param fFlags Flags of type GSTCTL_DIRLIST_F_XXX.
1028 * @param vecObjData Where to store the read directory entries on success.
1029 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
1030 */
1031int GuestDirectory::i_listInternal(uint32_t cMaxEntries, uint32_t fFlags, std::vector<GuestFsObjData> &vecObjData, int *pvrcGuest)
1032{
1033 int vrc;
1034
1035#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
1036 if (mSession->i_getParent()->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS)
1037 {
1038 GuestWaitEvent *pEvent = NULL;
1039 GuestEventTypes eventTypes;
1040 try
1041 {
1042 vrc = registerWaitEvent(eventTypes, &pEvent);
1043 }
1044 catch (std::bad_alloc &)
1045 {
1046 vrc = VERR_NO_MEMORY;
1047 }
1048
1049 if (RT_FAILURE(vrc))
1050 return vrc;
1051
1052 /* Prepare HGCM call. */
1053 VBOXHGCMSVCPARM paParms[4];
1054 int i = 0;
1055 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1056 HGCMSvcSetU32(&paParms[i++], mObjectID /* Directory handle */);
1057 HGCMSvcSetU32(&paParms[i++], cMaxEntries);
1058 HGCMSvcSetU32(&paParms[i++], 0 /* Flags */);
1059
1060 vrc = sendMessage(HOST_MSG_DIR_LIST, i, paParms);
1061 if (RT_SUCCESS(vrc))
1062 {
[104003]1063 vrc = pEvent->Wait(GSTCTL_DEFAULT_TIMEOUT_MS);
[99085]1064 if (RT_SUCCESS(vrc))
1065 {
1066 PCALLBACKDATA_DIR_NOTIFY const pDirNotify = (PCALLBACKDATA_DIR_NOTIFY)pEvent->Payload().Raw();
1067 AssertPtrReturn(pDirNotify, VERR_INVALID_POINTER);
1068 int vrcGuest = (int)pDirNotify->rc;
1069 if ( RT_SUCCESS(vrcGuest)
1070 /* Guest indicates that there are no more entries to read.
1071 * We still need to check if we have read something with this iteration though. */
1072 || vrcGuest == VERR_NO_MORE_FILES)
1073 {
1074 AssertReturn(pDirNotify->uType == GUEST_DIR_NOTIFYTYPE_LIST, VERR_INVALID_PARAMETER);
1075
1076 try
1077 {
1078 vecObjData.resize(pDirNotify->u.list.cEntries);
1079 }
1080 catch (std::bad_alloc &)
1081 {
1082 vrc = VERR_NO_MEMORY;
1083 }
1084
1085 if (RT_SUCCESS(vrc))
1086 {
1087 for (size_t a = 0; a < pDirNotify->u.list.cEntries; a++)
1088 {
1089 PCALLBACKDATA_DIR_ENTRY const pEntry = pDirNotify->u.list.paEntries[a];
1090 AssertPtr(pEntry);
1091
1092 AssertPtr(pEntry->pDirEntryEx);
1093 vecObjData[a].Init(pEntry->pDirEntryEx->szName);
1094 int vrc2 = vecObjData[a].FromGuestFsObjInfo(&pEntry->pDirEntryEx->Info, pEntry->pszUser, pEntry->pszGroups);
1095 if (RT_SUCCESS(vrc))
1096 vrc = vrc2;
1097 }
1098 }
1099 }
1100 else
1101 {
1102 *pvrcGuest = vrcGuest;
1103 vrc = VERR_GSTCTL_GUEST_ERROR;
1104 }
1105
1106 GuestDirectory::i_dirNotifyDataDestroy(pDirNotify);
1107 }
1108 else if (pEvent->HasGuestError())
1109 *pvrcGuest = pEvent->GuestResult();
1110 }
1111 }
1112 else
1113#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
1114 {
1115 RT_NOREF(cMaxEntries, fFlags, vecObjData, pvrcGuest);
1116 vrc = VERR_NOT_SUPPORTED;
1117 }
1118
1119 return vrc;
1120}
1121
1122/**
1123 * Lists the next directory entries.
1124 *
1125 * @return VBox status code.
1126 * @retval VERR_GSTCTL_GUEST_ERROR / VERR_NO_MORE_FILES if no more entries are available after this iteration.
1127 * \a vecObjData will contain the rest of the entries then (if any).
[99295]1128 * @retval VERR_NOT_SUPPORTED if the installed Guest Additions do not support this method.
[99085]1129 * @param cMaxEntries How many directory entries to read at max.
1130 * @param fFlags Flags of type GSTCTL_DIRLIST_F_XXX.
1131 * @param vecObjInfo Where to store the read directory entries on success.
1132 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
1133 */
1134int GuestDirectory::i_listEx(uint32_t cMaxEntries, uint32_t fFlags, std::vector<ComObjPtr<GuestFsObjInfo>> &vecObjInfo,
1135 int *pvrcGuest)
1136{
1137 AssertPtrReturn(pvrcGuest, VERR_INVALID_POINTER);
1138 AssertReturn(!(fFlags & ~GSTCTL_DIRLIST_F_VALID_MASK), VERR_INVALID_PARAMETER);
1139
1140 std::vector<GuestFsObjData> vecObjDataInt;
1141 int vrc = i_listInternal(cMaxEntries, fFlags, vecObjDataInt, pvrcGuest);
1142 if ( RT_SUCCESS(vrc)
1143 || ( vrc == VERR_GSTCTL_GUEST_ERROR
1144 && *pvrcGuest == VERR_NO_MORE_FILES))
1145 {
1146 try
1147 {
1148 vecObjInfo.resize(vecObjDataInt.size());
1149 for (size_t i = 0; i < vecObjDataInt.size(); i++)
1150 {
1151 HRESULT hrc = vecObjInfo[i].createObject();
1152 ComAssertComRCBreak(hrc, vrc = VERR_COM_UNEXPECTED);
1153
1154 vrc = vecObjInfo[i]->init(vecObjDataInt[i]);
1155 if (RT_FAILURE(vrc))
1156 break;
1157 }
1158 }
1159 catch (std::bad_alloc &)
1160 {
1161 vrc = VERR_NO_MEMORY;
1162 }
1163 }
1164
1165 return vrc;
1166}
1167
1168/**
1169 * Lists the next directory entries.
1170 *
1171 * @return VBox status code.
1172 * @retval VERR_GSTCTL_GUEST_ERROR / VERR_NO_MORE_FILES if no more entries are available after this iteration.
1173 * \a vecObjData will contain the rest of the entries then (if any).
[99295]1174 * @retval VERR_NOT_SUPPORTED if the installed Guest Additions do not support this method.
[99085]1175 * @param cMaxEntries How many directory entries to read at max.
1176 * @param vecObjInfo Where to store the read directory entries on success.
1177 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
1178 */
1179int GuestDirectory::i_list(uint32_t cMaxEntries, std::vector<ComObjPtr<GuestFsObjInfo>> &vecObjInfo, int *pvrcGuest)
1180{
1181 return i_listEx(cMaxEntries, GSTCTL_DIRLIST_F_NONE, vecObjInfo, pvrcGuest);
1182}
1183
1184/**
[83489]1185 * Reads the next directory entry.
1186 *
[99085]1187 * @return VBox status code.
1188 * @retval VERR_GSTCTL_GUEST_ERROR / VERR_NO_MORE_FILES if no more entries are available.
[83489]1189 * @param fsObjInfo Where to store the read directory entry.
[98526]1190 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
[83489]1191 */
[98526]1192int GuestDirectory::i_read(ComObjPtr<GuestFsObjInfo> &fsObjInfo, int *pvrcGuest)
[83489]1193{
[98526]1194 AssertPtrReturn(pvrcGuest, VERR_INVALID_POINTER);
[83489]1195
1196 /* Create the FS info object. */
1197 HRESULT hr = fsObjInfo.createObject();
1198 if (FAILED(hr))
1199 return VERR_COM_UNEXPECTED;
1200
[94958]1201 int vrc;
[83489]1202
1203 /* If we have a valid object data cache, read from it. */
1204 if (mData.mObjData.mName.isNotEmpty())
1205 {
[94958]1206 vrc = fsObjInfo->init(mData.mObjData);
1207 if (RT_SUCCESS(vrc))
[83489]1208 {
1209 mData.mObjData.mName = ""; /* Mark the object data as being empty (beacon). */
1210 }
1211 }
[98526]1212 else /* Otherwise ask the guest for the next object data. */
[83489]1213 {
1214 GuestFsObjData objData;
[98526]1215 vrc = i_readInternal(objData, pvrcGuest);
[94958]1216 if (RT_SUCCESS(vrc))
1217 vrc = fsObjInfo->init(objData);
[83489]1218 }
1219
[94958]1220 LogFlowThisFunc(("Returning vrc=%Rrc\n", vrc));
1221 return vrc;
[83489]1222}
1223
[98526]1224/**
1225 * Rewinds the directory reading.
1226 *
1227 * @returns VBox status code.
1228 * @retval VERR_GSTCTL_GUEST_ERROR when an error from the guest side has been received.
1229 * @param uTimeoutMS Timeout (in ms) to wait.
1230 * @param pvrcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
1231 */
1232int GuestDirectory::i_rewind(uint32_t uTimeoutMS, int *pvrcGuest)
1233{
1234 RT_NOREF(pvrcGuest);
1235#ifndef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
1236 RT_NOREF(uTimeoutMS, pvrcGuest);
1237#else
1238 /* Only available for Guest Additions 7.1+. */
1239 if (mSession->i_getParent()->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS)
1240 {
1241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1242
1243 int vrc;
1244
1245 GuestWaitEvent *pEvent = NULL;
1246 GuestEventTypes eventTypes;
1247 try
1248 {
1249 eventTypes.push_back(VBoxEventType_OnGuestDirectoryStateChanged);
1250 vrc = registerWaitEvent(eventTypes, &pEvent);
1251 }
1252 catch (std::bad_alloc &)
1253 {
1254 vrc = VERR_NO_MEMORY;
1255 }
1256
1257 if (RT_FAILURE(vrc))
1258 return vrc;
1259
1260 /* Prepare HGCM call. */
1261 VBOXHGCMSVCPARM paParms[4];
1262 int i = 0;
1263 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1264 HGCMSvcSetU32(&paParms[i++], mObjectID /* Directory handle */);
1265
1266 alock.release(); /* Drop lock before sending. */
1267
1268 vrc = sendMessage(HOST_MSG_DIR_REWIND, i, paParms);
1269 if (RT_SUCCESS(vrc))
1270 {
1271 VBoxEventType_T evtType;
1272 ComPtr<IEvent> pIEvent;
1273 vrc = waitForEvent(pEvent, uTimeoutMS, &evtType, pIEvent.asOutParam());
1274 if (RT_SUCCESS(vrc))
1275 {
1276 if (evtType == VBoxEventType_OnGuestDirectoryStateChanged)
1277 {
1278 ComPtr<IGuestDirectoryStateChangedEvent> pEvt = pIEvent;
1279 Assert(!pEvt.isNull());
1280 }
1281 else
1282 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
1283 }
1284 else if (pEvent->HasGuestError()) /* Return guest vrc if available. */
[98666]1285 vrc = pEvent->GuestResult();
[98526]1286 }
1287
1288 unregisterWaitEvent(pEvent);
1289 return vrc;
1290 }
1291#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
1292
1293 return VERR_NOT_SUPPORTED;
1294}
1295
1296/**
1297 * Sets the current internal directory object status.
1298 *
1299 * @returns VBox status code.
1300 * @param enmStatus New directory status to set.
1301 * @param vrcDir New result code to set.
1302 *
1303 * @note Takes the write lock.
1304 */
1305int GuestDirectory::i_setStatus(DirectoryStatus_T enmStatus, int vrcDir)
1306{
1307 LogFlowThisFuncEnter();
1308
1309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1310
1311 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, vrcDir=%Rrc\n", mData.mStatus, enmStatus, vrcDir));
1312
1313#ifdef VBOX_STRICT
1314 if (enmStatus == DirectoryStatus_Error)
1315 AssertMsg(RT_FAILURE(vrcDir), ("Guest vrc must be an error (%Rrc)\n", vrcDir));
1316 else
1317 AssertMsg(RT_SUCCESS(vrcDir), ("Guest vrc must not be an error (%Rrc)\n", vrcDir));
1318#endif
1319
1320 if (mData.mStatus != enmStatus)
1321 {
1322 mData.mStatus = enmStatus;
1323 mData.mLastError = vrcDir;
1324
1325 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1326 HRESULT hrc = errorInfo.createObject();
1327 ComAssertComRCRet(hrc, VERR_COM_UNEXPECTED);
1328 if (RT_FAILURE(vrcDir))
1329 {
1330 hrc = errorInfo->initEx(VBOX_E_GSTCTL_GUEST_ERROR, vrcDir,
1331 COM_IIDOF(IGuestDirectory), getComponentName(),
1332 i_guestErrorToString(vrcDir, mData.mOpenInfo.mPath.c_str()));
1333 ComAssertComRCRet(hrc, VERR_COM_UNEXPECTED);
1334 }
1335 /* Note: On vrcDir success, errorInfo is set to S_OK and also sent via the event below. */
1336
1337 alock.release(); /* Release lock before firing off event. */
1338
[98709]1339 ::FireGuestDirectoryStateChangedEvent(mEventSource, mSession, this, mData.mStatus, errorInfo);
[98526]1340 }
1341
1342 return VINF_SUCCESS;
1343}
1344
[98709]1345/**
1346 * Waits for a guest directory status change.
1347 *
1348 * @note Similar code in GuestFile::i_waitForStatusChange().
1349 *
1350 * @returns VBox status code.
1351 * @retval VERR_GSTCTL_GUEST_ERROR when an error from the guest side has been received.
1352 * @param pEvent Guest wait event to wait for.
1353 * @param uTimeoutMS Timeout (in ms) to wait.
1354 * @param penmStatus Where to return the directoy status on success.
1355 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR was returned.
1356 */
1357int GuestDirectory::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
1358 DirectoryStatus_T *penmStatus, int *prcGuest)
1359{
1360 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1361 /* penmStatus is optional. */
1362
1363 VBoxEventType_T evtType;
1364 ComPtr<IEvent> pIEvent;
1365 int vrc = waitForEvent(pEvent, uTimeoutMS, &evtType, pIEvent.asOutParam());
1366 if (RT_SUCCESS(vrc))
1367 {
1368 AssertReturn(evtType == VBoxEventType_OnGuestDirectoryStateChanged, VERR_WRONG_TYPE);
1369 ComPtr<IGuestDirectoryStateChangedEvent> pDirectoryEvent = pIEvent;
1370 AssertReturn(!pDirectoryEvent.isNull(), VERR_COM_UNEXPECTED);
1371
1372 HRESULT hr;
1373 if (penmStatus)
1374 {
1375 hr = pDirectoryEvent->COMGETTER(Status)(penmStatus);
1376 ComAssertComRC(hr);
1377 }
1378
1379 ComPtr<IVirtualBoxErrorInfo> errorInfo;
1380 hr = pDirectoryEvent->COMGETTER(Error)(errorInfo.asOutParam());
1381 ComAssertComRC(hr);
1382
1383 LONG lGuestRc;
1384 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
1385 ComAssertComRC(hr);
1386
1387 LogFlowThisFunc(("resultDetail=%RI32 (%Rrc)\n", lGuestRc, lGuestRc));
1388
1389 if (RT_FAILURE((int)lGuestRc))
1390 vrc = VERR_GSTCTL_GUEST_ERROR;
1391
1392 if (prcGuest)
1393 *prcGuest = (int)lGuestRc;
1394 }
1395 /* waitForEvent may also return VERR_GSTCTL_GUEST_ERROR like we do above, so make prcGuest is set. */
1396 /** @todo Also see todo in GuestFile::i_waitForStatusChange(). */
1397 else if (vrc == VERR_GSTCTL_GUEST_ERROR && prcGuest)
1398 *prcGuest = pEvent->GuestResult();
1399 Assert(vrc != VERR_GSTCTL_GUEST_ERROR || !prcGuest || *prcGuest != (int)0xcccccccc);
1400
1401 return vrc;
1402}
1403
[42084]1404// implementation of public methods
1405/////////////////////////////////////////////////////////////////////////////
[50559]1406HRESULT GuestDirectory::close()
[42673]1407{
[42897]1408 AutoCaller autoCaller(this);
[98262]1409 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
[42897]1410
[71560]1411 LogFlowThisFuncEnter();
1412
[94958]1413 HRESULT hrc = S_OK;
[43162]1414
[94958]1415 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
[98526]1416 int vrc = i_close(&vrcGuest);
[73003]1417 if (RT_FAILURE(vrc))
[47469]1418 {
[73003]1419 switch (vrc)
[47469]1420 {
[49349]1421 case VERR_GSTCTL_GUEST_ERROR:
[91503]1422 {
[94958]1423 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, mData.mOpenInfo.mPath.c_str());
1424 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Closing guest directory failed: %s"),
1425 GuestBase::getErrorAsString(ge).c_str());
[47469]1426 break;
[91503]1427 }
[47469]1428 case VERR_NOT_SUPPORTED:
1429 /* Silently skip old Guest Additions which do not support killing the
1430 * the guest directory handling process. */
1431 break;
1432
1433 default:
[94958]1434 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
1435 tr("Closing guest directory \"%s\" failed: %Rrc"), mData.mOpenInfo.mPath.c_str(), vrc);
[47469]1436 break;
1437 }
1438 }
1439
[94958]1440 return hrc;
[42673]1441}
1442
[99085]1443HRESULT GuestDirectory::list(ULONG aMaxEntries, std::vector<ComPtr<IFsObjInfo> > &aObjInfos)
1444{
1445 AutoCaller autoCaller(this);
1446 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
1447
1448 LogFlowThisFuncEnter();
1449
1450 HRESULT hrc = S_OK;
1451
1452 std::vector<ComObjPtr<GuestFsObjInfo> > vecObjInfo;
1453 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1454 int vrc = i_list(aMaxEntries, vecObjInfo, &vrcGuest);
1455
1456 /* Don't propagate an error to API callers when this is the last listing (i.e. no more files).
1457 * For subsequent reads the API caller gets an appropriate error then. */
1458 if ( vrc == VERR_GSTCTL_GUEST_ERROR
1459 && vrcGuest == VERR_NO_MORE_FILES
1460 && vecObjInfo.size())
1461 vrc = VINF_SUCCESS;
1462
1463 if (RT_SUCCESS(vrc))
1464 {
1465 try
1466 {
1467 aObjInfos.resize(vecObjInfo.size());
1468
1469 for (size_t i = 0; i < vecObjInfo.size(); i++)
1470 {
1471 hrc = vecObjInfo[i].queryInterfaceTo(aObjInfos[i].asOutParam());
1472 ComAssertComRCBreakRC(hrc);
1473 }
1474 }
1475 catch (std::bad_alloc &)
1476 {
1477 hrc = E_OUTOFMEMORY;
1478 }
1479 }
1480 else
1481 {
1482 switch (vrc)
1483 {
1484 case VERR_GSTCTL_GUEST_ERROR:
1485 {
1486 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, mData.mOpenInfo.mPath.c_str());
1487 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Listing guest directory failed: %s"),
1488 GuestBase::getErrorAsString(ge).c_str());
1489
1490 /* Return a dedicated error code when directory reading is done. See SDK reference. */
1491 if (vrcGuest == VERR_NO_MORE_FILES)
1492 hrc = VBOX_E_OBJECT_NOT_FOUND;
1493 break;
1494 }
1495
[99295]1496 case VERR_NOT_SUPPORTED: /* Returned from i_list(). */
1497 hrc = VBOX_E_NOT_SUPPORTED;
1498 break;
1499
[99085]1500 default:
1501 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Listing guest directory \"%s\" returned unhandled error: %Rrc\n"),
1502 mData.mOpenInfo.mPath.c_str(), vrc);
1503 break;
1504 }
1505 }
1506
1507 LogFlowThisFunc(("Returning hrc=%Rhrc / vrc=%Rrc\n", hrc, vrc));
1508 return hrc;
1509}
1510
[50559]1511HRESULT GuestDirectory::read(ComPtr<IFsObjInfo> &aObjInfo)
[42084]1512{
1513 AutoCaller autoCaller(this);
[98262]1514 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
[42084]1515
[71560]1516 LogFlowThisFuncEnter();
1517
[94958]1518 HRESULT hrc = S_OK;
[42673]1519
[79189]1520 ComObjPtr<GuestFsObjInfo> fsObjInfo;
[94958]1521 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1522 int vrc = i_read(fsObjInfo, &vrcGuest);
[73003]1523 if (RT_SUCCESS(vrc))
[42673]1524 {
[71250]1525 /* Return info object to the caller. */
[94958]1526 hrc = fsObjInfo.queryInterfaceTo(aObjInfo.asOutParam());
[42693]1527 }
[71250]1528 else
[42693]1529 {
[73003]1530 switch (vrc)
[42673]1531 {
[45078]1532 case VERR_GSTCTL_GUEST_ERROR:
[91503]1533 {
[98526]1534 GuestErrorInfo ge(
1535#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
1536 GuestErrorInfo::Type_ToolLs
1537#else
[99085]1538 GuestErrorInfo::Type_Directory
[98526]1539#endif
1540 , vrcGuest, mData.mOpenInfo.mPath.c_str());
[94958]1541 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Reading guest directory failed: %s"),
1542 GuestBase::getErrorAsString(ge).c_str());
[99085]1543
1544 /* Return a dedicated error code when directory reading is done. See SDK reference. */
1545 if (vrcGuest == VERR_NO_MORE_FILES)
1546 hrc = VBOX_E_OBJECT_NOT_FOUND;
[43162]1547 break;
[91503]1548 }
[98526]1549
1550#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
[73036]1551 case VERR_GSTCTL_PROCESS_EXIT_CODE:
[94958]1552 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" failed: %Rrc"),
1553 mData.mOpenInfo.mPath.c_str(), mData.mProcessTool.getRc());
[42693]1554 break;
[99085]1555
[42693]1556 case VERR_PATH_NOT_FOUND:
[94958]1557 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" failed: Path not found"),
1558 mData.mOpenInfo.mPath.c_str());
[42693]1559 break;
[42673]1560
[42693]1561 case VERR_NO_MORE_FILES:
[43162]1562 /* See SDK reference. */
[94958]1563 hrc = setErrorBoth(VBOX_E_OBJECT_NOT_FOUND, vrc, tr("Reading guest directory \"%s\" failed: No more entries"),
1564 mData.mOpenInfo.mPath.c_str());
[42693]1565 break;
[99085]1566#endif
[42693]1567 default:
[98526]1568 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading guest directory \"%s\" returned unhandled error: %Rrc\n"),
[94958]1569 mData.mOpenInfo.mPath.c_str(), vrc);
[42693]1570 break;
[42673]1571 }
1572 }
1573
[94958]1574 LogFlowThisFunc(("Returning hrc=%Rhrc / vrc=%Rrc\n", hrc, vrc));
1575 return hrc;
[42084]1576}
1577
[98526]1578HRESULT GuestDirectory::rewind(void)
1579{
1580 AutoCaller autoCaller(this);
1581 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
1582
1583 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
[104003]1584 int vrc = i_rewind(GSTCTL_DEFAULT_TIMEOUT_MS, &vrcGuest);
[98526]1585 if (RT_SUCCESS(vrc))
1586 return S_OK;
1587
1588 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, mData.mOpenInfo.mPath.c_str());
1589 return setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Rewinding guest directory failed: %s"),
1590 GuestBase::getErrorAsString(ge).c_str());
1591}
1592
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use