VirtualBox

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

Last change on this file since 99252 was 99011, checked in by vboxsync, 15 months ago

Guest Control/Main: Removed unused ACL parameters from GuestFsObjData::FromGuestFsObjInfo() and return vrc instead of VINF_SUCCESS. bugref:9783

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

© 2023 Oracle
ContactPrivacy policyTerms of Use