VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestSessionImpl.cpp@ 77070

Last change on this file since 77070 was 77070, checked in by vboxsync, 6 years ago

Guest Control/Main: Removed VERR_PERMISSION_DENIED in GuestSession::i_guestErrorToString(); already is covered by VERR_AUTHENTICATION_FAILURE.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 132.9 KB
Line 
1/* $Id: GuestSessionImpl.cpp 77070 2019-01-31 12:01:44Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest session handling.
4 */
5
6/*
7 * Copyright (C) 2012-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_MAIN_GUESTSESSION
23#include "LoggingNew.h"
24
25#include "GuestImpl.h"
26#ifndef VBOX_WITH_GUEST_CONTROL
27# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
28#endif
29#include "GuestSessionImpl.h"
30#include "GuestSessionImplTasks.h"
31#include "GuestCtrlImplPrivate.h"
32#include "VirtualBoxErrorInfoImpl.h"
33
34#include "Global.h"
35#include "AutoCaller.h"
36#include "ProgressImpl.h"
37#include "VBoxEvents.h"
38#include "VMMDev.h"
39#include "ThreadTask.h"
40
41#include <memory> /* For auto_ptr. */
42
43#include <iprt/cpp/utils.h> /* For unconst(). */
44#include <iprt/ctype.h>
45#include <iprt/env.h>
46#include <iprt/file.h> /* For CopyTo/From. */
47#include <iprt/path.h>
48#include <iprt/rand.h>
49
50#include <VBox/com/array.h>
51#include <VBox/com/listeners.h>
52#include <VBox/version.h>
53
54
55/**
56 * Base class representing an internal
57 * asynchronous session task.
58 */
59class GuestSessionTaskInternal : public ThreadTask
60{
61public:
62
63 GuestSessionTaskInternal(GuestSession *pSession)
64 : ThreadTask("GenericGuestSessionTaskInternal")
65 , mSession(pSession)
66 , mRC(VINF_SUCCESS) { }
67
68 virtual ~GuestSessionTaskInternal(void) { }
69
70 int rc(void) const { return mRC; }
71 bool isOk(void) const { return RT_SUCCESS(mRC); }
72 const ComObjPtr<GuestSession> &Session(void) const { return mSession; }
73
74protected:
75
76 const ComObjPtr<GuestSession> mSession;
77 int mRC;
78};
79
80/**
81 * Class for asynchronously starting a guest session.
82 */
83class GuestSessionTaskInternalStart : public GuestSessionTaskInternal
84{
85public:
86
87 GuestSessionTaskInternalStart(GuestSession *pSession)
88 : GuestSessionTaskInternal(pSession)
89 {
90 m_strTaskName = "gctlSesStart";
91 }
92
93 void handler()
94 {
95 /* Ignore rc */ GuestSession::i_startSessionThreadTask(this);
96 }
97};
98
99/**
100 * Internal listener class to serve events in an
101 * active manner, e.g. without polling delays.
102 */
103class GuestSessionListener
104{
105public:
106
107 GuestSessionListener(void)
108 {
109 }
110
111 virtual ~GuestSessionListener(void)
112 {
113 }
114
115 HRESULT init(GuestSession *pSession)
116 {
117 AssertPtrReturn(pSession, E_POINTER);
118 mSession = pSession;
119 return S_OK;
120 }
121
122 void uninit(void)
123 {
124 mSession = NULL;
125 }
126
127 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
128 {
129 switch (aType)
130 {
131 case VBoxEventType_OnGuestSessionStateChanged:
132 {
133 AssertPtrReturn(mSession, E_POINTER);
134 int rc2 = mSession->signalWaitEvent(aType, aEvent);
135 RT_NOREF(rc2);
136#ifdef DEBUG_andy
137 LogFlowFunc(("Signalling events of type=%RU32, session=%p resulted in rc=%Rrc\n",
138 aType, mSession, rc2));
139#endif
140 break;
141 }
142
143 default:
144 AssertMsgFailed(("Unhandled event %RU32\n", aType));
145 break;
146 }
147
148 return S_OK;
149 }
150
151private:
152
153 GuestSession *mSession;
154};
155typedef ListenerImpl<GuestSessionListener, GuestSession*> GuestSessionListenerImpl;
156
157VBOX_LISTENER_DECLARE(GuestSessionListenerImpl)
158
159// constructor / destructor
160/////////////////////////////////////////////////////////////////////////////
161
162DEFINE_EMPTY_CTOR_DTOR(GuestSession)
163
164HRESULT GuestSession::FinalConstruct(void)
165{
166 LogFlowThisFuncEnter();
167 return BaseFinalConstruct();
168}
169
170void GuestSession::FinalRelease(void)
171{
172 LogFlowThisFuncEnter();
173 uninit();
174 BaseFinalRelease();
175 LogFlowThisFuncLeave();
176}
177
178// public initializer/uninitializer for internal purposes only
179/////////////////////////////////////////////////////////////////////////////
180
181/**
182 * Initializes a guest session but does *not* open in on the guest side
183 * yet. This needs to be done via the openSession() / openSessionAsync calls.
184 *
185 * @return IPRT status code.
186 ** @todo Docs!
187 */
188int GuestSession::init(Guest *pGuest, const GuestSessionStartupInfo &ssInfo,
189 const GuestCredentials &guestCreds)
190{
191 LogFlowThisFunc(("pGuest=%p, ssInfo=%p, guestCreds=%p\n",
192 pGuest, &ssInfo, &guestCreds));
193
194 /* Enclose the state transition NotReady->InInit->Ready. */
195 AutoInitSpan autoInitSpan(this);
196 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
197
198 AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
199
200 /*
201 * Initialize our data members from the input.
202 */
203 mParent = pGuest;
204
205 /* Copy over startup info. */
206 /** @todo Use an overloaded copy operator. Later. */
207 mData.mSession.mID = ssInfo.mID;
208 mData.mSession.mIsInternal = ssInfo.mIsInternal;
209 mData.mSession.mName = ssInfo.mName;
210 mData.mSession.mOpenFlags = ssInfo.mOpenFlags;
211 mData.mSession.mOpenTimeoutMS = ssInfo.mOpenTimeoutMS;
212
213 /* Copy over session credentials. */
214 /** @todo Use an overloaded copy operator. Later. */
215 mData.mCredentials.mUser = guestCreds.mUser;
216 mData.mCredentials.mPassword = guestCreds.mPassword;
217 mData.mCredentials.mDomain = guestCreds.mDomain;
218
219 /* Initialize the remainder of the data. */
220 mData.mRC = VINF_SUCCESS;
221 mData.mStatus = GuestSessionStatus_Undefined;
222 mData.mpBaseEnvironment = NULL;
223
224 /*
225 * Register an object for the session itself to clearly
226 * distinguish callbacks which are for this session directly, or for
227 * objects (like files, directories, ...) which are bound to this session.
228 */
229 int rc = i_objectRegister(SESSIONOBJECTTYPE_SESSION, &mData.mObjectID);
230 if (RT_SUCCESS(rc))
231 {
232 rc = mData.mEnvironmentChanges.initChangeRecord();
233 if (RT_SUCCESS(rc))
234 {
235 rc = RTCritSectInit(&mWaitEventCritSect);
236 AssertRC(rc);
237 }
238 }
239
240 if (RT_SUCCESS(rc))
241 rc = i_determineProtocolVersion();
242
243 if (RT_SUCCESS(rc))
244 {
245 /*
246 * <Replace this if you figure out what the code is doing.>
247 */
248 HRESULT hr = unconst(mEventSource).createObject();
249 if (SUCCEEDED(hr))
250 hr = mEventSource->init();
251 if (SUCCEEDED(hr))
252 {
253 try
254 {
255 GuestSessionListener *pListener = new GuestSessionListener();
256 ComObjPtr<GuestSessionListenerImpl> thisListener;
257 hr = thisListener.createObject();
258 if (SUCCEEDED(hr))
259 hr = thisListener->init(pListener, this); /* thisListener takes ownership of pListener. */
260 if (SUCCEEDED(hr))
261 {
262 com::SafeArray <VBoxEventType_T> eventTypes;
263 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
264 hr = mEventSource->RegisterListener(thisListener,
265 ComSafeArrayAsInParam(eventTypes),
266 TRUE /* Active listener */);
267 if (SUCCEEDED(hr))
268 {
269 mLocalListener = thisListener;
270
271 /*
272 * Mark this object as operational and return success.
273 */
274 autoInitSpan.setSucceeded();
275 LogFlowThisFunc(("mName=%s mID=%RU32 mIsInternal=%RTbool rc=VINF_SUCCESS\n",
276 mData.mSession.mName.c_str(), mData.mSession.mID, mData.mSession.mIsInternal));
277 return VINF_SUCCESS;
278 }
279 }
280 }
281 catch (std::bad_alloc &)
282 {
283 hr = E_OUTOFMEMORY;
284 }
285 }
286 rc = Global::vboxStatusCodeFromCOM(hr);
287 }
288
289 autoInitSpan.setFailed();
290 LogThisFunc(("Failed! mName=%s mID=%RU32 mIsInternal=%RTbool => rc=%Rrc\n",
291 mData.mSession.mName.c_str(), mData.mSession.mID, mData.mSession.mIsInternal, rc));
292 return rc;
293}
294
295/**
296 * Uninitializes the instance.
297 * Called from FinalRelease().
298 */
299void GuestSession::uninit(void)
300{
301 /* Enclose the state transition Ready->InUninit->NotReady. */
302 AutoUninitSpan autoUninitSpan(this);
303 if (autoUninitSpan.uninitDone())
304 return;
305
306 LogFlowThisFuncEnter();
307
308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
309
310 LogFlowThisFunc(("Closing directories (%zu total)\n",
311 mData.mDirectories.size()));
312 for (SessionDirectories::iterator itDirs = mData.mDirectories.begin();
313 itDirs != mData.mDirectories.end(); ++itDirs)
314 {
315 itDirs->second->i_onRemove();
316 itDirs->second->uninit();
317 }
318 mData.mDirectories.clear();
319
320 LogFlowThisFunc(("Closing files (%zu total)\n",
321 mData.mFiles.size()));
322 for (SessionFiles::iterator itFiles = mData.mFiles.begin();
323 itFiles != mData.mFiles.end(); ++itFiles)
324 {
325 itFiles->second->i_onRemove();
326 itFiles->second->uninit();
327 }
328 mData.mFiles.clear();
329
330 LogFlowThisFunc(("Closing processes (%zu total)\n",
331 mData.mProcesses.size()));
332 for (SessionProcesses::iterator itProcs = mData.mProcesses.begin();
333 itProcs != mData.mProcesses.end(); ++itProcs)
334 {
335 itProcs->second->i_onRemove();
336 itProcs->second->uninit();
337 }
338 mData.mProcesses.clear();
339
340 /* Unregister the session's object ID. */
341 i_objectUnregister(mData.mObjectID);
342
343 mData.mObjects.clear();
344
345 mData.mEnvironmentChanges.reset();
346
347 if (mData.mpBaseEnvironment)
348 {
349 mData.mpBaseEnvironment->releaseConst();
350 mData.mpBaseEnvironment = NULL;
351 }
352
353 /* Unitialize our local listener. */
354 mLocalListener.setNull();
355
356 baseUninit();
357
358 LogFlowFuncLeave();
359}
360
361// implementation of public getters/setters for attributes
362/////////////////////////////////////////////////////////////////////////////
363
364HRESULT GuestSession::getUser(com::Utf8Str &aUser)
365{
366 LogFlowThisFuncEnter();
367
368 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
369
370 aUser = mData.mCredentials.mUser;
371
372 LogFlowThisFuncLeave();
373 return S_OK;
374}
375
376HRESULT GuestSession::getDomain(com::Utf8Str &aDomain)
377{
378 LogFlowThisFuncEnter();
379
380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
381
382 aDomain = mData.mCredentials.mDomain;
383
384 LogFlowThisFuncLeave();
385 return S_OK;
386}
387
388HRESULT GuestSession::getName(com::Utf8Str &aName)
389{
390 LogFlowThisFuncEnter();
391
392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
393
394 aName = mData.mSession.mName;
395
396 LogFlowThisFuncLeave();
397 return S_OK;
398}
399
400HRESULT GuestSession::getId(ULONG *aId)
401{
402 LogFlowThisFuncEnter();
403
404 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
405
406 *aId = mData.mSession.mID;
407
408 LogFlowThisFuncLeave();
409 return S_OK;
410}
411
412HRESULT GuestSession::getStatus(GuestSessionStatus_T *aStatus)
413{
414 LogFlowThisFuncEnter();
415
416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
417
418 *aStatus = mData.mStatus;
419
420 LogFlowThisFuncLeave();
421 return S_OK;
422}
423
424HRESULT GuestSession::getTimeout(ULONG *aTimeout)
425{
426 LogFlowThisFuncEnter();
427
428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
429
430 *aTimeout = mData.mTimeout;
431
432 LogFlowThisFuncLeave();
433 return S_OK;
434}
435
436HRESULT GuestSession::setTimeout(ULONG aTimeout)
437{
438 LogFlowThisFuncEnter();
439
440 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
441
442 mData.mTimeout = aTimeout;
443
444 LogFlowThisFuncLeave();
445 return S_OK;
446}
447
448HRESULT GuestSession::getProtocolVersion(ULONG *aProtocolVersion)
449{
450 LogFlowThisFuncEnter();
451
452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
453
454 *aProtocolVersion = mData.mProtocolVersion;
455
456 LogFlowThisFuncLeave();
457 return S_OK;
458}
459
460HRESULT GuestSession::getEnvironmentChanges(std::vector<com::Utf8Str> &aEnvironmentChanges)
461{
462 LogFlowThisFuncEnter();
463
464 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
465
466 int vrc = mData.mEnvironmentChanges.queryPutEnvArray(&aEnvironmentChanges);
467
468 LogFlowFuncLeaveRC(vrc);
469 return Global::vboxStatusCodeToCOM(vrc);
470}
471
472HRESULT GuestSession::setEnvironmentChanges(const std::vector<com::Utf8Str> &aEnvironmentChanges)
473{
474 LogFlowThisFuncEnter();
475
476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
477
478 mData.mEnvironmentChanges.reset();
479 int vrc = mData.mEnvironmentChanges.applyPutEnvArray(aEnvironmentChanges);
480
481 LogFlowFuncLeaveRC(vrc);
482 return Global::vboxStatusCodeToCOM(vrc);
483}
484
485HRESULT GuestSession::getEnvironmentBase(std::vector<com::Utf8Str> &aEnvironmentBase)
486{
487 LogFlowThisFuncEnter();
488
489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
490 HRESULT hrc;
491 if (mData.mpBaseEnvironment)
492 {
493 int vrc = mData.mpBaseEnvironment->queryPutEnvArray(&aEnvironmentBase);
494 hrc = Global::vboxStatusCodeToCOM(vrc);
495 }
496 else if (mData.mProtocolVersion < 99999)
497 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
498 else
499 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
500
501 LogFlowFuncLeave();
502 return hrc;
503}
504
505HRESULT GuestSession::getProcesses(std::vector<ComPtr<IGuestProcess> > &aProcesses)
506{
507 LogFlowThisFuncEnter();
508
509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
510
511 aProcesses.resize(mData.mProcesses.size());
512 size_t i = 0;
513 for(SessionProcesses::iterator it = mData.mProcesses.begin();
514 it != mData.mProcesses.end();
515 ++it, ++i)
516 {
517 it->second.queryInterfaceTo(aProcesses[i].asOutParam());
518 }
519
520 LogFlowFunc(("mProcesses=%zu\n", aProcesses.size()));
521 return S_OK;
522}
523
524HRESULT GuestSession::getPathStyle(PathStyle_T *aPathStyle)
525{
526 *aPathStyle = i_getPathStyle();
527 return S_OK;
528}
529
530HRESULT GuestSession::getCurrentDirectory(com::Utf8Str &aCurrentDirectory)
531{
532 RT_NOREF(aCurrentDirectory);
533 ReturnComNotImplemented();
534}
535
536HRESULT GuestSession::setCurrentDirectory(const com::Utf8Str &aCurrentDirectory)
537{
538 RT_NOREF(aCurrentDirectory);
539 ReturnComNotImplemented();
540}
541
542HRESULT GuestSession::getUserHome(com::Utf8Str &aUserHome)
543{
544 HRESULT hr = i_isReadyExternal();
545 if (FAILED(hr))
546 return hr;
547
548 int rcGuest;
549 int vrc = i_pathUserHome(aUserHome, &rcGuest);
550 if (RT_FAILURE(vrc))
551 {
552 switch (vrc)
553 {
554 case VERR_GSTCTL_GUEST_ERROR:
555 {
556 switch (rcGuest)
557 {
558 case VERR_NOT_SUPPORTED:
559 hr = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
560 tr("Getting the user's home path is not supported by installed Guest Additions"));
561 break;
562
563 default:
564 hr = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
565 tr("Getting the user's home path failed on the guest: %Rrc"), rcGuest);
566 break;
567 }
568 break;
569 }
570
571 default:
572 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Getting the user's home path failed: %Rrc"), vrc);
573 break;
574 }
575 }
576
577 return hr;
578}
579
580HRESULT GuestSession::getUserDocuments(com::Utf8Str &aUserDocuments)
581{
582 HRESULT hr = i_isReadyExternal();
583 if (FAILED(hr))
584 return hr;
585
586 int rcGuest;
587 int vrc = i_pathUserDocuments(aUserDocuments, &rcGuest);
588 if (RT_FAILURE(vrc))
589 {
590 switch (vrc)
591 {
592 case VERR_GSTCTL_GUEST_ERROR:
593 {
594 switch (rcGuest)
595 {
596 case VERR_NOT_SUPPORTED:
597 hr = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
598 tr("Getting the user's documents path is not supported by installed Guest Additions"));
599 break;
600
601 default:
602 hr = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
603 tr("Getting the user's documents path failed on the guest: %Rrc"), rcGuest);
604 break;
605 }
606 break;
607 }
608
609 default:
610 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Getting the user's documents path failed: %Rrc"), vrc);
611 break;
612 }
613 }
614
615 return hr;
616}
617
618HRESULT GuestSession::getDirectories(std::vector<ComPtr<IGuestDirectory> > &aDirectories)
619{
620 LogFlowThisFuncEnter();
621
622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
623
624 aDirectories.resize(mData.mDirectories.size());
625 size_t i = 0;
626 for(SessionDirectories::iterator it = mData.mDirectories.begin();
627 it != mData.mDirectories.end();
628 ++it, ++i)
629 {
630 it->second.queryInterfaceTo(aDirectories[i].asOutParam());
631 }
632
633 LogFlowFunc(("mDirectories=%zu\n", aDirectories.size()));
634 return S_OK;
635}
636
637HRESULT GuestSession::getFiles(std::vector<ComPtr<IGuestFile> > &aFiles)
638{
639 LogFlowThisFuncEnter();
640
641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
642
643 aFiles.resize(mData.mFiles.size());
644 size_t i = 0;
645 for(SessionFiles::iterator it = mData.mFiles.begin(); it != mData.mFiles.end(); ++it, ++i)
646 it->second.queryInterfaceTo(aFiles[i].asOutParam());
647
648 LogFlowFunc(("mDirectories=%zu\n", aFiles.size()));
649
650 return S_OK;
651}
652
653HRESULT GuestSession::getEventSource(ComPtr<IEventSource> &aEventSource)
654{
655 LogFlowThisFuncEnter();
656
657 // no need to lock - lifetime constant
658 mEventSource.queryInterfaceTo(aEventSource.asOutParam());
659
660 LogFlowThisFuncLeave();
661 return S_OK;
662}
663
664// private methods
665///////////////////////////////////////////////////////////////////////////////
666
667int GuestSession::i_closeSession(uint32_t uFlags, uint32_t uTimeoutMS, int *prcGuest)
668{
669 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
670
671 LogFlowThisFunc(("uFlags=%x, uTimeoutMS=%RU32\n", uFlags, uTimeoutMS));
672
673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
674
675 /* Guest Additions < 4.3 don't support closing dedicated
676 guest sessions, skip. */
677 if (mData.mProtocolVersion < 2)
678 {
679 LogFlowThisFunc(("Installed Guest Additions don't support closing dedicated sessions, skipping\n"));
680 return VINF_SUCCESS;
681 }
682
683 /** @todo uFlags validation. */
684
685 if (mData.mStatus != GuestSessionStatus_Started)
686 {
687 LogFlowThisFunc(("Session ID=%RU32 not started (anymore), status now is: %RU32\n",
688 mData.mSession.mID, mData.mStatus));
689 return VINF_SUCCESS;
690 }
691
692 int vrc;
693
694 GuestWaitEvent *pEvent = NULL;
695 GuestEventTypes eventTypes;
696 try
697 {
698 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
699
700 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
701 }
702 catch (std::bad_alloc &)
703 {
704 vrc = VERR_NO_MEMORY;
705 }
706
707 if (RT_FAILURE(vrc))
708 return vrc;
709
710 LogFlowThisFunc(("Sending closing request to guest session ID=%RU32, uFlags=%x\n",
711 mData.mSession.mID, uFlags));
712
713 VBOXHGCMSVCPARM paParms[4];
714 int i = 0;
715 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
716 HGCMSvcSetU32(&paParms[i++], uFlags);
717
718 alock.release(); /* Drop the write lock before waiting. */
719
720 vrc = i_sendMessage(HOST_MSG_SESSION_CLOSE, i, paParms, VBOX_GUESTCTRL_DST_BOTH);
721 if (RT_SUCCESS(vrc))
722 vrc = i_waitForStatusChange(pEvent, GuestSessionWaitForFlag_Terminate, uTimeoutMS,
723 NULL /* Session status */, prcGuest);
724
725 unregisterWaitEvent(pEvent);
726
727 LogFlowFuncLeaveRC(vrc);
728 return vrc;
729}
730
731/**
732 * Internal worker function for public APIs that handle copying elements from
733 * guest to the host.
734 *
735 * @return HRESULT
736 * @param SourceSet Source set specifying what to copy.
737 * @param strDestination Destination path on the host. Host path style.
738 * @param pProgress Progress object returned to the caller.
739 */
740HRESULT GuestSession::i_copyFromGuest(const GuestSessionFsSourceSet &SourceSet,
741 const com::Utf8Str &strDestination, ComPtr<IProgress> &pProgress)
742{
743 HRESULT hrc = i_isReadyExternal();
744 if (FAILED(hrc))
745 return hrc;
746
747 LogFlowThisFuncEnter();
748
749 /* Validate stuff. */
750 if (RT_UNLIKELY(SourceSet.size() == 0 || *(SourceSet[0].strSource.c_str()) == '\0')) /* At least one source must be present. */
751 return setError(E_INVALIDARG, tr("No source(s) specified"));
752 if (RT_UNLIKELY((strDestination.c_str()) == NULL || *(strDestination.c_str()) == '\0'))
753 return setError(E_INVALIDARG, tr("No destination specified"));
754
755 try
756 {
757 GuestSessionTaskCopyFrom *pTask = NULL;
758 ComObjPtr<Progress> pProgressObj;
759 try
760 {
761 pTask = new GuestSessionTaskCopyFrom(this /* GuestSession */, SourceSet, strDestination);
762 }
763 catch(...)
764 {
765 hrc = setError(VBOX_E_IPRT_ERROR, tr("Failed to create SessionTaskCopyFrom object"));
766 throw;
767 }
768
769 hrc = pTask->Init(Utf8StrFmt(tr("Copying to \"%s\" on the host"), strDestination.c_str()));
770 if (FAILED(hrc))
771 {
772 delete pTask;
773 hrc = setError(VBOX_E_IPRT_ERROR,
774 tr("Creating progress object for SessionTaskCopyFrom object failed"));
775 throw hrc;
776 }
777
778 hrc = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
779 if (SUCCEEDED(hrc))
780 {
781 /* Return progress to the caller. */
782 pProgressObj = pTask->GetProgressObject();
783 hrc = pProgressObj.queryInterfaceTo(pProgress.asOutParam());
784 }
785 else
786 hrc = setError(hrc, tr("Starting thread for copying from guest to \"%s\" on the host failed"), strDestination.c_str());
787
788 }
789 catch (std::bad_alloc &)
790 {
791 hrc = E_OUTOFMEMORY;
792 }
793 catch (HRESULT eHR)
794 {
795 hrc = eHR;
796 }
797
798 LogFlowFunc(("Returning %Rhrc\n", hrc));
799 return hrc;
800}
801
802/**
803 * Internal worker function for public APIs that handle copying elements from
804 * host to the guest.
805 *
806 * @return HRESULT
807 * @param SourceSet Source set specifying what to copy.
808 * @param strDestination Destination path on the guest. Guest path style.
809 * @param pProgress Progress object returned to the caller.
810 */
811HRESULT GuestSession::i_copyToGuest(const GuestSessionFsSourceSet &SourceSet,
812 const com::Utf8Str &strDestination, ComPtr<IProgress> &pProgress)
813{
814 HRESULT hrc = i_isReadyExternal();
815 if (FAILED(hrc))
816 return hrc;
817
818 LogFlowThisFuncEnter();
819
820 /* Validate stuff. */
821 if (RT_UNLIKELY(SourceSet.size() == 0 || *(SourceSet[0].strSource.c_str()) == '\0')) /* At least one source must be present. */
822 return setError(E_INVALIDARG, tr("No source(s) specified"));
823 if (RT_UNLIKELY((strDestination.c_str()) == NULL || *(strDestination.c_str()) == '\0'))
824 return setError(E_INVALIDARG, tr("No destination specified"));
825
826 try
827 {
828 GuestSessionTaskCopyTo *pTask = NULL;
829 ComObjPtr<Progress> pProgressObj;
830 try
831 {
832 pTask = new GuestSessionTaskCopyTo(this /* GuestSession */, SourceSet, strDestination);
833 }
834 catch(...)
835 {
836 hrc = setError(VBOX_E_IPRT_ERROR, tr("Failed to create SessionTaskCopyTo object"));
837 throw;
838 }
839
840 hrc = pTask->Init(Utf8StrFmt(tr("Copying to \"%s\" on the guest"), strDestination.c_str()));
841 if (FAILED(hrc))
842 {
843 delete pTask;
844 hrc = setError(VBOX_E_IPRT_ERROR,
845 tr("Creating progress object for SessionTaskCopyTo object failed"));
846 throw hrc;
847 }
848
849 hrc = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
850 if (SUCCEEDED(hrc))
851 {
852 /* Return progress to the caller. */
853 pProgressObj = pTask->GetProgressObject();
854 hrc = pProgressObj.queryInterfaceTo(pProgress.asOutParam());
855 }
856 else
857 hrc = setError(hrc, tr("Starting thread for copying from host to \"%s\" on the guest failed"), strDestination.c_str());
858
859 }
860 catch (std::bad_alloc &)
861 {
862 hrc = E_OUTOFMEMORY;
863 }
864 catch (HRESULT eHR)
865 {
866 hrc = eHR;
867 }
868
869 LogFlowFunc(("Returning %Rhrc\n", hrc));
870 return hrc;
871}
872
873/**
874 * Validates and extracts directory copy flags from a comma-separated string.
875 *
876 * @return COM status, error set on failure
877 * @param strFlags String to extract flags from.
878 * @param pfFlags Where to store the extracted (and validated) flags.
879 */
880HRESULT GuestSession::i_directoryCopyFlagFromStr(const com::Utf8Str &strFlags, DirectoryCopyFlag_T *pfFlags)
881{
882 unsigned fFlags = DirectoryCopyFlag_None;
883
884 /* Validate and set flags. */
885 if (strFlags.isNotEmpty())
886 {
887 const char *pszNext = strFlags.c_str();
888 for (;;)
889 {
890 /* Find the next keyword, ignoring all whitespace. */
891 pszNext = RTStrStripL(pszNext);
892
893 const char * const pszComma = strchr(pszNext, ',');
894 size_t cchKeyword = pszComma ? pszComma - pszNext : strlen(pszNext);
895 while (cchKeyword > 0 && RT_C_IS_SPACE(pszNext[cchKeyword - 1]))
896 cchKeyword--;
897
898 if (cchKeyword > 0)
899 {
900 /* Convert keyword to flag. */
901#define MATCH_KEYWORD(a_szKeyword) ( cchKeyword == sizeof(a_szKeyword) - 1U \
902 && memcmp(pszNext, a_szKeyword, sizeof(a_szKeyword) - 1U) == 0)
903 if (MATCH_KEYWORD("CopyIntoExisting"))
904 fFlags |= (unsigned)DirectoryCopyFlag_CopyIntoExisting;
905 else
906 return setError(E_INVALIDARG, tr("Invalid directory copy flag: %.*s"), (int)cchKeyword, pszNext);
907#undef MATCH_KEYWORD
908 }
909 if (!pszComma)
910 break;
911 pszNext = pszComma + 1;
912 }
913 }
914
915 if (pfFlags)
916 *pfFlags = (DirectoryCopyFlag_T)fFlags;
917 return S_OK;
918}
919
920int GuestSession::i_directoryCreate(const Utf8Str &strPath, uint32_t uMode,
921 uint32_t uFlags, int *prcGuest)
922{
923 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
924
925 LogFlowThisFunc(("strPath=%s, uMode=%x, uFlags=%x\n", strPath.c_str(), uMode, uFlags));
926
927 int vrc = VINF_SUCCESS;
928
929 GuestProcessStartupInfo procInfo;
930 procInfo.mFlags = ProcessCreateFlag_Hidden;
931 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_MKDIR);
932
933 try
934 {
935 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
936
937 /* Construct arguments. */
938 if (uFlags)
939 {
940 if (uFlags & DirectoryCreateFlag_Parents)
941 procInfo.mArguments.push_back(Utf8Str("--parents")); /* We also want to create the parent directories. */
942 else
943 vrc = VERR_INVALID_PARAMETER;
944 }
945
946 if ( RT_SUCCESS(vrc)
947 && uMode)
948 {
949 procInfo.mArguments.push_back(Utf8Str("--mode")); /* Set the creation mode. */
950
951 char szMode[16];
952 if (RTStrPrintf(szMode, sizeof(szMode), "%o", uMode))
953 {
954 procInfo.mArguments.push_back(Utf8Str(szMode));
955 }
956 else
957 vrc = VERR_BUFFER_OVERFLOW;
958 }
959
960 procInfo.mArguments.push_back("--"); /* '--version' is a valid directory name. */
961 procInfo.mArguments.push_back(strPath); /* The directory we want to create. */
962 }
963 catch (std::bad_alloc &)
964 {
965 vrc = VERR_NO_MEMORY;
966 }
967
968 if (RT_SUCCESS(vrc))
969 vrc = GuestProcessTool::run(this, procInfo, prcGuest);
970
971 LogFlowFuncLeaveRC(vrc);
972 return vrc;
973}
974
975inline bool GuestSession::i_directoryExists(uint32_t uDirID, ComObjPtr<GuestDirectory> *pDir)
976{
977 SessionDirectories::const_iterator it = mData.mDirectories.find(uDirID);
978 if (it != mData.mDirectories.end())
979 {
980 if (pDir)
981 *pDir = it->second;
982 return true;
983 }
984 return false;
985}
986
987int GuestSession::i_directoryQueryInfo(const Utf8Str &strPath, bool fFollowSymlinks,
988 GuestFsObjData &objData, int *prcGuest)
989{
990 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
991
992 LogFlowThisFunc(("strPath=%s, fFollowSymlinks=%RTbool\n", strPath.c_str(), fFollowSymlinks));
993
994 int vrc = i_fsQueryInfo(strPath, fFollowSymlinks, objData, prcGuest);
995 if (RT_SUCCESS(vrc))
996 {
997 vrc = objData.mType == FsObjType_Directory
998 ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
999 }
1000
1001 LogFlowFuncLeaveRC(vrc);
1002 return vrc;
1003}
1004
1005/**
1006 * Unregisters a directory object from a session.
1007 *
1008 * @return VBox status code. VERR_NOT_FOUND if the directory is not registered (anymore).
1009 * @param pDirectory Directory object to unregister from session.
1010 */
1011int GuestSession::i_directoryUnregister(GuestDirectory *pDirectory)
1012{
1013 AssertPtrReturn(pDirectory, VERR_INVALID_POINTER);
1014
1015 LogFlowThisFunc(("pDirectory=%p\n", pDirectory));
1016
1017 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1018
1019 const uint32_t idObject = pDirectory->getObjectID();
1020
1021 LogFlowFunc(("Removing directory (objectID=%RU32) ...\n", idObject));
1022
1023 int rc = i_objectUnregister(idObject);
1024 if (RT_FAILURE(rc))
1025 return rc;
1026
1027 SessionDirectories::iterator itDirs = mData.mDirectories.find(idObject);
1028 AssertReturn(itDirs != mData.mDirectories.end(), VERR_NOT_FOUND);
1029
1030 /* Make sure to consume the pointer before the one of the iterator gets released. */
1031 ComObjPtr<GuestDirectory> pDirConsumed = pDirectory;
1032
1033 LogFlowFunc(("Removing directory ID=%RU32 (session %RU32, now total %zu directories)\n",
1034 idObject, mData.mSession.mID, mData.mDirectories.size()));
1035
1036 rc = pDirConsumed->i_onRemove();
1037 AssertRCReturn(rc, rc);
1038
1039 mData.mDirectories.erase(itDirs);
1040
1041 alock.release(); /* Release lock before firing off event. */
1042
1043// fireGuestDirectoryRegisteredEvent(mEventSource, this /* Session */, pDirConsumed, false /* Process unregistered */);
1044
1045 pDirConsumed.setNull();
1046
1047 LogFlowFuncLeaveRC(rc);
1048 return rc;
1049}
1050
1051int GuestSession::i_directoryRemove(const Utf8Str &strPath, uint32_t uFlags, int *prcGuest)
1052{
1053 AssertReturn(!(uFlags & ~DIRREMOVEREC_FLAG_VALID_MASK), VERR_INVALID_PARAMETER);
1054 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
1055
1056 LogFlowThisFunc(("strPath=%s, uFlags=0x%x\n", strPath.c_str(), uFlags));
1057
1058 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1059
1060 GuestWaitEvent *pEvent = NULL;
1061 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
1062 if (RT_FAILURE(vrc))
1063 return vrc;
1064
1065 /* Prepare HGCM call. */
1066 VBOXHGCMSVCPARM paParms[8];
1067 int i = 0;
1068 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1069 HGCMSvcSetPv(&paParms[i++], (void*)strPath.c_str(),
1070 (ULONG)strPath.length() + 1);
1071 HGCMSvcSetU32(&paParms[i++], uFlags);
1072
1073 alock.release(); /* Drop write lock before sending. */
1074
1075 vrc = i_sendMessage(HOST_MSG_DIR_REMOVE, i, paParms);
1076 if (RT_SUCCESS(vrc))
1077 {
1078 vrc = pEvent->Wait(30 * 1000);
1079 if ( vrc == VERR_GSTCTL_GUEST_ERROR
1080 && prcGuest)
1081 *prcGuest = pEvent->GuestResult();
1082 }
1083
1084 unregisterWaitEvent(pEvent);
1085
1086 LogFlowFuncLeaveRC(vrc);
1087 return vrc;
1088}
1089
1090int GuestSession::i_fsCreateTemp(const Utf8Str &strTemplate, const Utf8Str &strPath, bool fDirectory, Utf8Str &strName,
1091 int *prcGuest)
1092{
1093 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
1094
1095 LogFlowThisFunc(("strTemplate=%s, strPath=%s, fDirectory=%RTbool\n",
1096 strTemplate.c_str(), strPath.c_str(), fDirectory));
1097
1098 GuestProcessStartupInfo procInfo;
1099 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
1100 try
1101 {
1102 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_MKTEMP);
1103 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
1104 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1105 if (fDirectory)
1106 procInfo.mArguments.push_back(Utf8Str("-d"));
1107 if (strPath.length()) /* Otherwise use /tmp or equivalent. */
1108 {
1109 procInfo.mArguments.push_back(Utf8Str("-t"));
1110 procInfo.mArguments.push_back(strPath);
1111 }
1112 procInfo.mArguments.push_back("--"); /* strTemplate could be '--help'. */
1113 procInfo.mArguments.push_back(strTemplate);
1114 }
1115 catch (std::bad_alloc &)
1116 {
1117 Log(("Out of memory!\n"));
1118 return VERR_NO_MEMORY;
1119 }
1120
1121 /** @todo Use an internal HGCM command for this operation, since
1122 * we now can run in a user-dedicated session. */
1123 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1124 GuestCtrlStreamObjects stdOut;
1125 int vrc = GuestProcessTool::runEx(this, procInfo, &stdOut, 1 /* cStrmOutObjects */, &vrcGuest);
1126 if (!GuestProcess::i_isGuestError(vrc))
1127 {
1128 GuestFsObjData objData;
1129 if (!stdOut.empty())
1130 {
1131 vrc = objData.FromMkTemp(stdOut.at(0));
1132 if (RT_FAILURE(vrc))
1133 {
1134 vrcGuest = vrc;
1135 if (prcGuest)
1136 *prcGuest = vrc;
1137 vrc = VERR_GSTCTL_GUEST_ERROR;
1138 }
1139 }
1140 else
1141 vrc = VERR_BROKEN_PIPE;
1142
1143 if (RT_SUCCESS(vrc))
1144 strName = objData.mName;
1145 }
1146 else if (prcGuest)
1147 *prcGuest = vrcGuest;
1148
1149 LogFlowThisFunc(("Returning vrc=%Rrc, vrcGuest=%Rrc\n", vrc, vrcGuest));
1150 return vrc;
1151}
1152
1153int GuestSession::i_directoryOpen(const GuestDirectoryOpenInfo &openInfo,
1154 ComObjPtr<GuestDirectory> &pDirectory, int *prcGuest)
1155{
1156 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
1157
1158 LogFlowThisFunc(("strPath=%s, strPath=%s, uFlags=%x\n",
1159 openInfo.mPath.c_str(), openInfo.mFilter.c_str(), openInfo.mFlags));
1160
1161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1162
1163 /* Register a new object ID. */
1164 uint32_t idObject;
1165 int rc = i_objectRegister(SESSIONOBJECTTYPE_DIRECTORY, &idObject);
1166 if (RT_FAILURE(rc))
1167 return rc;
1168
1169 /* Create the directory object. */
1170 HRESULT hr = pDirectory.createObject();
1171 if (FAILED(hr))
1172 return VERR_COM_UNEXPECTED;
1173
1174 Console *pConsole = mParent->i_getConsole();
1175 AssertPtr(pConsole);
1176
1177 int vrc = pDirectory->init(pConsole, this /* Parent */, idObject, openInfo);
1178 if (RT_FAILURE(vrc))
1179 return vrc;
1180
1181 /*
1182 * Since this is a synchronous guest call we have to
1183 * register the file object first, releasing the session's
1184 * lock and then proceed with the actual opening command
1185 * -- otherwise the file's opening callback would hang
1186 * because the session's lock still is in place.
1187 */
1188 try
1189 {
1190 /* Add the created directory to our map. */
1191 mData.mDirectories[idObject] = pDirectory;
1192
1193 LogFlowFunc(("Added new guest directory \"%s\" (Session: %RU32) (now total %zu directories)\n",
1194 openInfo.mPath.c_str(), mData.mSession.mID, mData.mDirectories.size()));
1195
1196 alock.release(); /* Release lock before firing off event. */
1197
1198 /** @todo Fire off a VBoxEventType_OnGuestDirectoryRegistered event? */
1199 }
1200 catch (std::bad_alloc &)
1201 {
1202 rc = VERR_NO_MEMORY;
1203 }
1204
1205 if (RT_SUCCESS(rc))
1206 {
1207 /* Nothing further to do here yet. */
1208 if (prcGuest)
1209 *prcGuest = VINF_SUCCESS;
1210 }
1211
1212 LogFlowFuncLeaveRC(vrc);
1213 return vrc;
1214}
1215
1216/**
1217 * Dispatches a host callback to its corresponding object.
1218 *
1219 * @return VBox status code. VERR_NOT_FOUND if no corresponding object was found.
1220 * @param pCtxCb Host callback context.
1221 * @param pSvcCb Service callback data.
1222 */
1223int GuestSession::i_dispatchToObject(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
1224{
1225 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
1226
1227 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
1228 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
1229
1230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1231
1232 /*
1233 * Find the object.
1234 */
1235 int rc = VERR_NOT_FOUND;
1236 const uint32_t idObject = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID);
1237 SessionObjects::const_iterator itObjs = mData.mObjects.find(idObject);
1238 if (itObjs != mData.mObjects.end())
1239 {
1240 /* Set protocol version so that pSvcCb can be interpreted right. */
1241 pCtxCb->uProtocol = mData.mProtocolVersion;
1242
1243 switch (itObjs->second.enmType)
1244 {
1245 case SESSIONOBJECTTYPE_ANONYMOUS:
1246 rc = VERR_NOT_SUPPORTED;
1247 break;
1248
1249 case SESSIONOBJECTTYPE_SESSION:
1250 {
1251 alock.release();
1252
1253 rc = i_dispatchToThis(pCtxCb, pSvcCb);
1254 break;
1255 }
1256 case SESSIONOBJECTTYPE_DIRECTORY:
1257 {
1258 SessionDirectories::const_iterator itDir = mData.mDirectories.find(idObject);
1259 if (itDir != mData.mDirectories.end())
1260 {
1261 ComObjPtr<GuestDirectory> pDirectory(itDir->second);
1262 Assert(!pDirectory.isNull());
1263
1264 alock.release();
1265
1266 rc = pDirectory->i_callbackDispatcher(pCtxCb, pSvcCb);
1267 }
1268 break;
1269 }
1270 case SESSIONOBJECTTYPE_FILE:
1271 {
1272 SessionFiles::const_iterator itFile = mData.mFiles.find(idObject);
1273 if (itFile != mData.mFiles.end())
1274 {
1275 ComObjPtr<GuestFile> pFile(itFile->second);
1276 Assert(!pFile.isNull());
1277
1278 alock.release();
1279
1280 rc = pFile->i_callbackDispatcher(pCtxCb, pSvcCb);
1281 }
1282 break;
1283 }
1284 case SESSIONOBJECTTYPE_PROCESS:
1285 {
1286 SessionProcesses::const_iterator itProc = mData.mProcesses.find(idObject);
1287 if (itProc != mData.mProcesses.end())
1288 {
1289 ComObjPtr<GuestProcess> pProcess(itProc->second);
1290 Assert(!pProcess.isNull());
1291
1292 alock.release();
1293
1294 rc = pProcess->i_callbackDispatcher(pCtxCb, pSvcCb);
1295 }
1296 break;
1297 }
1298 default:
1299 AssertMsgFailed(("%d\n", itObjs->second.enmType));
1300 rc = VERR_INTERNAL_ERROR_4;
1301 break;
1302 }
1303 }
1304
1305 LogFlowFuncLeaveRC(rc);
1306 return rc;
1307}
1308
1309int GuestSession::i_dispatchToThis(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
1310{
1311 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1312 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
1313
1314 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1315
1316 LogFlowThisFunc(("sessionID=%RU32, CID=%RU32, uMessage=%RU32, pSvcCb=%p\n",
1317 mData.mSession.mID, pCbCtx->uContextID, pCbCtx->uMessage, pSvcCb));
1318 int rc;
1319 switch (pCbCtx->uMessage)
1320 {
1321 case GUEST_MSG_DISCONNECTED:
1322 /** @todo Handle closing all guest objects. */
1323 rc = VERR_INTERNAL_ERROR;
1324 break;
1325
1326 case GUEST_MSG_SESSION_NOTIFY: /* Guest Additions >= 4.3.0. */
1327 {
1328 rc = i_onSessionStatusChange(pCbCtx, pSvcCb);
1329 break;
1330 }
1331
1332 default:
1333 rc = dispatchGeneric(pCbCtx, pSvcCb);
1334 break;
1335 }
1336
1337 LogFlowFuncLeaveRC(rc);
1338 return rc;
1339}
1340
1341/**
1342 * Validates and extracts file copy flags from a comma-separated string.
1343 *
1344 * @return COM status, error set on failure
1345 * @param strFlags String to extract flags from.
1346 * @param pfFlags Where to store the extracted (and validated) flags.
1347 */
1348HRESULT GuestSession::i_fileCopyFlagFromStr(const com::Utf8Str &strFlags, FileCopyFlag_T *pfFlags)
1349{
1350 unsigned fFlags = (unsigned)FileCopyFlag_None;
1351
1352 /* Validate and set flags. */
1353 if (strFlags.isNotEmpty())
1354 {
1355 const char *pszNext = strFlags.c_str();
1356 for (;;)
1357 {
1358 /* Find the next keyword, ignoring all whitespace. */
1359 pszNext = RTStrStripL(pszNext);
1360
1361 const char * const pszComma = strchr(pszNext, ',');
1362 size_t cchKeyword = pszComma ? pszComma - pszNext : strlen(pszNext);
1363 while (cchKeyword > 0 && RT_C_IS_SPACE(pszNext[cchKeyword - 1]))
1364 cchKeyword--;
1365
1366 if (cchKeyword > 0)
1367 {
1368 /* Convert keyword to flag. */
1369#define MATCH_KEYWORD(a_szKeyword) ( cchKeyword == sizeof(a_szKeyword) - 1U \
1370 && memcmp(pszNext, a_szKeyword, sizeof(a_szKeyword) - 1U) == 0)
1371 if (MATCH_KEYWORD("NoReplace"))
1372 fFlags |= (unsigned)FileCopyFlag_NoReplace;
1373 else if (MATCH_KEYWORD("FollowLinks"))
1374 fFlags |= (unsigned)FileCopyFlag_FollowLinks;
1375 else if (MATCH_KEYWORD("Update"))
1376 fFlags |= (unsigned)FileCopyFlag_Update;
1377 else
1378 return setError(E_INVALIDARG, tr("Invalid file copy flag: %.*s"), (int)cchKeyword, pszNext);
1379#undef MATCH_KEYWORD
1380 }
1381 if (!pszComma)
1382 break;
1383 pszNext = pszComma + 1;
1384 }
1385 }
1386
1387 if (pfFlags)
1388 *pfFlags = (FileCopyFlag_T)fFlags;
1389 return S_OK;
1390}
1391
1392inline bool GuestSession::i_fileExists(uint32_t uFileID, ComObjPtr<GuestFile> *pFile)
1393{
1394 SessionFiles::const_iterator it = mData.mFiles.find(uFileID);
1395 if (it != mData.mFiles.end())
1396 {
1397 if (pFile)
1398 *pFile = it->second;
1399 return true;
1400 }
1401 return false;
1402}
1403
1404/**
1405 * Unregisters a file object from a session.
1406 *
1407 * @return VBox status code. VERR_NOT_FOUND if the file is not registered (anymore).
1408 * @param pFile File object to unregister from session.
1409 */
1410int GuestSession::i_fileUnregister(GuestFile *pFile)
1411{
1412 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1413
1414 LogFlowThisFunc(("pFile=%p\n", pFile));
1415
1416 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1417
1418 const uint32_t idObject = pFile->getObjectID();
1419
1420 LogFlowFunc(("Removing file (objectID=%RU32) ...\n", idObject));
1421
1422 int rc = i_objectUnregister(idObject);
1423 if (RT_FAILURE(rc))
1424 return rc;
1425
1426 SessionFiles::iterator itFiles = mData.mFiles.find(idObject);
1427 AssertReturn(itFiles != mData.mFiles.end(), VERR_NOT_FOUND);
1428
1429 /* Make sure to consume the pointer before the one of the iterator gets released. */
1430 ComObjPtr<GuestFile> pFileConsumed = pFile;
1431
1432 LogFlowFunc(("Removing file ID=%RU32 (session %RU32, now total %zu files)\n",
1433 pFileConsumed->getObjectID(), mData.mSession.mID, mData.mFiles.size()));
1434
1435 rc = pFileConsumed->i_onRemove();
1436 AssertRCReturn(rc, rc);
1437
1438 mData.mFiles.erase(itFiles);
1439
1440 alock.release(); /* Release lock before firing off event. */
1441
1442 fireGuestFileRegisteredEvent(mEventSource, this, pFileConsumed, false /* Unregistered */);
1443
1444 pFileConsumed.setNull();
1445
1446 LogFlowFuncLeaveRC(rc);
1447 return rc;
1448}
1449
1450int GuestSession::i_fileRemove(const Utf8Str &strPath, int *prcGuest)
1451{
1452 LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
1453
1454 int vrc = VINF_SUCCESS;
1455
1456 GuestProcessStartupInfo procInfo;
1457 GuestProcessStream streamOut;
1458
1459 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
1460 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_RM);
1461
1462 try
1463 {
1464 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
1465 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1466 procInfo.mArguments.push_back("--"); /* strPath could be '--help', which is a valid filename. */
1467 procInfo.mArguments.push_back(strPath); /* The file we want to remove. */
1468 }
1469 catch (std::bad_alloc &)
1470 {
1471 vrc = VERR_NO_MEMORY;
1472 }
1473
1474 if (RT_SUCCESS(vrc))
1475 vrc = GuestProcessTool::run(this, procInfo, prcGuest);
1476
1477 LogFlowFuncLeaveRC(vrc);
1478 return vrc;
1479}
1480
1481int GuestSession::i_fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
1482 FileSharingMode_T aSharingMode, ULONG aCreationMode, const std::vector<FileOpenExFlag_T> &aFlags,
1483 ComObjPtr<GuestFile> &pFile, int *prcGuest)
1484{
1485 GuestFileOpenInfo openInfo;
1486 RT_ZERO(openInfo);
1487
1488 openInfo.mFilename = aPath;
1489 openInfo.mCreationMode = aCreationMode;
1490 openInfo.mAccessMode = aAccessMode;
1491 openInfo.mOpenAction = aOpenAction;
1492 openInfo.mSharingMode = aSharingMode;
1493
1494 /* Combine and validate flags. */
1495 uint32_t fOpenEx = 0;
1496 for (size_t i = 0; i < aFlags.size(); i++)
1497 fOpenEx = aFlags[i];
1498 if (fOpenEx)
1499 return VERR_INVALID_PARAMETER; /* FileOpenExFlag not implemented yet. */
1500 openInfo.mfOpenEx = fOpenEx;
1501
1502 return i_fileOpen(openInfo, pFile, prcGuest);
1503}
1504
1505int GuestSession::i_fileOpen(const GuestFileOpenInfo &openInfo, ComObjPtr<GuestFile> &pFile, int *prcGuest)
1506{
1507 LogFlowThisFunc(("strFile=%s, enmAccessMode=0x%x, enmOpenAction=0x%x, uCreationMode=%RU32, mfOpenEx=%RU32\n",
1508 openInfo.mFilename.c_str(), openInfo.mAccessMode, openInfo.mOpenAction, openInfo.mCreationMode,
1509 openInfo.mfOpenEx));
1510
1511 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1512
1513 /* Guest Additions < 4.3 don't support handling guest files, skip. */
1514 if (mData.mProtocolVersion < 2)
1515 {
1516 if (prcGuest)
1517 *prcGuest = VERR_NOT_SUPPORTED;
1518 return VERR_GSTCTL_GUEST_ERROR;
1519 }
1520
1521 /* Register a new object ID. */
1522 uint32_t idObject;
1523 int rc = i_objectRegister(SESSIONOBJECTTYPE_FILE, &idObject);
1524 if (RT_FAILURE(rc))
1525 return rc;
1526
1527 /* Create the directory object. */
1528 HRESULT hr = pFile.createObject();
1529 if (FAILED(hr))
1530 return VERR_COM_UNEXPECTED;
1531
1532 Console *pConsole = mParent->i_getConsole();
1533 AssertPtr(pConsole);
1534
1535 rc = pFile->init(pConsole, this /* GuestSession */, idObject, openInfo);
1536 if (RT_FAILURE(rc))
1537 return rc;
1538
1539 /*
1540 * Since this is a synchronous guest call we have to
1541 * register the file object first, releasing the session's
1542 * lock and then proceed with the actual opening command
1543 * -- otherwise the file's opening callback would hang
1544 * because the session's lock still is in place.
1545 */
1546 try
1547 {
1548 /* Add the created file to our vector. */
1549 mData.mFiles[idObject] = pFile;
1550
1551 LogFlowFunc(("Added new guest file \"%s\" (Session: %RU32) (now total %zu files)\n",
1552 openInfo.mFilename.c_str(), mData.mSession.mID, mData.mFiles.size()));
1553
1554 alock.release(); /* Release lock before firing off event. */
1555
1556 fireGuestFileRegisteredEvent(mEventSource, this, pFile, true /* Registered */);
1557 }
1558 catch (std::bad_alloc &)
1559 {
1560 rc = VERR_NO_MEMORY;
1561 }
1562
1563 if (RT_SUCCESS(rc))
1564 {
1565 int rcGuest;
1566 rc = pFile->i_openFile(30 * 1000 /* 30s timeout */, &rcGuest);
1567 if ( rc == VERR_GSTCTL_GUEST_ERROR
1568 && prcGuest)
1569 {
1570 *prcGuest = rcGuest;
1571 }
1572 }
1573
1574 LogFlowFuncLeaveRC(rc);
1575 return rc;
1576}
1577
1578int GuestSession::i_fileQueryInfo(const Utf8Str &strPath, bool fFollowSymlinks, GuestFsObjData &objData, int *prcGuest)
1579{
1580 LogFlowThisFunc(("strPath=%s fFollowSymlinks=%RTbool\n", strPath.c_str(), fFollowSymlinks));
1581
1582 int vrc = i_fsQueryInfo(strPath, fFollowSymlinks, objData, prcGuest);
1583 if (RT_SUCCESS(vrc))
1584 {
1585 vrc = objData.mType == FsObjType_File
1586 ? VINF_SUCCESS : VERR_NOT_A_FILE;
1587 }
1588
1589 LogFlowFuncLeaveRC(vrc);
1590 return vrc;
1591}
1592
1593int GuestSession::i_fileQuerySize(const Utf8Str &strPath, bool fFollowSymlinks, int64_t *pllSize, int *prcGuest)
1594{
1595 AssertPtrReturn(pllSize, VERR_INVALID_POINTER);
1596
1597 GuestFsObjData objData;
1598 int vrc = i_fileQueryInfo(strPath, fFollowSymlinks, objData, prcGuest);
1599 if (RT_SUCCESS(vrc))
1600 *pllSize = objData.mObjectSize;
1601
1602 return vrc;
1603}
1604
1605/**
1606 * Queries information of a file system object (file, directory, ...).
1607 *
1608 * @return IPRT status code.
1609 * @param strPath Path to file system object to query information for.
1610 * @param fFollowSymlinks Whether to follow symbolic links or not.
1611 * @param objData Where to return the file system object data, if found.
1612 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
1613 * Any other return code indicates some host side error.
1614 */
1615int GuestSession::i_fsQueryInfo(const Utf8Str &strPath, bool fFollowSymlinks, GuestFsObjData &objData, int *prcGuest)
1616{
1617 LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
1618
1619 /** @todo Merge this with IGuestFile::queryInfo(). */
1620 GuestProcessStartupInfo procInfo;
1621 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
1622 try
1623 {
1624 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_STAT);
1625 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
1626 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1627 if (fFollowSymlinks)
1628 procInfo.mArguments.push_back(Utf8Str("-L"));
1629 procInfo.mArguments.push_back("--"); /* strPath could be '--help', which is a valid filename. */
1630 procInfo.mArguments.push_back(strPath);
1631 }
1632 catch (std::bad_alloc &)
1633 {
1634 Log(("Out of memory!\n"));
1635 return VERR_NO_MEMORY;
1636 }
1637
1638 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1639 GuestCtrlStreamObjects stdOut;
1640 int vrc = GuestProcessTool::runEx(this, procInfo,
1641 &stdOut, 1 /* cStrmOutObjects */,
1642 &vrcGuest);
1643 if (!GuestProcess::i_isGuestError(vrc))
1644 {
1645 if (!stdOut.empty())
1646 {
1647 vrc = objData.FromStat(stdOut.at(0));
1648 if (RT_FAILURE(vrc))
1649 {
1650 vrcGuest = vrc;
1651 if (prcGuest)
1652 *prcGuest = vrc;
1653 vrc = VERR_GSTCTL_GUEST_ERROR;
1654 }
1655 }
1656 else
1657 vrc = VERR_BROKEN_PIPE;
1658 }
1659 else if (prcGuest)
1660 *prcGuest = vrcGuest;
1661
1662 LogFlowThisFunc(("Returning vrc=%Rrc, vrcGuest=%Rrc\n", vrc, vrcGuest));
1663 return vrc;
1664}
1665
1666const GuestCredentials& GuestSession::i_getCredentials(void)
1667{
1668 return mData.mCredentials;
1669}
1670
1671Utf8Str GuestSession::i_getName(void)
1672{
1673 return mData.mSession.mName;
1674}
1675
1676/* static */
1677Utf8Str GuestSession::i_guestErrorToString(int rcGuest)
1678{
1679 Utf8Str strError;
1680
1681 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
1682 switch (rcGuest)
1683 {
1684 case VERR_INVALID_VM_HANDLE:
1685 strError += Utf8StrFmt(tr("VMM device is not available (is the VM running?)"));
1686 break;
1687
1688 case VERR_HGCM_SERVICE_NOT_FOUND:
1689 strError += Utf8StrFmt(tr("The guest execution service is not available"));
1690 break;
1691
1692 case VERR_ACCOUNT_RESTRICTED:
1693 strError += Utf8StrFmt(tr("The specified user account on the guest is restricted and can't be used to logon"));
1694 break;
1695
1696 case VERR_AUTHENTICATION_FAILURE:
1697 strError += Utf8StrFmt(tr("The specified user was not able to logon on guest"));
1698 break;
1699
1700 case VERR_TIMEOUT:
1701 strError += Utf8StrFmt(tr("The guest did not respond within time"));
1702 break;
1703
1704 case VERR_CANCELLED:
1705 strError += Utf8StrFmt(tr("The session operation was canceled"));
1706 break;
1707
1708 case VERR_GSTCTL_MAX_OBJECTS_REACHED:
1709 strError += Utf8StrFmt(tr("Maximum number of concurrent guest processes has been reached"));
1710 break;
1711
1712 case VERR_NOT_FOUND:
1713 strError += Utf8StrFmt(tr("The guest execution service is not ready (yet)"));
1714 break;
1715
1716 default:
1717 strError += Utf8StrFmt("%Rrc", rcGuest);
1718 break;
1719 }
1720
1721 return strError;
1722}
1723
1724/**
1725 * Checks if this session is ready state where it can handle
1726 * all session-bound actions (like guest processes, guest files).
1727 * Only used by official API methods. Will set an external
1728 * error when not ready.
1729 */
1730HRESULT GuestSession::i_isReadyExternal(void)
1731{
1732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1733
1734 /** @todo Be a bit more informative. */
1735 if (mData.mStatus != GuestSessionStatus_Started)
1736 return setError(E_UNEXPECTED, tr("Session is not in started state"));
1737
1738 return S_OK;
1739}
1740
1741/**
1742 * Called by IGuest right before this session gets removed from
1743 * the public session list.
1744 */
1745int GuestSession::i_onRemove(void)
1746{
1747 LogFlowThisFuncEnter();
1748
1749 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1750
1751 int vrc = VINF_SUCCESS;
1752
1753 /*
1754 * Note: The event source stuff holds references to this object,
1755 * so make sure that this is cleaned up *before* calling uninit.
1756 */
1757 if (!mEventSource.isNull())
1758 {
1759 mEventSource->UnregisterListener(mLocalListener);
1760
1761 mLocalListener.setNull();
1762 unconst(mEventSource).setNull();
1763 }
1764
1765 LogFlowFuncLeaveRC(vrc);
1766 return vrc;
1767}
1768
1769/** No locking! */
1770int GuestSession::i_onSessionStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
1771{
1772 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1773 /* pCallback is optional. */
1774 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
1775
1776 if (pSvcCbData->mParms < 3)
1777 return VERR_INVALID_PARAMETER;
1778
1779 CALLBACKDATA_SESSION_NOTIFY dataCb;
1780 /* pSvcCb->mpaParms[0] always contains the context ID. */
1781 int vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[1], &dataCb.uType);
1782 AssertRCReturn(vrc, vrc);
1783 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[2], &dataCb.uResult);
1784 AssertRCReturn(vrc, vrc);
1785
1786 LogFlowThisFunc(("ID=%RU32, uType=%RU32, rcGuest=%Rrc\n",
1787 mData.mSession.mID, dataCb.uType, dataCb.uResult));
1788
1789 GuestSessionStatus_T sessionStatus = GuestSessionStatus_Undefined;
1790
1791 int rcGuest = dataCb.uResult; /** @todo uint32_t vs. int. */
1792 switch (dataCb.uType)
1793 {
1794 case GUEST_SESSION_NOTIFYTYPE_ERROR:
1795 sessionStatus = GuestSessionStatus_Error;
1796 break;
1797
1798 case GUEST_SESSION_NOTIFYTYPE_STARTED:
1799 sessionStatus = GuestSessionStatus_Started;
1800#if 0 /** @todo If we get some environment stuff along with this kind notification: */
1801 const char *pszzEnvBlock = ...;
1802 uint32_t cbEnvBlock = ...;
1803 if (!mData.mpBaseEnvironment)
1804 {
1805 GuestEnvironment *pBaseEnv;
1806 try { pBaseEnv = new GuestEnvironment(); } catch (std::bad_alloc &) { pBaseEnv = NULL; }
1807 if (pBaseEnv)
1808 {
1809 int vrc = pBaseEnv->initNormal();
1810 if (RT_SUCCESS(vrc))
1811 vrc = pBaseEnv->copyUtf8Block(pszzEnvBlock, cbEnvBlock);
1812 if (RT_SUCCESS(vrc))
1813 mData.mpBaseEnvironment = pBaseEnv;
1814 else
1815 pBaseEnv->release();
1816 }
1817 }
1818#endif
1819 break;
1820
1821 case GUEST_SESSION_NOTIFYTYPE_TEN:
1822 case GUEST_SESSION_NOTIFYTYPE_TES:
1823 case GUEST_SESSION_NOTIFYTYPE_TEA:
1824 sessionStatus = GuestSessionStatus_Terminated;
1825 break;
1826
1827 case GUEST_SESSION_NOTIFYTYPE_TOK:
1828 sessionStatus = GuestSessionStatus_TimedOutKilled;
1829 break;
1830
1831 case GUEST_SESSION_NOTIFYTYPE_TOA:
1832 sessionStatus = GuestSessionStatus_TimedOutAbnormally;
1833 break;
1834
1835 case GUEST_SESSION_NOTIFYTYPE_DWN:
1836 sessionStatus = GuestSessionStatus_Down;
1837 break;
1838
1839 case GUEST_SESSION_NOTIFYTYPE_UNDEFINED:
1840 default:
1841 vrc = VERR_NOT_SUPPORTED;
1842 break;
1843 }
1844
1845 if (RT_SUCCESS(vrc))
1846 {
1847 if (RT_FAILURE(rcGuest))
1848 sessionStatus = GuestSessionStatus_Error;
1849 }
1850
1851 /* Set the session status. */
1852 if (RT_SUCCESS(vrc))
1853 vrc = i_setSessionStatus(sessionStatus, rcGuest);
1854
1855 LogFlowThisFunc(("ID=%RU32, rcGuest=%Rrc\n", mData.mSession.mID, rcGuest));
1856
1857 LogFlowFuncLeaveRC(vrc);
1858 return vrc;
1859}
1860
1861PathStyle_T GuestSession::i_getPathStyle(void)
1862{
1863 PathStyle_T enmPathStyle;
1864
1865 VBOXOSTYPE enmOsType = mParent->i_getGuestOSType();
1866 if (enmOsType < VBOXOSTYPE_DOS)
1867 {
1868 LogFlowFunc(("returns PathStyle_Unknown\n"));
1869 enmPathStyle = PathStyle_Unknown;
1870 }
1871 else if (enmOsType < VBOXOSTYPE_Linux)
1872 {
1873 LogFlowFunc(("returns PathStyle_DOS\n"));
1874 enmPathStyle = PathStyle_DOS;
1875 }
1876 else
1877 {
1878 LogFlowFunc(("returns PathStyle_UNIX\n"));
1879 enmPathStyle = PathStyle_UNIX;
1880 }
1881
1882 return enmPathStyle;
1883}
1884
1885int GuestSession::i_startSession(int *prcGuest)
1886{
1887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1888
1889 LogFlowThisFunc(("mID=%RU32, mName=%s, uProtocolVersion=%RU32, openFlags=%x, openTimeoutMS=%RU32\n",
1890 mData.mSession.mID, mData.mSession.mName.c_str(), mData.mProtocolVersion,
1891 mData.mSession.mOpenFlags, mData.mSession.mOpenTimeoutMS));
1892
1893 /* Guest Additions < 4.3 don't support opening dedicated
1894 guest sessions. Simply return success here. */
1895 if (mData.mProtocolVersion < 2)
1896 {
1897 mData.mStatus = GuestSessionStatus_Started;
1898
1899 LogFlowThisFunc(("Installed Guest Additions don't support opening dedicated sessions, skipping\n"));
1900 return VINF_SUCCESS;
1901 }
1902
1903 if (mData.mStatus != GuestSessionStatus_Undefined)
1904 return VINF_SUCCESS;
1905
1906 /** @todo mData.mSession.uFlags validation. */
1907
1908 /* Set current session status. */
1909 mData.mStatus = GuestSessionStatus_Starting;
1910 mData.mRC = VINF_SUCCESS; /* Clear previous error, if any. */
1911
1912 int vrc;
1913
1914 GuestWaitEvent *pEvent = NULL;
1915 GuestEventTypes eventTypes;
1916 try
1917 {
1918 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
1919
1920 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
1921 }
1922 catch (std::bad_alloc &)
1923 {
1924 vrc = VERR_NO_MEMORY;
1925 }
1926
1927 if (RT_FAILURE(vrc))
1928 return vrc;
1929
1930 VBOXHGCMSVCPARM paParms[8];
1931
1932 int i = 0;
1933 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1934 HGCMSvcSetU32(&paParms[i++], mData.mProtocolVersion);
1935 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mUser.c_str(),
1936 (ULONG)mData.mCredentials.mUser.length() + 1);
1937 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mPassword.c_str(),
1938 (ULONG)mData.mCredentials.mPassword.length() + 1);
1939 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mDomain.c_str(),
1940 (ULONG)mData.mCredentials.mDomain.length() + 1);
1941 HGCMSvcSetU32(&paParms[i++], mData.mSession.mOpenFlags);
1942
1943 alock.release(); /* Drop write lock before sending. */
1944
1945 vrc = i_sendMessage(HOST_MSG_SESSION_CREATE, i, paParms, VBOX_GUESTCTRL_DST_ROOT_SVC);
1946 if (RT_SUCCESS(vrc))
1947 {
1948 vrc = i_waitForStatusChange(pEvent, GuestSessionWaitForFlag_Start,
1949 30 * 1000 /* 30s timeout */,
1950 NULL /* Session status */, prcGuest);
1951 }
1952 else
1953 {
1954 /*
1955 * Unable to start guest session - update its current state.
1956 * Since there is no (official API) way to recover a failed guest session
1957 * this also marks the end state. Internally just calling this
1958 * same function again will work though.
1959 */
1960 mData.mStatus = GuestSessionStatus_Error;
1961 mData.mRC = vrc;
1962 }
1963
1964 unregisterWaitEvent(pEvent);
1965
1966 LogFlowFuncLeaveRC(vrc);
1967 return vrc;
1968}
1969
1970/**
1971 * Starts the guest session asynchronously in a separate thread.
1972 *
1973 * @returns IPRT status code.
1974 */
1975int GuestSession::i_startSessionAsync(void)
1976{
1977 LogFlowThisFuncEnter();
1978
1979 int vrc;
1980 GuestSessionTaskInternalStart* pTask = NULL;
1981 try
1982 {
1983 pTask = new GuestSessionTaskInternalStart(this);
1984 if (!pTask->isOk())
1985 {
1986 delete pTask;
1987 LogFlow(("GuestSession: Could not create GuestSessionTaskInternalStart object\n"));
1988 throw VERR_MEMOBJ_INIT_FAILED;
1989 }
1990
1991 /* Asynchronously open the session on the guest by kicking off a worker thread. */
1992 /* Note: This function deletes pTask in case of exceptions, so there is no need in the call of delete operator. */
1993 HRESULT hrc = pTask->createThread();
1994 vrc = Global::vboxStatusCodeFromCOM(hrc);
1995 }
1996 catch(std::bad_alloc &)
1997 {
1998 vrc = VERR_NO_MEMORY;
1999 }
2000 catch(int eVRC)
2001 {
2002 vrc = eVRC;
2003 LogFlow(("GuestSession: Could not create thread for GuestSessionTaskInternalOpen task %Rrc\n", vrc));
2004 }
2005
2006 LogFlowFuncLeaveRC(vrc);
2007 return vrc;
2008}
2009
2010/**
2011 * Static function to start a guest session asynchronously.
2012 *
2013 * @returns IPRT status code.
2014 * @param pTask Task object to use for starting the guest session.
2015 */
2016/* static */
2017int GuestSession::i_startSessionThreadTask(GuestSessionTaskInternalStart *pTask)
2018{
2019 LogFlowFunc(("pTask=%p\n", pTask));
2020 AssertPtr(pTask);
2021
2022 const ComObjPtr<GuestSession> pSession(pTask->Session());
2023 Assert(!pSession.isNull());
2024
2025 AutoCaller autoCaller(pSession);
2026 if (FAILED(autoCaller.rc()))
2027 return VERR_COM_INVALID_OBJECT_STATE;
2028
2029 int vrc = pSession->i_startSession(NULL /* Guest rc, ignored */);
2030 /* Nothing to do here anymore. */
2031
2032 LogFlowFuncLeaveRC(vrc);
2033 return vrc;
2034}
2035
2036/**
2037 * Registers an object with the session, i.e. allocates an object ID.
2038 *
2039 * @return VBox status code.
2040 * @retval VERR_GSTCTL_MAX_OBJECTS_REACHED if the maximum of concurrent objects
2041 * is reached.
2042 * @param enmType Session object type to register.
2043 * @param pidObject Where to return the object ID on success.
2044 */
2045int GuestSession::i_objectRegister(SESSIONOBJECTTYPE enmType, uint32_t *pidObject)
2046{
2047 /*
2048 * Pick a random bit as starting point. If it's in use, search forward
2049 * for a free one, wrapping around. We've reserved both the zero'th and
2050 * max-1 IDs (see Data constructor).
2051 */
2052 uint32_t idObject = RTRandU32Ex(1, VBOX_GUESTCTRL_MAX_OBJECTS - 2);
2053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2054 if (!ASMBitTestAndSet(&mData.bmObjectIds[0], idObject))
2055 { /* likely */ }
2056 else if (mData.mObjects.size() < VBOX_GUESTCTRL_MAX_OBJECTS - 2 /* First and last are not used */)
2057 {
2058 /* Forward search. */
2059 int iHit = ASMBitNextClear(&mData.bmObjectIds[0], VBOX_GUESTCTRL_MAX_OBJECTS, idObject);
2060 if (iHit < 0)
2061 iHit = ASMBitFirstClear(&mData.bmObjectIds[0], VBOX_GUESTCTRL_MAX_OBJECTS);
2062 AssertLogRelMsgReturn(iHit >= 0, ("object count: %#zu\n", mData.mObjects.size()), VERR_GSTCTL_MAX_OBJECTS_REACHED);
2063 idObject = iHit;
2064 AssertLogRelMsgReturn(!ASMBitTestAndSet(&mData.bmObjectIds[0], idObject), ("idObject=%#x\n", idObject), VERR_INTERNAL_ERROR_2);
2065 }
2066 else
2067 {
2068 LogFunc(("enmType=%RU32 -> VERR_GSTCTL_MAX_OBJECTS_REACHED!! (%zu objects)\n", enmType, mData.mObjects.size()));
2069 return VERR_GSTCTL_MAX_OBJECTS_REACHED;
2070 }
2071
2072 Log2Func(("enmType=%RU32 -> idObject=%RU32 (%zu objects)\n", enmType, idObject, mData.mObjects.size()));
2073
2074 try
2075 {
2076 mData.mObjects[idObject].enmType = enmType;
2077 mData.mObjects[idObject].msBirth = RTTimeMilliTS();
2078 }
2079 catch (std::bad_alloc &)
2080 {
2081 ASMBitClear(&mData.bmObjectIds[0], idObject);
2082 return VERR_NO_MEMORY;
2083 }
2084
2085 alock.release();
2086
2087 *pidObject = idObject;
2088 return VINF_SUCCESS;
2089}
2090
2091/**
2092 * Unregisters an object from a session.
2093 *
2094 * @retval VINF_SUCCESS on success.
2095 * @retval VERR_NOT_FOUND if the object ID was not found.
2096 * @param idObject Object ID to unregister.
2097 */
2098int GuestSession::i_objectUnregister(uint32_t idObject)
2099{
2100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2101
2102 int rc = VINF_SUCCESS;
2103 AssertMsgStmt(ASMBitTestAndClear(&mData.bmObjectIds, idObject), ("idObject=%#x\n", idObject), rc = VERR_NOT_FOUND);
2104
2105 SessionObjects::iterator ItObj = mData.mObjects.find(idObject);
2106 AssertMsgReturn(ItObj != mData.mObjects.end(), ("idObject=%#x\n", idObject), VERR_NOT_FOUND);
2107 mData.mObjects.erase(ItObj);
2108
2109 return rc;
2110}
2111
2112int GuestSession::i_pathRename(const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags, int *prcGuest)
2113{
2114 AssertReturn(!(uFlags & ~PATHRENAME_FLAG_VALID_MASK), VERR_INVALID_PARAMETER);
2115
2116 LogFlowThisFunc(("strSource=%s, strDest=%s, uFlags=0x%x\n",
2117 strSource.c_str(), strDest.c_str(), uFlags));
2118
2119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2120
2121 GuestWaitEvent *pEvent = NULL;
2122 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2123 if (RT_FAILURE(vrc))
2124 return vrc;
2125
2126 /* Prepare HGCM call. */
2127 VBOXHGCMSVCPARM paParms[8];
2128 int i = 0;
2129 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2130 HGCMSvcSetPv(&paParms[i++], (void*)strSource.c_str(),
2131 (ULONG)strSource.length() + 1);
2132 HGCMSvcSetPv(&paParms[i++], (void*)strDest.c_str(),
2133 (ULONG)strDest.length() + 1);
2134 HGCMSvcSetU32(&paParms[i++], uFlags);
2135
2136 alock.release(); /* Drop write lock before sending. */
2137
2138 vrc = i_sendMessage(HOST_MSG_PATH_RENAME, i, paParms);
2139 if (RT_SUCCESS(vrc))
2140 {
2141 vrc = pEvent->Wait(30 * 1000);
2142 if ( vrc == VERR_GSTCTL_GUEST_ERROR
2143 && prcGuest)
2144 *prcGuest = pEvent->GuestResult();
2145 }
2146
2147 unregisterWaitEvent(pEvent);
2148
2149 LogFlowFuncLeaveRC(vrc);
2150 return vrc;
2151}
2152
2153/**
2154 * Returns the user's absolute documents path, if any.
2155 *
2156 * @return VBox status code.
2157 * @param strPath Where to store the user's document path.
2158 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
2159 * Any other return code indicates some host side error.
2160 */
2161int GuestSession::i_pathUserDocuments(Utf8Str &strPath, int *prcGuest)
2162{
2163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2164
2165 /** @todo Cache the user's document path? */
2166
2167 GuestWaitEvent *pEvent = NULL;
2168 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2169 if (RT_FAILURE(vrc))
2170 return vrc;
2171
2172 /* Prepare HGCM call. */
2173 VBOXHGCMSVCPARM paParms[2];
2174 int i = 0;
2175 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2176
2177 alock.release(); /* Drop write lock before sending. */
2178
2179 vrc = i_sendMessage(HOST_MSG_PATH_USER_DOCUMENTS, i, paParms);
2180 if (RT_SUCCESS(vrc))
2181 {
2182 vrc = pEvent->Wait(30 * 1000);
2183 if (RT_SUCCESS(vrc))
2184 {
2185 strPath = pEvent->Payload().ToString();
2186 }
2187 else
2188 {
2189 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2190 {
2191 if (prcGuest)
2192 *prcGuest = pEvent->GuestResult();
2193 }
2194 }
2195 }
2196
2197 unregisterWaitEvent(pEvent);
2198
2199 LogFlowFuncLeaveRC(vrc);
2200 return vrc;
2201}
2202
2203/**
2204 * Returns the user's absolute home path, if any.
2205 *
2206 * @return VBox status code.
2207 * @param strPath Where to store the user's home path.
2208 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
2209 * Any other return code indicates some host side error.
2210 */
2211int GuestSession::i_pathUserHome(Utf8Str &strPath, int *prcGuest)
2212{
2213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2214
2215 /** @todo Cache the user's home path? */
2216
2217 GuestWaitEvent *pEvent = NULL;
2218 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2219 if (RT_FAILURE(vrc))
2220 return vrc;
2221
2222 /* Prepare HGCM call. */
2223 VBOXHGCMSVCPARM paParms[2];
2224 int i = 0;
2225 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2226
2227 alock.release(); /* Drop write lock before sending. */
2228
2229 vrc = i_sendMessage(HOST_MSG_PATH_USER_HOME, i, paParms);
2230 if (RT_SUCCESS(vrc))
2231 {
2232 vrc = pEvent->Wait(30 * 1000);
2233 if (RT_SUCCESS(vrc))
2234 {
2235 strPath = pEvent->Payload().ToString();
2236 }
2237 else
2238 {
2239 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2240 {
2241 if (prcGuest)
2242 *prcGuest = pEvent->GuestResult();
2243 }
2244 }
2245 }
2246
2247 unregisterWaitEvent(pEvent);
2248
2249 LogFlowFuncLeaveRC(vrc);
2250 return vrc;
2251}
2252
2253/**
2254 * Unregisters a process object from a session.
2255 *
2256 * @return VBox status code. VERR_NOT_FOUND if the process is not registered (anymore).
2257 * @param pProcess Process object to unregister from session.
2258 */
2259int GuestSession::i_processUnregister(GuestProcess *pProcess)
2260{
2261 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
2262
2263 LogFlowThisFunc(("pProcess=%p\n", pProcess));
2264
2265 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2266
2267 const uint32_t idObject = pProcess->getObjectID();
2268
2269 LogFlowFunc(("Removing process (objectID=%RU32) ...\n", idObject));
2270
2271 int rc = i_objectUnregister(idObject);
2272 if (RT_FAILURE(rc))
2273 return rc;
2274
2275 SessionProcesses::iterator itProcs = mData.mProcesses.find(idObject);
2276 AssertReturn(itProcs != mData.mProcesses.end(), VERR_NOT_FOUND);
2277
2278 /* Make sure to consume the pointer before the one of the iterator gets released. */
2279 ComObjPtr<GuestProcess> pProc = pProcess;
2280
2281 ULONG uPID;
2282 HRESULT hr = pProc->COMGETTER(PID)(&uPID);
2283 ComAssertComRC(hr);
2284
2285 LogFlowFunc(("Removing process ID=%RU32 (session %RU32, guest PID %RU32, now total %zu processes)\n",
2286 idObject, mData.mSession.mID, uPID, mData.mProcesses.size()));
2287
2288 rc = pProcess->i_onRemove();
2289 AssertRCReturn(rc, rc);
2290
2291 mData.mProcesses.erase(itProcs);
2292
2293 alock.release(); /* Release lock before firing off event. */
2294
2295 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProc, uPID, false /* Process unregistered */);
2296
2297 pProc.setNull();
2298
2299 LogFlowFuncLeaveRC(rc);
2300 return rc;
2301}
2302
2303/**
2304 * Creates but does *not* start the process yet.
2305 *
2306 * See GuestProcess::startProcess() or GuestProcess::startProcessAsync() for
2307 * starting the process.
2308 *
2309 * @return IPRT status code.
2310 * @param procInfo
2311 * @param pProcess
2312 */
2313int GuestSession::i_processCreateEx(GuestProcessStartupInfo &procInfo, ComObjPtr<GuestProcess> &pProcess)
2314{
2315 LogFlowFunc(("mExe=%s, mFlags=%x, mTimeoutMS=%RU32\n",
2316 procInfo.mExecutable.c_str(), procInfo.mFlags, procInfo.mTimeoutMS));
2317#ifdef DEBUG
2318 if (procInfo.mArguments.size())
2319 {
2320 LogFlowFunc(("Arguments:"));
2321 ProcessArguments::const_iterator it = procInfo.mArguments.begin();
2322 while (it != procInfo.mArguments.end())
2323 {
2324 LogFlow((" %s", (*it).c_str()));
2325 ++it;
2326 }
2327 LogFlow(("\n"));
2328 }
2329#endif
2330
2331 /* Validate flags. */
2332 if (procInfo.mFlags)
2333 {
2334 if ( !(procInfo.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
2335 && !(procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
2336 && !(procInfo.mFlags & ProcessCreateFlag_Hidden)
2337 && !(procInfo.mFlags & ProcessCreateFlag_Profile)
2338 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2339 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdErr))
2340 {
2341 return VERR_INVALID_PARAMETER;
2342 }
2343 }
2344
2345 if ( (procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
2346 && ( (procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2347 || (procInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
2348 )
2349 )
2350 {
2351 return VERR_INVALID_PARAMETER;
2352 }
2353
2354 /* Adjust timeout.
2355 * If set to 0, we define an infinite timeout (unlimited process run time). */
2356 if (procInfo.mTimeoutMS == 0)
2357 procInfo.mTimeoutMS = UINT32_MAX;
2358
2359 /** @todo Implement process priority + affinity. */
2360
2361 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2362
2363 /* Register a new object ID. */
2364 uint32_t idObject;
2365 int rc = i_objectRegister(SESSIONOBJECTTYPE_PROCESS, &idObject);
2366 if (RT_FAILURE(rc))
2367 return rc;
2368
2369 /* Create the process object. */
2370 HRESULT hr = pProcess.createObject();
2371 if (FAILED(hr))
2372 return VERR_COM_UNEXPECTED;
2373
2374 rc = pProcess->init(mParent->i_getConsole() /* Console */, this /* Session */, idObject,
2375 procInfo, mData.mpBaseEnvironment);
2376 if (RT_FAILURE(rc))
2377 return rc;
2378
2379 /* Add the created process to our map. */
2380 try
2381 {
2382 mData.mProcesses[idObject] = pProcess;
2383
2384 LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %zu processes)\n",
2385 mData.mSession.mID, idObject, mData.mProcesses.size()));
2386
2387 alock.release(); /* Release lock before firing off event. */
2388
2389 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProcess, 0 /* PID */, true /* Process registered */);
2390 }
2391 catch (std::bad_alloc &)
2392 {
2393 rc = VERR_NO_MEMORY;
2394 }
2395
2396 return rc;
2397}
2398
2399inline bool GuestSession::i_processExists(uint32_t uProcessID, ComObjPtr<GuestProcess> *pProcess)
2400{
2401 SessionProcesses::const_iterator it = mData.mProcesses.find(uProcessID);
2402 if (it != mData.mProcesses.end())
2403 {
2404 if (pProcess)
2405 *pProcess = it->second;
2406 return true;
2407 }
2408 return false;
2409}
2410
2411inline int GuestSession::i_processGetByPID(ULONG uPID, ComObjPtr<GuestProcess> *pProcess)
2412{
2413 AssertReturn(uPID, false);
2414 /* pProcess is optional. */
2415
2416 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
2417 for (; itProcs != mData.mProcesses.end(); ++itProcs)
2418 {
2419 ComObjPtr<GuestProcess> pCurProc = itProcs->second;
2420 AutoCaller procCaller(pCurProc);
2421 if (procCaller.rc())
2422 return VERR_COM_INVALID_OBJECT_STATE;
2423
2424 ULONG uCurPID;
2425 HRESULT hr = pCurProc->COMGETTER(PID)(&uCurPID);
2426 ComAssertComRC(hr);
2427
2428 if (uCurPID == uPID)
2429 {
2430 if (pProcess)
2431 *pProcess = pCurProc;
2432 return VINF_SUCCESS;
2433 }
2434 }
2435
2436 return VERR_NOT_FOUND;
2437}
2438
2439int GuestSession::i_sendMessage(uint32_t uMessage, uint32_t uParms, PVBOXHGCMSVCPARM paParms,
2440 uint64_t fDst /*= VBOX_GUESTCTRL_DST_SESSION*/)
2441{
2442 LogFlowThisFuncEnter();
2443
2444#ifndef VBOX_GUESTCTRL_TEST_CASE
2445 ComObjPtr<Console> pConsole = mParent->i_getConsole();
2446 Assert(!pConsole.isNull());
2447
2448 /* Forward the information to the VMM device. */
2449 VMMDev *pVMMDev = pConsole->i_getVMMDev();
2450 AssertPtr(pVMMDev);
2451
2452 LogFlowThisFunc(("uMessage=%RU32 (%s), uParms=%RU32\n", uMessage, GstCtrlHostMsgtoStr((guestControl::eHostMsg)uMessage), uParms));
2453
2454 /* HACK ALERT! We extend the first parameter to 64-bit and use the
2455 two topmost bits for call destination information. */
2456 Assert(fDst == VBOX_GUESTCTRL_DST_SESSION || fDst == VBOX_GUESTCTRL_DST_ROOT_SVC || fDst == VBOX_GUESTCTRL_DST_BOTH);
2457 Assert(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT);
2458 paParms[0].type = VBOX_HGCM_SVC_PARM_64BIT;
2459 paParms[0].u.uint64 = (uint64_t)paParms[0].u.uint32 | fDst;
2460
2461 /* Make the call. */
2462 int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uMessage, uParms, paParms);
2463 if (RT_FAILURE(vrc))
2464 {
2465 /** @todo What to do here? */
2466 }
2467#else
2468 /* Not needed within testcases. */
2469 int vrc = VINF_SUCCESS;
2470#endif
2471 LogFlowFuncLeaveRC(vrc);
2472 return vrc;
2473}
2474
2475/* static */
2476HRESULT GuestSession::i_setErrorExternal(VirtualBoxBase *pInterface, int rcGuest)
2477{
2478 AssertPtr(pInterface);
2479 AssertMsg(RT_FAILURE(rcGuest), ("Guest rc does not indicate a failure when setting error\n"));
2480
2481 return pInterface->setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, GuestSession::i_guestErrorToString(rcGuest).c_str());
2482}
2483
2484/* Does not do locking; caller is responsible for that! */
2485int GuestSession::i_setSessionStatus(GuestSessionStatus_T sessionStatus, int sessionRc)
2486{
2487 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, sessionRc=%Rrc\n",
2488 mData.mStatus, sessionStatus, sessionRc));
2489
2490 if (sessionStatus == GuestSessionStatus_Error)
2491 {
2492 AssertMsg(RT_FAILURE(sessionRc), ("Guest rc must be an error (%Rrc)\n", sessionRc));
2493 /* Do not allow overwriting an already set error. If this happens
2494 * this means we forgot some error checking/locking somewhere. */
2495 AssertMsg(RT_SUCCESS(mData.mRC), ("Guest rc already set (to %Rrc)\n", mData.mRC));
2496 }
2497 else
2498 AssertMsg(RT_SUCCESS(sessionRc), ("Guest rc must not be an error (%Rrc)\n", sessionRc));
2499
2500 if (mData.mStatus != sessionStatus)
2501 {
2502 mData.mStatus = sessionStatus;
2503 mData.mRC = sessionRc;
2504
2505 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
2506 HRESULT hr = errorInfo.createObject();
2507 ComAssertComRC(hr);
2508 int rc2 = errorInfo->initEx(VBOX_E_IPRT_ERROR, sessionRc,
2509 COM_IIDOF(IGuestSession), getComponentName(),
2510 i_guestErrorToString(sessionRc));
2511 AssertRC(rc2);
2512
2513 fireGuestSessionStateChangedEvent(mEventSource, this,
2514 mData.mSession.mID, sessionStatus, errorInfo);
2515 }
2516
2517 return VINF_SUCCESS;
2518}
2519
2520int GuestSession::i_signalWaiters(GuestSessionWaitResult_T enmWaitResult, int rc /*= VINF_SUCCESS */)
2521{
2522 RT_NOREF(enmWaitResult, rc);
2523
2524 /*LogFlowThisFunc(("enmWaitResult=%d, rc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n",
2525 enmWaitResult, rc, mData.mWaitCount, mData.mWaitEvent));*/
2526
2527 /* Note: No write locking here -- already done in the caller. */
2528
2529 int vrc = VINF_SUCCESS;
2530 /*if (mData.mWaitEvent)
2531 vrc = mData.mWaitEvent->Signal(enmWaitResult, rc);*/
2532 LogFlowFuncLeaveRC(vrc);
2533 return vrc;
2534}
2535
2536/**
2537 * Determines the protocol version (sets mData.mProtocolVersion).
2538 *
2539 * This is called from the init method prior to to establishing a guest
2540 * session.
2541 *
2542 * @return IPRT status code.
2543 */
2544int GuestSession::i_determineProtocolVersion(void)
2545{
2546 /*
2547 * We currently do this based on the reported guest additions version,
2548 * ASSUMING that VBoxService and VBoxDrv are at the same version.
2549 */
2550 ComObjPtr<Guest> pGuest = mParent;
2551 AssertReturn(!pGuest.isNull(), VERR_NOT_SUPPORTED);
2552 uint32_t uGaVersion = pGuest->i_getAdditionsVersion();
2553
2554 /* Everyone supports version one, if they support anything at all. */
2555 mData.mProtocolVersion = 1;
2556
2557 /* Guest control 2.0 was introduced with 4.3.0. */
2558 if (uGaVersion >= VBOX_FULL_VERSION_MAKE(4,3,0))
2559 mData.mProtocolVersion = 2; /* Guest control 2.0. */
2560
2561 LogFlowThisFunc(("uGaVersion=%u.%u.%u => mProtocolVersion=%u\n",
2562 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
2563 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
2564
2565 /*
2566 * Inform the user about outdated guest additions (VM release log).
2567 */
2568 if (mData.mProtocolVersion < 2)
2569 LogRelMax(3, (tr("Warning: Guest Additions v%u.%u.%u only supports the older guest control protocol version %u.\n"
2570 " Please upgrade GAs to the current version to get full guest control capabilities.\n"),
2571 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
2572 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
2573
2574 return VINF_SUCCESS;
2575}
2576
2577int GuestSession::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *prcGuest)
2578{
2579 LogFlowThisFuncEnter();
2580
2581 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
2582
2583 /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, prcGuest=%p\n",
2584 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, prcGuest));*/
2585
2586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2587
2588 /* Did some error occur before? Then skip waiting and return. */
2589 if (mData.mStatus == GuestSessionStatus_Error)
2590 {
2591 waitResult = GuestSessionWaitResult_Error;
2592 AssertMsg(RT_FAILURE(mData.mRC), ("No error rc (%Rrc) set when guest session indicated an error\n", mData.mRC));
2593 if (prcGuest)
2594 *prcGuest = mData.mRC; /* Return last set error. */
2595 return VERR_GSTCTL_GUEST_ERROR;
2596 }
2597
2598 /* Guest Additions < 4.3 don't support session handling, skip. */
2599 if (mData.mProtocolVersion < 2)
2600 {
2601 waitResult = GuestSessionWaitResult_WaitFlagNotSupported;
2602
2603 LogFlowThisFunc(("Installed Guest Additions don't support waiting for dedicated sessions, skipping\n"));
2604 return VINF_SUCCESS;
2605 }
2606
2607 waitResult = GuestSessionWaitResult_None;
2608 if (fWaitFlags & GuestSessionWaitForFlag_Terminate)
2609 {
2610 switch (mData.mStatus)
2611 {
2612 case GuestSessionStatus_Terminated:
2613 case GuestSessionStatus_Down:
2614 waitResult = GuestSessionWaitResult_Terminate;
2615 break;
2616
2617 case GuestSessionStatus_TimedOutKilled:
2618 case GuestSessionStatus_TimedOutAbnormally:
2619 waitResult = GuestSessionWaitResult_Timeout;
2620 break;
2621
2622 case GuestSessionStatus_Error:
2623 /* Handled above. */
2624 break;
2625
2626 case GuestSessionStatus_Started:
2627 waitResult = GuestSessionWaitResult_Start;
2628 break;
2629
2630 case GuestSessionStatus_Undefined:
2631 case GuestSessionStatus_Starting:
2632 /* Do the waiting below. */
2633 break;
2634
2635 default:
2636 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2637 return VERR_NOT_IMPLEMENTED;
2638 }
2639 }
2640 else if (fWaitFlags & GuestSessionWaitForFlag_Start)
2641 {
2642 switch (mData.mStatus)
2643 {
2644 case GuestSessionStatus_Started:
2645 case GuestSessionStatus_Terminating:
2646 case GuestSessionStatus_Terminated:
2647 case GuestSessionStatus_Down:
2648 waitResult = GuestSessionWaitResult_Start;
2649 break;
2650
2651 case GuestSessionStatus_Error:
2652 waitResult = GuestSessionWaitResult_Error;
2653 break;
2654
2655 case GuestSessionStatus_TimedOutKilled:
2656 case GuestSessionStatus_TimedOutAbnormally:
2657 waitResult = GuestSessionWaitResult_Timeout;
2658 break;
2659
2660 case GuestSessionStatus_Undefined:
2661 case GuestSessionStatus_Starting:
2662 /* Do the waiting below. */
2663 break;
2664
2665 default:
2666 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2667 return VERR_NOT_IMPLEMENTED;
2668 }
2669 }
2670
2671 LogFlowThisFunc(("sessionStatus=%RU32, sessionRc=%Rrc, waitResult=%RU32\n",
2672 mData.mStatus, mData.mRC, waitResult));
2673
2674 /* No waiting needed? Return immediately using the last set error. */
2675 if (waitResult != GuestSessionWaitResult_None)
2676 {
2677 if (prcGuest)
2678 *prcGuest = mData.mRC; /* Return last set error (if any). */
2679 return RT_SUCCESS(mData.mRC) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
2680 }
2681
2682 int vrc;
2683
2684 GuestWaitEvent *pEvent = NULL;
2685 GuestEventTypes eventTypes;
2686 try
2687 {
2688 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
2689
2690 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
2691 }
2692 catch (std::bad_alloc &)
2693 {
2694 vrc = VERR_NO_MEMORY;
2695 }
2696
2697 if (RT_FAILURE(vrc))
2698 return vrc;
2699
2700 alock.release(); /* Release lock before waiting. */
2701
2702 GuestSessionStatus_T sessionStatus;
2703 vrc = i_waitForStatusChange(pEvent, fWaitFlags,
2704 uTimeoutMS, &sessionStatus, prcGuest);
2705 if (RT_SUCCESS(vrc))
2706 {
2707 switch (sessionStatus)
2708 {
2709 case GuestSessionStatus_Started:
2710 waitResult = GuestSessionWaitResult_Start;
2711 break;
2712
2713 case GuestSessionStatus_Terminated:
2714 waitResult = GuestSessionWaitResult_Terminate;
2715 break;
2716
2717 case GuestSessionStatus_TimedOutKilled:
2718 case GuestSessionStatus_TimedOutAbnormally:
2719 waitResult = GuestSessionWaitResult_Timeout;
2720 break;
2721
2722 case GuestSessionStatus_Down:
2723 waitResult = GuestSessionWaitResult_Terminate;
2724 break;
2725
2726 case GuestSessionStatus_Error:
2727 waitResult = GuestSessionWaitResult_Error;
2728 break;
2729
2730 default:
2731 waitResult = GuestSessionWaitResult_Status;
2732 break;
2733 }
2734 }
2735
2736 unregisterWaitEvent(pEvent);
2737
2738 LogFlowFuncLeaveRC(vrc);
2739 return vrc;
2740}
2741
2742int GuestSession::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
2743 GuestSessionStatus_T *pSessionStatus, int *prcGuest)
2744{
2745 RT_NOREF(fWaitFlags);
2746 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
2747
2748 VBoxEventType_T evtType;
2749 ComPtr<IEvent> pIEvent;
2750 int vrc = waitForEvent(pEvent, uTimeoutMS,
2751 &evtType, pIEvent.asOutParam());
2752 if (RT_SUCCESS(vrc))
2753 {
2754 Assert(evtType == VBoxEventType_OnGuestSessionStateChanged);
2755
2756 ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pIEvent;
2757 Assert(!pChangedEvent.isNull());
2758
2759 GuestSessionStatus_T sessionStatus;
2760 pChangedEvent->COMGETTER(Status)(&sessionStatus);
2761 if (pSessionStatus)
2762 *pSessionStatus = sessionStatus;
2763
2764 ComPtr<IVirtualBoxErrorInfo> errorInfo;
2765 HRESULT hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
2766 ComAssertComRC(hr);
2767
2768 LONG lGuestRc;
2769 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
2770 ComAssertComRC(hr);
2771 if (RT_FAILURE((int)lGuestRc))
2772 vrc = VERR_GSTCTL_GUEST_ERROR;
2773 if (prcGuest)
2774 *prcGuest = (int)lGuestRc;
2775
2776 LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n",
2777 mData.mSession.mID, sessionStatus,
2778 RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
2779 }
2780
2781 LogFlowFuncLeaveRC(vrc);
2782 return vrc;
2783}
2784
2785// implementation of public methods
2786/////////////////////////////////////////////////////////////////////////////
2787
2788HRESULT GuestSession::close()
2789{
2790 AutoCaller autoCaller(this);
2791 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2792
2793 LogFlowThisFuncEnter();
2794
2795 /* Note: Don't check if the session is ready via i_isReadyExternal() here;
2796 * the session (already) could be in a stopped / aborted state. */
2797
2798 /* Close session on guest. */
2799 int rcGuest = VINF_SUCCESS;
2800 int vrc = i_closeSession(0 /* Flags */, 30 * 1000 /* Timeout */, &rcGuest);
2801 /* On failure don't return here, instead do all the cleanup
2802 * work first and then return an error. */
2803
2804 /* Remove ourselves from the session list. */
2805 AssertPtr(mParent);
2806 int vrc2 = mParent->i_sessionRemove(mData.mSession.mID);
2807 if (vrc2 == VERR_NOT_FOUND) /* Not finding the session anymore isn't critical. */
2808 vrc2 = VINF_SUCCESS;
2809
2810 if (RT_SUCCESS(vrc))
2811 vrc = vrc2;
2812
2813 LogFlowThisFunc(("Returning rc=%Rrc, rcGuest=%Rrc\n", vrc, rcGuest));
2814
2815 if (RT_FAILURE(vrc))
2816 {
2817 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2818 return GuestSession::i_setErrorExternal(this, rcGuest);
2819 return setError(VBOX_E_IPRT_ERROR, tr("Closing guest session failed with %Rrc"), vrc);
2820 }
2821
2822 return S_OK;
2823}
2824
2825HRESULT GuestSession::fileCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2826 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2827{
2828 RT_NOREF(aSource, aDestination, aFlags, aProgress);
2829 ReturnComNotImplemented();
2830}
2831
2832HRESULT GuestSession::fileCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2833 const std::vector<FileCopyFlag_T> &aFlags,
2834 ComPtr<IProgress> &aProgress)
2835{
2836 AutoCaller autoCaller(this);
2837 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2838
2839 uint32_t fFlags = FileCopyFlag_None;
2840 if (aFlags.size())
2841 {
2842 for (size_t i = 0; i < aFlags.size(); i++)
2843 fFlags |= aFlags[i];
2844 }
2845
2846 GuestSessionFsSourceSet SourceSet;
2847
2848 GuestSessionFsSourceSpec source;
2849 source.strSource = aSource;
2850 source.enmType = FsObjType_File;
2851 source.enmPathStyle = i_getPathStyle();
2852 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
2853
2854 SourceSet.push_back(source);
2855
2856 return i_copyFromGuest(SourceSet, aDestination, aProgress);
2857}
2858
2859HRESULT GuestSession::fileCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2860 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2861{
2862 AutoCaller autoCaller(this);
2863 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2864
2865 uint32_t fFlags = FileCopyFlag_None;
2866 if (aFlags.size())
2867 {
2868 for (size_t i = 0; i < aFlags.size(); i++)
2869 fFlags |= aFlags[i];
2870 }
2871
2872 GuestSessionFsSourceSet SourceSet;
2873
2874 GuestSessionFsSourceSpec source;
2875 source.strSource = aSource;
2876 source.enmType = FsObjType_File;
2877 source.enmPathStyle = i_getPathStyle();
2878 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
2879
2880 SourceSet.push_back(source);
2881
2882 return i_copyToGuest(SourceSet, aDestination, aProgress);
2883}
2884
2885HRESULT GuestSession::copyFromGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
2886 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
2887 ComPtr<IProgress> &aProgress)
2888{
2889 AutoCaller autoCaller(this);
2890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2891
2892 const size_t cSources = aSources.size();
2893 if ( (aFilters.size() && aFilters.size() != cSources)
2894 || (aFlags.size() && aFlags.size() != cSources))
2895 {
2896 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
2897 }
2898
2899 GuestSessionFsSourceSet SourceSet;
2900
2901 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
2902 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
2903 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
2904
2905 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
2906 const bool fFollowSymlinks = true; /** @todo Ditto. */
2907
2908 while (itSource != aSources.end())
2909 {
2910 GuestFsObjData objData;
2911 int rcGuest;
2912 int vrc = i_fsQueryInfo(*(itSource), fFollowSymlinks, objData, &rcGuest);
2913 if ( RT_FAILURE(vrc)
2914 && !fContinueOnErrors)
2915 {
2916 if (GuestProcess::i_isGuestError(vrc))
2917 return setError(E_FAIL, tr("Unable to query type for source '%s': %s"), (*itSource).c_str(),
2918 GuestProcess::i_guestErrorToString(rcGuest).c_str());
2919 else
2920 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
2921 }
2922
2923 Utf8Str strFlags;
2924 if (itFlags != aFlags.end())
2925 {
2926 strFlags = *itFlags;
2927 ++itFlags;
2928 }
2929
2930 Utf8Str strFilter;
2931 if (itFilter != aFilters.end())
2932 {
2933 strFilter = *itFilter;
2934 ++itFilter;
2935 }
2936
2937 GuestSessionFsSourceSpec source;
2938 source.strSource = *itSource;
2939 source.strFilter = strFilter;
2940 source.enmType = objData.mType;
2941 source.enmPathStyle = i_getPathStyle();
2942
2943 HRESULT hrc;
2944 if (source.enmType == FsObjType_Directory)
2945 {
2946 hrc = GuestSession::i_directoryCopyFlagFromStr(strFlags, &source.Type.Dir.fCopyFlags);
2947 source.Type.Dir.fRecursive = true; /* Implicit. */
2948 }
2949 else if (source.enmType == FsObjType_File)
2950 hrc = GuestSession::i_fileCopyFlagFromStr(strFlags, &source.Type.File.fCopyFlags);
2951 else
2952 return setError(E_INVALIDARG, tr("Source type %d invalid / not supported"), source.enmType);
2953 if (FAILED(hrc))
2954 return hrc;
2955
2956 SourceSet.push_back(source);
2957
2958 ++itSource;
2959 }
2960
2961 return i_copyFromGuest(SourceSet, aDestination, aProgress);
2962}
2963
2964HRESULT GuestSession::copyToGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
2965 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
2966 ComPtr<IProgress> &aProgress)
2967{
2968 AutoCaller autoCaller(this);
2969 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2970
2971 const size_t cSources = aSources.size();
2972 if ( (aFilters.size() && aFilters.size() != cSources)
2973 || (aFlags.size() && aFlags.size() != cSources))
2974 {
2975 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
2976 }
2977
2978 GuestSessionFsSourceSet SourceSet;
2979
2980 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
2981 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
2982 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
2983
2984 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
2985
2986 while (itSource != aSources.end())
2987 {
2988 RTFSOBJINFO objInfo;
2989 int vrc = RTPathQueryInfo((*itSource).c_str(), &objInfo, RTFSOBJATTRADD_NOTHING);
2990 if ( RT_FAILURE(vrc)
2991 && !fContinueOnErrors)
2992 {
2993 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
2994 }
2995
2996 Utf8Str strFlags;
2997 if (itFlags != aFlags.end())
2998 {
2999 strFlags = *itFlags;
3000 ++itFlags;
3001 }
3002
3003 Utf8Str strFilter;
3004 if (itFilter != aFilters.end())
3005 {
3006 strFilter = *itFilter;
3007 ++itFilter;
3008 }
3009
3010 GuestSessionFsSourceSpec source;
3011 source.strSource = *itSource;
3012 source.strFilter = strFilter;
3013 source.enmType = GuestBase::fileModeToFsObjType(objInfo.Attr.fMode);
3014 source.enmPathStyle = i_getPathStyle();
3015
3016 HRESULT hrc;
3017 if (source.enmType == FsObjType_Directory)
3018 {
3019 hrc = GuestSession::i_directoryCopyFlagFromStr(strFlags, &source.Type.Dir.fCopyFlags);
3020 source.Type.Dir.fFollowSymlinks = true; /** @todo Add a flag for that in DirectoryCopyFlag_T. Later. */
3021 source.Type.Dir.fRecursive = true; /* Implicit. */
3022 }
3023 else if (source.enmType == FsObjType_File)
3024 hrc = GuestSession::i_fileCopyFlagFromStr(strFlags, &source.Type.File.fCopyFlags);
3025 else
3026 return setError(E_INVALIDARG, tr("Source type %d invalid / not supported"), source.enmType);
3027 if (FAILED(hrc))
3028 return hrc;
3029
3030 SourceSet.push_back(source);
3031
3032 ++itSource;
3033 }
3034
3035 return i_copyToGuest(SourceSet, aDestination, aProgress);
3036}
3037
3038HRESULT GuestSession::directoryCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3039 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3040{
3041 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3042 ReturnComNotImplemented();
3043}
3044
3045HRESULT GuestSession::directoryCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3046 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3047{
3048 AutoCaller autoCaller(this);
3049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3050
3051 uint32_t fFlags = DirectoryCopyFlag_None;
3052 if (aFlags.size())
3053 {
3054 for (size_t i = 0; i < aFlags.size(); i++)
3055 fFlags |= aFlags[i];
3056 }
3057
3058 GuestSessionFsSourceSet SourceSet;
3059
3060 GuestSessionFsSourceSpec source;
3061 source.strSource = aSource;
3062 source.enmType = FsObjType_Directory;
3063 source.enmPathStyle = i_getPathStyle();
3064 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3065 source.Type.Dir.fRecursive = true; /* Implicit. */
3066
3067 SourceSet.push_back(source);
3068
3069 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3070}
3071
3072HRESULT GuestSession::directoryCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3073 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3074{
3075 AutoCaller autoCaller(this);
3076 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3077
3078 uint32_t fFlags = DirectoryCopyFlag_None;
3079 if (aFlags.size())
3080 {
3081 for (size_t i = 0; i < aFlags.size(); i++)
3082 fFlags |= aFlags[i];
3083 }
3084
3085 GuestSessionFsSourceSet SourceSet;
3086
3087 GuestSessionFsSourceSpec source;
3088 source.strSource = aSource;
3089 source.enmType = FsObjType_Directory;
3090 source.enmPathStyle = i_getPathStyle();
3091 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3092 source.Type.Dir.fFollowSymlinks = true; /** @todo Add a flag for that in DirectoryCopyFlag_T. Later. */
3093 source.Type.Dir.fRecursive = true; /* Implicit. */
3094
3095 SourceSet.push_back(source);
3096
3097 return i_copyToGuest(SourceSet, aDestination, aProgress);
3098}
3099
3100HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode,
3101 const std::vector<DirectoryCreateFlag_T> &aFlags)
3102{
3103 AutoCaller autoCaller(this);
3104 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3105
3106 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3107 return setError(E_INVALIDARG, tr("No directory to create specified"));
3108
3109 uint32_t fFlags = DirectoryCreateFlag_None;
3110 if (aFlags.size())
3111 {
3112 for (size_t i = 0; i < aFlags.size(); i++)
3113 fFlags |= aFlags[i];
3114
3115 if (fFlags)
3116 if (!(fFlags & DirectoryCreateFlag_Parents))
3117 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
3118 }
3119
3120 HRESULT hrc = i_isReadyExternal();
3121 if (FAILED(hrc))
3122 return hrc;
3123
3124 LogFlowThisFuncEnter();
3125
3126 ComObjPtr <GuestDirectory> pDirectory; int rcGuest;
3127 int vrc = i_directoryCreate(aPath, (uint32_t)aMode, fFlags, &rcGuest);
3128 if (RT_FAILURE(vrc))
3129 {
3130 if (GuestProcess::i_isGuestError(vrc))
3131 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
3132 tr("Directory creation failed: %s"), GuestDirectory::i_guestErrorToString(rcGuest).c_str());
3133 else
3134 {
3135 switch (vrc)
3136 {
3137 case VERR_INVALID_PARAMETER:
3138 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: Invalid parameters given"));
3139 break;
3140
3141 case VERR_BROKEN_PIPE:
3142 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: Unexpectedly aborted"));
3143 break;
3144
3145 default:
3146 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: %Rrc"), vrc);
3147 break;
3148 }
3149 }
3150 }
3151
3152 return hrc;
3153}
3154
3155HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath,
3156 BOOL aSecure, com::Utf8Str &aDirectory)
3157{
3158 RT_NOREF(aMode, aSecure);
3159
3160 AutoCaller autoCaller(this);
3161 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3162
3163 if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0'))
3164 return setError(E_INVALIDARG, tr("No template specified"));
3165 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3166 return setError(E_INVALIDARG, tr("No directory name specified"));
3167
3168 HRESULT hrc = i_isReadyExternal();
3169 if (FAILED(hrc))
3170 return hrc;
3171
3172 LogFlowThisFuncEnter();
3173
3174 int rcGuest;
3175 int vrc = i_fsCreateTemp(aTemplateName, aPath, true /* Directory */, aDirectory, &rcGuest);
3176 if (!RT_SUCCESS(vrc))
3177 {
3178 switch (vrc)
3179 {
3180 case VERR_GSTCTL_GUEST_ERROR:
3181 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3182 break;
3183
3184 default:
3185 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Temporary directory creation \"%s\" with template \"%s\" failed: %Rrc"),
3186 aPath.c_str(), aTemplateName.c_str(), vrc);
3187 break;
3188 }
3189 }
3190
3191 return hrc;
3192}
3193
3194HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3195{
3196 AutoCaller autoCaller(this);
3197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3198
3199 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3200 return setError(E_INVALIDARG, tr("No directory to check existence for specified"));
3201
3202 HRESULT hrc = i_isReadyExternal();
3203 if (FAILED(hrc))
3204 return hrc;
3205
3206 LogFlowThisFuncEnter();
3207
3208 GuestFsObjData objData; int rcGuest;
3209 int vrc = i_directoryQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3210 if (RT_SUCCESS(vrc))
3211 *aExists = objData.mType == FsObjType_Directory;
3212 else
3213 {
3214 switch (vrc)
3215 {
3216 case VERR_GSTCTL_GUEST_ERROR:
3217 {
3218 switch (rcGuest)
3219 {
3220 case VERR_PATH_NOT_FOUND:
3221 *aExists = FALSE;
3222 break;
3223 default:
3224 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying directory existence \"%s\" failed: %s"),
3225 aPath.c_str(), GuestProcess::i_guestErrorToString(rcGuest).c_str());
3226 break;
3227 }
3228 break;
3229 }
3230
3231 default:
3232 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying directory existence \"%s\" failed: %Rrc"),
3233 aPath.c_str(), vrc);
3234 break;
3235 }
3236 }
3237
3238 return hrc;
3239}
3240
3241HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter,
3242 const std::vector<DirectoryOpenFlag_T> &aFlags, ComPtr<IGuestDirectory> &aDirectory)
3243{
3244 AutoCaller autoCaller(this);
3245 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3246
3247 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3248 return setError(E_INVALIDARG, tr("No directory to open specified"));
3249 if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0'))
3250 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
3251
3252 uint32_t fFlags = DirectoryOpenFlag_None;
3253 if (aFlags.size())
3254 {
3255 for (size_t i = 0; i < aFlags.size(); i++)
3256 fFlags |= aFlags[i];
3257
3258 if (fFlags)
3259 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
3260 }
3261
3262 HRESULT hrc = i_isReadyExternal();
3263 if (FAILED(hrc))
3264 return hrc;
3265
3266 LogFlowThisFuncEnter();
3267
3268 GuestDirectoryOpenInfo openInfo;
3269 openInfo.mPath = aPath;
3270 openInfo.mFilter = aFilter;
3271 openInfo.mFlags = fFlags;
3272
3273 ComObjPtr <GuestDirectory> pDirectory; int rcGuest;
3274 int vrc = i_directoryOpen(openInfo, pDirectory, &rcGuest);
3275 if (RT_SUCCESS(vrc))
3276 {
3277 /* Return directory object to the caller. */
3278 hrc = pDirectory.queryInterfaceTo(aDirectory.asOutParam());
3279 }
3280 else
3281 {
3282 switch (vrc)
3283 {
3284 case VERR_INVALID_PARAMETER:
3285 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening directory \"%s\" failed; invalid parameters given"),
3286 aPath.c_str());
3287 break;
3288
3289 case VERR_GSTCTL_GUEST_ERROR:
3290 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3291 break;
3292
3293 default:
3294 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3295 break;
3296 }
3297 }
3298
3299 return hrc;
3300}
3301
3302HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath)
3303{
3304 AutoCaller autoCaller(this);
3305 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3306
3307 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3308 return setError(E_INVALIDARG, tr("No directory to remove specified"));
3309
3310 HRESULT hrc = i_isReadyExternal();
3311 if (FAILED(hrc))
3312 return hrc;
3313
3314 LogFlowThisFuncEnter();
3315
3316 /* No flags; only remove the directory when empty. */
3317 uint32_t uFlags = 0;
3318
3319 int rcGuest;
3320 int vrc = i_directoryRemove(aPath, uFlags, &rcGuest);
3321 if (RT_FAILURE(vrc))
3322 {
3323 switch (vrc)
3324 {
3325 case VERR_NOT_SUPPORTED:
3326 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3327 tr("Handling removing guest directories not supported by installed Guest Additions"));
3328 break;
3329
3330 case VERR_GSTCTL_GUEST_ERROR:
3331 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3332 break;
3333
3334 default:
3335 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3336 break;
3337 }
3338 }
3339
3340 return hrc;
3341}
3342
3343HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector<DirectoryRemoveRecFlag_T> &aFlags,
3344 ComPtr<IProgress> &aProgress)
3345{
3346 RT_NOREF(aFlags);
3347
3348 AutoCaller autoCaller(this);
3349 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3350
3351 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3352 return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
3353
3354 uint32_t fFlags = DIRREMOVEREC_FLAG_NONE;
3355 if (aFlags.size())
3356 {
3357 for (size_t i = 0; i < aFlags.size(); i++)
3358 {
3359 switch (aFlags[i])
3360 {
3361 case DirectoryRemoveRecFlag_ContentAndDir:
3362 fFlags = DIRREMOVEREC_FLAG_RECURSIVE | DIRREMOVEREC_FLAG_CONTENT_AND_DIR;
3363 break;
3364
3365 case DirectoryRemoveRecFlag_ContentOnly:
3366 fFlags = DIRREMOVEREC_FLAG_RECURSIVE | DIRREMOVEREC_FLAG_CONTENT_ONLY;
3367 break;
3368
3369 default:
3370 return setError(E_INVALIDARG, tr("Invalid flags specified"));
3371 }
3372 }
3373 }
3374
3375 HRESULT hrc = i_isReadyExternal();
3376 if (FAILED(hrc))
3377 return hrc;
3378
3379 LogFlowThisFuncEnter();
3380
3381 ComObjPtr<Progress> pProgress;
3382 hrc = pProgress.createObject();
3383 if (SUCCEEDED(hrc))
3384 hrc = pProgress->init(static_cast<IGuestSession *>(this),
3385 Bstr(tr("Removing guest directory")).raw(),
3386 TRUE /*aCancelable*/);
3387 if (FAILED(hrc))
3388 return hrc;
3389
3390 /* Note: At the moment we don't supply progress information while
3391 * deleting a guest directory recursively. So just complete
3392 * the progress object right now. */
3393 /** @todo Implement progress reporting on guest directory deletion! */
3394 hrc = pProgress->i_notifyComplete(S_OK);
3395 if (FAILED(hrc))
3396 return hrc;
3397
3398 int rcGuest;
3399 int vrc = i_directoryRemove(aPath, fFlags, &rcGuest);
3400 if (RT_FAILURE(vrc))
3401 {
3402 switch (vrc)
3403 {
3404 case VERR_NOT_SUPPORTED:
3405 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3406 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
3407 break;
3408
3409 case VERR_GSTCTL_GUEST_ERROR:
3410 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3411 break;
3412
3413 default:
3414 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
3415 aPath.c_str(), vrc);
3416 break;
3417 }
3418 }
3419 else
3420 {
3421 pProgress.queryInterfaceTo(aProgress.asOutParam());
3422 }
3423
3424 return hrc;
3425}
3426
3427HRESULT GuestSession::environmentScheduleSet(const com::Utf8Str &aName, const com::Utf8Str &aValue)
3428{
3429 AutoCaller autoCaller(this);
3430 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3431
3432 HRESULT hrc;
3433 if (RT_LIKELY(aName.isNotEmpty()))
3434 {
3435 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3436 {
3437 LogFlowThisFuncEnter();
3438
3439 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3440 int vrc = mData.mEnvironmentChanges.setVariable(aName, aValue);
3441 if (RT_SUCCESS(vrc))
3442 hrc = S_OK;
3443 else
3444 hrc = setErrorVrc(vrc);
3445 }
3446 else
3447 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3448 }
3449 else
3450 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3451
3452 LogFlowThisFuncLeave();
3453 return hrc;
3454}
3455
3456HRESULT GuestSession::environmentScheduleUnset(const com::Utf8Str &aName)
3457{
3458 AutoCaller autoCaller(this);
3459 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3460
3461 HRESULT hrc;
3462 if (RT_LIKELY(aName.isNotEmpty()))
3463 {
3464 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3465 {
3466 LogFlowThisFuncEnter();
3467
3468 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3469 int vrc = mData.mEnvironmentChanges.unsetVariable(aName);
3470 if (RT_SUCCESS(vrc))
3471 hrc = S_OK;
3472 else
3473 hrc = setErrorVrc(vrc);
3474 }
3475 else
3476 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3477 }
3478 else
3479 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3480
3481 LogFlowThisFuncLeave();
3482 return hrc;
3483}
3484
3485HRESULT GuestSession::environmentGetBaseVariable(const com::Utf8Str &aName, com::Utf8Str &aValue)
3486{
3487 AutoCaller autoCaller(this);
3488 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3489
3490 HRESULT hrc;
3491 if (RT_LIKELY(aName.isNotEmpty()))
3492 {
3493 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3494 {
3495 LogFlowThisFuncEnter();
3496
3497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3498 if (mData.mpBaseEnvironment)
3499 {
3500 int vrc = mData.mpBaseEnvironment->getVariable(aName, &aValue);
3501 if (RT_SUCCESS(vrc))
3502 hrc = S_OK;
3503 else
3504 hrc = setErrorVrc(vrc);
3505 }
3506 else if (mData.mProtocolVersion < 99999)
3507 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3508 else
3509 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3510 }
3511 else
3512 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3513 }
3514 else
3515 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3516
3517 LogFlowThisFuncLeave();
3518 return hrc;
3519}
3520
3521HRESULT GuestSession::environmentDoesBaseVariableExist(const com::Utf8Str &aName, BOOL *aExists)
3522{
3523 AutoCaller autoCaller(this);
3524 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3525
3526 *aExists = FALSE;
3527
3528 HRESULT hrc;
3529 if (RT_LIKELY(aName.isNotEmpty()))
3530 {
3531 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3532 {
3533 LogFlowThisFuncEnter();
3534
3535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3536 if (mData.mpBaseEnvironment)
3537 {
3538 hrc = S_OK;
3539 *aExists = mData.mpBaseEnvironment->doesVariableExist(aName);
3540 }
3541 else if (mData.mProtocolVersion < 99999)
3542 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3543 else
3544 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3545 }
3546 else
3547 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3548 }
3549 else
3550 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3551
3552 LogFlowThisFuncLeave();
3553 return hrc;
3554}
3555
3556HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure,
3557 ComPtr<IGuestFile> &aFile)
3558{
3559 RT_NOREF(aTemplateName, aMode, aPath, aSecure, aFile);
3560 ReturnComNotImplemented();
3561}
3562
3563HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3564{
3565 AutoCaller autoCaller(this);
3566 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3567
3568 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3569 {
3570 *aExists = FALSE;
3571 return S_OK;
3572 }
3573
3574 HRESULT hrc = i_isReadyExternal();
3575 if (FAILED(hrc))
3576 return hrc;
3577
3578 LogFlowThisFuncEnter();
3579
3580 GuestFsObjData objData; int rcGuest;
3581 int vrc = i_fileQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3582 if (RT_SUCCESS(vrc))
3583 {
3584 *aExists = TRUE;
3585 return S_OK;
3586 }
3587
3588 switch (vrc)
3589 {
3590 case VERR_GSTCTL_GUEST_ERROR:
3591 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3592 break;
3593
3594/** @todo r=bird: what about VERR_PATH_NOT_FOUND and VERR_FILE_NOT_FOUND?
3595 * Where does that get converted to *aExists = FALSE? */
3596 case VERR_NOT_A_FILE:
3597 *aExists = FALSE;
3598 break;
3599
3600 default:
3601 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying file information for \"%s\" failed: %Rrc"),
3602 aPath.c_str(), vrc);
3603 break;
3604 }
3605
3606 return hrc;
3607}
3608
3609HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3610 ULONG aCreationMode, ComPtr<IGuestFile> &aFile)
3611{
3612 LogFlowThisFuncEnter();
3613
3614 const std::vector<FileOpenExFlag_T> EmptyFlags;
3615 return fileOpenEx(aPath, aAccessMode, aOpenAction, FileSharingMode_All, aCreationMode, EmptyFlags, aFile);
3616}
3617
3618HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3619 FileSharingMode_T aSharingMode, ULONG aCreationMode,
3620 const std::vector<FileOpenExFlag_T> &aFlags, ComPtr<IGuestFile> &aFile)
3621{
3622 AutoCaller autoCaller(this);
3623 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3624
3625 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3626 return setError(E_INVALIDARG, tr("No file to open specified"));
3627
3628 HRESULT hrc = i_isReadyExternal();
3629 if (FAILED(hrc))
3630 return hrc;
3631
3632 LogFlowThisFuncEnter();
3633
3634 GuestFileOpenInfo openInfo;
3635 openInfo.mFilename = aPath;
3636 openInfo.mCreationMode = aCreationMode;
3637
3638 /* Validate aAccessMode. */
3639 switch (aAccessMode)
3640 {
3641 case FileAccessMode_ReadOnly:
3642 RT_FALL_THRU();
3643 case FileAccessMode_WriteOnly:
3644 RT_FALL_THRU();
3645 case FileAccessMode_ReadWrite:
3646 openInfo.mAccessMode = aAccessMode;
3647 break;
3648 case FileAccessMode_AppendOnly:
3649 RT_FALL_THRU();
3650 case FileAccessMode_AppendRead:
3651 return setError(E_NOTIMPL, tr("Append access modes are not yet implemented"));
3652 default:
3653 return setError(E_INVALIDARG, tr("Unknown FileAccessMode value %u (%#x)"), aAccessMode, aAccessMode);
3654 }
3655
3656 /* Validate aOpenAction to the old format. */
3657 switch (aOpenAction)
3658 {
3659 case FileOpenAction_OpenExisting:
3660 RT_FALL_THRU();
3661 case FileOpenAction_OpenOrCreate:
3662 RT_FALL_THRU();
3663 case FileOpenAction_CreateNew:
3664 RT_FALL_THRU();
3665 case FileOpenAction_CreateOrReplace:
3666 RT_FALL_THRU();
3667 case FileOpenAction_OpenExistingTruncated:
3668 RT_FALL_THRU();
3669 case FileOpenAction_AppendOrCreate:
3670 openInfo.mOpenAction = aOpenAction;
3671 break;
3672 default:
3673 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3674 }
3675
3676 /* Validate aSharingMode. */
3677 switch (aSharingMode)
3678 {
3679 case FileSharingMode_All:
3680 openInfo.mSharingMode = aSharingMode;
3681 break;
3682 case FileSharingMode_Read:
3683 case FileSharingMode_Write:
3684 case FileSharingMode_ReadWrite:
3685 case FileSharingMode_Delete:
3686 case FileSharingMode_ReadDelete:
3687 case FileSharingMode_WriteDelete:
3688 return setError(E_NOTIMPL, tr("Only FileSharingMode_All is currently implemented"));
3689
3690 default:
3691 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3692 }
3693
3694 /* Combine and validate flags. */
3695 uint32_t fOpenEx = 0;
3696 for (size_t i = 0; i < aFlags.size(); i++)
3697 fOpenEx = aFlags[i];
3698 if (fOpenEx)
3699 return setError(E_INVALIDARG, tr("Unsupported FileOpenExFlag value(s) in aFlags (%#x)"), fOpenEx);
3700 openInfo.mfOpenEx = fOpenEx;
3701
3702 ComObjPtr <GuestFile> pFile;
3703 int rcGuest;
3704 int vrc = i_fileOpenEx(aPath, aAccessMode, aOpenAction, aSharingMode, aCreationMode, aFlags, pFile, &rcGuest);
3705 if (RT_SUCCESS(vrc))
3706 /* Return directory object to the caller. */
3707 hrc = pFile.queryInterfaceTo(aFile.asOutParam());
3708 else
3709 {
3710 switch (vrc)
3711 {
3712 case VERR_NOT_SUPPORTED:
3713 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3714 tr("Handling guest files not supported by installed Guest Additions"));
3715 break;
3716
3717 case VERR_GSTCTL_GUEST_ERROR:
3718 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3719 break;
3720
3721 default:
3722 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3723 break;
3724 }
3725 }
3726
3727 return hrc;
3728}
3729
3730HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymlinks, LONG64 *aSize)
3731{
3732 AutoCaller autoCaller(this);
3733 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3734
3735 if (aPath.isEmpty())
3736 return setError(E_INVALIDARG, tr("No path specified"));
3737
3738 HRESULT hrc = i_isReadyExternal();
3739 if (FAILED(hrc))
3740 return hrc;
3741
3742 int64_t llSize; int rcGuest;
3743 int vrc = i_fileQuerySize(aPath, aFollowSymlinks != FALSE, &llSize, &rcGuest);
3744 if (RT_SUCCESS(vrc))
3745 {
3746 *aSize = llSize;
3747 }
3748 else
3749 {
3750 if (GuestProcess::i_isGuestError(vrc))
3751 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3752 else
3753 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying file size failed: %Rrc"), vrc);
3754 }
3755
3756 return hrc;
3757}
3758
3759HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3760{
3761 AutoCaller autoCaller(this);
3762 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3763
3764 if (aPath.isEmpty())
3765 return setError(E_INVALIDARG, tr("No path specified"));
3766
3767 HRESULT hrc = i_isReadyExternal();
3768 if (FAILED(hrc))
3769 return hrc;
3770
3771 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3772
3773 *aExists = false;
3774
3775 GuestFsObjData objData;
3776 int rcGuest;
3777 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3778 if (RT_SUCCESS(vrc))
3779 {
3780 *aExists = TRUE;
3781 }
3782 else
3783 {
3784 if (GuestProcess::i_isGuestError(vrc))
3785 {
3786 if ( rcGuest == VERR_NOT_A_FILE
3787 || rcGuest == VERR_PATH_NOT_FOUND
3788 || rcGuest == VERR_FILE_NOT_FOUND
3789 || rcGuest == VERR_INVALID_NAME)
3790 {
3791 hrc = S_OK; /* Ignore these vrc values. */
3792 }
3793 else
3794 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3795 }
3796 else
3797 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3798 }
3799
3800 return hrc;
3801}
3802
3803HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSymlinks, ComPtr<IGuestFsObjInfo> &aInfo)
3804{
3805 AutoCaller autoCaller(this);
3806 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3807
3808 if (aPath.isEmpty())
3809 return setError(E_INVALIDARG, tr("No path specified"));
3810
3811 HRESULT hrc = i_isReadyExternal();
3812 if (FAILED(hrc))
3813 return hrc;
3814
3815 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3816
3817 GuestFsObjData Info; int rcGuest;
3818 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, Info, &rcGuest);
3819 if (RT_SUCCESS(vrc))
3820 {
3821 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
3822 hrc = ptrFsObjInfo.createObject();
3823 if (SUCCEEDED(hrc))
3824 {
3825 vrc = ptrFsObjInfo->init(Info);
3826 if (RT_SUCCESS(vrc))
3827 hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam());
3828 else
3829 hrc = setErrorVrc(vrc);
3830 }
3831 }
3832 else
3833 {
3834 if (GuestProcess::i_isGuestError(vrc))
3835 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3836 else
3837 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3838 }
3839
3840 return hrc;
3841}
3842
3843HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath)
3844{
3845 AutoCaller autoCaller(this);
3846 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3847
3848 if (RT_UNLIKELY(aPath.isEmpty()))
3849 return setError(E_INVALIDARG, tr("No path specified"));
3850
3851 HRESULT hrc = i_isReadyExternal();
3852 if (FAILED(hrc))
3853 return hrc;
3854
3855 LogFlowThisFunc(("aPath=%s\n", aPath.c_str()));
3856
3857 int rcGuest;
3858 int vrc = i_fileRemove(aPath, &rcGuest);
3859 if (RT_FAILURE(vrc))
3860 {
3861 if (GuestProcess::i_isGuestError(vrc))
3862 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3863 else
3864 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3865 }
3866
3867 return hrc;
3868}
3869
3870HRESULT GuestSession::fsObjRemoveArray(const std::vector<com::Utf8Str> &aPaths, ComPtr<IProgress> &aProgress)
3871{
3872 AutoCaller autoCaller(this);
3873 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3874
3875 RT_NOREF(aPaths, aProgress);
3876
3877 return E_NOTIMPL;
3878}
3879
3880HRESULT GuestSession::fsObjRename(const com::Utf8Str &aSource,
3881 const com::Utf8Str &aDestination,
3882 const std::vector<FsObjRenameFlag_T> &aFlags)
3883{
3884 AutoCaller autoCaller(this);
3885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3886
3887 if (RT_UNLIKELY(aSource.isEmpty()))
3888 return setError(E_INVALIDARG, tr("No source path specified"));
3889
3890 if (RT_UNLIKELY(aDestination.isEmpty()))
3891 return setError(E_INVALIDARG, tr("No destination path specified"));
3892
3893 HRESULT hrc = i_isReadyExternal();
3894 if (FAILED(hrc))
3895 return hrc;
3896
3897 /* Combine, validate and convert flags. */
3898 uint32_t fApiFlags = 0;
3899 for (size_t i = 0; i < aFlags.size(); i++)
3900 fApiFlags |= aFlags[i];
3901 if (fApiFlags & ~((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace))
3902 return setError(E_INVALIDARG, tr("Unknown rename flag: %#x"), fApiFlags);
3903
3904 LogFlowThisFunc(("aSource=%s, aDestination=%s\n", aSource.c_str(), aDestination.c_str()));
3905
3906 AssertCompile(FsObjRenameFlag_NoReplace == 0);
3907 AssertCompile(FsObjRenameFlag_Replace != 0);
3908 uint32_t fBackend;
3909 if ((fApiFlags & ((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace)) == FsObjRenameFlag_Replace)
3910 fBackend = PATHRENAME_FLAG_REPLACE;
3911 else
3912 fBackend = PATHRENAME_FLAG_NO_REPLACE;
3913
3914 /* Call worker to do the job. */
3915 int rcGuest;
3916 int vrc = i_pathRename(aSource, aDestination, fBackend, &rcGuest);
3917 if (RT_FAILURE(vrc))
3918 {
3919 switch (vrc)
3920 {
3921 case VERR_NOT_SUPPORTED:
3922 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3923 tr("Handling renaming guest directories not supported by installed Guest Additions"));
3924 break;
3925
3926 case VERR_GSTCTL_GUEST_ERROR:
3927 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Renaming guest directory failed: %Rrc"), rcGuest);
3928 break;
3929
3930 default:
3931 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming guest directory \"%s\" failed: %Rrc"),
3932 aSource.c_str(), vrc);
3933 break;
3934 }
3935 }
3936
3937 return hrc;
3938}
3939
3940HRESULT GuestSession::fsObjMove(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3941 const std::vector<FsObjMoveFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3942{
3943 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3944 ReturnComNotImplemented();
3945}
3946
3947HRESULT GuestSession::fsObjMoveArray(const std::vector<com::Utf8Str> &aSource,
3948 const com::Utf8Str &aDestination,
3949 const std::vector<FsObjMoveFlag_T> &aFlags,
3950 ComPtr<IProgress> &aProgress)
3951{
3952 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3953 ReturnComNotImplemented();
3954}
3955
3956HRESULT GuestSession::fsObjCopyArray(const std::vector<com::Utf8Str> &aSource,
3957 const com::Utf8Str &aDestination,
3958 const std::vector<FileCopyFlag_T> &aFlags,
3959 ComPtr<IProgress> &aProgress)
3960{
3961 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3962 ReturnComNotImplemented();
3963}
3964
3965HRESULT GuestSession::fsObjSetACL(const com::Utf8Str &aPath, BOOL aFollowSymlinks, const com::Utf8Str &aAcl, ULONG aMode)
3966{
3967 RT_NOREF(aPath, aFollowSymlinks, aAcl, aMode);
3968 ReturnComNotImplemented();
3969}
3970
3971
3972HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3973 const std::vector<com::Utf8Str> &aEnvironment,
3974 const std::vector<ProcessCreateFlag_T> &aFlags,
3975 ULONG aTimeoutMS, ComPtr<IGuestProcess> &aGuestProcess)
3976{
3977 LogFlowThisFuncEnter();
3978
3979 std::vector<LONG> affinityIgnored;
3980 return processCreateEx(aExecutable, aArguments, aEnvironment, aFlags, aTimeoutMS, ProcessPriority_Default,
3981 affinityIgnored, aGuestProcess);
3982}
3983
3984HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3985 const std::vector<com::Utf8Str> &aEnvironment,
3986 const std::vector<ProcessCreateFlag_T> &aFlags, ULONG aTimeoutMS,
3987 ProcessPriority_T aPriority, const std::vector<LONG> &aAffinity,
3988 ComPtr<IGuestProcess> &aGuestProcess)
3989{
3990 AutoCaller autoCaller(this);
3991 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3992
3993 HRESULT hr = i_isReadyExternal();
3994 if (FAILED(hr))
3995 return hr;
3996
3997 /** @todo r=bird: Check input better? aPriority is passed on to the guest
3998 * without any validation. Flags not existing in this vbox version are
3999 * ignored, potentially doing something entirely different than what the
4000 * caller had in mind. */
4001
4002 /*
4003 * Must have an executable to execute. If none is given, we try use the
4004 * zero'th argument.
4005 */
4006 const char *pszExecutable = aExecutable.c_str();
4007 if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0'))
4008 {
4009 if (aArguments.size() > 0)
4010 pszExecutable = aArguments[0].c_str();
4011 if (pszExecutable == NULL || *pszExecutable == '\0')
4012 return setError(E_INVALIDARG, tr("No command to execute specified"));
4013 }
4014
4015 LogFlowThisFuncEnter();
4016
4017 /*
4018 * Build the process startup info.
4019 */
4020 GuestProcessStartupInfo procInfo;
4021
4022 /* Executable and arguments. */
4023 procInfo.mExecutable = pszExecutable;
4024 if (aArguments.size())
4025 for (size_t i = 0; i < aArguments.size(); i++)
4026 procInfo.mArguments.push_back(aArguments[i]);
4027
4028 /* Combine the environment changes associated with the ones passed in by
4029 the caller, giving priority to the latter. The changes are putenv style
4030 and will be applied to the standard environment for the guest user. */
4031 int vrc = procInfo.mEnvironmentChanges.copy(mData.mEnvironmentChanges);
4032 if (RT_SUCCESS(vrc))
4033 vrc = procInfo.mEnvironmentChanges.applyPutEnvArray(aEnvironment);
4034 if (RT_SUCCESS(vrc))
4035 {
4036 /* Convert the flag array into a mask. */
4037 if (aFlags.size())
4038 for (size_t i = 0; i < aFlags.size(); i++)
4039 procInfo.mFlags |= aFlags[i];
4040
4041 procInfo.mTimeoutMS = aTimeoutMS;
4042
4043 /** @todo use RTCPUSET instead of archaic 64-bit variables! */
4044 if (aAffinity.size())
4045 for (size_t i = 0; i < aAffinity.size(); i++)
4046 if (aAffinity[i])
4047 procInfo.mAffinity |= (uint64_t)1 << i;
4048
4049 procInfo.mPriority = aPriority;
4050
4051 /*
4052 * Create a guest process object.
4053 */
4054 ComObjPtr<GuestProcess> pProcess;
4055 vrc = i_processCreateEx(procInfo, pProcess);
4056 if (RT_SUCCESS(vrc))
4057 {
4058 ComPtr<IGuestProcess> pIProcess;
4059 hr = pProcess.queryInterfaceTo(pIProcess.asOutParam());
4060 if (SUCCEEDED(hr))
4061 {
4062 /*
4063 * Start the process.
4064 */
4065 vrc = pProcess->i_startProcessAsync();
4066 if (RT_SUCCESS(vrc))
4067 {
4068 aGuestProcess = pIProcess;
4069
4070 LogFlowFuncLeaveRC(vrc);
4071 return S_OK;
4072 }
4073
4074 hr = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc);
4075 }
4076 }
4077 else if (vrc == VERR_GSTCTL_MAX_OBJECTS_REACHED)
4078 hr = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"),
4079 VBOX_GUESTCTRL_MAX_OBJECTS);
4080 else
4081 hr = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc);
4082 }
4083 else
4084 hr = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc);
4085
4086 LogFlowFuncLeaveRC(vrc);
4087 return hr;
4088}
4089
4090HRESULT GuestSession::processGet(ULONG aPid, ComPtr<IGuestProcess> &aGuestProcess)
4091
4092{
4093 AutoCaller autoCaller(this);
4094 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4095
4096 if (aPid == 0)
4097 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
4098
4099 LogFlowThisFunc(("PID=%RU32\n", aPid));
4100
4101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4102
4103 HRESULT hr = S_OK;
4104
4105 ComObjPtr<GuestProcess> pProcess;
4106 int rc = i_processGetByPID(aPid, &pProcess);
4107 if (RT_FAILURE(rc))
4108 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid);
4109
4110 /* This will set (*aProcess) to NULL if pProgress is NULL. */
4111 HRESULT hr2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
4112 if (SUCCEEDED(hr))
4113 hr = hr2;
4114
4115 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", (IGuestProcess*)aGuestProcess, hr));
4116 return hr;
4117}
4118
4119HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType)
4120{
4121 RT_NOREF(aSource, aTarget, aType);
4122 ReturnComNotImplemented();
4123}
4124
4125HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists)
4126
4127{
4128 RT_NOREF(aSymlink, aExists);
4129 ReturnComNotImplemented();
4130}
4131
4132HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector<SymlinkReadFlag_T> &aFlags,
4133 com::Utf8Str &aTarget)
4134{
4135 RT_NOREF(aSymlink, aFlags, aTarget);
4136 ReturnComNotImplemented();
4137}
4138
4139HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
4140{
4141 AutoCaller autoCaller(this);
4142 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4143
4144 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4145
4146 LogFlowThisFuncEnter();
4147
4148 HRESULT hrc = S_OK;
4149
4150 /*
4151 * Note: Do not hold any locks here while waiting!
4152 */
4153 int rcGuest; GuestSessionWaitResult_T waitResult;
4154 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &rcGuest);
4155 if (RT_SUCCESS(vrc))
4156 *aReason = waitResult;
4157 else
4158 {
4159 switch (vrc)
4160 {
4161 case VERR_GSTCTL_GUEST_ERROR:
4162 hrc = GuestSession::i_setErrorExternal(this, rcGuest);
4163 break;
4164
4165 case VERR_TIMEOUT:
4166 *aReason = GuestSessionWaitResult_Timeout;
4167 break;
4168
4169 default:
4170 {
4171 const char *pszSessionName = mData.mSession.mName.c_str();
4172 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4173 tr("Waiting for guest session \"%s\" failed: %Rrc"),
4174 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
4175 break;
4176 }
4177 }
4178 }
4179
4180 LogFlowFuncLeaveRC(vrc);
4181 return hrc;
4182}
4183
4184HRESULT GuestSession::waitForArray(const std::vector<GuestSessionWaitForFlag_T> &aWaitFor, ULONG aTimeoutMS,
4185 GuestSessionWaitResult_T *aReason)
4186{
4187 AutoCaller autoCaller(this);
4188 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4189
4190 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4191
4192 LogFlowThisFuncEnter();
4193
4194 /*
4195 * Note: Do not hold any locks here while waiting!
4196 */
4197 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
4198 for (size_t i = 0; i < aWaitFor.size(); i++)
4199 fWaitFor |= aWaitFor[i];
4200
4201 return WaitFor(fWaitFor, aTimeoutMS, aReason);
4202}
Note: See TracBrowser for help on using the repository browser.

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