VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestCtrlPrivate.cpp@ 98532

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

Guest Control: Initial commit (work in progress, disabled by default) [build fixes]. bugref:9783

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 67.3 KB
Line 
1/* $Id: GuestCtrlPrivate.cpp 98532 2023-02-10 15:49:30Z vboxsync $ */
2/** @file
3 * Internal helpers/structures for guest control functionality.
4 */
5
6/*
7 * Copyright (C) 2011-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
33#include "LoggingNew.h"
34
35#ifndef VBOX_WITH_GUEST_CONTROL
36# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
37#endif
38#include "GuestCtrlImplPrivate.h"
39#include "GuestSessionImpl.h"
40#include "VMMDev.h"
41
42#include <iprt/asm.h>
43#include <iprt/cpp/utils.h> /* For unconst(). */
44#include <iprt/ctype.h>
45#ifdef DEBUG
46# include <iprt/file.h>
47#endif
48#include <iprt/fs.h>
49#include <iprt/path.h>
50#include <iprt/rand.h>
51#include <iprt/time.h>
52#include <VBox/AssertGuest.h>
53
54
55/**
56 * Returns a stringyfied error of a guest fs error.
57 *
58 * @returns Stringyfied error.
59 * @param guestErrorInfo Guest error info to get stringyfied error for.
60 */
61/* static */
62Utf8Str GuestFs::guestErrorToString(const GuestErrorInfo &guestErrorInfo)
63{
64 Utf8Str strErr;
65
66 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
67 switch (guestErrorInfo.getVrc())
68 {
69 case VERR_ACCESS_DENIED:
70 strErr.printf(tr("Access to \"%s\" denied"), guestErrorInfo.getWhat().c_str());
71 break;
72
73 case VERR_FILE_NOT_FOUND: /* This is the most likely error. */
74 RT_FALL_THROUGH();
75 case VERR_PATH_NOT_FOUND:
76 strErr.printf(tr("No such file or directory \"%s\""), guestErrorInfo.getWhat().c_str());
77 break;
78
79 case VERR_INVALID_VM_HANDLE:
80 strErr.printf(tr("VMM device is not available (is the VM running?)"));
81 break;
82
83 case VERR_HGCM_SERVICE_NOT_FOUND:
84 strErr.printf(tr("The guest execution service is not available"));
85 break;
86
87 case VERR_BAD_EXE_FORMAT:
88 strErr.printf(tr("The file \"%s\" is not an executable format"), guestErrorInfo.getWhat().c_str());
89 break;
90
91 case VERR_AUTHENTICATION_FAILURE:
92 strErr.printf(tr("The user \"%s\" was not able to logon"), guestErrorInfo.getWhat().c_str());
93 break;
94
95 case VERR_INVALID_NAME:
96 strErr.printf(tr("The file \"%s\" is an invalid name"), guestErrorInfo.getWhat().c_str());
97 break;
98
99 case VERR_TIMEOUT:
100 strErr.printf(tr("The guest did not respond within time"));
101 break;
102
103 case VERR_CANCELLED:
104 strErr.printf(tr("The execution operation was canceled"));
105 break;
106
107 case VERR_GSTCTL_MAX_CID_OBJECTS_REACHED:
108 strErr.printf(tr("Maximum number of concurrent guest processes has been reached"));
109 break;
110
111 case VERR_NOT_FOUND:
112 strErr.printf(tr("The guest execution service is not ready (yet)"));
113 break;
114
115 default:
116 strErr.printf(tr("Unhandled error %Rrc for \"%s\" occurred on guest -- please file a bug report"),
117 guestErrorInfo.getVrc(), guestErrorInfo.getWhat().c_str());
118 break;
119 }
120
121 return strErr;
122}
123
124#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
125/**
126 * Set the file system object data from a given GSTCTLFSOBJINFO struct.
127 *
128 * @returns VBox status code.
129 * @param pFsObjInfo Pointer to GSTCTLFSOBJINFO struct to use.
130 * @param strUser Resolved user name owning the object on the guest.
131 * @param strGroups Resolved user group(s) the object on the guest is associated with.
132 * On Windows there can be multiple groups assigned. The groups are separated with ";"
133 * The first group found is always the primary group.
134 * @param pvACL ACL data associated with the object.
135 * @param cbACL Size of ACL data (in bytes) associated with the object.
136 */
137int GuestFsObjData::FromGuestFsObjInfo(PCGSTCTLFSOBJINFO pFsObjInfo,
138 const Utf8Str &strUser /* = "" */, const Utf8Str &strGroups /* = "" */, const void *pvACL /* = NULL */, size_t cbACL /* = 0 */)
139{
140 RT_NOREF(pvACL, cbACL);
141
142 int vrc;
143
144 mType = GuestBase::fileModeToFsObjType(pFsObjInfo->Attr.fMode);
145
146 mFileAttrs = "";
147 switch (mType)
148 {
149 case FsObjType_File: mFileAttrs += '-'; break;
150 case FsObjType_Directory: mFileAttrs += 'd'; break;
151 case FsObjType_Symlink: mFileAttrs += 'l'; break;
152 case FsObjType_DevChar: mFileAttrs += 'c'; break;
153 case FsObjType_DevBlock: mFileAttrs += 'b'; break;
154 case FsObjType_Fifo: mFileAttrs += 'f'; break;
155 case FsObjType_Socket: mFileAttrs += 's'; break;
156 case FsObjType_WhiteOut: mFileAttrs += 'w'; break;
157 default:
158 mFileAttrs += '?';
159 AssertFailedStmt(vrc = VERR_NOT_SUPPORTED);
160 break;
161 }
162
163#define ADD_ATTR(a_Flag, a_Set, a_Clear) \
164 mFileAttrs += pFsObjInfo->Attr.fMode & a_Flag ? a_Set : a_Clear
165
166 ADD_ATTR(RTFS_UNIX_IRUSR, 'r', '-');
167 ADD_ATTR(RTFS_UNIX_IWUSR, 'w', '-');
168 ADD_ATTR(RTFS_UNIX_IXUSR, 'x', '-');
169
170 ADD_ATTR(RTFS_UNIX_IRGRP, 'r', '-');
171 ADD_ATTR(RTFS_UNIX_IWGRP, 'w', '-');
172 ADD_ATTR(RTFS_UNIX_IXGRP, 'x', '-');
173
174 ADD_ATTR(RTFS_UNIX_IROTH, 'r', '-');
175 ADD_ATTR(RTFS_UNIX_IWOTH, 'w', '-');
176 ADD_ATTR(RTFS_UNIX_IXOTH, 'x', '-');
177
178 /** @todo Implement sticky bits. */
179 mFileAttrs += " "; /* Reserve 3 chars for sticky bits. */
180
181 mFileAttrs += " "; /* Separator. */
182
183 ADD_ATTR(RTFS_DOS_READONLY , 'R', '-');
184 ADD_ATTR(RTFS_DOS_HIDDEN , 'H', '-');
185 ADD_ATTR(RTFS_DOS_SYSTEM , 'S', '-');
186 ADD_ATTR(RTFS_DOS_DIRECTORY , 'D', '-');
187 ADD_ATTR(RTFS_DOS_ARCHIVED , 'A', '-');
188 ADD_ATTR(RTFS_DOS_NT_DEVICE , 'd', '-');
189 ADD_ATTR(RTFS_DOS_NT_NORMAL , 'N', '-');
190 ADD_ATTR(RTFS_DOS_NT_TEMPORARY , 'T', '-');
191 ADD_ATTR(RTFS_DOS_NT_SPARSE_FILE , 'P', '-');
192 ADD_ATTR(RTFS_DOS_NT_REPARSE_POINT , 'J', '-');
193 ADD_ATTR(RTFS_DOS_NT_COMPRESSED , 'C', '-');
194 ADD_ATTR(RTFS_DOS_NT_OFFLINE , 'O', '-');
195 ADD_ATTR(RTFS_DOS_NT_NOT_CONTENT_INDEXED, 'I', '-');
196 ADD_ATTR(RTFS_DOS_NT_ENCRYPTED , 'E', '-');
197
198#undef ADD_ATTR
199
200 mObjectSize = pFsObjInfo->cbObject;
201 mAllocatedSize = pFsObjInfo->cbAllocated;
202 mAccessTime = pFsObjInfo->AccessTime.i64NanosecondsRelativeToUnixEpoch;
203 mBirthTime = pFsObjInfo->BirthTime.i64NanosecondsRelativeToUnixEpoch;
204 mChangeTime = pFsObjInfo->ChangeTime.i64NanosecondsRelativeToUnixEpoch;
205 mModificationTime = pFsObjInfo->ModificationTime.i64NanosecondsRelativeToUnixEpoch;
206 mUserName = strUser;
207 mUID = pFsObjInfo->Attr.u.Unix.uid;
208 mGID = pFsObjInfo->Attr.u.Unix.gid;
209 mGroupName = strGroups; /** @todo Separate multiple group. */
210 mNumHardLinks = pFsObjInfo->Attr.u.Unix.cHardlinks;
211 mNodeIDDevice = pFsObjInfo->Attr.u.Unix.INodeIdDevice;
212 mNodeID = pFsObjInfo->Attr.u.Unix.INodeId;
213 mDeviceNumber = RTFS_IS_DEV_BLOCK(pFsObjInfo->Attr.fMode)
214 || RTFS_IS_DEV_CHAR (pFsObjInfo->Attr.fMode) ? pFsObjInfo->Attr.u.Unix.Device : 0;
215 mGenerationID = pFsObjInfo->Attr.u.Unix.GenerationId;
216 mUserFlags = 0;
217
218 mACL = ""; /** @todo Implement ACL handling. */
219
220 return VINF_SUCCESS;
221}
222#endif /* VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS */
223
224#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
225/**
226 * Extracts the timespec from a given stream block key.
227 *
228 * @return Pointer to handed-in timespec, or NULL if invalid / not found.
229 * @param strmBlk Stream block to extract timespec from.
230 * @param strKey Key to get timespec for.
231 * @param pTimeSpec Where to store the extracted timespec.
232 */
233/* static */
234PRTTIMESPEC GuestFsObjData::TimeSpecFromKey(const GuestToolboxStreamBlock &strmBlk, const Utf8Str &strKey, PRTTIMESPEC pTimeSpec)
235{
236 AssertPtrReturn(pTimeSpec, NULL);
237
238 Utf8Str strTime = strmBlk.GetString(strKey.c_str());
239 if (strTime.isEmpty())
240 return NULL;
241
242 if (!RTTimeSpecFromString(pTimeSpec, strTime.c_str()))
243 return NULL;
244
245 return pTimeSpec;
246}
247
248/**
249 * Extracts the nanoseconds relative from Unix epoch for a given stream block key.
250 *
251 * @return Nanoseconds relative from Unix epoch, or 0 if invalid / not found.
252 * @param strmBlk Stream block to extract nanoseconds from.
253 * @param strKey Key to get nanoseconds for.
254 */
255/* static */
256int64_t GuestFsObjData::UnixEpochNsFromKey(const GuestToolboxStreamBlock &strmBlk, const Utf8Str &strKey)
257{
258 RTTIMESPEC TimeSpec;
259 if (!GuestFsObjData::TimeSpecFromKey(strmBlk, strKey, &TimeSpec))
260 return 0;
261
262 return TimeSpec.i64NanosecondsRelativeToUnixEpoch;
263}
264
265/**
266 * Initializes this object data with a stream block from VBOXSERVICE_TOOL_LS.
267 *
268 * This is also used by FromStat since the output should be identical given that
269 * they use the same output function on the guest side when fLong is true.
270 *
271 * @return VBox status code.
272 * @param strmBlk Stream block to use for initialization.
273 * @param fLong Whether the stream block contains long (detailed) information or not.
274 */
275int GuestFsObjData::FromToolboxLs(const GuestToolboxStreamBlock &strmBlk, bool fLong)
276{
277 LogFlowFunc(("\n"));
278#ifdef DEBUG
279 strmBlk.DumpToLog();
280#endif
281
282 /* Object name. */
283 mName = strmBlk.GetString("name");
284 ASSERT_GUEST_RETURN(mName.isNotEmpty(), VERR_NOT_FOUND);
285
286 /* Type & attributes. */
287 bool fHaveAttribs = false;
288 char szAttribs[32];
289 memset(szAttribs, '?', sizeof(szAttribs) - 1);
290 mType = FsObjType_Unknown;
291 const char *psz = strmBlk.GetString("ftype");
292 if (psz)
293 {
294 fHaveAttribs = true;
295 szAttribs[0] = *psz;
296 switch (*psz)
297 {
298 case '-': mType = FsObjType_File; break;
299 case 'd': mType = FsObjType_Directory; break;
300 case 'l': mType = FsObjType_Symlink; break;
301 case 'c': mType = FsObjType_DevChar; break;
302 case 'b': mType = FsObjType_DevBlock; break;
303 case 'f': mType = FsObjType_Fifo; break;
304 case 's': mType = FsObjType_Socket; break;
305 case 'w': mType = FsObjType_WhiteOut; break;
306 default:
307 AssertMsgFailed(("%s\n", psz));
308 szAttribs[0] = '?';
309 fHaveAttribs = false;
310 break;
311 }
312 }
313 psz = strmBlk.GetString("owner_mask");
314 if ( psz
315 && (psz[0] == '-' || psz[0] == 'r')
316 && (psz[1] == '-' || psz[1] == 'w')
317 && (psz[2] == '-' || psz[2] == 'x'))
318 {
319 szAttribs[1] = psz[0];
320 szAttribs[2] = psz[1];
321 szAttribs[3] = psz[2];
322 fHaveAttribs = true;
323 }
324 psz = strmBlk.GetString("group_mask");
325 if ( psz
326 && (psz[0] == '-' || psz[0] == 'r')
327 && (psz[1] == '-' || psz[1] == 'w')
328 && (psz[2] == '-' || psz[2] == 'x'))
329 {
330 szAttribs[4] = psz[0];
331 szAttribs[5] = psz[1];
332 szAttribs[6] = psz[2];
333 fHaveAttribs = true;
334 }
335 psz = strmBlk.GetString("other_mask");
336 if ( psz
337 && (psz[0] == '-' || psz[0] == 'r')
338 && (psz[1] == '-' || psz[1] == 'w')
339 && (psz[2] == '-' || psz[2] == 'x'))
340 {
341 szAttribs[7] = psz[0];
342 szAttribs[8] = psz[1];
343 szAttribs[9] = psz[2];
344 fHaveAttribs = true;
345 }
346 szAttribs[10] = ' '; /* Reserve three chars for sticky bits. */
347 szAttribs[11] = ' ';
348 szAttribs[12] = ' ';
349 szAttribs[13] = ' '; /* Separator. */
350 psz = strmBlk.GetString("dos_mask");
351 if ( psz
352 && (psz[ 0] == '-' || psz[ 0] == 'R')
353 && (psz[ 1] == '-' || psz[ 1] == 'H')
354 && (psz[ 2] == '-' || psz[ 2] == 'S')
355 && (psz[ 3] == '-' || psz[ 3] == 'D')
356 && (psz[ 4] == '-' || psz[ 4] == 'A')
357 && (psz[ 5] == '-' || psz[ 5] == 'd')
358 && (psz[ 6] == '-' || psz[ 6] == 'N')
359 && (psz[ 7] == '-' || psz[ 7] == 'T')
360 && (psz[ 8] == '-' || psz[ 8] == 'P')
361 && (psz[ 9] == '-' || psz[ 9] == 'J')
362 && (psz[10] == '-' || psz[10] == 'C')
363 && (psz[11] == '-' || psz[11] == 'O')
364 && (psz[12] == '-' || psz[12] == 'I')
365 && (psz[13] == '-' || psz[13] == 'E'))
366 {
367 memcpy(&szAttribs[14], psz, 14);
368 fHaveAttribs = true;
369 }
370 szAttribs[28] = '\0';
371 if (fHaveAttribs)
372 mFileAttrs = szAttribs;
373
374 /* Object size. */
375 int vrc = strmBlk.GetInt64Ex("st_size", &mObjectSize);
376 ASSERT_GUEST_RC_RETURN(vrc, vrc);
377 strmBlk.GetInt64Ex("alloc", &mAllocatedSize);
378
379 /* INode number and device. */
380 psz = strmBlk.GetString("node_id");
381 if (!psz)
382 psz = strmBlk.GetString("cnode_id"); /* copy & past error fixed in 6.0 RC1 */
383 if (psz)
384 mNodeID = RTStrToInt64(psz);
385 mNodeIDDevice = strmBlk.GetUInt32("inode_dev"); /* (Produced by GAs prior to 6.0 RC1.) */
386
387 if (fLong)
388 {
389 /* Dates. */
390 mAccessTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_atime");
391 mBirthTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_birthtime");
392 mChangeTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_ctime");
393 mModificationTime = GuestFsObjData::UnixEpochNsFromKey(strmBlk, "st_mtime");
394
395 /* Owner & group. */
396 mUID = strmBlk.GetInt32("uid");
397 psz = strmBlk.GetString("username");
398 if (psz)
399 mUserName = psz;
400 mGID = strmBlk.GetInt32("gid");
401 psz = strmBlk.GetString("groupname");
402 if (psz)
403 mGroupName = psz;
404
405 /* Misc attributes: */
406 mNumHardLinks = strmBlk.GetUInt32("hlinks", 1);
407 mDeviceNumber = strmBlk.GetUInt32("st_rdev");
408 mGenerationID = strmBlk.GetUInt32("st_gen");
409 mUserFlags = strmBlk.GetUInt32("st_flags");
410
411 /** @todo ACL */
412 }
413
414 LogFlowFuncLeave();
415 return VINF_SUCCESS;
416}
417
418/**
419 * Parses stream block output data which came from the 'rm' (vbox_rm)
420 * VBoxService toolbox command. The result will be stored in this object.
421 *
422 * @returns VBox status code.
423 * @param strmBlk Stream block output data to parse.
424 */
425int GuestFsObjData::FromToolboxRm(const GuestToolboxStreamBlock &strmBlk)
426{
427#ifdef DEBUG
428 strmBlk.DumpToLog();
429#endif
430 /* Object name. */
431 mName = strmBlk.GetString("fname");
432
433 /* Return the stream block's vrc. */
434 return strmBlk.GetVrc();
435}
436
437/**
438 * Parses stream block output data which came from the 'stat' (vbox_stat)
439 * VBoxService toolbox command. The result will be stored in this object.
440 *
441 * @returns VBox status code.
442 * @param strmBlk Stream block output data to parse.
443 */
444int GuestFsObjData::FromToolboxStat(const GuestToolboxStreamBlock &strmBlk)
445{
446 /* Should be identical output. */
447 return GuestFsObjData::FromToolboxLs(strmBlk, true /*fLong*/);
448}
449
450/**
451 * Parses stream block output data which came from the 'mktemp' (vbox_mktemp)
452 * VBoxService toolbox command. The result will be stored in this object.
453 *
454 * @returns VBox status code.
455 * @param strmBlk Stream block output data to parse.
456 */
457int GuestFsObjData::FromToolboxMkTemp(const GuestToolboxStreamBlock &strmBlk)
458{
459 LogFlowFunc(("\n"));
460
461#ifdef DEBUG
462 strmBlk.DumpToLog();
463#endif
464 /* Object name. */
465 mName = strmBlk.GetString("name");
466 ASSERT_GUEST_RETURN(mName.isNotEmpty(), VERR_NOT_FOUND);
467
468 /* Assign the stream block's vrc. */
469 int const vrc = strmBlk.GetVrc();
470 LogFlowFuncLeaveRC(vrc);
471 return vrc;
472}
473
474#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
475
476/**
477 * Returns the IPRT-compatible file mode.
478 * Note: Only handling RTFS_TYPE_ flags are implemented for now.
479 *
480 * @return IPRT file mode.
481 */
482RTFMODE GuestFsObjData::GetFileMode(void) const
483{
484 RTFMODE fMode = 0;
485
486 switch (mType)
487 {
488 case FsObjType_Directory:
489 fMode |= RTFS_TYPE_DIRECTORY;
490 break;
491
492 case FsObjType_File:
493 fMode |= RTFS_TYPE_FILE;
494 break;
495
496 case FsObjType_Symlink:
497 fMode |= RTFS_TYPE_SYMLINK;
498 break;
499
500 default:
501 break;
502 }
503
504 /** @todo Implement more stuff. */
505
506 return fMode;
507}
508
509///////////////////////////////////////////////////////////////////////////////
510
511#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
512/** @todo *NOT* thread safe yet! */
513/** @todo Add exception handling for STL stuff! */
514
515GuestToolboxStreamBlock::GuestToolboxStreamBlock(void)
516{
517
518}
519
520GuestToolboxStreamBlock::~GuestToolboxStreamBlock()
521{
522 Clear();
523}
524
525/**
526 * Clears (destroys) the currently stored stream pairs.
527 */
528void GuestToolboxStreamBlock::Clear(void)
529{
530 mPairs.clear();
531}
532
533#ifdef DEBUG
534/**
535 * Dumps the currently stored stream pairs to the (debug) log.
536 */
537void GuestToolboxStreamBlock::DumpToLog(void) const
538{
539 LogFlowFunc(("Dumping contents of stream block=0x%p (%ld items):\n",
540 this, mPairs.size()));
541
542 for (GuestCtrlStreamPairMapIterConst it = mPairs.begin();
543 it != mPairs.end(); ++it)
544 {
545 LogFlowFunc(("\t%s=%s\n", it->first.c_str(), it->second.mValue.c_str()));
546 }
547}
548#endif
549
550/**
551 * Returns a 64-bit signed integer of a specified key.
552 *
553 * @return VBox status code. VERR_NOT_FOUND if key was not found.
554 * @param pszKey Name of key to get the value for.
555 * @param piVal Pointer to value to return.
556 */
557int GuestToolboxStreamBlock::GetInt64Ex(const char *pszKey, int64_t *piVal) const
558{
559 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
560 AssertPtrReturn(piVal, VERR_INVALID_POINTER);
561 const char *pszValue = GetString(pszKey);
562 if (pszValue)
563 {
564 *piVal = RTStrToInt64(pszValue);
565 return VINF_SUCCESS;
566 }
567 return VERR_NOT_FOUND;
568}
569
570/**
571 * Returns a 64-bit integer of a specified key.
572 *
573 * @return int64_t Value to return, 0 if not found / on failure.
574 * @param pszKey Name of key to get the value for.
575 */
576int64_t GuestToolboxStreamBlock::GetInt64(const char *pszKey) const
577{
578 int64_t iVal;
579 if (RT_SUCCESS(GetInt64Ex(pszKey, &iVal)))
580 return iVal;
581 return 0;
582}
583
584/**
585 * Returns the current number of stream pairs.
586 *
587 * @return uint32_t Current number of stream pairs.
588 */
589size_t GuestToolboxStreamBlock::GetCount(void) const
590{
591 return mPairs.size();
592}
593
594/**
595 * Gets the return code (name = "rc") of this stream block.
596 *
597 * @return VBox status code.
598 * @retval VERR_NOT_FOUND if the return code string ("rc") was not found.
599 */
600int GuestToolboxStreamBlock::GetVrc(void) const
601{
602 const char *pszValue = GetString("rc");
603 if (pszValue)
604 return RTStrToInt16(pszValue);
605 /** @todo We probably should have a dedicated error for that, VERR_GSTCTL_GUEST_TOOLBOX_whatever. */
606 return VERR_NOT_FOUND;
607}
608
609/**
610 * Returns a string value of a specified key.
611 *
612 * @return uint32_t Pointer to string to return, NULL if not found / on failure.
613 * @param pszKey Name of key to get the value for.
614 */
615const char *GuestToolboxStreamBlock::GetString(const char *pszKey) const
616{
617 AssertPtrReturn(pszKey, NULL);
618
619 try
620 {
621 GuestCtrlStreamPairMapIterConst itPairs = mPairs.find(pszKey);
622 if (itPairs != mPairs.end())
623 return itPairs->second.mValue.c_str();
624 }
625 catch (const std::exception &ex)
626 {
627 RT_NOREF(ex);
628 }
629 return NULL;
630}
631
632/**
633 * Returns a 32-bit unsigned integer of a specified key.
634 *
635 * @return VBox status code. VERR_NOT_FOUND if key was not found.
636 * @param pszKey Name of key to get the value for.
637 * @param puVal Pointer to value to return.
638 */
639int GuestToolboxStreamBlock::GetUInt32Ex(const char *pszKey, uint32_t *puVal) const
640{
641 const char *pszValue = GetString(pszKey);
642 if (pszValue)
643 {
644 *puVal = RTStrToUInt32(pszValue);
645 return VINF_SUCCESS;
646 }
647 return VERR_NOT_FOUND;
648}
649
650/**
651 * Returns a 32-bit signed integer of a specified key.
652 *
653 * @returns 32-bit signed value
654 * @param pszKey Name of key to get the value for.
655 * @param iDefault The default to return on error if not found.
656 */
657int32_t GuestToolboxStreamBlock::GetInt32(const char *pszKey, int32_t iDefault) const
658{
659 const char *pszValue = GetString(pszKey);
660 if (pszValue)
661 {
662 int32_t iRet;
663 int vrc = RTStrToInt32Full(pszValue, 0, &iRet);
664 if (RT_SUCCESS(vrc))
665 return iRet;
666 ASSERT_GUEST_MSG_FAILED(("%s=%s\n", pszKey, pszValue));
667 }
668 return iDefault;
669}
670
671/**
672 * Returns a 32-bit unsigned integer of a specified key.
673 *
674 * @return uint32_t Value to return, 0 if not found / on failure.
675 * @param pszKey Name of key to get the value for.
676 * @param uDefault The default value to return.
677 */
678uint32_t GuestToolboxStreamBlock::GetUInt32(const char *pszKey, uint32_t uDefault /*= 0*/) const
679{
680 uint32_t uVal;
681 if (RT_SUCCESS(GetUInt32Ex(pszKey, &uVal)))
682 return uVal;
683 return uDefault;
684}
685
686/**
687 * Sets a value to a key or deletes a key by setting a NULL value.
688 *
689 * @return VBox status code.
690 * @param pszKey Key name to process.
691 * @param pszValue Value to set. Set NULL for deleting the key.
692 */
693int GuestToolboxStreamBlock::SetValue(const char *pszKey, const char *pszValue)
694{
695 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
696
697 int vrc = VINF_SUCCESS;
698 try
699 {
700 Utf8Str const strKey(pszKey);
701
702 /* Take a shortcut and prevent crashes on some funny versions
703 * of STL if map is empty initially. */
704 if (!mPairs.empty())
705 {
706 GuestCtrlStreamPairMapIter it = mPairs.find(strKey);
707 if (it != mPairs.end())
708 mPairs.erase(it);
709 }
710
711 if (pszValue)
712 {
713 GuestToolboxStreamValue val(pszValue);
714 mPairs[strKey] = val;
715 }
716 }
717 catch (const std::exception &)
718 {
719 /** @todo set vrc? */
720 }
721 return vrc;
722}
723
724///////////////////////////////////////////////////////////////////////////////
725
726GuestToolboxStream::GuestToolboxStream(void)
727 : m_cbMax(_32M)
728 , m_cbAllocated(0)
729 , m_cbUsed(0)
730 , m_offBuffer(0)
731 , m_pbBuffer(NULL) { }
732
733GuestToolboxStream::~GuestToolboxStream(void)
734{
735 Destroy();
736}
737
738/**
739 * Adds data to the internal parser buffer. Useful if there
740 * are multiple rounds of adding data needed.
741 *
742 * @return VBox status code. Will return VERR_TOO_MUCH_DATA if the buffer's maximum (limit) has been reached.
743 * @param pbData Pointer to data to add.
744 * @param cbData Size (in bytes) of data to add.
745 */
746int GuestToolboxStream::AddData(const BYTE *pbData, size_t cbData)
747{
748 AssertPtrReturn(pbData, VERR_INVALID_POINTER);
749 AssertReturn(cbData, VERR_INVALID_PARAMETER);
750
751 int vrc = VINF_SUCCESS;
752
753 /* Rewind the buffer if it's empty. */
754 size_t cbInBuf = m_cbUsed - m_offBuffer;
755 bool const fAddToSet = cbInBuf == 0;
756 if (fAddToSet)
757 m_cbUsed = m_offBuffer = 0;
758
759 /* Try and see if we can simply append the data. */
760 if (cbData + m_cbUsed <= m_cbAllocated)
761 {
762 memcpy(&m_pbBuffer[m_cbUsed], pbData, cbData);
763 m_cbUsed += cbData;
764 }
765 else
766 {
767 /* Move any buffered data to the front. */
768 cbInBuf = m_cbUsed - m_offBuffer;
769 if (cbInBuf == 0)
770 m_cbUsed = m_offBuffer = 0;
771 else if (m_offBuffer) /* Do we have something to move? */
772 {
773 memmove(m_pbBuffer, &m_pbBuffer[m_offBuffer], cbInBuf);
774 m_cbUsed = cbInBuf;
775 m_offBuffer = 0;
776 }
777
778 /* Do we need to grow the buffer? */
779 if (cbData + m_cbUsed > m_cbAllocated)
780 {
781 size_t cbAlloc = m_cbUsed + cbData;
782 if (cbAlloc <= m_cbMax)
783 {
784 cbAlloc = RT_ALIGN_Z(cbAlloc, _64K);
785 void *pvNew = RTMemRealloc(m_pbBuffer, cbAlloc);
786 if (pvNew)
787 {
788 m_pbBuffer = (uint8_t *)pvNew;
789 m_cbAllocated = cbAlloc;
790 }
791 else
792 vrc = VERR_NO_MEMORY;
793 }
794 else
795 vrc = VERR_TOO_MUCH_DATA;
796 }
797
798 /* Finally, copy the data. */
799 if (RT_SUCCESS(vrc))
800 {
801 if (cbData + m_cbUsed <= m_cbAllocated)
802 {
803 memcpy(&m_pbBuffer[m_cbUsed], pbData, cbData);
804 m_cbUsed += cbData;
805 }
806 else
807 vrc = VERR_BUFFER_OVERFLOW;
808 }
809 }
810
811 return vrc;
812}
813
814/**
815 * Destroys the internal data buffer.
816 */
817void GuestToolboxStream::Destroy(void)
818{
819 if (m_pbBuffer)
820 {
821 RTMemFree(m_pbBuffer);
822 m_pbBuffer = NULL;
823 }
824
825 m_cbAllocated = 0;
826 m_cbUsed = 0;
827 m_offBuffer = 0;
828}
829
830#ifdef DEBUG
831/**
832 * Dumps the raw guest process output to a file on the host.
833 * If the file on the host already exists, it will be overwritten.
834 *
835 * @param pszFile Absolute path to host file to dump the output to.
836 */
837void GuestToolboxStream::Dump(const char *pszFile)
838{
839 LogFlowFunc(("Dumping contents of stream=0x%p (cbAlloc=%u, cbSize=%u, cbOff=%u) to %s\n",
840 m_pbBuffer, m_cbAllocated, m_cbUsed, m_offBuffer, pszFile));
841
842 RTFILE hFile;
843 int vrc = RTFileOpen(&hFile, pszFile, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
844 if (RT_SUCCESS(vrc))
845 {
846 vrc = RTFileWrite(hFile, m_pbBuffer, m_cbUsed, NULL /* pcbWritten */);
847 RTFileClose(hFile);
848 }
849}
850#endif
851
852/**
853 * Tries to parse the next upcoming pair block within the internal
854 * buffer.
855 *
856 * Returns VERR_NO_DATA is no data is in internal buffer or buffer has been
857 * completely parsed already.
858 *
859 * Returns VERR_MORE_DATA if current block was parsed (with zero or more pairs
860 * stored in stream block) but still contains incomplete (unterminated)
861 * data.
862 *
863 * Returns VINF_SUCCESS if current block was parsed until the next upcoming
864 * block (with zero or more pairs stored in stream block).
865 *
866 * @return VBox status code.
867 * @param streamBlock Reference to guest stream block to fill.
868 */
869int GuestToolboxStream::ParseBlock(GuestToolboxStreamBlock &streamBlock)
870{
871 if ( !m_pbBuffer
872 || !m_cbUsed)
873 return VERR_NO_DATA;
874
875 AssertReturn(m_offBuffer <= m_cbUsed, VERR_INVALID_PARAMETER);
876 if (m_offBuffer == m_cbUsed)
877 return VERR_NO_DATA;
878
879 int vrc = VINF_SUCCESS;
880 char * const pszOff = (char *)&m_pbBuffer[m_offBuffer];
881 size_t cbLeft = m_offBuffer < m_cbUsed ? m_cbUsed - m_offBuffer : 0;
882 char *pszStart = pszOff;
883 while (cbLeft > 0 && *pszStart != '\0')
884 {
885 char * const pszPairEnd = RTStrEnd(pszStart, cbLeft);
886 if (!pszPairEnd)
887 {
888 vrc = VERR_MORE_DATA;
889 break;
890 }
891 size_t const cchPair = (size_t)(pszPairEnd - pszStart);
892 char *pszSep = (char *)memchr(pszStart, '=', cchPair);
893 if (pszSep)
894 *pszSep = '\0'; /* Terminate the separator so that we can use pszStart as our key from now on. */
895 else
896 {
897 vrc = VERR_MORE_DATA; /** @todo r=bird: This is BOGUS because we'll be stuck here if the guest feeds us bad data! */
898 break;
899 }
900 char const * const pszVal = pszSep + 1;
901
902 vrc = streamBlock.SetValue(pszStart, pszVal);
903 if (RT_FAILURE(vrc))
904 return vrc;
905
906 /* Next pair. */
907 pszStart = pszPairEnd + 1;
908 cbLeft -= cchPair + 1;
909 }
910
911 /* If we did not do any movement but we have stuff left
912 * in our buffer just skip the current termination so that
913 * we can try next time. */
914 size_t cbDistance = (pszStart - pszOff);
915 if ( !cbDistance
916 && cbLeft > 0
917 && *pszStart == '\0'
918 && m_offBuffer < m_cbUsed)
919 cbDistance++;
920 m_offBuffer += cbDistance;
921
922 return vrc;
923}
924#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
925
926GuestBase::GuestBase(void)
927 : mConsole(NULL)
928 , mNextContextID(RTRandU32() % VBOX_GUESTCTRL_MAX_CONTEXTS)
929{
930}
931
932GuestBase::~GuestBase(void)
933{
934}
935
936/**
937 * Separate initialization function for the base class.
938 *
939 * @returns VBox status code.
940 */
941int GuestBase::baseInit(void)
942{
943 int const vrc = RTCritSectInit(&mWaitEventCritSect);
944 LogFlowFuncLeaveRC(vrc);
945 return vrc;
946}
947
948/**
949 * Separate uninitialization function for the base class.
950 */
951void GuestBase::baseUninit(void)
952{
953 LogFlowThisFuncEnter();
954
955 /* Make sure to cancel any outstanding wait events. */
956 int vrc2 = cancelWaitEvents();
957 AssertRC(vrc2);
958
959 vrc2 = RTCritSectDelete(&mWaitEventCritSect);
960 AssertRC(vrc2);
961
962 LogFlowFuncLeaveRC(vrc2);
963 /* No return value. */
964}
965
966/**
967 * Cancels all outstanding wait events.
968 *
969 * @returns VBox status code.
970 */
971int GuestBase::cancelWaitEvents(void)
972{
973 LogFlowThisFuncEnter();
974
975 int vrc = RTCritSectEnter(&mWaitEventCritSect);
976 if (RT_SUCCESS(vrc))
977 {
978 GuestEventGroup::iterator itEventGroups = mWaitEventGroups.begin();
979 while (itEventGroups != mWaitEventGroups.end())
980 {
981 GuestWaitEvents::iterator itEvents = itEventGroups->second.begin();
982 while (itEvents != itEventGroups->second.end())
983 {
984 GuestWaitEvent *pEvent = itEvents->second;
985 AssertPtr(pEvent);
986
987 /*
988 * Just cancel the event, but don't remove it from the
989 * wait events map. Don't delete it though, this (hopefully)
990 * is done by the caller using unregisterWaitEvent().
991 */
992 int vrc2 = pEvent->Cancel();
993 AssertRC(vrc2);
994
995 ++itEvents;
996 }
997
998 ++itEventGroups;
999 }
1000
1001 int vrc2 = RTCritSectLeave(&mWaitEventCritSect);
1002 if (RT_SUCCESS(vrc))
1003 vrc = vrc2;
1004 }
1005
1006 LogFlowFuncLeaveRC(vrc);
1007 return vrc;
1008}
1009
1010/**
1011 * Handles generic messages not bound to a specific object type.
1012 *
1013 * @return VBox status code. VERR_NOT_FOUND if no handler has been found or VERR_NOT_SUPPORTED
1014 * if this class does not support the specified callback.
1015 * @param pCtxCb Host callback context.
1016 * @param pSvcCb Service callback data.
1017 */
1018int GuestBase::dispatchGeneric(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
1019{
1020 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
1021
1022 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
1023 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
1024
1025 int vrc;
1026
1027 try
1028 {
1029 Log2Func(("uFunc=%RU32, cParms=%RU32\n", pCtxCb->uMessage, pSvcCb->mParms));
1030
1031 switch (pCtxCb->uMessage)
1032 {
1033 case GUEST_MSG_PROGRESS_UPDATE:
1034 vrc = VINF_SUCCESS;
1035 break;
1036
1037 case GUEST_MSG_REPLY:
1038 {
1039 if (pSvcCb->mParms >= 4)
1040 {
1041 int idx = 1; /* Current parameter index. */
1042 CALLBACKDATA_MSG_REPLY dataCb;
1043 /* pSvcCb->mpaParms[0] always contains the context ID. */
1044 vrc = HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.uType);
1045 AssertRCReturn(vrc, vrc);
1046 vrc = HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.rc);
1047 AssertRCReturn(vrc, vrc);
1048 vrc = HGCMSvcGetPv(&pSvcCb->mpaParms[idx++], &dataCb.pvPayload, &dataCb.cbPayload);
1049 AssertRCReturn(vrc, vrc);
1050
1051 try
1052 {
1053 GuestWaitEventPayload evPayload(dataCb.uType, dataCb.pvPayload, dataCb.cbPayload);
1054 vrc = signalWaitEventInternal(pCtxCb, dataCb.rc, &evPayload);
1055 }
1056 catch (int vrcEx) /* Thrown by GuestWaitEventPayload constructor. */
1057 {
1058 vrc = vrcEx;
1059 }
1060 }
1061 else
1062 vrc = VERR_INVALID_PARAMETER;
1063 break;
1064 }
1065
1066 default:
1067 vrc = VERR_NOT_SUPPORTED;
1068 break;
1069 }
1070 }
1071 catch (std::bad_alloc &)
1072 {
1073 vrc = VERR_NO_MEMORY;
1074 }
1075 catch (int vrcCatch)
1076 {
1077 vrc = vrcCatch;
1078 }
1079
1080 LogFlowFuncLeaveRC(vrc);
1081 return vrc;
1082}
1083
1084/**
1085 * Generates a context ID (CID) by incrementing the object's count.
1086 * A CID consists of a session ID, an object ID and a count.
1087 *
1088 * Note: This function does not guarantee that the returned CID is unique;
1089 * the caller has to take care of that and eventually retry.
1090 *
1091 * @returns VBox status code.
1092 * @param uSessionID Session ID to use for CID generation.
1093 * @param uObjectID Object ID to use for CID generation.
1094 * @param puContextID Where to store the generated CID on success.
1095 */
1096int GuestBase::generateContextID(uint32_t uSessionID, uint32_t uObjectID, uint32_t *puContextID)
1097{
1098 AssertPtrReturn(puContextID, VERR_INVALID_POINTER);
1099
1100 if ( uSessionID >= VBOX_GUESTCTRL_MAX_SESSIONS
1101 || uObjectID >= VBOX_GUESTCTRL_MAX_OBJECTS)
1102 return VERR_INVALID_PARAMETER;
1103
1104 uint32_t uCount = ASMAtomicIncU32(&mNextContextID);
1105 uCount %= VBOX_GUESTCTRL_MAX_CONTEXTS;
1106
1107 uint32_t uNewContextID = VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID, uObjectID, uCount);
1108
1109 *puContextID = uNewContextID;
1110
1111#if 0
1112 LogFlowThisFunc(("mNextContextID=%RU32, uSessionID=%RU32, uObjectID=%RU32, uCount=%RU32, uNewContextID=%RU32\n",
1113 mNextContextID, uSessionID, uObjectID, uCount, uNewContextID));
1114#endif
1115 return VINF_SUCCESS;
1116}
1117
1118/**
1119 * Registers (creates) a new wait event based on a given session and object ID.
1120 *
1121 * From those IDs an unique context ID (CID) will be built, which only can be
1122 * around once at a time.
1123 *
1124 * @returns VBox status code.
1125 * @retval VERR_GSTCTL_MAX_CID_COUNT_REACHED if unable to generate a free context ID (CID, the count part (bits 15:0)).
1126 * @param uSessionID Session ID to register wait event for.
1127 * @param uObjectID Object ID to register wait event for.
1128 * @param ppEvent Pointer to registered (created) wait event on success.
1129 * Must be destroyed with unregisterWaitEvent().
1130 */
1131int GuestBase::registerWaitEvent(uint32_t uSessionID, uint32_t uObjectID, GuestWaitEvent **ppEvent)
1132{
1133 GuestEventTypes eventTypesEmpty;
1134 return registerWaitEventEx(uSessionID, uObjectID, eventTypesEmpty, ppEvent);
1135}
1136
1137/**
1138 * Creates and registers a new wait event object that waits on a set of events
1139 * related to a given object within the session.
1140 *
1141 * From the session ID and object ID a one-time unique context ID (CID) is built
1142 * for this wait object. Normally the CID is then passed to the guest along
1143 * with a request, and the guest passed the CID back with the reply. The
1144 * handler for the reply then emits a signal on the event type associated with
1145 * the reply, which includes signalling the object returned by this method and
1146 * the waking up the thread waiting on it.
1147 *
1148 * @returns VBox status code.
1149 * @retval VERR_GSTCTL_MAX_CID_COUNT_REACHED if unable to generate a free context ID (CID, the count part (bits 15:0)).
1150 * @param uSessionID Session ID to register wait event for.
1151 * @param uObjectID Object ID to register wait event for.
1152 * @param lstEvents List of events to register the wait event for.
1153 * @param ppEvent Pointer to registered (created) wait event on success.
1154 * Must be destroyed with unregisterWaitEvent().
1155 */
1156int GuestBase::registerWaitEventEx(uint32_t uSessionID, uint32_t uObjectID, const GuestEventTypes &lstEvents,
1157 GuestWaitEvent **ppEvent)
1158{
1159 AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
1160
1161 uint32_t idContext;
1162 int vrc = generateContextID(uSessionID, uObjectID, &idContext);
1163 AssertRCReturn(vrc, vrc);
1164
1165 GuestWaitEvent *pEvent = new GuestWaitEvent();
1166 AssertPtrReturn(pEvent, VERR_NO_MEMORY);
1167
1168 vrc = pEvent->Init(idContext, lstEvents);
1169 AssertRCReturn(vrc, vrc);
1170
1171 LogFlowThisFunc(("New event=%p, CID=%RU32\n", pEvent, idContext));
1172
1173 vrc = RTCritSectEnter(&mWaitEventCritSect);
1174 if (RT_SUCCESS(vrc))
1175 {
1176 /*
1177 * Check that we don't have any context ID collisions (should be very unlikely).
1178 *
1179 * The ASSUMPTION here is that mWaitEvents has all the same events as
1180 * mWaitEventGroups, so it suffices to check one of the two.
1181 */
1182 if (mWaitEvents.find(idContext) != mWaitEvents.end())
1183 {
1184 uint32_t cTries = 0;
1185 do
1186 {
1187 vrc = generateContextID(uSessionID, uObjectID, &idContext);
1188 AssertRCBreak(vrc);
1189 LogFunc(("Found context ID duplicate; trying a different context ID: %#x\n", idContext));
1190 if (mWaitEvents.find(idContext) != mWaitEvents.end())
1191 vrc = VERR_GSTCTL_MAX_CID_COUNT_REACHED;
1192 } while (RT_FAILURE_NP(vrc) && cTries++ < 10);
1193 }
1194 if (RT_SUCCESS(vrc))
1195 {
1196 /*
1197 * Insert event into matching event group. This is for faster per-group lookup of all events later.
1198 */
1199 uint32_t cInserts = 0;
1200 for (GuestEventTypes::const_iterator ItType = lstEvents.begin(); ItType != lstEvents.end(); ++ItType)
1201 {
1202 GuestWaitEvents &eventGroup = mWaitEventGroups[*ItType];
1203 if (eventGroup.find(idContext) == eventGroup.end())
1204 {
1205 try
1206 {
1207 eventGroup.insert(std::pair<uint32_t, GuestWaitEvent *>(idContext, pEvent));
1208 cInserts++;
1209 }
1210 catch (std::bad_alloc &)
1211 {
1212 while (ItType != lstEvents.begin())
1213 {
1214 --ItType;
1215 mWaitEventGroups[*ItType].erase(idContext);
1216 }
1217 vrc = VERR_NO_MEMORY;
1218 break;
1219 }
1220 }
1221 else
1222 Assert(cInserts > 0); /* else: lstEvents has duplicate entries. */
1223 }
1224 if (RT_SUCCESS(vrc))
1225 {
1226 Assert(cInserts > 0 || lstEvents.size() == 0);
1227 RT_NOREF(cInserts);
1228
1229 /*
1230 * Register event in the regular event list.
1231 */
1232 try
1233 {
1234 mWaitEvents[idContext] = pEvent;
1235 }
1236 catch (std::bad_alloc &)
1237 {
1238 for (GuestEventTypes::const_iterator ItType = lstEvents.begin(); ItType != lstEvents.end(); ++ItType)
1239 mWaitEventGroups[*ItType].erase(idContext);
1240 vrc = VERR_NO_MEMORY;
1241 }
1242 }
1243 }
1244
1245 RTCritSectLeave(&mWaitEventCritSect);
1246 }
1247 if (RT_SUCCESS(vrc))
1248 {
1249 *ppEvent = pEvent;
1250 return vrc;
1251 }
1252
1253 if (pEvent)
1254 delete pEvent;
1255
1256 return vrc;
1257}
1258
1259/**
1260 * Signals all wait events of a specific type (if found)
1261 * and notifies external events accordingly.
1262 *
1263 * @returns VBox status code.
1264 * @param aType Event type to signal.
1265 * @param aEvent Which external event to notify.
1266 */
1267int GuestBase::signalWaitEvent(VBoxEventType_T aType, IEvent *aEvent)
1268{
1269 int vrc = RTCritSectEnter(&mWaitEventCritSect);
1270#ifdef DEBUG
1271 uint32_t cEvents = 0;
1272#endif
1273 if (RT_SUCCESS(vrc))
1274 {
1275 GuestEventGroup::iterator itGroup = mWaitEventGroups.find(aType);
1276 if (itGroup != mWaitEventGroups.end())
1277 {
1278 /* Signal all events in the group, leaving the group empty afterwards. */
1279 GuestWaitEvents::iterator ItWaitEvt;
1280 while ((ItWaitEvt = itGroup->second.begin()) != itGroup->second.end())
1281 {
1282 LogFlowThisFunc(("Signalling event=%p, type=%ld (CID %#x: Session=%RU32, Object=%RU32, Count=%RU32) ...\n",
1283 ItWaitEvt->second, aType, ItWaitEvt->first, VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(ItWaitEvt->first),
1284 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(ItWaitEvt->first), VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(ItWaitEvt->first)));
1285
1286 int vrc2 = ItWaitEvt->second->SignalExternal(aEvent);
1287 AssertRC(vrc2);
1288
1289 /* Take down the wait event object details before we erase it from this list and invalid ItGrpEvt. */
1290 const GuestEventTypes &EvtTypes = ItWaitEvt->second->Types();
1291 uint32_t idContext = ItWaitEvt->first;
1292 itGroup->second.erase(ItWaitEvt);
1293
1294 for (GuestEventTypes::const_iterator ItType = EvtTypes.begin(); ItType != EvtTypes.end(); ++ItType)
1295 {
1296 GuestEventGroup::iterator EvtTypeGrp = mWaitEventGroups.find(*ItType);
1297 if (EvtTypeGrp != mWaitEventGroups.end())
1298 {
1299 ItWaitEvt = EvtTypeGrp->second.find(idContext);
1300 if (ItWaitEvt != EvtTypeGrp->second.end())
1301 {
1302 LogFlowThisFunc(("Removing event %p (CID %#x) from type %d group\n", ItWaitEvt->second, idContext, *ItType));
1303 EvtTypeGrp->second.erase(ItWaitEvt);
1304 LogFlowThisFunc(("%zu events left for type %d\n", EvtTypeGrp->second.size(), *ItType));
1305 Assert(EvtTypeGrp->second.find(idContext) == EvtTypeGrp->second.end()); /* no duplicates */
1306 }
1307 }
1308 }
1309 }
1310 }
1311
1312 int vrc2 = RTCritSectLeave(&mWaitEventCritSect);
1313 if (RT_SUCCESS(vrc))
1314 vrc = vrc2;
1315 }
1316
1317#ifdef DEBUG
1318 LogFlowThisFunc(("Signalled %RU32 events, vrc=%Rrc\n", cEvents, vrc));
1319#endif
1320 return vrc;
1321}
1322
1323/**
1324 * Signals a wait event which is registered to a specific callback (bound to a context ID (CID)).
1325 *
1326 * @returns VBox status code.
1327 * @param pCbCtx Pointer to host service callback context.
1328 * @param vrcGuest Guest return VBox status code to set.
1329 * @param pPayload Additional wait event payload data set set on return. Optional.
1330 */
1331int GuestBase::signalWaitEventInternal(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, int vrcGuest, const GuestWaitEventPayload *pPayload)
1332{
1333 if (RT_SUCCESS(vrcGuest))
1334 return signalWaitEventInternalEx(pCbCtx, VINF_SUCCESS, VINF_SUCCESS /* vrcGuest */, pPayload);
1335
1336 return signalWaitEventInternalEx(pCbCtx, VERR_GSTCTL_GUEST_ERROR, vrcGuest, pPayload);
1337}
1338
1339/**
1340 * Signals a wait event which is registered to a specific callback (bound to a context ID (CID)).
1341 * Extended version.
1342 *
1343 * @returns VBox status code.
1344 * @param pCbCtx Pointer to host service callback context.
1345 * @param vrc Return VBox status code to set as wait result.
1346 * @param vrcGuest Guest return VBox status code to set additionally, if
1347 * vrc is set to VERR_GSTCTL_GUEST_ERROR.
1348 * @param pPayload Additional wait event payload data set set on return. Optional.
1349 */
1350int GuestBase::signalWaitEventInternalEx(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, int vrc, int vrcGuest,
1351 const GuestWaitEventPayload *pPayload)
1352{
1353 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1354 /* pPayload is optional. */
1355
1356 int vrc2 = RTCritSectEnter(&mWaitEventCritSect);
1357 if (RT_SUCCESS(vrc2))
1358 {
1359 GuestWaitEvents::iterator itEvent = mWaitEvents.find(pCbCtx->uContextID);
1360 if (itEvent != mWaitEvents.end())
1361 {
1362 LogFlowThisFunc(("Signalling event=%p (CID %RU32, vrc=%Rrc, vrcGuest=%Rrc, pPayload=%p) ...\n",
1363 itEvent->second, itEvent->first, vrc, vrcGuest, pPayload));
1364 GuestWaitEvent *pEvent = itEvent->second;
1365 AssertPtr(pEvent);
1366 vrc2 = pEvent->SignalInternal(vrc, vrcGuest, pPayload);
1367 }
1368 else
1369 vrc2 = VERR_NOT_FOUND;
1370
1371 int vrc3 = RTCritSectLeave(&mWaitEventCritSect);
1372 if (RT_SUCCESS(vrc2))
1373 vrc2 = vrc3;
1374 }
1375
1376 return vrc2;
1377}
1378
1379/**
1380 * Unregisters (deletes) a wait event.
1381 *
1382 * After successful unregistration the event will not be valid anymore.
1383 *
1384 * @returns VBox status code.
1385 * @param pWaitEvt Wait event to unregister (delete).
1386 */
1387int GuestBase::unregisterWaitEvent(GuestWaitEvent *pWaitEvt)
1388{
1389 if (!pWaitEvt) /* Nothing to unregister. */
1390 return VINF_SUCCESS;
1391
1392 int vrc = RTCritSectEnter(&mWaitEventCritSect);
1393 if (RT_SUCCESS(vrc))
1394 {
1395 LogFlowThisFunc(("pWaitEvt=%p\n", pWaitEvt));
1396
1397/** @todo r=bird: One way of optimizing this would be to use the pointer
1398 * instead of the context ID as index into the groups, i.e. revert the value
1399 * pair for the GuestWaitEvents type.
1400 *
1401 * An even more efficent way, would be to not use sexy std::xxx containers for
1402 * the types, but iprt/list.h, as that would just be a RTListNodeRemove call for
1403 * each type w/o needing to iterate much at all. I.e. add a struct {
1404 * RTLISTNODE, GuestWaitEvent *pSelf} array to GuestWaitEvent, and change
1405 * GuestEventGroup to std::map<VBoxEventType_T, RTListAnchorClass>
1406 * (RTListAnchorClass == RTLISTANCHOR wrapper with a constructor)).
1407 *
1408 * P.S. the try/catch is now longer needed after I changed pWaitEvt->Types() to
1409 * return a const reference rather than a copy of the type list (and it think it
1410 * is safe to assume iterators are not hitting the heap). Copy vs reference is
1411 * an easy mistake to make in C++.
1412 *
1413 * P.P.S. The mWaitEventGroups optimization is probably just a lot of extra work
1414 * with little payoff.
1415 */
1416 try
1417 {
1418 /* Remove the event from all event type groups. */
1419 const GuestEventTypes &lstTypes = pWaitEvt->Types();
1420 for (GuestEventTypes::const_iterator itType = lstTypes.begin();
1421 itType != lstTypes.end(); ++itType)
1422 {
1423 /** @todo Slow O(n) lookup. Optimize this. */
1424 GuestWaitEvents::iterator itCurEvent = mWaitEventGroups[(*itType)].begin();
1425 while (itCurEvent != mWaitEventGroups[(*itType)].end())
1426 {
1427 if (itCurEvent->second == pWaitEvt)
1428 {
1429 mWaitEventGroups[(*itType)].erase(itCurEvent);
1430 break;
1431 }
1432 ++itCurEvent;
1433 }
1434 }
1435
1436 /* Remove the event from the general event list as well. */
1437 GuestWaitEvents::iterator itEvent = mWaitEvents.find(pWaitEvt->ContextID());
1438
1439 Assert(itEvent != mWaitEvents.end());
1440 Assert(itEvent->second == pWaitEvt);
1441
1442 mWaitEvents.erase(itEvent);
1443
1444 delete pWaitEvt;
1445 pWaitEvt = NULL;
1446 }
1447 catch (const std::exception &ex)
1448 {
1449 RT_NOREF(ex);
1450 AssertFailedStmt(vrc = VERR_NOT_FOUND);
1451 }
1452
1453 int vrc2 = RTCritSectLeave(&mWaitEventCritSect);
1454 if (RT_SUCCESS(vrc))
1455 vrc = vrc2;
1456 }
1457
1458 return vrc;
1459}
1460
1461/**
1462 * Waits for an already registered guest wait event.
1463 *
1464 * @return VBox status code.
1465 * @retval VERR_GSTCTL_GUEST_ERROR may be returned, call GuestResult() to get
1466 * the actual result.
1467 *
1468 * @param pWaitEvt Pointer to event to wait for.
1469 * @param msTimeout Timeout (in ms) for waiting.
1470 * @param pType Event type of following IEvent. Optional.
1471 * @param ppEvent Pointer to IEvent which got triggered for this event. Optional.
1472 */
1473int GuestBase::waitForEvent(GuestWaitEvent *pWaitEvt, uint32_t msTimeout, VBoxEventType_T *pType, IEvent **ppEvent)
1474{
1475 AssertPtrReturn(pWaitEvt, VERR_INVALID_POINTER);
1476 /* pType is optional. */
1477 /* ppEvent is optional. */
1478
1479 int vrc = pWaitEvt->Wait(msTimeout);
1480 if (RT_SUCCESS(vrc))
1481 {
1482 const ComPtr<IEvent> pThisEvent = pWaitEvt->Event();
1483 if (pThisEvent.isNotNull()) /* Make sure that we actually have an event associated. */
1484 {
1485 if (pType)
1486 {
1487 HRESULT hrc = pThisEvent->COMGETTER(Type)(pType);
1488 if (FAILED(hrc))
1489 vrc = VERR_COM_UNEXPECTED;
1490 }
1491 if ( RT_SUCCESS(vrc)
1492 && ppEvent)
1493 pThisEvent.queryInterfaceTo(ppEvent);
1494
1495 unconst(pThisEvent).setNull();
1496 }
1497 }
1498
1499 return vrc;
1500}
1501
1502#ifndef VBOX_GUESTCTRL_TEST_CASE
1503/**
1504 * Convenience function to return a pre-formatted string using an action description and a guest error information.
1505 *
1506 * @returns Pre-formatted string with a user-friendly error string.
1507 * @param strAction Action of when the error occurred.
1508 * @param guestErrorInfo Related guest error information to use.
1509 */
1510/* static */ Utf8Str GuestBase::getErrorAsString(const Utf8Str& strAction, const GuestErrorInfo& guestErrorInfo)
1511{
1512 Assert(strAction.isNotEmpty());
1513 return Utf8StrFmt("%s: %s", strAction.c_str(), getErrorAsString(guestErrorInfo).c_str());
1514}
1515
1516/**
1517 * Returns a user-friendly error message from a given GuestErrorInfo object.
1518 *
1519 * @returns Error message string.
1520 * @param guestErrorInfo Guest error info to return error message for.
1521 */
1522/* static */ Utf8Str GuestBase::getErrorAsString(const GuestErrorInfo& guestErrorInfo)
1523{
1524 AssertMsg(RT_FAILURE(guestErrorInfo.getVrc()), ("Guest vrc does not indicate a failure\n"));
1525
1526 Utf8Str strErr;
1527
1528#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
1529# define CASE_TOOL_ERROR(a_eType, a_strTool) \
1530 case a_eType: \
1531 { \
1532 strErr = GuestProcessToolbox::guestErrorToString(a_strTool, guestErrorInfo); \
1533 break; \
1534 }
1535#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
1536
1537 switch (guestErrorInfo.getType())
1538 {
1539 case GuestErrorInfo::Type_Session:
1540 strErr = GuestSession::i_guestErrorToString(guestErrorInfo.getVrc());
1541 break;
1542
1543 case GuestErrorInfo::Type_Process:
1544 strErr = GuestProcess::i_guestErrorToString(guestErrorInfo.getVrc(), guestErrorInfo.getWhat().c_str());
1545 break;
1546
1547 case GuestErrorInfo::Type_File:
1548 strErr = GuestFile::i_guestErrorToString(guestErrorInfo.getVrc(), guestErrorInfo.getWhat().c_str());
1549 break;
1550
1551 case GuestErrorInfo::Type_Directory:
1552 strErr = GuestDirectory::i_guestErrorToString(guestErrorInfo.getVrc(), guestErrorInfo.getWhat().c_str());
1553 break;
1554
1555 case GuestErrorInfo::Type_Fs:
1556 strErr = GuestFs::guestErrorToString(guestErrorInfo);
1557 break;
1558
1559#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
1560 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolLs, VBOXSERVICE_TOOL_LS);
1561 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolMkDir, VBOXSERVICE_TOOL_MKDIR);
1562 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolMkTemp, VBOXSERVICE_TOOL_MKTEMP);
1563 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolRm, VBOXSERVICE_TOOL_RM);
1564 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolStat, VBOXSERVICE_TOOL_STAT);
1565#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
1566 default:
1567 AssertMsgFailed(("Type not implemented (type=%RU32, vrc=%Rrc)\n", guestErrorInfo.getType(), guestErrorInfo.getVrc()));
1568 strErr = Utf8StrFmt("Unknown / Not implemented -- Please file a bug report (type=%RU32, vrc=%Rrc)\n",
1569 guestErrorInfo.getType(), guestErrorInfo.getVrc());
1570 break;
1571 }
1572
1573 return strErr;
1574}
1575
1576#endif /* VBOX_GUESTCTRL_TEST_CASE */
1577
1578/**
1579 * Converts RTFMODE to FsObjType_T.
1580 *
1581 * @return Converted FsObjType_T type.
1582 * @param fMode RTFMODE to convert.
1583 */
1584/* static */
1585FsObjType_T GuestBase::fileModeToFsObjType(RTFMODE fMode)
1586{
1587 if (RTFS_IS_FIFO(fMode)) return FsObjType_Fifo;
1588 else if (RTFS_IS_DEV_CHAR(fMode)) return FsObjType_DevChar;
1589 else if (RTFS_IS_DIRECTORY(fMode)) return FsObjType_Directory;
1590 else if (RTFS_IS_DEV_BLOCK(fMode)) return FsObjType_DevBlock;
1591 else if (RTFS_IS_FILE(fMode)) return FsObjType_File;
1592 else if (RTFS_IS_SYMLINK(fMode)) return FsObjType_Symlink;
1593 else if (RTFS_IS_SOCKET(fMode)) return FsObjType_Socket;
1594 else if (RTFS_IS_WHITEOUT(fMode)) return FsObjType_WhiteOut;
1595
1596 return FsObjType_Unknown;
1597}
1598
1599/**
1600 * Converts a FsObjType_T to a human-readable string.
1601 *
1602 * @returns Human-readable string of FsObjType_T.
1603 * @param enmType FsObjType_T to convert.
1604 */
1605/* static */
1606const char *GuestBase::fsObjTypeToStr(FsObjType_T enmType)
1607{
1608 switch (enmType)
1609 {
1610 case FsObjType_Directory: return "directory";
1611 case FsObjType_Symlink: return "symbolic link";
1612 case FsObjType_File: return "file";
1613 default: break;
1614 }
1615
1616 return "unknown";
1617}
1618
1619/**
1620 * Converts a PathStyle_T to a human-readable string.
1621 *
1622 * @returns Human-readable string of PathStyle_T.
1623 * @param enmPathStyle PathStyle_T to convert.
1624 */
1625/* static */
1626const char *GuestBase::pathStyleToStr(PathStyle_T enmPathStyle)
1627{
1628 switch (enmPathStyle)
1629 {
1630 case PathStyle_DOS: return "DOS";
1631 case PathStyle_UNIX: return "UNIX";
1632 case PathStyle_Unknown: return "Unknown";
1633 default: break;
1634 }
1635
1636 return "<invalid>";
1637}
1638
1639GuestObject::GuestObject(void)
1640 : mSession(NULL),
1641 mObjectID(0)
1642{
1643}
1644
1645GuestObject::~GuestObject(void)
1646{
1647}
1648
1649/**
1650 * Binds this guest (control) object to a specific guest (control) session.
1651 *
1652 * @returns VBox status code.
1653 * @param pConsole Pointer to console object to use.
1654 * @param pSession Pointer to session to bind this object to.
1655 * @param uObjectID Object ID for this object to use within that specific session.
1656 * Each object ID must be unique per session.
1657 */
1658int GuestObject::bindToSession(Console *pConsole, GuestSession *pSession, uint32_t uObjectID)
1659{
1660 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
1661 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1662
1663 mConsole = pConsole;
1664 mSession = pSession;
1665 mObjectID = uObjectID;
1666
1667 return VINF_SUCCESS;
1668}
1669
1670/**
1671 * Registers (creates) a new wait event.
1672 *
1673 * @returns VBox status code.
1674 * @param lstEvents List of events which the new wait event gets triggered at.
1675 * @param ppEvent Returns the new wait event on success.
1676 */
1677int GuestObject::registerWaitEvent(const GuestEventTypes &lstEvents,
1678 GuestWaitEvent **ppEvent)
1679{
1680 AssertPtr(mSession);
1681 return GuestBase::registerWaitEventEx(mSession->i_getId(), mObjectID, lstEvents, ppEvent);
1682}
1683
1684/**
1685 * Sends a HGCM message to the guest (via the guest control host service).
1686 *
1687 * @returns VBox status code.
1688 * @param uMessage Message ID of message to send.
1689 * @param cParms Number of HGCM message parameters to send.
1690 * @param paParms Array of HGCM message parameters to send.
1691 */
1692int GuestObject::sendMessage(uint32_t uMessage, uint32_t cParms, PVBOXHGCMSVCPARM paParms)
1693{
1694#ifndef VBOX_GUESTCTRL_TEST_CASE
1695 ComObjPtr<Console> pConsole = mConsole;
1696 Assert(!pConsole.isNull());
1697
1698 int vrc = VERR_HGCM_SERVICE_NOT_FOUND;
1699
1700 /* Forward the information to the VMM device. */
1701 VMMDev *pVMMDev = pConsole->i_getVMMDev();
1702 if (pVMMDev)
1703 {
1704 /* HACK ALERT! We extend the first parameter to 64-bit and use the
1705 two topmost bits for call destination information. */
1706 Assert(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT);
1707 paParms[0].type = VBOX_HGCM_SVC_PARM_64BIT;
1708 paParms[0].u.uint64 = (uint64_t)paParms[0].u.uint32 | VBOX_GUESTCTRL_DST_SESSION;
1709
1710 /* Make the call. */
1711 LogFlowThisFunc(("uMessage=%RU32, cParms=%RU32\n", uMessage, cParms));
1712 vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uMessage, cParms, paParms);
1713 if (RT_FAILURE(vrc))
1714 {
1715 /** @todo What to do here? */
1716 }
1717 }
1718#else
1719 LogFlowThisFuncEnter();
1720
1721 /* Not needed within testcases. */
1722 RT_NOREF(uMessage, cParms, paParms);
1723 int vrc = VINF_SUCCESS;
1724#endif
1725 return vrc;
1726}
1727
1728GuestWaitEventBase::GuestWaitEventBase(void)
1729 : mfAborted(false),
1730 mCID(0),
1731 mEventSem(NIL_RTSEMEVENT),
1732 mVrc(VINF_SUCCESS),
1733 mGuestRc(VINF_SUCCESS)
1734{
1735}
1736
1737GuestWaitEventBase::~GuestWaitEventBase(void)
1738{
1739 if (mEventSem != NIL_RTSEMEVENT)
1740 {
1741 RTSemEventDestroy(mEventSem);
1742 mEventSem = NIL_RTSEMEVENT;
1743 }
1744}
1745
1746/**
1747 * Initializes a wait event with a specific context ID (CID).
1748 *
1749 * @returns VBox status code.
1750 * @param uCID Context ID (CID) to initialize wait event with.
1751 */
1752int GuestWaitEventBase::Init(uint32_t uCID)
1753{
1754 mCID = uCID;
1755
1756 return RTSemEventCreate(&mEventSem);
1757}
1758
1759/**
1760 * Signals a wait event.
1761 *
1762 * @returns VBox status code.
1763 * @param vrc Return VBox status code to set as wait result.
1764 * @param vrcGuest Guest return VBox status code to set additionally, if
1765 * @a vrc is set to VERR_GSTCTL_GUEST_ERROR.
1766 * @param pPayload Additional wait event payload data set set on return. Optional.
1767 */
1768int GuestWaitEventBase::SignalInternal(int vrc, int vrcGuest, const GuestWaitEventPayload *pPayload)
1769{
1770 if (mfAborted)
1771 return VERR_CANCELLED;
1772
1773#ifdef VBOX_STRICT
1774 if (vrc == VERR_GSTCTL_GUEST_ERROR)
1775 AssertMsg(RT_FAILURE(vrcGuest), ("Guest error indicated but no actual guest error set (%Rrc)\n", vrcGuest));
1776 else
1777 AssertMsg(RT_SUCCESS(vrcGuest), ("No guest error indicated but actual guest error set (%Rrc)\n", vrcGuest));
1778#endif
1779
1780 int vrc2;
1781 if (pPayload)
1782 vrc2 = mPayload.CopyFromDeep(*pPayload);
1783 else
1784 vrc2 = VINF_SUCCESS;
1785 if (RT_SUCCESS(vrc2))
1786 {
1787 mVrc = vrc;
1788 mGuestRc = vrcGuest;
1789
1790 vrc2 = RTSemEventSignal(mEventSem);
1791 }
1792
1793 return vrc2;
1794}
1795
1796/**
1797 * Waits for the event to get triggered. Will return success if the
1798 * wait was successufl (e.g. was being triggered), otherwise an error will be returned.
1799 *
1800 * @returns VBox status code.
1801 * @retval VERR_GSTCTL_GUEST_ERROR may be returned, call GuestResult() to get
1802 * the actual result.
1803 *
1804 * @param msTimeout Timeout (in ms) to wait.
1805 * Specifiy 0 to wait indefinitely.
1806 */
1807int GuestWaitEventBase::Wait(RTMSINTERVAL msTimeout)
1808{
1809 int vrc;
1810 if (!mfAborted)
1811 {
1812 AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
1813
1814 vrc = RTSemEventWait(mEventSem, msTimeout ? msTimeout : RT_INDEFINITE_WAIT);
1815 if ( RT_SUCCESS(vrc)
1816 && mfAborted)
1817 vrc = VERR_CANCELLED;
1818
1819 if (RT_SUCCESS(vrc))
1820 {
1821 /* If waiting succeeded, return the overall
1822 * result code. */
1823 vrc = mVrc;
1824 }
1825 }
1826 else
1827 vrc = VERR_CANCELLED;
1828 return vrc;
1829}
1830
1831GuestWaitEvent::GuestWaitEvent(void)
1832{
1833}
1834
1835GuestWaitEvent::~GuestWaitEvent(void)
1836{
1837
1838}
1839
1840/**
1841 * Cancels the event.
1842 */
1843int GuestWaitEvent::Cancel(void)
1844{
1845 if (mfAborted) /* Already aborted? */
1846 return VINF_SUCCESS;
1847
1848 mfAborted = true;
1849
1850#ifdef DEBUG_andy
1851 LogFlowThisFunc(("Cancelling %p ...\n"));
1852#endif
1853 return RTSemEventSignal(mEventSem);
1854}
1855
1856/**
1857 * Initializes a wait event with a given context ID (CID).
1858 *
1859 * @returns VBox status code.
1860 * @param uCID Context ID to initialize wait event with.
1861 */
1862int GuestWaitEvent::Init(uint32_t uCID)
1863{
1864 return GuestWaitEventBase::Init(uCID);
1865}
1866
1867/**
1868 * Initializes a wait event with a given context ID (CID) and a list of event types to wait for.
1869 *
1870 * @returns VBox status code.
1871 * @param uCID Context ID to initialize wait event with.
1872 * @param lstEvents List of event types to wait for this wait event to get signalled.
1873 */
1874int GuestWaitEvent::Init(uint32_t uCID, const GuestEventTypes &lstEvents)
1875{
1876 int vrc = GuestWaitEventBase::Init(uCID);
1877 if (RT_SUCCESS(vrc))
1878 mEventTypes = lstEvents;
1879
1880 return vrc;
1881}
1882
1883/**
1884 * Signals the event.
1885 *
1886 * @return VBox status code.
1887 * @param pEvent Public IEvent to associate.
1888 * Optional.
1889 */
1890int GuestWaitEvent::SignalExternal(IEvent *pEvent)
1891{
1892 if (pEvent)
1893 mEvent = pEvent;
1894
1895 return RTSemEventSignal(mEventSem);
1896}
1897
1898
1899//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1900// GuestPath
1901//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1902
1903/**
1904 * Builds a (final) destination path from a given source + destination path.
1905 *
1906 * This does not utilize any file system access whatsoever. Used for guest and host paths.
1907 *
1908 * @returns VBox status code.
1909 * @param strSrcPath Source path to build destination path for.
1910 * @param enmSrcPathStyle Path style the source path is in.
1911 * @param strDstPath Destination path to use for building the (final) destination path.
1912 * @param enmDstPathStyle Path style the destination path is in.
1913 *
1914 * @note See rules within the function.
1915 */
1916/* static */
1917int GuestPath::BuildDestinationPath(const Utf8Str &strSrcPath, PathStyle_T enmSrcPathStyle,
1918 Utf8Str &strDstPath, PathStyle_T enmDstPathStyle)
1919{
1920 /*
1921 * Rules:
1922 *
1923 * # source dest final dest remarks
1924 *
1925 * 1 /src/path1/ /dst/path2/ /dst/path2/<contents of path1> Just copies contents of <contents of path1>, not the path1 itself.
1926 * 2 /src/path1 /dst/path2/ /dst/path2/path1 Copies path1 into path2.
1927 * 3 /src/path1 /dst/path2 /dst/path2 Overwrites stuff from path2 with stuff from path1.
1928 * 4 Dotdot ("..") directories are forbidden for security reasons.
1929 */
1930 const char *pszSrcName = RTPathFilenameEx(strSrcPath.c_str(),
1931 enmSrcPathStyle == PathStyle_DOS
1932 ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
1933
1934 const char *pszDstName = RTPathFilenameEx(strDstPath.c_str(),
1935 enmDstPathStyle == PathStyle_DOS
1936 ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
1937
1938 if ( (!pszSrcName && !pszDstName) /* #1 */
1939 || ( pszSrcName && pszDstName)) /* #3 */
1940 {
1941 /* Note: Must have DirectoryFlag_CopyIntoExisting + FileFlag_NoReplace *not* set. */
1942 }
1943 else if (pszSrcName && !pszDstName) /* #2 */
1944 {
1945 if (!strDstPath.endsWith(PATH_STYLE_SEP_STR(enmDstPathStyle)))
1946 strDstPath += PATH_STYLE_SEP_STR(enmDstPathStyle);
1947 strDstPath += pszSrcName;
1948 }
1949
1950 /* Translate the built destination path to a path compatible with the destination. */
1951 int vrc = GuestPath::Translate(strDstPath, enmSrcPathStyle, enmDstPathStyle);
1952 if (RT_SUCCESS(vrc))
1953 {
1954 union
1955 {
1956 RTPATHPARSED Parsed;
1957 RTPATHSPLIT Split;
1958 uint8_t ab[4096];
1959 } u;
1960 vrc = RTPathParse(strDstPath.c_str(), &u.Parsed, sizeof(u), enmDstPathStyle == PathStyle_DOS
1961 ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
1962 if (RT_SUCCESS(vrc))
1963 {
1964 if (u.Parsed.fProps & RTPATH_PROP_DOTDOT_REFS) /* #4 */
1965 vrc = VERR_INVALID_PARAMETER;
1966 }
1967 }
1968
1969 LogRel2(("Guest Control: Building destination path for '%s' (%s) -> '%s' (%s): %Rrc\n",
1970 strSrcPath.c_str(), GuestBase::pathStyleToStr(enmSrcPathStyle),
1971 strDstPath.c_str(), GuestBase::pathStyleToStr(enmDstPathStyle), vrc));
1972
1973 return vrc;
1974}
1975
1976/**
1977 * Translates a path from a specific path style into another.
1978 *
1979 * @returns VBox status code.
1980 * @retval VERR_NOT_SUPPORTED if a conversion is not supported.
1981 * @retval VERR_NOT_IMPLEMENTED if path style conversion is not implemented yet.
1982 * @param strPath Path to translate. Will contain the translated path on success. UTF-8 only.
1983 * @param enmSrcPathStyle Source path style \a strPath is expected in.
1984 * @param enmDstPathStyle Destination path style to convert to.
1985 * @param fForce Whether to force the translation to the destination path style or not.
1986 *
1987 * @note This does NOT remove any trailing slashes and/or perform file system lookups!
1988 */
1989/* static */
1990int GuestPath::Translate(Utf8Str &strPath, PathStyle_T enmSrcPathStyle, PathStyle_T enmDstPathStyle, bool fForce /* = false */)
1991{
1992 if (strPath.isEmpty())
1993 return VINF_SUCCESS;
1994
1995 AssertReturn(RTStrIsValidEncoding(strPath.c_str()), VERR_INVALID_PARAMETER);
1996
1997 int vrc = VINF_SUCCESS;
1998
1999 Utf8Str strTranslated;
2000
2001 if ( ( enmSrcPathStyle == PathStyle_DOS
2002 && enmDstPathStyle == PathStyle_UNIX)
2003 || (fForce && enmDstPathStyle == PathStyle_UNIX))
2004 {
2005 strTranslated = strPath;
2006 RTPathChangeToUnixSlashes(strTranslated.mutableRaw(), true /* fForce */);
2007 }
2008 else if ( ( enmSrcPathStyle == PathStyle_UNIX
2009 && enmDstPathStyle == PathStyle_DOS)
2010 || (fForce && enmDstPathStyle == PathStyle_DOS))
2011
2012 {
2013 strTranslated = strPath;
2014 RTPathChangeToDosSlashes(strTranslated.mutableRaw(), true /* fForce */);
2015 }
2016
2017 if ( strTranslated.isEmpty() /* Not forced. */
2018 && enmSrcPathStyle == enmDstPathStyle)
2019 {
2020 strTranslated = strPath;
2021 }
2022
2023 if (RT_FAILURE(vrc))
2024 {
2025 LogRel(("Guest Control: Translating path '%s' (%s) -> '%s' (%s) failed, vrc=%Rrc\n",
2026 strPath.c_str(), GuestBase::pathStyleToStr(enmSrcPathStyle),
2027 strTranslated.c_str(), GuestBase::pathStyleToStr(enmDstPathStyle), vrc));
2028 return vrc;
2029 }
2030
2031 /* Cleanup. */
2032 const char *psz = strTranslated.mutableRaw();
2033 size_t const cch = strTranslated.length();
2034 size_t off = 0;
2035 while (off < cch)
2036 {
2037 if (off + 1 > cch)
2038 break;
2039 /* Remove double back slashes (DOS only). */
2040 if ( enmDstPathStyle == PathStyle_DOS
2041 && psz[off] == '\\'
2042 && psz[off + 1] == '\\')
2043 {
2044 strTranslated.erase(off + 1, 1);
2045 off++;
2046 }
2047 /* Remove double forward slashes (UNIX only). */
2048 if ( enmDstPathStyle == PathStyle_UNIX
2049 && psz[off] == '/'
2050 && psz[off + 1] == '/')
2051 {
2052 strTranslated.erase(off + 1, 1);
2053 off++;
2054 }
2055 off++;
2056 }
2057
2058 /* Note: Do not trim() paths here, as technically it's possible to create paths with trailing spaces. */
2059
2060 strTranslated.jolt();
2061
2062 LogRel2(("Guest Control: Translating '%s' (%s) -> '%s' (%s): %Rrc\n",
2063 strPath.c_str(), GuestBase::pathStyleToStr(enmSrcPathStyle),
2064 strTranslated.c_str(), GuestBase::pathStyleToStr(enmDstPathStyle), vrc));
2065
2066 if (RT_SUCCESS(vrc))
2067 strPath = strTranslated;
2068
2069 return vrc;
2070}
2071
Note: See TracBrowser for help on using the repository browser.

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