VirtualBox

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

Last change on this file since 101381 was 99295, checked in by vboxsync, 14 months ago

Guest Additions/Main: Return and document a dedicated error if I[Guest]Directory::list() is not supported by the installed Guest Additions. bugref:9783

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

© 2023 Oracle
ContactPrivacy policyTerms of Use