VirtualBox

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

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

Guest Control/Main: Check flags of IGuestSession::directoryRemoveRecursive() and renamed DIRREMOVE_FLAG_ flags to DIRREMOVEREC_FLAG_ to match usage.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 133.1 KB
Line 
1/* $Id: GuestSessionImpl.cpp 77069 2019-01-31 11:59:33Z 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_PERMISSION_DENIED: /** @todo r=bird: This is probably completely and utterly misleading. VERR_AUTHENTICATION_FAILURE could have this message. */
1709 strError += Utf8StrFmt(tr("Invalid user/password credentials"));
1710 break;
1711
1712 case VERR_GSTCTL_MAX_OBJECTS_REACHED:
1713 strError += Utf8StrFmt(tr("Maximum number of concurrent guest processes has been reached"));
1714 break;
1715
1716 case VERR_NOT_FOUND:
1717 strError += Utf8StrFmt(tr("The guest execution service is not ready (yet)"));
1718 break;
1719
1720 default:
1721 strError += Utf8StrFmt("%Rrc", rcGuest);
1722 break;
1723 }
1724
1725 return strError;
1726}
1727
1728/**
1729 * Checks if this session is ready state where it can handle
1730 * all session-bound actions (like guest processes, guest files).
1731 * Only used by official API methods. Will set an external
1732 * error when not ready.
1733 */
1734HRESULT GuestSession::i_isReadyExternal(void)
1735{
1736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1737
1738 /** @todo Be a bit more informative. */
1739 if (mData.mStatus != GuestSessionStatus_Started)
1740 return setError(E_UNEXPECTED, tr("Session is not in started state"));
1741
1742 return S_OK;
1743}
1744
1745/**
1746 * Called by IGuest right before this session gets removed from
1747 * the public session list.
1748 */
1749int GuestSession::i_onRemove(void)
1750{
1751 LogFlowThisFuncEnter();
1752
1753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1754
1755 int vrc = VINF_SUCCESS;
1756
1757 /*
1758 * Note: The event source stuff holds references to this object,
1759 * so make sure that this is cleaned up *before* calling uninit.
1760 */
1761 if (!mEventSource.isNull())
1762 {
1763 mEventSource->UnregisterListener(mLocalListener);
1764
1765 mLocalListener.setNull();
1766 unconst(mEventSource).setNull();
1767 }
1768
1769 LogFlowFuncLeaveRC(vrc);
1770 return vrc;
1771}
1772
1773/** No locking! */
1774int GuestSession::i_onSessionStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
1775{
1776 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1777 /* pCallback is optional. */
1778 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
1779
1780 if (pSvcCbData->mParms < 3)
1781 return VERR_INVALID_PARAMETER;
1782
1783 CALLBACKDATA_SESSION_NOTIFY dataCb;
1784 /* pSvcCb->mpaParms[0] always contains the context ID. */
1785 int vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[1], &dataCb.uType);
1786 AssertRCReturn(vrc, vrc);
1787 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[2], &dataCb.uResult);
1788 AssertRCReturn(vrc, vrc);
1789
1790 LogFlowThisFunc(("ID=%RU32, uType=%RU32, rcGuest=%Rrc\n",
1791 mData.mSession.mID, dataCb.uType, dataCb.uResult));
1792
1793 GuestSessionStatus_T sessionStatus = GuestSessionStatus_Undefined;
1794
1795 int rcGuest = dataCb.uResult; /** @todo uint32_t vs. int. */
1796 switch (dataCb.uType)
1797 {
1798 case GUEST_SESSION_NOTIFYTYPE_ERROR:
1799 sessionStatus = GuestSessionStatus_Error;
1800 break;
1801
1802 case GUEST_SESSION_NOTIFYTYPE_STARTED:
1803 sessionStatus = GuestSessionStatus_Started;
1804#if 0 /** @todo If we get some environment stuff along with this kind notification: */
1805 const char *pszzEnvBlock = ...;
1806 uint32_t cbEnvBlock = ...;
1807 if (!mData.mpBaseEnvironment)
1808 {
1809 GuestEnvironment *pBaseEnv;
1810 try { pBaseEnv = new GuestEnvironment(); } catch (std::bad_alloc &) { pBaseEnv = NULL; }
1811 if (pBaseEnv)
1812 {
1813 int vrc = pBaseEnv->initNormal();
1814 if (RT_SUCCESS(vrc))
1815 vrc = pBaseEnv->copyUtf8Block(pszzEnvBlock, cbEnvBlock);
1816 if (RT_SUCCESS(vrc))
1817 mData.mpBaseEnvironment = pBaseEnv;
1818 else
1819 pBaseEnv->release();
1820 }
1821 }
1822#endif
1823 break;
1824
1825 case GUEST_SESSION_NOTIFYTYPE_TEN:
1826 case GUEST_SESSION_NOTIFYTYPE_TES:
1827 case GUEST_SESSION_NOTIFYTYPE_TEA:
1828 sessionStatus = GuestSessionStatus_Terminated;
1829 break;
1830
1831 case GUEST_SESSION_NOTIFYTYPE_TOK:
1832 sessionStatus = GuestSessionStatus_TimedOutKilled;
1833 break;
1834
1835 case GUEST_SESSION_NOTIFYTYPE_TOA:
1836 sessionStatus = GuestSessionStatus_TimedOutAbnormally;
1837 break;
1838
1839 case GUEST_SESSION_NOTIFYTYPE_DWN:
1840 sessionStatus = GuestSessionStatus_Down;
1841 break;
1842
1843 case GUEST_SESSION_NOTIFYTYPE_UNDEFINED:
1844 default:
1845 vrc = VERR_NOT_SUPPORTED;
1846 break;
1847 }
1848
1849 if (RT_SUCCESS(vrc))
1850 {
1851 if (RT_FAILURE(rcGuest))
1852 sessionStatus = GuestSessionStatus_Error;
1853 }
1854
1855 /* Set the session status. */
1856 if (RT_SUCCESS(vrc))
1857 vrc = i_setSessionStatus(sessionStatus, rcGuest);
1858
1859 LogFlowThisFunc(("ID=%RU32, rcGuest=%Rrc\n", mData.mSession.mID, rcGuest));
1860
1861 LogFlowFuncLeaveRC(vrc);
1862 return vrc;
1863}
1864
1865PathStyle_T GuestSession::i_getPathStyle(void)
1866{
1867 PathStyle_T enmPathStyle;
1868
1869 VBOXOSTYPE enmOsType = mParent->i_getGuestOSType();
1870 if (enmOsType < VBOXOSTYPE_DOS)
1871 {
1872 LogFlowFunc(("returns PathStyle_Unknown\n"));
1873 enmPathStyle = PathStyle_Unknown;
1874 }
1875 else if (enmOsType < VBOXOSTYPE_Linux)
1876 {
1877 LogFlowFunc(("returns PathStyle_DOS\n"));
1878 enmPathStyle = PathStyle_DOS;
1879 }
1880 else
1881 {
1882 LogFlowFunc(("returns PathStyle_UNIX\n"));
1883 enmPathStyle = PathStyle_UNIX;
1884 }
1885
1886 return enmPathStyle;
1887}
1888
1889int GuestSession::i_startSession(int *prcGuest)
1890{
1891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1892
1893 LogFlowThisFunc(("mID=%RU32, mName=%s, uProtocolVersion=%RU32, openFlags=%x, openTimeoutMS=%RU32\n",
1894 mData.mSession.mID, mData.mSession.mName.c_str(), mData.mProtocolVersion,
1895 mData.mSession.mOpenFlags, mData.mSession.mOpenTimeoutMS));
1896
1897 /* Guest Additions < 4.3 don't support opening dedicated
1898 guest sessions. Simply return success here. */
1899 if (mData.mProtocolVersion < 2)
1900 {
1901 mData.mStatus = GuestSessionStatus_Started;
1902
1903 LogFlowThisFunc(("Installed Guest Additions don't support opening dedicated sessions, skipping\n"));
1904 return VINF_SUCCESS;
1905 }
1906
1907 if (mData.mStatus != GuestSessionStatus_Undefined)
1908 return VINF_SUCCESS;
1909
1910 /** @todo mData.mSession.uFlags validation. */
1911
1912 /* Set current session status. */
1913 mData.mStatus = GuestSessionStatus_Starting;
1914 mData.mRC = VINF_SUCCESS; /* Clear previous error, if any. */
1915
1916 int vrc;
1917
1918 GuestWaitEvent *pEvent = NULL;
1919 GuestEventTypes eventTypes;
1920 try
1921 {
1922 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
1923
1924 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
1925 }
1926 catch (std::bad_alloc &)
1927 {
1928 vrc = VERR_NO_MEMORY;
1929 }
1930
1931 if (RT_FAILURE(vrc))
1932 return vrc;
1933
1934 VBOXHGCMSVCPARM paParms[8];
1935
1936 int i = 0;
1937 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1938 HGCMSvcSetU32(&paParms[i++], mData.mProtocolVersion);
1939 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mUser.c_str(),
1940 (ULONG)mData.mCredentials.mUser.length() + 1);
1941 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mPassword.c_str(),
1942 (ULONG)mData.mCredentials.mPassword.length() + 1);
1943 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mDomain.c_str(),
1944 (ULONG)mData.mCredentials.mDomain.length() + 1);
1945 HGCMSvcSetU32(&paParms[i++], mData.mSession.mOpenFlags);
1946
1947 alock.release(); /* Drop write lock before sending. */
1948
1949 vrc = i_sendMessage(HOST_MSG_SESSION_CREATE, i, paParms, VBOX_GUESTCTRL_DST_ROOT_SVC);
1950 if (RT_SUCCESS(vrc))
1951 {
1952 vrc = i_waitForStatusChange(pEvent, GuestSessionWaitForFlag_Start,
1953 30 * 1000 /* 30s timeout */,
1954 NULL /* Session status */, prcGuest);
1955 }
1956 else
1957 {
1958 /*
1959 * Unable to start guest session - update its current state.
1960 * Since there is no (official API) way to recover a failed guest session
1961 * this also marks the end state. Internally just calling this
1962 * same function again will work though.
1963 */
1964 mData.mStatus = GuestSessionStatus_Error;
1965 mData.mRC = vrc;
1966 }
1967
1968 unregisterWaitEvent(pEvent);
1969
1970 LogFlowFuncLeaveRC(vrc);
1971 return vrc;
1972}
1973
1974/**
1975 * Starts the guest session asynchronously in a separate thread.
1976 *
1977 * @returns IPRT status code.
1978 */
1979int GuestSession::i_startSessionAsync(void)
1980{
1981 LogFlowThisFuncEnter();
1982
1983 int vrc;
1984 GuestSessionTaskInternalStart* pTask = NULL;
1985 try
1986 {
1987 pTask = new GuestSessionTaskInternalStart(this);
1988 if (!pTask->isOk())
1989 {
1990 delete pTask;
1991 LogFlow(("GuestSession: Could not create GuestSessionTaskInternalStart object\n"));
1992 throw VERR_MEMOBJ_INIT_FAILED;
1993 }
1994
1995 /* Asynchronously open the session on the guest by kicking off a worker thread. */
1996 /* Note: This function deletes pTask in case of exceptions, so there is no need in the call of delete operator. */
1997 HRESULT hrc = pTask->createThread();
1998 vrc = Global::vboxStatusCodeFromCOM(hrc);
1999 }
2000 catch(std::bad_alloc &)
2001 {
2002 vrc = VERR_NO_MEMORY;
2003 }
2004 catch(int eVRC)
2005 {
2006 vrc = eVRC;
2007 LogFlow(("GuestSession: Could not create thread for GuestSessionTaskInternalOpen task %Rrc\n", vrc));
2008 }
2009
2010 LogFlowFuncLeaveRC(vrc);
2011 return vrc;
2012}
2013
2014/**
2015 * Static function to start a guest session asynchronously.
2016 *
2017 * @returns IPRT status code.
2018 * @param pTask Task object to use for starting the guest session.
2019 */
2020/* static */
2021int GuestSession::i_startSessionThreadTask(GuestSessionTaskInternalStart *pTask)
2022{
2023 LogFlowFunc(("pTask=%p\n", pTask));
2024 AssertPtr(pTask);
2025
2026 const ComObjPtr<GuestSession> pSession(pTask->Session());
2027 Assert(!pSession.isNull());
2028
2029 AutoCaller autoCaller(pSession);
2030 if (FAILED(autoCaller.rc()))
2031 return VERR_COM_INVALID_OBJECT_STATE;
2032
2033 int vrc = pSession->i_startSession(NULL /* Guest rc, ignored */);
2034 /* Nothing to do here anymore. */
2035
2036 LogFlowFuncLeaveRC(vrc);
2037 return vrc;
2038}
2039
2040/**
2041 * Registers an object with the session, i.e. allocates an object ID.
2042 *
2043 * @return VBox status code.
2044 * @retval VERR_GSTCTL_MAX_OBJECTS_REACHED if the maximum of concurrent objects
2045 * is reached.
2046 * @param enmType Session object type to register.
2047 * @param pidObject Where to return the object ID on success.
2048 */
2049int GuestSession::i_objectRegister(SESSIONOBJECTTYPE enmType, uint32_t *pidObject)
2050{
2051 /*
2052 * Pick a random bit as starting point. If it's in use, search forward
2053 * for a free one, wrapping around. We've reserved both the zero'th and
2054 * max-1 IDs (see Data constructor).
2055 */
2056 uint32_t idObject = RTRandU32Ex(1, VBOX_GUESTCTRL_MAX_OBJECTS - 2);
2057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2058 if (!ASMBitTestAndSet(&mData.bmObjectIds[0], idObject))
2059 { /* likely */ }
2060 else if (mData.mObjects.size() < VBOX_GUESTCTRL_MAX_OBJECTS - 2 /* First and last are not used */)
2061 {
2062 /* Forward search. */
2063 int iHit = ASMBitNextClear(&mData.bmObjectIds[0], VBOX_GUESTCTRL_MAX_OBJECTS, idObject);
2064 if (iHit < 0)
2065 iHit = ASMBitFirstClear(&mData.bmObjectIds[0], VBOX_GUESTCTRL_MAX_OBJECTS);
2066 AssertLogRelMsgReturn(iHit >= 0, ("object count: %#zu\n", mData.mObjects.size()), VERR_GSTCTL_MAX_OBJECTS_REACHED);
2067 idObject = iHit;
2068 AssertLogRelMsgReturn(!ASMBitTestAndSet(&mData.bmObjectIds[0], idObject), ("idObject=%#x\n", idObject), VERR_INTERNAL_ERROR_2);
2069 }
2070 else
2071 {
2072 LogFunc(("enmType=%RU32 -> VERR_GSTCTL_MAX_OBJECTS_REACHED!! (%zu objects)\n", enmType, mData.mObjects.size()));
2073 return VERR_GSTCTL_MAX_OBJECTS_REACHED;
2074 }
2075
2076 Log2Func(("enmType=%RU32 -> idObject=%RU32 (%zu objects)\n", enmType, idObject, mData.mObjects.size()));
2077
2078 try
2079 {
2080 mData.mObjects[idObject].enmType = enmType;
2081 mData.mObjects[idObject].msBirth = RTTimeMilliTS();
2082 }
2083 catch (std::bad_alloc &)
2084 {
2085 ASMBitClear(&mData.bmObjectIds[0], idObject);
2086 return VERR_NO_MEMORY;
2087 }
2088
2089 alock.release();
2090
2091 *pidObject = idObject;
2092 return VINF_SUCCESS;
2093}
2094
2095/**
2096 * Unregisters an object from a session.
2097 *
2098 * @retval VINF_SUCCESS on success.
2099 * @retval VERR_NOT_FOUND if the object ID was not found.
2100 * @param idObject Object ID to unregister.
2101 */
2102int GuestSession::i_objectUnregister(uint32_t idObject)
2103{
2104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2105
2106 int rc = VINF_SUCCESS;
2107 AssertMsgStmt(ASMBitTestAndClear(&mData.bmObjectIds, idObject), ("idObject=%#x\n", idObject), rc = VERR_NOT_FOUND);
2108
2109 SessionObjects::iterator ItObj = mData.mObjects.find(idObject);
2110 AssertMsgReturn(ItObj != mData.mObjects.end(), ("idObject=%#x\n", idObject), VERR_NOT_FOUND);
2111 mData.mObjects.erase(ItObj);
2112
2113 return rc;
2114}
2115
2116int GuestSession::i_pathRename(const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags, int *prcGuest)
2117{
2118 AssertReturn(!(uFlags & ~PATHRENAME_FLAG_VALID_MASK), VERR_INVALID_PARAMETER);
2119
2120 LogFlowThisFunc(("strSource=%s, strDest=%s, uFlags=0x%x\n",
2121 strSource.c_str(), strDest.c_str(), uFlags));
2122
2123 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2124
2125 GuestWaitEvent *pEvent = NULL;
2126 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2127 if (RT_FAILURE(vrc))
2128 return vrc;
2129
2130 /* Prepare HGCM call. */
2131 VBOXHGCMSVCPARM paParms[8];
2132 int i = 0;
2133 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2134 HGCMSvcSetPv(&paParms[i++], (void*)strSource.c_str(),
2135 (ULONG)strSource.length() + 1);
2136 HGCMSvcSetPv(&paParms[i++], (void*)strDest.c_str(),
2137 (ULONG)strDest.length() + 1);
2138 HGCMSvcSetU32(&paParms[i++], uFlags);
2139
2140 alock.release(); /* Drop write lock before sending. */
2141
2142 vrc = i_sendMessage(HOST_MSG_PATH_RENAME, i, paParms);
2143 if (RT_SUCCESS(vrc))
2144 {
2145 vrc = pEvent->Wait(30 * 1000);
2146 if ( vrc == VERR_GSTCTL_GUEST_ERROR
2147 && prcGuest)
2148 *prcGuest = pEvent->GuestResult();
2149 }
2150
2151 unregisterWaitEvent(pEvent);
2152
2153 LogFlowFuncLeaveRC(vrc);
2154 return vrc;
2155}
2156
2157/**
2158 * Returns the user's absolute documents path, if any.
2159 *
2160 * @return VBox status code.
2161 * @param strPath Where to store the user's document path.
2162 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
2163 * Any other return code indicates some host side error.
2164 */
2165int GuestSession::i_pathUserDocuments(Utf8Str &strPath, int *prcGuest)
2166{
2167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2168
2169 /** @todo Cache the user's document path? */
2170
2171 GuestWaitEvent *pEvent = NULL;
2172 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2173 if (RT_FAILURE(vrc))
2174 return vrc;
2175
2176 /* Prepare HGCM call. */
2177 VBOXHGCMSVCPARM paParms[2];
2178 int i = 0;
2179 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2180
2181 alock.release(); /* Drop write lock before sending. */
2182
2183 vrc = i_sendMessage(HOST_MSG_PATH_USER_DOCUMENTS, i, paParms);
2184 if (RT_SUCCESS(vrc))
2185 {
2186 vrc = pEvent->Wait(30 * 1000);
2187 if (RT_SUCCESS(vrc))
2188 {
2189 strPath = pEvent->Payload().ToString();
2190 }
2191 else
2192 {
2193 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2194 {
2195 if (prcGuest)
2196 *prcGuest = pEvent->GuestResult();
2197 }
2198 }
2199 }
2200
2201 unregisterWaitEvent(pEvent);
2202
2203 LogFlowFuncLeaveRC(vrc);
2204 return vrc;
2205}
2206
2207/**
2208 * Returns the user's absolute home path, if any.
2209 *
2210 * @return VBox status code.
2211 * @param strPath Where to store the user's home path.
2212 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
2213 * Any other return code indicates some host side error.
2214 */
2215int GuestSession::i_pathUserHome(Utf8Str &strPath, int *prcGuest)
2216{
2217 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2218
2219 /** @todo Cache the user's home path? */
2220
2221 GuestWaitEvent *pEvent = NULL;
2222 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2223 if (RT_FAILURE(vrc))
2224 return vrc;
2225
2226 /* Prepare HGCM call. */
2227 VBOXHGCMSVCPARM paParms[2];
2228 int i = 0;
2229 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2230
2231 alock.release(); /* Drop write lock before sending. */
2232
2233 vrc = i_sendMessage(HOST_MSG_PATH_USER_HOME, i, paParms);
2234 if (RT_SUCCESS(vrc))
2235 {
2236 vrc = pEvent->Wait(30 * 1000);
2237 if (RT_SUCCESS(vrc))
2238 {
2239 strPath = pEvent->Payload().ToString();
2240 }
2241 else
2242 {
2243 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2244 {
2245 if (prcGuest)
2246 *prcGuest = pEvent->GuestResult();
2247 }
2248 }
2249 }
2250
2251 unregisterWaitEvent(pEvent);
2252
2253 LogFlowFuncLeaveRC(vrc);
2254 return vrc;
2255}
2256
2257/**
2258 * Unregisters a process object from a session.
2259 *
2260 * @return VBox status code. VERR_NOT_FOUND if the process is not registered (anymore).
2261 * @param pProcess Process object to unregister from session.
2262 */
2263int GuestSession::i_processUnregister(GuestProcess *pProcess)
2264{
2265 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
2266
2267 LogFlowThisFunc(("pProcess=%p\n", pProcess));
2268
2269 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2270
2271 const uint32_t idObject = pProcess->getObjectID();
2272
2273 LogFlowFunc(("Removing process (objectID=%RU32) ...\n", idObject));
2274
2275 int rc = i_objectUnregister(idObject);
2276 if (RT_FAILURE(rc))
2277 return rc;
2278
2279 SessionProcesses::iterator itProcs = mData.mProcesses.find(idObject);
2280 AssertReturn(itProcs != mData.mProcesses.end(), VERR_NOT_FOUND);
2281
2282 /* Make sure to consume the pointer before the one of the iterator gets released. */
2283 ComObjPtr<GuestProcess> pProc = pProcess;
2284
2285 ULONG uPID;
2286 HRESULT hr = pProc->COMGETTER(PID)(&uPID);
2287 ComAssertComRC(hr);
2288
2289 LogFlowFunc(("Removing process ID=%RU32 (session %RU32, guest PID %RU32, now total %zu processes)\n",
2290 idObject, mData.mSession.mID, uPID, mData.mProcesses.size()));
2291
2292 rc = pProcess->i_onRemove();
2293 AssertRCReturn(rc, rc);
2294
2295 mData.mProcesses.erase(itProcs);
2296
2297 alock.release(); /* Release lock before firing off event. */
2298
2299 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProc, uPID, false /* Process unregistered */);
2300
2301 pProc.setNull();
2302
2303 LogFlowFuncLeaveRC(rc);
2304 return rc;
2305}
2306
2307/**
2308 * Creates but does *not* start the process yet.
2309 *
2310 * See GuestProcess::startProcess() or GuestProcess::startProcessAsync() for
2311 * starting the process.
2312 *
2313 * @return IPRT status code.
2314 * @param procInfo
2315 * @param pProcess
2316 */
2317int GuestSession::i_processCreateEx(GuestProcessStartupInfo &procInfo, ComObjPtr<GuestProcess> &pProcess)
2318{
2319 LogFlowFunc(("mExe=%s, mFlags=%x, mTimeoutMS=%RU32\n",
2320 procInfo.mExecutable.c_str(), procInfo.mFlags, procInfo.mTimeoutMS));
2321#ifdef DEBUG
2322 if (procInfo.mArguments.size())
2323 {
2324 LogFlowFunc(("Arguments:"));
2325 ProcessArguments::const_iterator it = procInfo.mArguments.begin();
2326 while (it != procInfo.mArguments.end())
2327 {
2328 LogFlow((" %s", (*it).c_str()));
2329 ++it;
2330 }
2331 LogFlow(("\n"));
2332 }
2333#endif
2334
2335 /* Validate flags. */
2336 if (procInfo.mFlags)
2337 {
2338 if ( !(procInfo.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
2339 && !(procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
2340 && !(procInfo.mFlags & ProcessCreateFlag_Hidden)
2341 && !(procInfo.mFlags & ProcessCreateFlag_Profile)
2342 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2343 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdErr))
2344 {
2345 return VERR_INVALID_PARAMETER;
2346 }
2347 }
2348
2349 if ( (procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
2350 && ( (procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2351 || (procInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
2352 )
2353 )
2354 {
2355 return VERR_INVALID_PARAMETER;
2356 }
2357
2358 /* Adjust timeout.
2359 * If set to 0, we define an infinite timeout (unlimited process run time). */
2360 if (procInfo.mTimeoutMS == 0)
2361 procInfo.mTimeoutMS = UINT32_MAX;
2362
2363 /** @todo Implement process priority + affinity. */
2364
2365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2366
2367 /* Register a new object ID. */
2368 uint32_t idObject;
2369 int rc = i_objectRegister(SESSIONOBJECTTYPE_PROCESS, &idObject);
2370 if (RT_FAILURE(rc))
2371 return rc;
2372
2373 /* Create the process object. */
2374 HRESULT hr = pProcess.createObject();
2375 if (FAILED(hr))
2376 return VERR_COM_UNEXPECTED;
2377
2378 rc = pProcess->init(mParent->i_getConsole() /* Console */, this /* Session */, idObject,
2379 procInfo, mData.mpBaseEnvironment);
2380 if (RT_FAILURE(rc))
2381 return rc;
2382
2383 /* Add the created process to our map. */
2384 try
2385 {
2386 mData.mProcesses[idObject] = pProcess;
2387
2388 LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %zu processes)\n",
2389 mData.mSession.mID, idObject, mData.mProcesses.size()));
2390
2391 alock.release(); /* Release lock before firing off event. */
2392
2393 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProcess, 0 /* PID */, true /* Process registered */);
2394 }
2395 catch (std::bad_alloc &)
2396 {
2397 rc = VERR_NO_MEMORY;
2398 }
2399
2400 return rc;
2401}
2402
2403inline bool GuestSession::i_processExists(uint32_t uProcessID, ComObjPtr<GuestProcess> *pProcess)
2404{
2405 SessionProcesses::const_iterator it = mData.mProcesses.find(uProcessID);
2406 if (it != mData.mProcesses.end())
2407 {
2408 if (pProcess)
2409 *pProcess = it->second;
2410 return true;
2411 }
2412 return false;
2413}
2414
2415inline int GuestSession::i_processGetByPID(ULONG uPID, ComObjPtr<GuestProcess> *pProcess)
2416{
2417 AssertReturn(uPID, false);
2418 /* pProcess is optional. */
2419
2420 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
2421 for (; itProcs != mData.mProcesses.end(); ++itProcs)
2422 {
2423 ComObjPtr<GuestProcess> pCurProc = itProcs->second;
2424 AutoCaller procCaller(pCurProc);
2425 if (procCaller.rc())
2426 return VERR_COM_INVALID_OBJECT_STATE;
2427
2428 ULONG uCurPID;
2429 HRESULT hr = pCurProc->COMGETTER(PID)(&uCurPID);
2430 ComAssertComRC(hr);
2431
2432 if (uCurPID == uPID)
2433 {
2434 if (pProcess)
2435 *pProcess = pCurProc;
2436 return VINF_SUCCESS;
2437 }
2438 }
2439
2440 return VERR_NOT_FOUND;
2441}
2442
2443int GuestSession::i_sendMessage(uint32_t uMessage, uint32_t uParms, PVBOXHGCMSVCPARM paParms,
2444 uint64_t fDst /*= VBOX_GUESTCTRL_DST_SESSION*/)
2445{
2446 LogFlowThisFuncEnter();
2447
2448#ifndef VBOX_GUESTCTRL_TEST_CASE
2449 ComObjPtr<Console> pConsole = mParent->i_getConsole();
2450 Assert(!pConsole.isNull());
2451
2452 /* Forward the information to the VMM device. */
2453 VMMDev *pVMMDev = pConsole->i_getVMMDev();
2454 AssertPtr(pVMMDev);
2455
2456 LogFlowThisFunc(("uMessage=%RU32 (%s), uParms=%RU32\n", uMessage, GstCtrlHostMsgtoStr((guestControl::eHostMsg)uMessage), uParms));
2457
2458 /* HACK ALERT! We extend the first parameter to 64-bit and use the
2459 two topmost bits for call destination information. */
2460 Assert(fDst == VBOX_GUESTCTRL_DST_SESSION || fDst == VBOX_GUESTCTRL_DST_ROOT_SVC || fDst == VBOX_GUESTCTRL_DST_BOTH);
2461 Assert(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT);
2462 paParms[0].type = VBOX_HGCM_SVC_PARM_64BIT;
2463 paParms[0].u.uint64 = (uint64_t)paParms[0].u.uint32 | fDst;
2464
2465 /* Make the call. */
2466 int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uMessage, uParms, paParms);
2467 if (RT_FAILURE(vrc))
2468 {
2469 /** @todo What to do here? */
2470 }
2471#else
2472 /* Not needed within testcases. */
2473 int vrc = VINF_SUCCESS;
2474#endif
2475 LogFlowFuncLeaveRC(vrc);
2476 return vrc;
2477}
2478
2479/* static */
2480HRESULT GuestSession::i_setErrorExternal(VirtualBoxBase *pInterface, int rcGuest)
2481{
2482 AssertPtr(pInterface);
2483 AssertMsg(RT_FAILURE(rcGuest), ("Guest rc does not indicate a failure when setting error\n"));
2484
2485 return pInterface->setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, GuestSession::i_guestErrorToString(rcGuest).c_str());
2486}
2487
2488/* Does not do locking; caller is responsible for that! */
2489int GuestSession::i_setSessionStatus(GuestSessionStatus_T sessionStatus, int sessionRc)
2490{
2491 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, sessionRc=%Rrc\n",
2492 mData.mStatus, sessionStatus, sessionRc));
2493
2494 if (sessionStatus == GuestSessionStatus_Error)
2495 {
2496 AssertMsg(RT_FAILURE(sessionRc), ("Guest rc must be an error (%Rrc)\n", sessionRc));
2497 /* Do not allow overwriting an already set error. If this happens
2498 * this means we forgot some error checking/locking somewhere. */
2499 AssertMsg(RT_SUCCESS(mData.mRC), ("Guest rc already set (to %Rrc)\n", mData.mRC));
2500 }
2501 else
2502 AssertMsg(RT_SUCCESS(sessionRc), ("Guest rc must not be an error (%Rrc)\n", sessionRc));
2503
2504 if (mData.mStatus != sessionStatus)
2505 {
2506 mData.mStatus = sessionStatus;
2507 mData.mRC = sessionRc;
2508
2509 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
2510 HRESULT hr = errorInfo.createObject();
2511 ComAssertComRC(hr);
2512 int rc2 = errorInfo->initEx(VBOX_E_IPRT_ERROR, sessionRc,
2513 COM_IIDOF(IGuestSession), getComponentName(),
2514 i_guestErrorToString(sessionRc));
2515 AssertRC(rc2);
2516
2517 fireGuestSessionStateChangedEvent(mEventSource, this,
2518 mData.mSession.mID, sessionStatus, errorInfo);
2519 }
2520
2521 return VINF_SUCCESS;
2522}
2523
2524int GuestSession::i_signalWaiters(GuestSessionWaitResult_T enmWaitResult, int rc /*= VINF_SUCCESS */)
2525{
2526 RT_NOREF(enmWaitResult, rc);
2527
2528 /*LogFlowThisFunc(("enmWaitResult=%d, rc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n",
2529 enmWaitResult, rc, mData.mWaitCount, mData.mWaitEvent));*/
2530
2531 /* Note: No write locking here -- already done in the caller. */
2532
2533 int vrc = VINF_SUCCESS;
2534 /*if (mData.mWaitEvent)
2535 vrc = mData.mWaitEvent->Signal(enmWaitResult, rc);*/
2536 LogFlowFuncLeaveRC(vrc);
2537 return vrc;
2538}
2539
2540/**
2541 * Determines the protocol version (sets mData.mProtocolVersion).
2542 *
2543 * This is called from the init method prior to to establishing a guest
2544 * session.
2545 *
2546 * @return IPRT status code.
2547 */
2548int GuestSession::i_determineProtocolVersion(void)
2549{
2550 /*
2551 * We currently do this based on the reported guest additions version,
2552 * ASSUMING that VBoxService and VBoxDrv are at the same version.
2553 */
2554 ComObjPtr<Guest> pGuest = mParent;
2555 AssertReturn(!pGuest.isNull(), VERR_NOT_SUPPORTED);
2556 uint32_t uGaVersion = pGuest->i_getAdditionsVersion();
2557
2558 /* Everyone supports version one, if they support anything at all. */
2559 mData.mProtocolVersion = 1;
2560
2561 /* Guest control 2.0 was introduced with 4.3.0. */
2562 if (uGaVersion >= VBOX_FULL_VERSION_MAKE(4,3,0))
2563 mData.mProtocolVersion = 2; /* Guest control 2.0. */
2564
2565 LogFlowThisFunc(("uGaVersion=%u.%u.%u => mProtocolVersion=%u\n",
2566 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
2567 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
2568
2569 /*
2570 * Inform the user about outdated guest additions (VM release log).
2571 */
2572 if (mData.mProtocolVersion < 2)
2573 LogRelMax(3, (tr("Warning: Guest Additions v%u.%u.%u only supports the older guest control protocol version %u.\n"
2574 " Please upgrade GAs to the current version to get full guest control capabilities.\n"),
2575 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
2576 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
2577
2578 return VINF_SUCCESS;
2579}
2580
2581int GuestSession::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *prcGuest)
2582{
2583 LogFlowThisFuncEnter();
2584
2585 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
2586
2587 /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, prcGuest=%p\n",
2588 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, prcGuest));*/
2589
2590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2591
2592 /* Did some error occur before? Then skip waiting and return. */
2593 if (mData.mStatus == GuestSessionStatus_Error)
2594 {
2595 waitResult = GuestSessionWaitResult_Error;
2596 AssertMsg(RT_FAILURE(mData.mRC), ("No error rc (%Rrc) set when guest session indicated an error\n", mData.mRC));
2597 if (prcGuest)
2598 *prcGuest = mData.mRC; /* Return last set error. */
2599 return VERR_GSTCTL_GUEST_ERROR;
2600 }
2601
2602 /* Guest Additions < 4.3 don't support session handling, skip. */
2603 if (mData.mProtocolVersion < 2)
2604 {
2605 waitResult = GuestSessionWaitResult_WaitFlagNotSupported;
2606
2607 LogFlowThisFunc(("Installed Guest Additions don't support waiting for dedicated sessions, skipping\n"));
2608 return VINF_SUCCESS;
2609 }
2610
2611 waitResult = GuestSessionWaitResult_None;
2612 if (fWaitFlags & GuestSessionWaitForFlag_Terminate)
2613 {
2614 switch (mData.mStatus)
2615 {
2616 case GuestSessionStatus_Terminated:
2617 case GuestSessionStatus_Down:
2618 waitResult = GuestSessionWaitResult_Terminate;
2619 break;
2620
2621 case GuestSessionStatus_TimedOutKilled:
2622 case GuestSessionStatus_TimedOutAbnormally:
2623 waitResult = GuestSessionWaitResult_Timeout;
2624 break;
2625
2626 case GuestSessionStatus_Error:
2627 /* Handled above. */
2628 break;
2629
2630 case GuestSessionStatus_Started:
2631 waitResult = GuestSessionWaitResult_Start;
2632 break;
2633
2634 case GuestSessionStatus_Undefined:
2635 case GuestSessionStatus_Starting:
2636 /* Do the waiting below. */
2637 break;
2638
2639 default:
2640 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2641 return VERR_NOT_IMPLEMENTED;
2642 }
2643 }
2644 else if (fWaitFlags & GuestSessionWaitForFlag_Start)
2645 {
2646 switch (mData.mStatus)
2647 {
2648 case GuestSessionStatus_Started:
2649 case GuestSessionStatus_Terminating:
2650 case GuestSessionStatus_Terminated:
2651 case GuestSessionStatus_Down:
2652 waitResult = GuestSessionWaitResult_Start;
2653 break;
2654
2655 case GuestSessionStatus_Error:
2656 waitResult = GuestSessionWaitResult_Error;
2657 break;
2658
2659 case GuestSessionStatus_TimedOutKilled:
2660 case GuestSessionStatus_TimedOutAbnormally:
2661 waitResult = GuestSessionWaitResult_Timeout;
2662 break;
2663
2664 case GuestSessionStatus_Undefined:
2665 case GuestSessionStatus_Starting:
2666 /* Do the waiting below. */
2667 break;
2668
2669 default:
2670 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2671 return VERR_NOT_IMPLEMENTED;
2672 }
2673 }
2674
2675 LogFlowThisFunc(("sessionStatus=%RU32, sessionRc=%Rrc, waitResult=%RU32\n",
2676 mData.mStatus, mData.mRC, waitResult));
2677
2678 /* No waiting needed? Return immediately using the last set error. */
2679 if (waitResult != GuestSessionWaitResult_None)
2680 {
2681 if (prcGuest)
2682 *prcGuest = mData.mRC; /* Return last set error (if any). */
2683 return RT_SUCCESS(mData.mRC) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
2684 }
2685
2686 int vrc;
2687
2688 GuestWaitEvent *pEvent = NULL;
2689 GuestEventTypes eventTypes;
2690 try
2691 {
2692 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
2693
2694 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
2695 }
2696 catch (std::bad_alloc &)
2697 {
2698 vrc = VERR_NO_MEMORY;
2699 }
2700
2701 if (RT_FAILURE(vrc))
2702 return vrc;
2703
2704 alock.release(); /* Release lock before waiting. */
2705
2706 GuestSessionStatus_T sessionStatus;
2707 vrc = i_waitForStatusChange(pEvent, fWaitFlags,
2708 uTimeoutMS, &sessionStatus, prcGuest);
2709 if (RT_SUCCESS(vrc))
2710 {
2711 switch (sessionStatus)
2712 {
2713 case GuestSessionStatus_Started:
2714 waitResult = GuestSessionWaitResult_Start;
2715 break;
2716
2717 case GuestSessionStatus_Terminated:
2718 waitResult = GuestSessionWaitResult_Terminate;
2719 break;
2720
2721 case GuestSessionStatus_TimedOutKilled:
2722 case GuestSessionStatus_TimedOutAbnormally:
2723 waitResult = GuestSessionWaitResult_Timeout;
2724 break;
2725
2726 case GuestSessionStatus_Down:
2727 waitResult = GuestSessionWaitResult_Terminate;
2728 break;
2729
2730 case GuestSessionStatus_Error:
2731 waitResult = GuestSessionWaitResult_Error;
2732 break;
2733
2734 default:
2735 waitResult = GuestSessionWaitResult_Status;
2736 break;
2737 }
2738 }
2739
2740 unregisterWaitEvent(pEvent);
2741
2742 LogFlowFuncLeaveRC(vrc);
2743 return vrc;
2744}
2745
2746int GuestSession::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
2747 GuestSessionStatus_T *pSessionStatus, int *prcGuest)
2748{
2749 RT_NOREF(fWaitFlags);
2750 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
2751
2752 VBoxEventType_T evtType;
2753 ComPtr<IEvent> pIEvent;
2754 int vrc = waitForEvent(pEvent, uTimeoutMS,
2755 &evtType, pIEvent.asOutParam());
2756 if (RT_SUCCESS(vrc))
2757 {
2758 Assert(evtType == VBoxEventType_OnGuestSessionStateChanged);
2759
2760 ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pIEvent;
2761 Assert(!pChangedEvent.isNull());
2762
2763 GuestSessionStatus_T sessionStatus;
2764 pChangedEvent->COMGETTER(Status)(&sessionStatus);
2765 if (pSessionStatus)
2766 *pSessionStatus = sessionStatus;
2767
2768 ComPtr<IVirtualBoxErrorInfo> errorInfo;
2769 HRESULT hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
2770 ComAssertComRC(hr);
2771
2772 LONG lGuestRc;
2773 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
2774 ComAssertComRC(hr);
2775 if (RT_FAILURE((int)lGuestRc))
2776 vrc = VERR_GSTCTL_GUEST_ERROR;
2777 if (prcGuest)
2778 *prcGuest = (int)lGuestRc;
2779
2780 LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n",
2781 mData.mSession.mID, sessionStatus,
2782 RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
2783 }
2784
2785 LogFlowFuncLeaveRC(vrc);
2786 return vrc;
2787}
2788
2789// implementation of public methods
2790/////////////////////////////////////////////////////////////////////////////
2791
2792HRESULT GuestSession::close()
2793{
2794 AutoCaller autoCaller(this);
2795 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2796
2797 LogFlowThisFuncEnter();
2798
2799 /* Note: Don't check if the session is ready via i_isReadyExternal() here;
2800 * the session (already) could be in a stopped / aborted state. */
2801
2802 /* Close session on guest. */
2803 int rcGuest = VINF_SUCCESS;
2804 int vrc = i_closeSession(0 /* Flags */, 30 * 1000 /* Timeout */, &rcGuest);
2805 /* On failure don't return here, instead do all the cleanup
2806 * work first and then return an error. */
2807
2808 /* Remove ourselves from the session list. */
2809 AssertPtr(mParent);
2810 int vrc2 = mParent->i_sessionRemove(mData.mSession.mID);
2811 if (vrc2 == VERR_NOT_FOUND) /* Not finding the session anymore isn't critical. */
2812 vrc2 = VINF_SUCCESS;
2813
2814 if (RT_SUCCESS(vrc))
2815 vrc = vrc2;
2816
2817 LogFlowThisFunc(("Returning rc=%Rrc, rcGuest=%Rrc\n", vrc, rcGuest));
2818
2819 if (RT_FAILURE(vrc))
2820 {
2821 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2822 return GuestSession::i_setErrorExternal(this, rcGuest);
2823 return setError(VBOX_E_IPRT_ERROR, tr("Closing guest session failed with %Rrc"), vrc);
2824 }
2825
2826 return S_OK;
2827}
2828
2829HRESULT GuestSession::fileCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2830 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2831{
2832 RT_NOREF(aSource, aDestination, aFlags, aProgress);
2833 ReturnComNotImplemented();
2834}
2835
2836HRESULT GuestSession::fileCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2837 const std::vector<FileCopyFlag_T> &aFlags,
2838 ComPtr<IProgress> &aProgress)
2839{
2840 AutoCaller autoCaller(this);
2841 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2842
2843 uint32_t fFlags = FileCopyFlag_None;
2844 if (aFlags.size())
2845 {
2846 for (size_t i = 0; i < aFlags.size(); i++)
2847 fFlags |= aFlags[i];
2848 }
2849
2850 GuestSessionFsSourceSet SourceSet;
2851
2852 GuestSessionFsSourceSpec source;
2853 source.strSource = aSource;
2854 source.enmType = FsObjType_File;
2855 source.enmPathStyle = i_getPathStyle();
2856 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
2857
2858 SourceSet.push_back(source);
2859
2860 return i_copyFromGuest(SourceSet, aDestination, aProgress);
2861}
2862
2863HRESULT GuestSession::fileCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2864 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2865{
2866 AutoCaller autoCaller(this);
2867 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2868
2869 uint32_t fFlags = FileCopyFlag_None;
2870 if (aFlags.size())
2871 {
2872 for (size_t i = 0; i < aFlags.size(); i++)
2873 fFlags |= aFlags[i];
2874 }
2875
2876 GuestSessionFsSourceSet SourceSet;
2877
2878 GuestSessionFsSourceSpec source;
2879 source.strSource = aSource;
2880 source.enmType = FsObjType_File;
2881 source.enmPathStyle = i_getPathStyle();
2882 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
2883
2884 SourceSet.push_back(source);
2885
2886 return i_copyToGuest(SourceSet, aDestination, aProgress);
2887}
2888
2889HRESULT GuestSession::copyFromGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
2890 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
2891 ComPtr<IProgress> &aProgress)
2892{
2893 AutoCaller autoCaller(this);
2894 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2895
2896 const size_t cSources = aSources.size();
2897 if ( (aFilters.size() && aFilters.size() != cSources)
2898 || (aFlags.size() && aFlags.size() != cSources))
2899 {
2900 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
2901 }
2902
2903 GuestSessionFsSourceSet SourceSet;
2904
2905 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
2906 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
2907 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
2908
2909 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
2910 const bool fFollowSymlinks = true; /** @todo Ditto. */
2911
2912 while (itSource != aSources.end())
2913 {
2914 GuestFsObjData objData;
2915 int rcGuest;
2916 int vrc = i_fsQueryInfo(*(itSource), fFollowSymlinks, objData, &rcGuest);
2917 if ( RT_FAILURE(vrc)
2918 && !fContinueOnErrors)
2919 {
2920 if (GuestProcess::i_isGuestError(vrc))
2921 return setError(E_FAIL, tr("Unable to query type for source '%s': %s"), (*itSource).c_str(),
2922 GuestProcess::i_guestErrorToString(rcGuest).c_str());
2923 else
2924 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
2925 }
2926
2927 Utf8Str strFlags;
2928 if (itFlags != aFlags.end())
2929 {
2930 strFlags = *itFlags;
2931 ++itFlags;
2932 }
2933
2934 Utf8Str strFilter;
2935 if (itFilter != aFilters.end())
2936 {
2937 strFilter = *itFilter;
2938 ++itFilter;
2939 }
2940
2941 GuestSessionFsSourceSpec source;
2942 source.strSource = *itSource;
2943 source.strFilter = strFilter;
2944 source.enmType = objData.mType;
2945 source.enmPathStyle = i_getPathStyle();
2946
2947 HRESULT hrc;
2948 if (source.enmType == FsObjType_Directory)
2949 {
2950 hrc = GuestSession::i_directoryCopyFlagFromStr(strFlags, &source.Type.Dir.fCopyFlags);
2951 source.Type.Dir.fRecursive = true; /* Implicit. */
2952 }
2953 else if (source.enmType == FsObjType_File)
2954 hrc = GuestSession::i_fileCopyFlagFromStr(strFlags, &source.Type.File.fCopyFlags);
2955 else
2956 return setError(E_INVALIDARG, tr("Source type %d invalid / not supported"), source.enmType);
2957 if (FAILED(hrc))
2958 return hrc;
2959
2960 SourceSet.push_back(source);
2961
2962 ++itSource;
2963 }
2964
2965 return i_copyFromGuest(SourceSet, aDestination, aProgress);
2966}
2967
2968HRESULT GuestSession::copyToGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
2969 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
2970 ComPtr<IProgress> &aProgress)
2971{
2972 AutoCaller autoCaller(this);
2973 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2974
2975 const size_t cSources = aSources.size();
2976 if ( (aFilters.size() && aFilters.size() != cSources)
2977 || (aFlags.size() && aFlags.size() != cSources))
2978 {
2979 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
2980 }
2981
2982 GuestSessionFsSourceSet SourceSet;
2983
2984 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
2985 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
2986 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
2987
2988 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
2989
2990 while (itSource != aSources.end())
2991 {
2992 RTFSOBJINFO objInfo;
2993 int vrc = RTPathQueryInfo((*itSource).c_str(), &objInfo, RTFSOBJATTRADD_NOTHING);
2994 if ( RT_FAILURE(vrc)
2995 && !fContinueOnErrors)
2996 {
2997 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
2998 }
2999
3000 Utf8Str strFlags;
3001 if (itFlags != aFlags.end())
3002 {
3003 strFlags = *itFlags;
3004 ++itFlags;
3005 }
3006
3007 Utf8Str strFilter;
3008 if (itFilter != aFilters.end())
3009 {
3010 strFilter = *itFilter;
3011 ++itFilter;
3012 }
3013
3014 GuestSessionFsSourceSpec source;
3015 source.strSource = *itSource;
3016 source.strFilter = strFilter;
3017 source.enmType = GuestBase::fileModeToFsObjType(objInfo.Attr.fMode);
3018 source.enmPathStyle = i_getPathStyle();
3019
3020 HRESULT hrc;
3021 if (source.enmType == FsObjType_Directory)
3022 {
3023 hrc = GuestSession::i_directoryCopyFlagFromStr(strFlags, &source.Type.Dir.fCopyFlags);
3024 source.Type.Dir.fFollowSymlinks = true; /** @todo Add a flag for that in DirectoryCopyFlag_T. Later. */
3025 source.Type.Dir.fRecursive = true; /* Implicit. */
3026 }
3027 else if (source.enmType == FsObjType_File)
3028 hrc = GuestSession::i_fileCopyFlagFromStr(strFlags, &source.Type.File.fCopyFlags);
3029 else
3030 return setError(E_INVALIDARG, tr("Source type %d invalid / not supported"), source.enmType);
3031 if (FAILED(hrc))
3032 return hrc;
3033
3034 SourceSet.push_back(source);
3035
3036 ++itSource;
3037 }
3038
3039 return i_copyToGuest(SourceSet, aDestination, aProgress);
3040}
3041
3042HRESULT GuestSession::directoryCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3043 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3044{
3045 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3046 ReturnComNotImplemented();
3047}
3048
3049HRESULT GuestSession::directoryCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3050 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3051{
3052 AutoCaller autoCaller(this);
3053 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3054
3055 uint32_t fFlags = DirectoryCopyFlag_None;
3056 if (aFlags.size())
3057 {
3058 for (size_t i = 0; i < aFlags.size(); i++)
3059 fFlags |= aFlags[i];
3060 }
3061
3062 GuestSessionFsSourceSet SourceSet;
3063
3064 GuestSessionFsSourceSpec source;
3065 source.strSource = aSource;
3066 source.enmType = FsObjType_Directory;
3067 source.enmPathStyle = i_getPathStyle();
3068 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3069 source.Type.Dir.fRecursive = true; /* Implicit. */
3070
3071 SourceSet.push_back(source);
3072
3073 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3074}
3075
3076HRESULT GuestSession::directoryCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3077 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3078{
3079 AutoCaller autoCaller(this);
3080 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3081
3082 uint32_t fFlags = DirectoryCopyFlag_None;
3083 if (aFlags.size())
3084 {
3085 for (size_t i = 0; i < aFlags.size(); i++)
3086 fFlags |= aFlags[i];
3087 }
3088
3089 GuestSessionFsSourceSet SourceSet;
3090
3091 GuestSessionFsSourceSpec source;
3092 source.strSource = aSource;
3093 source.enmType = FsObjType_Directory;
3094 source.enmPathStyle = i_getPathStyle();
3095 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3096 source.Type.Dir.fFollowSymlinks = true; /** @todo Add a flag for that in DirectoryCopyFlag_T. Later. */
3097 source.Type.Dir.fRecursive = true; /* Implicit. */
3098
3099 SourceSet.push_back(source);
3100
3101 return i_copyToGuest(SourceSet, aDestination, aProgress);
3102}
3103
3104HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode,
3105 const std::vector<DirectoryCreateFlag_T> &aFlags)
3106{
3107 AutoCaller autoCaller(this);
3108 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3109
3110 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3111 return setError(E_INVALIDARG, tr("No directory to create specified"));
3112
3113 uint32_t fFlags = DirectoryCreateFlag_None;
3114 if (aFlags.size())
3115 {
3116 for (size_t i = 0; i < aFlags.size(); i++)
3117 fFlags |= aFlags[i];
3118
3119 if (fFlags)
3120 if (!(fFlags & DirectoryCreateFlag_Parents))
3121 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
3122 }
3123
3124 HRESULT hrc = i_isReadyExternal();
3125 if (FAILED(hrc))
3126 return hrc;
3127
3128 LogFlowThisFuncEnter();
3129
3130 ComObjPtr <GuestDirectory> pDirectory; int rcGuest;
3131 int vrc = i_directoryCreate(aPath, (uint32_t)aMode, fFlags, &rcGuest);
3132 if (RT_FAILURE(vrc))
3133 {
3134 if (GuestProcess::i_isGuestError(vrc))
3135 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
3136 tr("Directory creation failed: %s"), GuestDirectory::i_guestErrorToString(rcGuest).c_str());
3137 else
3138 {
3139 switch (vrc)
3140 {
3141 case VERR_INVALID_PARAMETER:
3142 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: Invalid parameters given"));
3143 break;
3144
3145 case VERR_BROKEN_PIPE:
3146 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: Unexpectedly aborted"));
3147 break;
3148
3149 default:
3150 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: %Rrc"), vrc);
3151 break;
3152 }
3153 }
3154 }
3155
3156 return hrc;
3157}
3158
3159HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath,
3160 BOOL aSecure, com::Utf8Str &aDirectory)
3161{
3162 RT_NOREF(aMode, aSecure);
3163
3164 AutoCaller autoCaller(this);
3165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3166
3167 if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0'))
3168 return setError(E_INVALIDARG, tr("No template specified"));
3169 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3170 return setError(E_INVALIDARG, tr("No directory name specified"));
3171
3172 HRESULT hrc = i_isReadyExternal();
3173 if (FAILED(hrc))
3174 return hrc;
3175
3176 LogFlowThisFuncEnter();
3177
3178 int rcGuest;
3179 int vrc = i_fsCreateTemp(aTemplateName, aPath, true /* Directory */, aDirectory, &rcGuest);
3180 if (!RT_SUCCESS(vrc))
3181 {
3182 switch (vrc)
3183 {
3184 case VERR_GSTCTL_GUEST_ERROR:
3185 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3186 break;
3187
3188 default:
3189 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Temporary directory creation \"%s\" with template \"%s\" failed: %Rrc"),
3190 aPath.c_str(), aTemplateName.c_str(), vrc);
3191 break;
3192 }
3193 }
3194
3195 return hrc;
3196}
3197
3198HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3199{
3200 AutoCaller autoCaller(this);
3201 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3202
3203 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3204 return setError(E_INVALIDARG, tr("No directory to check existence for specified"));
3205
3206 HRESULT hrc = i_isReadyExternal();
3207 if (FAILED(hrc))
3208 return hrc;
3209
3210 LogFlowThisFuncEnter();
3211
3212 GuestFsObjData objData; int rcGuest;
3213 int vrc = i_directoryQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3214 if (RT_SUCCESS(vrc))
3215 *aExists = objData.mType == FsObjType_Directory;
3216 else
3217 {
3218 switch (vrc)
3219 {
3220 case VERR_GSTCTL_GUEST_ERROR:
3221 {
3222 switch (rcGuest)
3223 {
3224 case VERR_PATH_NOT_FOUND:
3225 *aExists = FALSE;
3226 break;
3227 default:
3228 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying directory existence \"%s\" failed: %s"),
3229 aPath.c_str(), GuestProcess::i_guestErrorToString(rcGuest).c_str());
3230 break;
3231 }
3232 break;
3233 }
3234
3235 default:
3236 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying directory existence \"%s\" failed: %Rrc"),
3237 aPath.c_str(), vrc);
3238 break;
3239 }
3240 }
3241
3242 return hrc;
3243}
3244
3245HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter,
3246 const std::vector<DirectoryOpenFlag_T> &aFlags, ComPtr<IGuestDirectory> &aDirectory)
3247{
3248 AutoCaller autoCaller(this);
3249 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3250
3251 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3252 return setError(E_INVALIDARG, tr("No directory to open specified"));
3253 if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0'))
3254 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
3255
3256 uint32_t fFlags = DirectoryOpenFlag_None;
3257 if (aFlags.size())
3258 {
3259 for (size_t i = 0; i < aFlags.size(); i++)
3260 fFlags |= aFlags[i];
3261
3262 if (fFlags)
3263 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
3264 }
3265
3266 HRESULT hrc = i_isReadyExternal();
3267 if (FAILED(hrc))
3268 return hrc;
3269
3270 LogFlowThisFuncEnter();
3271
3272 GuestDirectoryOpenInfo openInfo;
3273 openInfo.mPath = aPath;
3274 openInfo.mFilter = aFilter;
3275 openInfo.mFlags = fFlags;
3276
3277 ComObjPtr <GuestDirectory> pDirectory; int rcGuest;
3278 int vrc = i_directoryOpen(openInfo, pDirectory, &rcGuest);
3279 if (RT_SUCCESS(vrc))
3280 {
3281 /* Return directory object to the caller. */
3282 hrc = pDirectory.queryInterfaceTo(aDirectory.asOutParam());
3283 }
3284 else
3285 {
3286 switch (vrc)
3287 {
3288 case VERR_INVALID_PARAMETER:
3289 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening directory \"%s\" failed; invalid parameters given"),
3290 aPath.c_str());
3291 break;
3292
3293 case VERR_GSTCTL_GUEST_ERROR:
3294 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3295 break;
3296
3297 default:
3298 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3299 break;
3300 }
3301 }
3302
3303 return hrc;
3304}
3305
3306HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath)
3307{
3308 AutoCaller autoCaller(this);
3309 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3310
3311 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3312 return setError(E_INVALIDARG, tr("No directory to remove specified"));
3313
3314 HRESULT hrc = i_isReadyExternal();
3315 if (FAILED(hrc))
3316 return hrc;
3317
3318 LogFlowThisFuncEnter();
3319
3320 /* No flags; only remove the directory when empty. */
3321 uint32_t uFlags = 0;
3322
3323 int rcGuest;
3324 int vrc = i_directoryRemove(aPath, uFlags, &rcGuest);
3325 if (RT_FAILURE(vrc))
3326 {
3327 switch (vrc)
3328 {
3329 case VERR_NOT_SUPPORTED:
3330 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3331 tr("Handling removing guest directories not supported by installed Guest Additions"));
3332 break;
3333
3334 case VERR_GSTCTL_GUEST_ERROR:
3335 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3336 break;
3337
3338 default:
3339 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3340 break;
3341 }
3342 }
3343
3344 return hrc;
3345}
3346
3347HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector<DirectoryRemoveRecFlag_T> &aFlags,
3348 ComPtr<IProgress> &aProgress)
3349{
3350 RT_NOREF(aFlags);
3351
3352 AutoCaller autoCaller(this);
3353 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3354
3355 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3356 return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
3357
3358 uint32_t fFlags = DIRREMOVEREC_FLAG_NONE;
3359 if (aFlags.size())
3360 {
3361 for (size_t i = 0; i < aFlags.size(); i++)
3362 {
3363 switch (aFlags[i])
3364 {
3365 case DirectoryRemoveRecFlag_ContentAndDir:
3366 fFlags = DIRREMOVEREC_FLAG_RECURSIVE | DIRREMOVEREC_FLAG_CONTENT_AND_DIR;
3367 break;
3368
3369 case DirectoryRemoveRecFlag_ContentOnly:
3370 fFlags = DIRREMOVEREC_FLAG_RECURSIVE | DIRREMOVEREC_FLAG_CONTENT_ONLY;
3371 break;
3372
3373 default:
3374 return setError(E_INVALIDARG, tr("Invalid flags specified"));
3375 }
3376 }
3377 }
3378
3379 HRESULT hrc = i_isReadyExternal();
3380 if (FAILED(hrc))
3381 return hrc;
3382
3383 LogFlowThisFuncEnter();
3384
3385 ComObjPtr<Progress> pProgress;
3386 hrc = pProgress.createObject();
3387 if (SUCCEEDED(hrc))
3388 hrc = pProgress->init(static_cast<IGuestSession *>(this),
3389 Bstr(tr("Removing guest directory")).raw(),
3390 TRUE /*aCancelable*/);
3391 if (FAILED(hrc))
3392 return hrc;
3393
3394 /* Note: At the moment we don't supply progress information while
3395 * deleting a guest directory recursively. So just complete
3396 * the progress object right now. */
3397 /** @todo Implement progress reporting on guest directory deletion! */
3398 hrc = pProgress->i_notifyComplete(S_OK);
3399 if (FAILED(hrc))
3400 return hrc;
3401
3402 int rcGuest;
3403 int vrc = i_directoryRemove(aPath, fFlags, &rcGuest);
3404 if (RT_FAILURE(vrc))
3405 {
3406 switch (vrc)
3407 {
3408 case VERR_NOT_SUPPORTED:
3409 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3410 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
3411 break;
3412
3413 case VERR_GSTCTL_GUEST_ERROR:
3414 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3415 break;
3416
3417 default:
3418 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
3419 aPath.c_str(), vrc);
3420 break;
3421 }
3422 }
3423 else
3424 {
3425 pProgress.queryInterfaceTo(aProgress.asOutParam());
3426 }
3427
3428 return hrc;
3429}
3430
3431HRESULT GuestSession::environmentScheduleSet(const com::Utf8Str &aName, const com::Utf8Str &aValue)
3432{
3433 AutoCaller autoCaller(this);
3434 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3435
3436 HRESULT hrc;
3437 if (RT_LIKELY(aName.isNotEmpty()))
3438 {
3439 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3440 {
3441 LogFlowThisFuncEnter();
3442
3443 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3444 int vrc = mData.mEnvironmentChanges.setVariable(aName, aValue);
3445 if (RT_SUCCESS(vrc))
3446 hrc = S_OK;
3447 else
3448 hrc = setErrorVrc(vrc);
3449 }
3450 else
3451 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3452 }
3453 else
3454 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3455
3456 LogFlowThisFuncLeave();
3457 return hrc;
3458}
3459
3460HRESULT GuestSession::environmentScheduleUnset(const com::Utf8Str &aName)
3461{
3462 AutoCaller autoCaller(this);
3463 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3464
3465 HRESULT hrc;
3466 if (RT_LIKELY(aName.isNotEmpty()))
3467 {
3468 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3469 {
3470 LogFlowThisFuncEnter();
3471
3472 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3473 int vrc = mData.mEnvironmentChanges.unsetVariable(aName);
3474 if (RT_SUCCESS(vrc))
3475 hrc = S_OK;
3476 else
3477 hrc = setErrorVrc(vrc);
3478 }
3479 else
3480 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3481 }
3482 else
3483 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3484
3485 LogFlowThisFuncLeave();
3486 return hrc;
3487}
3488
3489HRESULT GuestSession::environmentGetBaseVariable(const com::Utf8Str &aName, com::Utf8Str &aValue)
3490{
3491 AutoCaller autoCaller(this);
3492 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3493
3494 HRESULT hrc;
3495 if (RT_LIKELY(aName.isNotEmpty()))
3496 {
3497 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3498 {
3499 LogFlowThisFuncEnter();
3500
3501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3502 if (mData.mpBaseEnvironment)
3503 {
3504 int vrc = mData.mpBaseEnvironment->getVariable(aName, &aValue);
3505 if (RT_SUCCESS(vrc))
3506 hrc = S_OK;
3507 else
3508 hrc = setErrorVrc(vrc);
3509 }
3510 else if (mData.mProtocolVersion < 99999)
3511 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3512 else
3513 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3514 }
3515 else
3516 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3517 }
3518 else
3519 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3520
3521 LogFlowThisFuncLeave();
3522 return hrc;
3523}
3524
3525HRESULT GuestSession::environmentDoesBaseVariableExist(const com::Utf8Str &aName, BOOL *aExists)
3526{
3527 AutoCaller autoCaller(this);
3528 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3529
3530 *aExists = FALSE;
3531
3532 HRESULT hrc;
3533 if (RT_LIKELY(aName.isNotEmpty()))
3534 {
3535 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3536 {
3537 LogFlowThisFuncEnter();
3538
3539 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3540 if (mData.mpBaseEnvironment)
3541 {
3542 hrc = S_OK;
3543 *aExists = mData.mpBaseEnvironment->doesVariableExist(aName);
3544 }
3545 else if (mData.mProtocolVersion < 99999)
3546 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3547 else
3548 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3549 }
3550 else
3551 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3552 }
3553 else
3554 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3555
3556 LogFlowThisFuncLeave();
3557 return hrc;
3558}
3559
3560HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure,
3561 ComPtr<IGuestFile> &aFile)
3562{
3563 RT_NOREF(aTemplateName, aMode, aPath, aSecure, aFile);
3564 ReturnComNotImplemented();
3565}
3566
3567HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3568{
3569 AutoCaller autoCaller(this);
3570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3571
3572 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3573 {
3574 *aExists = FALSE;
3575 return S_OK;
3576 }
3577
3578 HRESULT hrc = i_isReadyExternal();
3579 if (FAILED(hrc))
3580 return hrc;
3581
3582 LogFlowThisFuncEnter();
3583
3584 GuestFsObjData objData; int rcGuest;
3585 int vrc = i_fileQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3586 if (RT_SUCCESS(vrc))
3587 {
3588 *aExists = TRUE;
3589 return S_OK;
3590 }
3591
3592 switch (vrc)
3593 {
3594 case VERR_GSTCTL_GUEST_ERROR:
3595 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3596 break;
3597
3598/** @todo r=bird: what about VERR_PATH_NOT_FOUND and VERR_FILE_NOT_FOUND?
3599 * Where does that get converted to *aExists = FALSE? */
3600 case VERR_NOT_A_FILE:
3601 *aExists = FALSE;
3602 break;
3603
3604 default:
3605 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying file information for \"%s\" failed: %Rrc"),
3606 aPath.c_str(), vrc);
3607 break;
3608 }
3609
3610 return hrc;
3611}
3612
3613HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3614 ULONG aCreationMode, ComPtr<IGuestFile> &aFile)
3615{
3616 LogFlowThisFuncEnter();
3617
3618 const std::vector<FileOpenExFlag_T> EmptyFlags;
3619 return fileOpenEx(aPath, aAccessMode, aOpenAction, FileSharingMode_All, aCreationMode, EmptyFlags, aFile);
3620}
3621
3622HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3623 FileSharingMode_T aSharingMode, ULONG aCreationMode,
3624 const std::vector<FileOpenExFlag_T> &aFlags, ComPtr<IGuestFile> &aFile)
3625{
3626 AutoCaller autoCaller(this);
3627 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3628
3629 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3630 return setError(E_INVALIDARG, tr("No file to open specified"));
3631
3632 HRESULT hrc = i_isReadyExternal();
3633 if (FAILED(hrc))
3634 return hrc;
3635
3636 LogFlowThisFuncEnter();
3637
3638 GuestFileOpenInfo openInfo;
3639 openInfo.mFilename = aPath;
3640 openInfo.mCreationMode = aCreationMode;
3641
3642 /* Validate aAccessMode. */
3643 switch (aAccessMode)
3644 {
3645 case FileAccessMode_ReadOnly:
3646 RT_FALL_THRU();
3647 case FileAccessMode_WriteOnly:
3648 RT_FALL_THRU();
3649 case FileAccessMode_ReadWrite:
3650 openInfo.mAccessMode = aAccessMode;
3651 break;
3652 case FileAccessMode_AppendOnly:
3653 RT_FALL_THRU();
3654 case FileAccessMode_AppendRead:
3655 return setError(E_NOTIMPL, tr("Append access modes are not yet implemented"));
3656 default:
3657 return setError(E_INVALIDARG, tr("Unknown FileAccessMode value %u (%#x)"), aAccessMode, aAccessMode);
3658 }
3659
3660 /* Validate aOpenAction to the old format. */
3661 switch (aOpenAction)
3662 {
3663 case FileOpenAction_OpenExisting:
3664 RT_FALL_THRU();
3665 case FileOpenAction_OpenOrCreate:
3666 RT_FALL_THRU();
3667 case FileOpenAction_CreateNew:
3668 RT_FALL_THRU();
3669 case FileOpenAction_CreateOrReplace:
3670 RT_FALL_THRU();
3671 case FileOpenAction_OpenExistingTruncated:
3672 RT_FALL_THRU();
3673 case FileOpenAction_AppendOrCreate:
3674 openInfo.mOpenAction = aOpenAction;
3675 break;
3676 default:
3677 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3678 }
3679
3680 /* Validate aSharingMode. */
3681 switch (aSharingMode)
3682 {
3683 case FileSharingMode_All:
3684 openInfo.mSharingMode = aSharingMode;
3685 break;
3686 case FileSharingMode_Read:
3687 case FileSharingMode_Write:
3688 case FileSharingMode_ReadWrite:
3689 case FileSharingMode_Delete:
3690 case FileSharingMode_ReadDelete:
3691 case FileSharingMode_WriteDelete:
3692 return setError(E_NOTIMPL, tr("Only FileSharingMode_All is currently implemented"));
3693
3694 default:
3695 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3696 }
3697
3698 /* Combine and validate flags. */
3699 uint32_t fOpenEx = 0;
3700 for (size_t i = 0; i < aFlags.size(); i++)
3701 fOpenEx = aFlags[i];
3702 if (fOpenEx)
3703 return setError(E_INVALIDARG, tr("Unsupported FileOpenExFlag value(s) in aFlags (%#x)"), fOpenEx);
3704 openInfo.mfOpenEx = fOpenEx;
3705
3706 ComObjPtr <GuestFile> pFile;
3707 int rcGuest;
3708 int vrc = i_fileOpenEx(aPath, aAccessMode, aOpenAction, aSharingMode, aCreationMode, aFlags, pFile, &rcGuest);
3709 if (RT_SUCCESS(vrc))
3710 /* Return directory object to the caller. */
3711 hrc = pFile.queryInterfaceTo(aFile.asOutParam());
3712 else
3713 {
3714 switch (vrc)
3715 {
3716 case VERR_NOT_SUPPORTED:
3717 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3718 tr("Handling guest files not supported by installed Guest Additions"));
3719 break;
3720
3721 case VERR_GSTCTL_GUEST_ERROR:
3722 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3723 break;
3724
3725 default:
3726 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3727 break;
3728 }
3729 }
3730
3731 return hrc;
3732}
3733
3734HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymlinks, LONG64 *aSize)
3735{
3736 AutoCaller autoCaller(this);
3737 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3738
3739 if (aPath.isEmpty())
3740 return setError(E_INVALIDARG, tr("No path specified"));
3741
3742 HRESULT hrc = i_isReadyExternal();
3743 if (FAILED(hrc))
3744 return hrc;
3745
3746 int64_t llSize; int rcGuest;
3747 int vrc = i_fileQuerySize(aPath, aFollowSymlinks != FALSE, &llSize, &rcGuest);
3748 if (RT_SUCCESS(vrc))
3749 {
3750 *aSize = llSize;
3751 }
3752 else
3753 {
3754 if (GuestProcess::i_isGuestError(vrc))
3755 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3756 else
3757 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying file size failed: %Rrc"), vrc);
3758 }
3759
3760 return hrc;
3761}
3762
3763HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3764{
3765 AutoCaller autoCaller(this);
3766 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3767
3768 if (aPath.isEmpty())
3769 return setError(E_INVALIDARG, tr("No path specified"));
3770
3771 HRESULT hrc = i_isReadyExternal();
3772 if (FAILED(hrc))
3773 return hrc;
3774
3775 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3776
3777 *aExists = false;
3778
3779 GuestFsObjData objData;
3780 int rcGuest;
3781 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3782 if (RT_SUCCESS(vrc))
3783 {
3784 *aExists = TRUE;
3785 }
3786 else
3787 {
3788 if (GuestProcess::i_isGuestError(vrc))
3789 {
3790 if ( rcGuest == VERR_NOT_A_FILE
3791 || rcGuest == VERR_PATH_NOT_FOUND
3792 || rcGuest == VERR_FILE_NOT_FOUND
3793 || rcGuest == VERR_INVALID_NAME)
3794 {
3795 hrc = S_OK; /* Ignore these vrc values. */
3796 }
3797 else
3798 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3799 }
3800 else
3801 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3802 }
3803
3804 return hrc;
3805}
3806
3807HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSymlinks, ComPtr<IGuestFsObjInfo> &aInfo)
3808{
3809 AutoCaller autoCaller(this);
3810 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3811
3812 if (aPath.isEmpty())
3813 return setError(E_INVALIDARG, tr("No path specified"));
3814
3815 HRESULT hrc = i_isReadyExternal();
3816 if (FAILED(hrc))
3817 return hrc;
3818
3819 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3820
3821 GuestFsObjData Info; int rcGuest;
3822 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, Info, &rcGuest);
3823 if (RT_SUCCESS(vrc))
3824 {
3825 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
3826 hrc = ptrFsObjInfo.createObject();
3827 if (SUCCEEDED(hrc))
3828 {
3829 vrc = ptrFsObjInfo->init(Info);
3830 if (RT_SUCCESS(vrc))
3831 hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam());
3832 else
3833 hrc = setErrorVrc(vrc);
3834 }
3835 }
3836 else
3837 {
3838 if (GuestProcess::i_isGuestError(vrc))
3839 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3840 else
3841 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3842 }
3843
3844 return hrc;
3845}
3846
3847HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath)
3848{
3849 AutoCaller autoCaller(this);
3850 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3851
3852 if (RT_UNLIKELY(aPath.isEmpty()))
3853 return setError(E_INVALIDARG, tr("No path specified"));
3854
3855 HRESULT hrc = i_isReadyExternal();
3856 if (FAILED(hrc))
3857 return hrc;
3858
3859 LogFlowThisFunc(("aPath=%s\n", aPath.c_str()));
3860
3861 int rcGuest;
3862 int vrc = i_fileRemove(aPath, &rcGuest);
3863 if (RT_FAILURE(vrc))
3864 {
3865 if (GuestProcess::i_isGuestError(vrc))
3866 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3867 else
3868 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3869 }
3870
3871 return hrc;
3872}
3873
3874HRESULT GuestSession::fsObjRemoveArray(const std::vector<com::Utf8Str> &aPaths, ComPtr<IProgress> &aProgress)
3875{
3876 AutoCaller autoCaller(this);
3877 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3878
3879 RT_NOREF(aPaths, aProgress);
3880
3881 return E_NOTIMPL;
3882}
3883
3884HRESULT GuestSession::fsObjRename(const com::Utf8Str &aSource,
3885 const com::Utf8Str &aDestination,
3886 const std::vector<FsObjRenameFlag_T> &aFlags)
3887{
3888 AutoCaller autoCaller(this);
3889 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3890
3891 if (RT_UNLIKELY(aSource.isEmpty()))
3892 return setError(E_INVALIDARG, tr("No source path specified"));
3893
3894 if (RT_UNLIKELY(aDestination.isEmpty()))
3895 return setError(E_INVALIDARG, tr("No destination path specified"));
3896
3897 HRESULT hrc = i_isReadyExternal();
3898 if (FAILED(hrc))
3899 return hrc;
3900
3901 /* Combine, validate and convert flags. */
3902 uint32_t fApiFlags = 0;
3903 for (size_t i = 0; i < aFlags.size(); i++)
3904 fApiFlags |= aFlags[i];
3905 if (fApiFlags & ~((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace))
3906 return setError(E_INVALIDARG, tr("Unknown rename flag: %#x"), fApiFlags);
3907
3908 LogFlowThisFunc(("aSource=%s, aDestination=%s\n", aSource.c_str(), aDestination.c_str()));
3909
3910 AssertCompile(FsObjRenameFlag_NoReplace == 0);
3911 AssertCompile(FsObjRenameFlag_Replace != 0);
3912 uint32_t fBackend;
3913 if ((fApiFlags & ((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace)) == FsObjRenameFlag_Replace)
3914 fBackend = PATHRENAME_FLAG_REPLACE;
3915 else
3916 fBackend = PATHRENAME_FLAG_NO_REPLACE;
3917
3918 /* Call worker to do the job. */
3919 int rcGuest;
3920 int vrc = i_pathRename(aSource, aDestination, fBackend, &rcGuest);
3921 if (RT_FAILURE(vrc))
3922 {
3923 switch (vrc)
3924 {
3925 case VERR_NOT_SUPPORTED:
3926 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3927 tr("Handling renaming guest directories not supported by installed Guest Additions"));
3928 break;
3929
3930 case VERR_GSTCTL_GUEST_ERROR:
3931 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Renaming guest directory failed: %Rrc"), rcGuest);
3932 break;
3933
3934 default:
3935 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming guest directory \"%s\" failed: %Rrc"),
3936 aSource.c_str(), vrc);
3937 break;
3938 }
3939 }
3940
3941 return hrc;
3942}
3943
3944HRESULT GuestSession::fsObjMove(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3945 const std::vector<FsObjMoveFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3946{
3947 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3948 ReturnComNotImplemented();
3949}
3950
3951HRESULT GuestSession::fsObjMoveArray(const std::vector<com::Utf8Str> &aSource,
3952 const com::Utf8Str &aDestination,
3953 const std::vector<FsObjMoveFlag_T> &aFlags,
3954 ComPtr<IProgress> &aProgress)
3955{
3956 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3957 ReturnComNotImplemented();
3958}
3959
3960HRESULT GuestSession::fsObjCopyArray(const std::vector<com::Utf8Str> &aSource,
3961 const com::Utf8Str &aDestination,
3962 const std::vector<FileCopyFlag_T> &aFlags,
3963 ComPtr<IProgress> &aProgress)
3964{
3965 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3966 ReturnComNotImplemented();
3967}
3968
3969HRESULT GuestSession::fsObjSetACL(const com::Utf8Str &aPath, BOOL aFollowSymlinks, const com::Utf8Str &aAcl, ULONG aMode)
3970{
3971 RT_NOREF(aPath, aFollowSymlinks, aAcl, aMode);
3972 ReturnComNotImplemented();
3973}
3974
3975
3976HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3977 const std::vector<com::Utf8Str> &aEnvironment,
3978 const std::vector<ProcessCreateFlag_T> &aFlags,
3979 ULONG aTimeoutMS, ComPtr<IGuestProcess> &aGuestProcess)
3980{
3981 LogFlowThisFuncEnter();
3982
3983 std::vector<LONG> affinityIgnored;
3984 return processCreateEx(aExecutable, aArguments, aEnvironment, aFlags, aTimeoutMS, ProcessPriority_Default,
3985 affinityIgnored, aGuestProcess);
3986}
3987
3988HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3989 const std::vector<com::Utf8Str> &aEnvironment,
3990 const std::vector<ProcessCreateFlag_T> &aFlags, ULONG aTimeoutMS,
3991 ProcessPriority_T aPriority, const std::vector<LONG> &aAffinity,
3992 ComPtr<IGuestProcess> &aGuestProcess)
3993{
3994 AutoCaller autoCaller(this);
3995 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3996
3997 HRESULT hr = i_isReadyExternal();
3998 if (FAILED(hr))
3999 return hr;
4000
4001 /** @todo r=bird: Check input better? aPriority is passed on to the guest
4002 * without any validation. Flags not existing in this vbox version are
4003 * ignored, potentially doing something entirely different than what the
4004 * caller had in mind. */
4005
4006 /*
4007 * Must have an executable to execute. If none is given, we try use the
4008 * zero'th argument.
4009 */
4010 const char *pszExecutable = aExecutable.c_str();
4011 if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0'))
4012 {
4013 if (aArguments.size() > 0)
4014 pszExecutable = aArguments[0].c_str();
4015 if (pszExecutable == NULL || *pszExecutable == '\0')
4016 return setError(E_INVALIDARG, tr("No command to execute specified"));
4017 }
4018
4019 LogFlowThisFuncEnter();
4020
4021 /*
4022 * Build the process startup info.
4023 */
4024 GuestProcessStartupInfo procInfo;
4025
4026 /* Executable and arguments. */
4027 procInfo.mExecutable = pszExecutable;
4028 if (aArguments.size())
4029 for (size_t i = 0; i < aArguments.size(); i++)
4030 procInfo.mArguments.push_back(aArguments[i]);
4031
4032 /* Combine the environment changes associated with the ones passed in by
4033 the caller, giving priority to the latter. The changes are putenv style
4034 and will be applied to the standard environment for the guest user. */
4035 int vrc = procInfo.mEnvironmentChanges.copy(mData.mEnvironmentChanges);
4036 if (RT_SUCCESS(vrc))
4037 vrc = procInfo.mEnvironmentChanges.applyPutEnvArray(aEnvironment);
4038 if (RT_SUCCESS(vrc))
4039 {
4040 /* Convert the flag array into a mask. */
4041 if (aFlags.size())
4042 for (size_t i = 0; i < aFlags.size(); i++)
4043 procInfo.mFlags |= aFlags[i];
4044
4045 procInfo.mTimeoutMS = aTimeoutMS;
4046
4047 /** @todo use RTCPUSET instead of archaic 64-bit variables! */
4048 if (aAffinity.size())
4049 for (size_t i = 0; i < aAffinity.size(); i++)
4050 if (aAffinity[i])
4051 procInfo.mAffinity |= (uint64_t)1 << i;
4052
4053 procInfo.mPriority = aPriority;
4054
4055 /*
4056 * Create a guest process object.
4057 */
4058 ComObjPtr<GuestProcess> pProcess;
4059 vrc = i_processCreateEx(procInfo, pProcess);
4060 if (RT_SUCCESS(vrc))
4061 {
4062 ComPtr<IGuestProcess> pIProcess;
4063 hr = pProcess.queryInterfaceTo(pIProcess.asOutParam());
4064 if (SUCCEEDED(hr))
4065 {
4066 /*
4067 * Start the process.
4068 */
4069 vrc = pProcess->i_startProcessAsync();
4070 if (RT_SUCCESS(vrc))
4071 {
4072 aGuestProcess = pIProcess;
4073
4074 LogFlowFuncLeaveRC(vrc);
4075 return S_OK;
4076 }
4077
4078 hr = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc);
4079 }
4080 }
4081 else if (vrc == VERR_GSTCTL_MAX_OBJECTS_REACHED)
4082 hr = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"),
4083 VBOX_GUESTCTRL_MAX_OBJECTS);
4084 else
4085 hr = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc);
4086 }
4087 else
4088 hr = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc);
4089
4090 LogFlowFuncLeaveRC(vrc);
4091 return hr;
4092}
4093
4094HRESULT GuestSession::processGet(ULONG aPid, ComPtr<IGuestProcess> &aGuestProcess)
4095
4096{
4097 AutoCaller autoCaller(this);
4098 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4099
4100 if (aPid == 0)
4101 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
4102
4103 LogFlowThisFunc(("PID=%RU32\n", aPid));
4104
4105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4106
4107 HRESULT hr = S_OK;
4108
4109 ComObjPtr<GuestProcess> pProcess;
4110 int rc = i_processGetByPID(aPid, &pProcess);
4111 if (RT_FAILURE(rc))
4112 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid);
4113
4114 /* This will set (*aProcess) to NULL if pProgress is NULL. */
4115 HRESULT hr2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
4116 if (SUCCEEDED(hr))
4117 hr = hr2;
4118
4119 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", (IGuestProcess*)aGuestProcess, hr));
4120 return hr;
4121}
4122
4123HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType)
4124{
4125 RT_NOREF(aSource, aTarget, aType);
4126 ReturnComNotImplemented();
4127}
4128
4129HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists)
4130
4131{
4132 RT_NOREF(aSymlink, aExists);
4133 ReturnComNotImplemented();
4134}
4135
4136HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector<SymlinkReadFlag_T> &aFlags,
4137 com::Utf8Str &aTarget)
4138{
4139 RT_NOREF(aSymlink, aFlags, aTarget);
4140 ReturnComNotImplemented();
4141}
4142
4143HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
4144{
4145 AutoCaller autoCaller(this);
4146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4147
4148 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4149
4150 LogFlowThisFuncEnter();
4151
4152 HRESULT hrc = S_OK;
4153
4154 /*
4155 * Note: Do not hold any locks here while waiting!
4156 */
4157 int rcGuest; GuestSessionWaitResult_T waitResult;
4158 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &rcGuest);
4159 if (RT_SUCCESS(vrc))
4160 *aReason = waitResult;
4161 else
4162 {
4163 switch (vrc)
4164 {
4165 case VERR_GSTCTL_GUEST_ERROR:
4166 hrc = GuestSession::i_setErrorExternal(this, rcGuest);
4167 break;
4168
4169 case VERR_TIMEOUT:
4170 *aReason = GuestSessionWaitResult_Timeout;
4171 break;
4172
4173 default:
4174 {
4175 const char *pszSessionName = mData.mSession.mName.c_str();
4176 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4177 tr("Waiting for guest session \"%s\" failed: %Rrc"),
4178 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
4179 break;
4180 }
4181 }
4182 }
4183
4184 LogFlowFuncLeaveRC(vrc);
4185 return hrc;
4186}
4187
4188HRESULT GuestSession::waitForArray(const std::vector<GuestSessionWaitForFlag_T> &aWaitFor, ULONG aTimeoutMS,
4189 GuestSessionWaitResult_T *aReason)
4190{
4191 AutoCaller autoCaller(this);
4192 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4193
4194 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4195
4196 LogFlowThisFuncEnter();
4197
4198 /*
4199 * Note: Do not hold any locks here while waiting!
4200 */
4201 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
4202 for (size_t i = 0; i < aWaitFor.size(); i++)
4203 fWaitFor |= aWaitFor[i];
4204
4205 return WaitFor(fWaitFor, aTimeoutMS, aReason);
4206}
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