VirtualBox

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

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

Guest Control/Main: Fixed deleting files via built-in toolbox; GuestToolboxStreamBlock::GetVrc() returns VERR_NOT_FOUND when the stream block key "rc" was not found. However, some IPRT-provided tools, such as RTPathRmCmd(), only output rc on failure but not on success (exit code 0).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 67.7 KB
Line 
1/* $Id: GuestCtrlPrivate.cpp 98725 2023-02-24 14:48:07Z 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"); /* Note: RTPathRmCmd() only sets this on failure. */
432
433 /* Return the stream block's vrc. */
434 return strmBlk.GetVrc(true /* fSucceedIfNotFound */);
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 * @param fSucceedIfNotFound When set to @c true, this reports back VINF_SUCCESS when the key ("rc") is not found.
600 * This can happen with some (older) IPRT-provided tools such as RTPathRmCmd(), which only outputs
601 * rc on failure but not on success. Defaults to @c false.
602 */
603int GuestToolboxStreamBlock::GetVrc(bool fSucceedIfNotFound /* = false */) const
604{
605 const char *pszValue = GetString("rc");
606 if (pszValue)
607 return RTStrToInt16(pszValue);
608 if (fSucceedIfNotFound)
609 return VINF_SUCCESS;
610 /** @todo We probably should have a dedicated error for that, VERR_GSTCTL_GUEST_TOOLBOX_whatever. */
611 return VERR_NOT_FOUND;
612}
613
614/**
615 * Returns a string value of a specified key.
616 *
617 * @return uint32_t Pointer to string to return, NULL if not found / on failure.
618 * @param pszKey Name of key to get the value for.
619 */
620const char *GuestToolboxStreamBlock::GetString(const char *pszKey) const
621{
622 AssertPtrReturn(pszKey, NULL);
623
624 try
625 {
626 GuestCtrlStreamPairMapIterConst itPairs = mPairs.find(pszKey);
627 if (itPairs != mPairs.end())
628 return itPairs->second.mValue.c_str();
629 }
630 catch (const std::exception &ex)
631 {
632 RT_NOREF(ex);
633 }
634 return NULL;
635}
636
637/**
638 * Returns a 32-bit unsigned integer of a specified key.
639 *
640 * @return VBox status code. VERR_NOT_FOUND if key was not found.
641 * @param pszKey Name of key to get the value for.
642 * @param puVal Pointer to value to return.
643 */
644int GuestToolboxStreamBlock::GetUInt32Ex(const char *pszKey, uint32_t *puVal) const
645{
646 const char *pszValue = GetString(pszKey);
647 if (pszValue)
648 {
649 *puVal = RTStrToUInt32(pszValue);
650 return VINF_SUCCESS;
651 }
652 return VERR_NOT_FOUND;
653}
654
655/**
656 * Returns a 32-bit signed integer of a specified key.
657 *
658 * @returns 32-bit signed value
659 * @param pszKey Name of key to get the value for.
660 * @param iDefault The default to return on error if not found.
661 */
662int32_t GuestToolboxStreamBlock::GetInt32(const char *pszKey, int32_t iDefault) const
663{
664 const char *pszValue = GetString(pszKey);
665 if (pszValue)
666 {
667 int32_t iRet;
668 int vrc = RTStrToInt32Full(pszValue, 0, &iRet);
669 if (RT_SUCCESS(vrc))
670 return iRet;
671 ASSERT_GUEST_MSG_FAILED(("%s=%s\n", pszKey, pszValue));
672 }
673 return iDefault;
674}
675
676/**
677 * Returns a 32-bit unsigned integer of a specified key.
678 *
679 * @return uint32_t Value to return, 0 if not found / on failure.
680 * @param pszKey Name of key to get the value for.
681 * @param uDefault The default value to return.
682 */
683uint32_t GuestToolboxStreamBlock::GetUInt32(const char *pszKey, uint32_t uDefault /*= 0*/) const
684{
685 uint32_t uVal;
686 if (RT_SUCCESS(GetUInt32Ex(pszKey, &uVal)))
687 return uVal;
688 return uDefault;
689}
690
691/**
692 * Sets a value to a key or deletes a key by setting a NULL value.
693 *
694 * @return VBox status code.
695 * @param pszKey Key name to process.
696 * @param pszValue Value to set. Set NULL for deleting the key.
697 */
698int GuestToolboxStreamBlock::SetValue(const char *pszKey, const char *pszValue)
699{
700 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
701
702 int vrc = VINF_SUCCESS;
703 try
704 {
705 Utf8Str const strKey(pszKey);
706
707 /* Take a shortcut and prevent crashes on some funny versions
708 * of STL if map is empty initially. */
709 if (!mPairs.empty())
710 {
711 GuestCtrlStreamPairMapIter it = mPairs.find(strKey);
712 if (it != mPairs.end())
713 mPairs.erase(it);
714 }
715
716 if (pszValue)
717 {
718 GuestToolboxStreamValue val(pszValue);
719 mPairs[strKey] = val;
720 }
721 }
722 catch (const std::exception &)
723 {
724 /** @todo set vrc? */
725 }
726 return vrc;
727}
728
729///////////////////////////////////////////////////////////////////////////////
730
731GuestToolboxStream::GuestToolboxStream(void)
732 : m_cbMax(_32M)
733 , m_cbAllocated(0)
734 , m_cbUsed(0)
735 , m_offBuffer(0)
736 , m_pbBuffer(NULL) { }
737
738GuestToolboxStream::~GuestToolboxStream(void)
739{
740 Destroy();
741}
742
743/**
744 * Adds data to the internal parser buffer. Useful if there
745 * are multiple rounds of adding data needed.
746 *
747 * @return VBox status code. Will return VERR_TOO_MUCH_DATA if the buffer's maximum (limit) has been reached.
748 * @param pbData Pointer to data to add.
749 * @param cbData Size (in bytes) of data to add.
750 */
751int GuestToolboxStream::AddData(const BYTE *pbData, size_t cbData)
752{
753 AssertPtrReturn(pbData, VERR_INVALID_POINTER);
754 AssertReturn(cbData, VERR_INVALID_PARAMETER);
755
756 int vrc = VINF_SUCCESS;
757
758 /* Rewind the buffer if it's empty. */
759 size_t cbInBuf = m_cbUsed - m_offBuffer;
760 bool const fAddToSet = cbInBuf == 0;
761 if (fAddToSet)
762 m_cbUsed = m_offBuffer = 0;
763
764 /* Try and see if we can simply append the data. */
765 if (cbData + m_cbUsed <= m_cbAllocated)
766 {
767 memcpy(&m_pbBuffer[m_cbUsed], pbData, cbData);
768 m_cbUsed += cbData;
769 }
770 else
771 {
772 /* Move any buffered data to the front. */
773 cbInBuf = m_cbUsed - m_offBuffer;
774 if (cbInBuf == 0)
775 m_cbUsed = m_offBuffer = 0;
776 else if (m_offBuffer) /* Do we have something to move? */
777 {
778 memmove(m_pbBuffer, &m_pbBuffer[m_offBuffer], cbInBuf);
779 m_cbUsed = cbInBuf;
780 m_offBuffer = 0;
781 }
782
783 /* Do we need to grow the buffer? */
784 if (cbData + m_cbUsed > m_cbAllocated)
785 {
786 size_t cbAlloc = m_cbUsed + cbData;
787 if (cbAlloc <= m_cbMax)
788 {
789 cbAlloc = RT_ALIGN_Z(cbAlloc, _64K);
790 void *pvNew = RTMemRealloc(m_pbBuffer, cbAlloc);
791 if (pvNew)
792 {
793 m_pbBuffer = (uint8_t *)pvNew;
794 m_cbAllocated = cbAlloc;
795 }
796 else
797 vrc = VERR_NO_MEMORY;
798 }
799 else
800 vrc = VERR_TOO_MUCH_DATA;
801 }
802
803 /* Finally, copy the data. */
804 if (RT_SUCCESS(vrc))
805 {
806 if (cbData + m_cbUsed <= m_cbAllocated)
807 {
808 memcpy(&m_pbBuffer[m_cbUsed], pbData, cbData);
809 m_cbUsed += cbData;
810 }
811 else
812 vrc = VERR_BUFFER_OVERFLOW;
813 }
814 }
815
816 return vrc;
817}
818
819/**
820 * Destroys the internal data buffer.
821 */
822void GuestToolboxStream::Destroy(void)
823{
824 if (m_pbBuffer)
825 {
826 RTMemFree(m_pbBuffer);
827 m_pbBuffer = NULL;
828 }
829
830 m_cbAllocated = 0;
831 m_cbUsed = 0;
832 m_offBuffer = 0;
833}
834
835#ifdef DEBUG
836/**
837 * Dumps the raw guest process output to a file on the host.
838 * If the file on the host already exists, it will be overwritten.
839 *
840 * @param pszFile Absolute path to host file to dump the output to.
841 */
842void GuestToolboxStream::Dump(const char *pszFile)
843{
844 LogFlowFunc(("Dumping contents of stream=0x%p (cbAlloc=%u, cbSize=%u, cbOff=%u) to %s\n",
845 m_pbBuffer, m_cbAllocated, m_cbUsed, m_offBuffer, pszFile));
846
847 RTFILE hFile;
848 int vrc = RTFileOpen(&hFile, pszFile, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
849 if (RT_SUCCESS(vrc))
850 {
851 vrc = RTFileWrite(hFile, m_pbBuffer, m_cbUsed, NULL /* pcbWritten */);
852 RTFileClose(hFile);
853 }
854}
855#endif
856
857/**
858 * Tries to parse the next upcoming pair block within the internal
859 * buffer.
860 *
861 * Returns VERR_NO_DATA is no data is in internal buffer or buffer has been
862 * completely parsed already.
863 *
864 * Returns VERR_MORE_DATA if current block was parsed (with zero or more pairs
865 * stored in stream block) but still contains incomplete (unterminated)
866 * data.
867 *
868 * Returns VINF_SUCCESS if current block was parsed until the next upcoming
869 * block (with zero or more pairs stored in stream block).
870 *
871 * @return VBox status code.
872 * @param streamBlock Reference to guest stream block to fill.
873 */
874int GuestToolboxStream::ParseBlock(GuestToolboxStreamBlock &streamBlock)
875{
876 if ( !m_pbBuffer
877 || !m_cbUsed)
878 return VERR_NO_DATA;
879
880 AssertReturn(m_offBuffer <= m_cbUsed, VERR_INVALID_PARAMETER);
881 if (m_offBuffer == m_cbUsed)
882 return VERR_NO_DATA;
883
884 int vrc = VINF_SUCCESS;
885 char * const pszOff = (char *)&m_pbBuffer[m_offBuffer];
886 size_t cbLeft = m_offBuffer < m_cbUsed ? m_cbUsed - m_offBuffer : 0;
887 char *pszStart = pszOff;
888 while (cbLeft > 0 && *pszStart != '\0')
889 {
890 char * const pszPairEnd = RTStrEnd(pszStart, cbLeft);
891 if (!pszPairEnd)
892 {
893 vrc = VERR_MORE_DATA;
894 break;
895 }
896 size_t const cchPair = (size_t)(pszPairEnd - pszStart);
897 char *pszSep = (char *)memchr(pszStart, '=', cchPair);
898 if (pszSep)
899 *pszSep = '\0'; /* Terminate the separator so that we can use pszStart as our key from now on. */
900 else
901 {
902 vrc = VERR_MORE_DATA; /** @todo r=bird: This is BOGUS because we'll be stuck here if the guest feeds us bad data! */
903 break;
904 }
905 char const * const pszVal = pszSep + 1;
906
907 vrc = streamBlock.SetValue(pszStart, pszVal);
908 if (RT_FAILURE(vrc))
909 return vrc;
910
911 /* Next pair. */
912 pszStart = pszPairEnd + 1;
913 cbLeft -= cchPair + 1;
914 }
915
916 /* If we did not do any movement but we have stuff left
917 * in our buffer just skip the current termination so that
918 * we can try next time. */
919 size_t cbDistance = (pszStart - pszOff);
920 if ( !cbDistance
921 && cbLeft > 0
922 && *pszStart == '\0'
923 && m_offBuffer < m_cbUsed)
924 cbDistance++;
925 m_offBuffer += cbDistance;
926
927 return vrc;
928}
929#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
930
931GuestBase::GuestBase(void)
932 : mConsole(NULL)
933 , mNextContextID(RTRandU32() % VBOX_GUESTCTRL_MAX_CONTEXTS)
934{
935}
936
937GuestBase::~GuestBase(void)
938{
939}
940
941/**
942 * Separate initialization function for the base class.
943 *
944 * @returns VBox status code.
945 */
946int GuestBase::baseInit(void)
947{
948 int const vrc = RTCritSectInit(&mWaitEventCritSect);
949 LogFlowFuncLeaveRC(vrc);
950 return vrc;
951}
952
953/**
954 * Separate uninitialization function for the base class.
955 */
956void GuestBase::baseUninit(void)
957{
958 LogFlowThisFuncEnter();
959
960 /* Make sure to cancel any outstanding wait events. */
961 int vrc2 = cancelWaitEvents();
962 AssertRC(vrc2);
963
964 vrc2 = RTCritSectDelete(&mWaitEventCritSect);
965 AssertRC(vrc2);
966
967 LogFlowFuncLeaveRC(vrc2);
968 /* No return value. */
969}
970
971/**
972 * Cancels all outstanding wait events.
973 *
974 * @returns VBox status code.
975 */
976int GuestBase::cancelWaitEvents(void)
977{
978 LogFlowThisFuncEnter();
979
980 int vrc = RTCritSectEnter(&mWaitEventCritSect);
981 if (RT_SUCCESS(vrc))
982 {
983 GuestEventGroup::iterator itEventGroups = mWaitEventGroups.begin();
984 while (itEventGroups != mWaitEventGroups.end())
985 {
986 GuestWaitEvents::iterator itEvents = itEventGroups->second.begin();
987 while (itEvents != itEventGroups->second.end())
988 {
989 GuestWaitEvent *pEvent = itEvents->second;
990 AssertPtr(pEvent);
991
992 /*
993 * Just cancel the event, but don't remove it from the
994 * wait events map. Don't delete it though, this (hopefully)
995 * is done by the caller using unregisterWaitEvent().
996 */
997 int vrc2 = pEvent->Cancel();
998 AssertRC(vrc2);
999
1000 ++itEvents;
1001 }
1002
1003 ++itEventGroups;
1004 }
1005
1006 int vrc2 = RTCritSectLeave(&mWaitEventCritSect);
1007 if (RT_SUCCESS(vrc))
1008 vrc = vrc2;
1009 }
1010
1011 LogFlowFuncLeaveRC(vrc);
1012 return vrc;
1013}
1014
1015/**
1016 * Handles generic messages not bound to a specific object type.
1017 *
1018 * @return VBox status code. VERR_NOT_FOUND if no handler has been found or VERR_NOT_SUPPORTED
1019 * if this class does not support the specified callback.
1020 * @param pCtxCb Host callback context.
1021 * @param pSvcCb Service callback data.
1022 */
1023int GuestBase::dispatchGeneric(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
1024{
1025 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
1026
1027 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
1028 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
1029
1030 int vrc;
1031
1032 try
1033 {
1034 Log2Func(("uFunc=%RU32, cParms=%RU32\n", pCtxCb->uMessage, pSvcCb->mParms));
1035
1036 switch (pCtxCb->uMessage)
1037 {
1038 case GUEST_MSG_PROGRESS_UPDATE:
1039 vrc = VINF_SUCCESS;
1040 break;
1041
1042 case GUEST_MSG_REPLY:
1043 {
1044 if (pSvcCb->mParms >= 4)
1045 {
1046 int idx = 1; /* Current parameter index. */
1047 CALLBACKDATA_MSG_REPLY dataCb;
1048 /* pSvcCb->mpaParms[0] always contains the context ID. */
1049 vrc = HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.uType);
1050 AssertRCReturn(vrc, vrc);
1051 vrc = HGCMSvcGetU32(&pSvcCb->mpaParms[idx++], &dataCb.rc);
1052 AssertRCReturn(vrc, vrc);
1053 vrc = HGCMSvcGetPv(&pSvcCb->mpaParms[idx++], &dataCb.pvPayload, &dataCb.cbPayload);
1054 AssertRCReturn(vrc, vrc);
1055
1056 try
1057 {
1058 GuestWaitEventPayload evPayload(dataCb.uType, dataCb.pvPayload, dataCb.cbPayload);
1059 vrc = signalWaitEventInternal(pCtxCb, dataCb.rc, &evPayload);
1060 }
1061 catch (int vrcEx) /* Thrown by GuestWaitEventPayload constructor. */
1062 {
1063 vrc = vrcEx;
1064 }
1065 }
1066 else
1067 vrc = VERR_INVALID_PARAMETER;
1068 break;
1069 }
1070
1071 default:
1072 vrc = VERR_NOT_SUPPORTED;
1073 break;
1074 }
1075 }
1076 catch (std::bad_alloc &)
1077 {
1078 vrc = VERR_NO_MEMORY;
1079 }
1080 catch (int vrcCatch)
1081 {
1082 vrc = vrcCatch;
1083 }
1084
1085 LogFlowFuncLeaveRC(vrc);
1086 return vrc;
1087}
1088
1089/**
1090 * Generates a context ID (CID) by incrementing the object's count.
1091 * A CID consists of a session ID, an object ID and a count.
1092 *
1093 * Note: This function does not guarantee that the returned CID is unique;
1094 * the caller has to take care of that and eventually retry.
1095 *
1096 * @returns VBox status code.
1097 * @param uSessionID Session ID to use for CID generation.
1098 * @param uObjectID Object ID to use for CID generation.
1099 * @param puContextID Where to store the generated CID on success.
1100 */
1101int GuestBase::generateContextID(uint32_t uSessionID, uint32_t uObjectID, uint32_t *puContextID)
1102{
1103 AssertPtrReturn(puContextID, VERR_INVALID_POINTER);
1104
1105 if ( uSessionID >= VBOX_GUESTCTRL_MAX_SESSIONS
1106 || uObjectID >= VBOX_GUESTCTRL_MAX_OBJECTS)
1107 return VERR_INVALID_PARAMETER;
1108
1109 uint32_t uCount = ASMAtomicIncU32(&mNextContextID);
1110 uCount %= VBOX_GUESTCTRL_MAX_CONTEXTS;
1111
1112 uint32_t uNewContextID = VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID, uObjectID, uCount);
1113
1114 *puContextID = uNewContextID;
1115
1116#if 0
1117 LogFlowThisFunc(("mNextContextID=%RU32, uSessionID=%RU32, uObjectID=%RU32, uCount=%RU32, uNewContextID=%RU32\n",
1118 mNextContextID, uSessionID, uObjectID, uCount, uNewContextID));
1119#endif
1120 return VINF_SUCCESS;
1121}
1122
1123/**
1124 * Registers (creates) a new wait event based on a given session and object ID.
1125 *
1126 * From those IDs an unique context ID (CID) will be built, which only can be
1127 * around once at a time.
1128 *
1129 * @returns VBox status code.
1130 * @retval VERR_GSTCTL_MAX_CID_COUNT_REACHED if unable to generate a free context ID (CID, the count part (bits 15:0)).
1131 * @param uSessionID Session ID to register wait event for.
1132 * @param uObjectID Object ID to register wait event for.
1133 * @param ppEvent Pointer to registered (created) wait event on success.
1134 * Must be destroyed with unregisterWaitEvent().
1135 */
1136int GuestBase::registerWaitEvent(uint32_t uSessionID, uint32_t uObjectID, GuestWaitEvent **ppEvent)
1137{
1138 GuestEventTypes eventTypesEmpty;
1139 return registerWaitEventEx(uSessionID, uObjectID, eventTypesEmpty, ppEvent);
1140}
1141
1142/**
1143 * Creates and registers a new wait event object that waits on a set of events
1144 * related to a given object within the session.
1145 *
1146 * From the session ID and object ID a one-time unique context ID (CID) is built
1147 * for this wait object. Normally the CID is then passed to the guest along
1148 * with a request, and the guest passed the CID back with the reply. The
1149 * handler for the reply then emits a signal on the event type associated with
1150 * the reply, which includes signalling the object returned by this method and
1151 * the waking up the thread waiting on it.
1152 *
1153 * @returns VBox status code.
1154 * @retval VERR_GSTCTL_MAX_CID_COUNT_REACHED if unable to generate a free context ID (CID, the count part (bits 15:0)).
1155 * @param uSessionID Session ID to register wait event for.
1156 * @param uObjectID Object ID to register wait event for.
1157 * @param lstEvents List of events to register the wait event for.
1158 * @param ppEvent Pointer to registered (created) wait event on success.
1159 * Must be destroyed with unregisterWaitEvent().
1160 */
1161int GuestBase::registerWaitEventEx(uint32_t uSessionID, uint32_t uObjectID, const GuestEventTypes &lstEvents,
1162 GuestWaitEvent **ppEvent)
1163{
1164 AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
1165
1166 uint32_t idContext;
1167 int vrc = generateContextID(uSessionID, uObjectID, &idContext);
1168 AssertRCReturn(vrc, vrc);
1169
1170 GuestWaitEvent *pEvent = new GuestWaitEvent();
1171 AssertPtrReturn(pEvent, VERR_NO_MEMORY);
1172
1173 vrc = pEvent->Init(idContext, lstEvents);
1174 AssertRCReturn(vrc, vrc);
1175
1176 LogFlowThisFunc(("New event=%p, CID=%RU32\n", pEvent, idContext));
1177
1178 vrc = RTCritSectEnter(&mWaitEventCritSect);
1179 if (RT_SUCCESS(vrc))
1180 {
1181 /*
1182 * Check that we don't have any context ID collisions (should be very unlikely).
1183 *
1184 * The ASSUMPTION here is that mWaitEvents has all the same events as
1185 * mWaitEventGroups, so it suffices to check one of the two.
1186 */
1187 if (mWaitEvents.find(idContext) != mWaitEvents.end())
1188 {
1189 uint32_t cTries = 0;
1190 do
1191 {
1192 vrc = generateContextID(uSessionID, uObjectID, &idContext);
1193 AssertRCBreak(vrc);
1194 LogFunc(("Found context ID duplicate; trying a different context ID: %#x\n", idContext));
1195 if (mWaitEvents.find(idContext) != mWaitEvents.end())
1196 vrc = VERR_GSTCTL_MAX_CID_COUNT_REACHED;
1197 } while (RT_FAILURE_NP(vrc) && cTries++ < 10);
1198 }
1199 if (RT_SUCCESS(vrc))
1200 {
1201 /*
1202 * Insert event into matching event group. This is for faster per-group lookup of all events later.
1203 */
1204 uint32_t cInserts = 0;
1205 for (GuestEventTypes::const_iterator ItType = lstEvents.begin(); ItType != lstEvents.end(); ++ItType)
1206 {
1207 GuestWaitEvents &eventGroup = mWaitEventGroups[*ItType];
1208 if (eventGroup.find(idContext) == eventGroup.end())
1209 {
1210 try
1211 {
1212 eventGroup.insert(std::pair<uint32_t, GuestWaitEvent *>(idContext, pEvent));
1213 cInserts++;
1214 }
1215 catch (std::bad_alloc &)
1216 {
1217 while (ItType != lstEvents.begin())
1218 {
1219 --ItType;
1220 mWaitEventGroups[*ItType].erase(idContext);
1221 }
1222 vrc = VERR_NO_MEMORY;
1223 break;
1224 }
1225 }
1226 else
1227 Assert(cInserts > 0); /* else: lstEvents has duplicate entries. */
1228 }
1229 if (RT_SUCCESS(vrc))
1230 {
1231 Assert(cInserts > 0 || lstEvents.size() == 0);
1232 RT_NOREF(cInserts);
1233
1234 /*
1235 * Register event in the regular event list.
1236 */
1237 try
1238 {
1239 mWaitEvents[idContext] = pEvent;
1240 }
1241 catch (std::bad_alloc &)
1242 {
1243 for (GuestEventTypes::const_iterator ItType = lstEvents.begin(); ItType != lstEvents.end(); ++ItType)
1244 mWaitEventGroups[*ItType].erase(idContext);
1245 vrc = VERR_NO_MEMORY;
1246 }
1247 }
1248 }
1249
1250 RTCritSectLeave(&mWaitEventCritSect);
1251 }
1252 if (RT_SUCCESS(vrc))
1253 {
1254 *ppEvent = pEvent;
1255 return vrc;
1256 }
1257
1258 if (pEvent)
1259 delete pEvent;
1260
1261 return vrc;
1262}
1263
1264/**
1265 * Signals all wait events of a specific type (if found)
1266 * and notifies external events accordingly.
1267 *
1268 * @returns VBox status code.
1269 * @param aType Event type to signal.
1270 * @param aEvent Which external event to notify.
1271 */
1272int GuestBase::signalWaitEvent(VBoxEventType_T aType, IEvent *aEvent)
1273{
1274 int vrc = RTCritSectEnter(&mWaitEventCritSect);
1275#ifdef DEBUG
1276 uint32_t cEvents = 0;
1277#endif
1278 if (RT_SUCCESS(vrc))
1279 {
1280 GuestEventGroup::iterator itGroup = mWaitEventGroups.find(aType);
1281 if (itGroup != mWaitEventGroups.end())
1282 {
1283 /* Signal all events in the group, leaving the group empty afterwards. */
1284 GuestWaitEvents::iterator ItWaitEvt;
1285 while ((ItWaitEvt = itGroup->second.begin()) != itGroup->second.end())
1286 {
1287 LogFlowThisFunc(("Signalling event=%p, type=%ld (CID %#x: Session=%RU32, Object=%RU32, Count=%RU32) ...\n",
1288 ItWaitEvt->second, aType, ItWaitEvt->first, VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(ItWaitEvt->first),
1289 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(ItWaitEvt->first), VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(ItWaitEvt->first)));
1290
1291 int vrc2 = ItWaitEvt->second->SignalExternal(aEvent);
1292 AssertRC(vrc2);
1293
1294 /* Take down the wait event object details before we erase it from this list and invalid ItGrpEvt. */
1295 const GuestEventTypes &EvtTypes = ItWaitEvt->second->Types();
1296 uint32_t idContext = ItWaitEvt->first;
1297 itGroup->second.erase(ItWaitEvt);
1298
1299 for (GuestEventTypes::const_iterator ItType = EvtTypes.begin(); ItType != EvtTypes.end(); ++ItType)
1300 {
1301 GuestEventGroup::iterator EvtTypeGrp = mWaitEventGroups.find(*ItType);
1302 if (EvtTypeGrp != mWaitEventGroups.end())
1303 {
1304 ItWaitEvt = EvtTypeGrp->second.find(idContext);
1305 if (ItWaitEvt != EvtTypeGrp->second.end())
1306 {
1307 LogFlowThisFunc(("Removing event %p (CID %#x) from type %d group\n", ItWaitEvt->second, idContext, *ItType));
1308 EvtTypeGrp->second.erase(ItWaitEvt);
1309 LogFlowThisFunc(("%zu events left for type %d\n", EvtTypeGrp->second.size(), *ItType));
1310 Assert(EvtTypeGrp->second.find(idContext) == EvtTypeGrp->second.end()); /* no duplicates */
1311 }
1312 }
1313 }
1314 }
1315 }
1316
1317 int vrc2 = RTCritSectLeave(&mWaitEventCritSect);
1318 if (RT_SUCCESS(vrc))
1319 vrc = vrc2;
1320 }
1321
1322#ifdef DEBUG
1323 LogFlowThisFunc(("Signalled %RU32 events, vrc=%Rrc\n", cEvents, vrc));
1324#endif
1325 return vrc;
1326}
1327
1328/**
1329 * Signals a wait event which is registered to a specific callback (bound to a context ID (CID)).
1330 *
1331 * @returns VBox status code.
1332 * @param pCbCtx Pointer to host service callback context.
1333 * @param vrcGuest Guest return VBox status code to set.
1334 * @param pPayload Additional wait event payload data set set on return. Optional.
1335 */
1336int GuestBase::signalWaitEventInternal(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, int vrcGuest, const GuestWaitEventPayload *pPayload)
1337{
1338 if (RT_SUCCESS(vrcGuest))
1339 return signalWaitEventInternalEx(pCbCtx, VINF_SUCCESS, VINF_SUCCESS /* vrcGuest */, pPayload);
1340
1341 return signalWaitEventInternalEx(pCbCtx, VERR_GSTCTL_GUEST_ERROR, vrcGuest, pPayload);
1342}
1343
1344/**
1345 * Signals a wait event which is registered to a specific callback (bound to a context ID (CID)).
1346 * Extended version.
1347 *
1348 * @returns VBox status code.
1349 * @param pCbCtx Pointer to host service callback context.
1350 * @param vrc Return VBox status code to set as wait result.
1351 * @param vrcGuest Guest return VBox status code to set additionally, if
1352 * vrc is set to VERR_GSTCTL_GUEST_ERROR.
1353 * @param pPayload Additional wait event payload data set set on return. Optional.
1354 */
1355int GuestBase::signalWaitEventInternalEx(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, int vrc, int vrcGuest,
1356 const GuestWaitEventPayload *pPayload)
1357{
1358 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1359 /* pPayload is optional. */
1360
1361 int vrc2 = RTCritSectEnter(&mWaitEventCritSect);
1362 if (RT_SUCCESS(vrc2))
1363 {
1364 GuestWaitEvents::iterator itEvent = mWaitEvents.find(pCbCtx->uContextID);
1365 if (itEvent != mWaitEvents.end())
1366 {
1367 LogFlowThisFunc(("Signalling event=%p (CID %RU32, vrc=%Rrc, vrcGuest=%Rrc, pPayload=%p) ...\n",
1368 itEvent->second, itEvent->first, vrc, vrcGuest, pPayload));
1369 GuestWaitEvent *pEvent = itEvent->second;
1370 AssertPtr(pEvent);
1371 vrc2 = pEvent->SignalInternal(vrc, vrcGuest, pPayload);
1372 }
1373 else
1374 vrc2 = VERR_NOT_FOUND;
1375
1376 int vrc3 = RTCritSectLeave(&mWaitEventCritSect);
1377 if (RT_SUCCESS(vrc2))
1378 vrc2 = vrc3;
1379 }
1380
1381 return vrc2;
1382}
1383
1384/**
1385 * Unregisters (deletes) a wait event.
1386 *
1387 * After successful unregistration the event will not be valid anymore.
1388 *
1389 * @returns VBox status code.
1390 * @param pWaitEvt Wait event to unregister (delete).
1391 */
1392int GuestBase::unregisterWaitEvent(GuestWaitEvent *pWaitEvt)
1393{
1394 if (!pWaitEvt) /* Nothing to unregister. */
1395 return VINF_SUCCESS;
1396
1397 int vrc = RTCritSectEnter(&mWaitEventCritSect);
1398 if (RT_SUCCESS(vrc))
1399 {
1400 LogFlowThisFunc(("pWaitEvt=%p\n", pWaitEvt));
1401
1402/** @todo r=bird: One way of optimizing this would be to use the pointer
1403 * instead of the context ID as index into the groups, i.e. revert the value
1404 * pair for the GuestWaitEvents type.
1405 *
1406 * An even more efficent way, would be to not use sexy std::xxx containers for
1407 * the types, but iprt/list.h, as that would just be a RTListNodeRemove call for
1408 * each type w/o needing to iterate much at all. I.e. add a struct {
1409 * RTLISTNODE, GuestWaitEvent *pSelf} array to GuestWaitEvent, and change
1410 * GuestEventGroup to std::map<VBoxEventType_T, RTListAnchorClass>
1411 * (RTListAnchorClass == RTLISTANCHOR wrapper with a constructor)).
1412 *
1413 * P.S. the try/catch is now longer needed after I changed pWaitEvt->Types() to
1414 * return a const reference rather than a copy of the type list (and it think it
1415 * is safe to assume iterators are not hitting the heap). Copy vs reference is
1416 * an easy mistake to make in C++.
1417 *
1418 * P.P.S. The mWaitEventGroups optimization is probably just a lot of extra work
1419 * with little payoff.
1420 */
1421 try
1422 {
1423 /* Remove the event from all event type groups. */
1424 const GuestEventTypes &lstTypes = pWaitEvt->Types();
1425 for (GuestEventTypes::const_iterator itType = lstTypes.begin();
1426 itType != lstTypes.end(); ++itType)
1427 {
1428 /** @todo Slow O(n) lookup. Optimize this. */
1429 GuestWaitEvents::iterator itCurEvent = mWaitEventGroups[(*itType)].begin();
1430 while (itCurEvent != mWaitEventGroups[(*itType)].end())
1431 {
1432 if (itCurEvent->second == pWaitEvt)
1433 {
1434 mWaitEventGroups[(*itType)].erase(itCurEvent);
1435 break;
1436 }
1437 ++itCurEvent;
1438 }
1439 }
1440
1441 /* Remove the event from the general event list as well. */
1442 GuestWaitEvents::iterator itEvent = mWaitEvents.find(pWaitEvt->ContextID());
1443
1444 Assert(itEvent != mWaitEvents.end());
1445 Assert(itEvent->second == pWaitEvt);
1446
1447 mWaitEvents.erase(itEvent);
1448
1449 delete pWaitEvt;
1450 pWaitEvt = NULL;
1451 }
1452 catch (const std::exception &ex)
1453 {
1454 RT_NOREF(ex);
1455 AssertFailedStmt(vrc = VERR_NOT_FOUND);
1456 }
1457
1458 int vrc2 = RTCritSectLeave(&mWaitEventCritSect);
1459 if (RT_SUCCESS(vrc))
1460 vrc = vrc2;
1461 }
1462
1463 return vrc;
1464}
1465
1466/**
1467 * Waits for an already registered guest wait event.
1468 *
1469 * @return VBox status code.
1470 * @retval VERR_GSTCTL_GUEST_ERROR may be returned, call GuestResult() to get
1471 * the actual result.
1472 *
1473 * @param pWaitEvt Pointer to event to wait for.
1474 * @param msTimeout Timeout (in ms) for waiting.
1475 * @param pType Event type of following IEvent. Optional.
1476 * @param ppEvent Pointer to IEvent which got triggered for this event. Optional.
1477 */
1478int GuestBase::waitForEvent(GuestWaitEvent *pWaitEvt, uint32_t msTimeout, VBoxEventType_T *pType, IEvent **ppEvent)
1479{
1480 AssertPtrReturn(pWaitEvt, VERR_INVALID_POINTER);
1481 /* pType is optional. */
1482 /* ppEvent is optional. */
1483
1484 int vrc = pWaitEvt->Wait(msTimeout);
1485 if (RT_SUCCESS(vrc))
1486 {
1487 const ComPtr<IEvent> pThisEvent = pWaitEvt->Event();
1488 if (pThisEvent.isNotNull()) /* Make sure that we actually have an event associated. */
1489 {
1490 if (pType)
1491 {
1492 HRESULT hrc = pThisEvent->COMGETTER(Type)(pType);
1493 if (FAILED(hrc))
1494 vrc = VERR_COM_UNEXPECTED;
1495 }
1496 if ( RT_SUCCESS(vrc)
1497 && ppEvent)
1498 pThisEvent.queryInterfaceTo(ppEvent);
1499
1500 unconst(pThisEvent).setNull();
1501 }
1502 }
1503
1504 return vrc;
1505}
1506
1507#ifndef VBOX_GUESTCTRL_TEST_CASE
1508/**
1509 * Convenience function to return a pre-formatted string using an action description and a guest error information.
1510 *
1511 * @returns Pre-formatted string with a user-friendly error string.
1512 * @param strAction Action of when the error occurred.
1513 * @param guestErrorInfo Related guest error information to use.
1514 */
1515/* static */ Utf8Str GuestBase::getErrorAsString(const Utf8Str& strAction, const GuestErrorInfo& guestErrorInfo)
1516{
1517 Assert(strAction.isNotEmpty());
1518 return Utf8StrFmt("%s: %s", strAction.c_str(), getErrorAsString(guestErrorInfo).c_str());
1519}
1520
1521/**
1522 * Returns a user-friendly error message from a given GuestErrorInfo object.
1523 *
1524 * @returns Error message string.
1525 * @param guestErrorInfo Guest error info to return error message for.
1526 */
1527/* static */ Utf8Str GuestBase::getErrorAsString(const GuestErrorInfo& guestErrorInfo)
1528{
1529 AssertMsg(RT_FAILURE(guestErrorInfo.getVrc()), ("Guest vrc does not indicate a failure\n"));
1530
1531 Utf8Str strErr;
1532
1533#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
1534# define CASE_TOOL_ERROR(a_eType, a_strTool) \
1535 case a_eType: \
1536 { \
1537 strErr = GuestProcessToolbox::guestErrorToString(a_strTool, guestErrorInfo); \
1538 break; \
1539 }
1540#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
1541
1542 switch (guestErrorInfo.getType())
1543 {
1544 case GuestErrorInfo::Type_Session:
1545 strErr = GuestSession::i_guestErrorToString(guestErrorInfo.getVrc());
1546 break;
1547
1548 case GuestErrorInfo::Type_Process:
1549 strErr = GuestProcess::i_guestErrorToString(guestErrorInfo.getVrc(), guestErrorInfo.getWhat().c_str());
1550 break;
1551
1552 case GuestErrorInfo::Type_File:
1553 strErr = GuestFile::i_guestErrorToString(guestErrorInfo.getVrc(), guestErrorInfo.getWhat().c_str());
1554 break;
1555
1556 case GuestErrorInfo::Type_Directory:
1557 strErr = GuestDirectory::i_guestErrorToString(guestErrorInfo.getVrc(), guestErrorInfo.getWhat().c_str());
1558 break;
1559
1560 case GuestErrorInfo::Type_Fs:
1561 strErr = GuestFs::guestErrorToString(guestErrorInfo);
1562 break;
1563
1564#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
1565 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolLs, VBOXSERVICE_TOOL_LS);
1566 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolMkDir, VBOXSERVICE_TOOL_MKDIR);
1567 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolMkTemp, VBOXSERVICE_TOOL_MKTEMP);
1568 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolRm, VBOXSERVICE_TOOL_RM);
1569 CASE_TOOL_ERROR(GuestErrorInfo::Type_ToolStat, VBOXSERVICE_TOOL_STAT);
1570#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
1571 default:
1572 AssertMsgFailed(("Type not implemented (type=%RU32, vrc=%Rrc)\n", guestErrorInfo.getType(), guestErrorInfo.getVrc()));
1573 strErr = Utf8StrFmt("Unknown / Not implemented -- Please file a bug report (type=%RU32, vrc=%Rrc)\n",
1574 guestErrorInfo.getType(), guestErrorInfo.getVrc());
1575 break;
1576 }
1577
1578 return strErr;
1579}
1580
1581#endif /* VBOX_GUESTCTRL_TEST_CASE */
1582
1583/**
1584 * Converts RTFMODE to FsObjType_T.
1585 *
1586 * @return Converted FsObjType_T type.
1587 * @param fMode RTFMODE to convert.
1588 */
1589/* static */
1590FsObjType_T GuestBase::fileModeToFsObjType(RTFMODE fMode)
1591{
1592 if (RTFS_IS_FIFO(fMode)) return FsObjType_Fifo;
1593 else if (RTFS_IS_DEV_CHAR(fMode)) return FsObjType_DevChar;
1594 else if (RTFS_IS_DIRECTORY(fMode)) return FsObjType_Directory;
1595 else if (RTFS_IS_DEV_BLOCK(fMode)) return FsObjType_DevBlock;
1596 else if (RTFS_IS_FILE(fMode)) return FsObjType_File;
1597 else if (RTFS_IS_SYMLINK(fMode)) return FsObjType_Symlink;
1598 else if (RTFS_IS_SOCKET(fMode)) return FsObjType_Socket;
1599 else if (RTFS_IS_WHITEOUT(fMode)) return FsObjType_WhiteOut;
1600
1601 return FsObjType_Unknown;
1602}
1603
1604/**
1605 * Converts a FsObjType_T to a human-readable string.
1606 *
1607 * @returns Human-readable string of FsObjType_T.
1608 * @param enmType FsObjType_T to convert.
1609 */
1610/* static */
1611const char *GuestBase::fsObjTypeToStr(FsObjType_T enmType)
1612{
1613 switch (enmType)
1614 {
1615 case FsObjType_Directory: return "directory";
1616 case FsObjType_Symlink: return "symbolic link";
1617 case FsObjType_File: return "file";
1618 default: break;
1619 }
1620
1621 return "unknown";
1622}
1623
1624/**
1625 * Converts a PathStyle_T to a human-readable string.
1626 *
1627 * @returns Human-readable string of PathStyle_T.
1628 * @param enmPathStyle PathStyle_T to convert.
1629 */
1630/* static */
1631const char *GuestBase::pathStyleToStr(PathStyle_T enmPathStyle)
1632{
1633 switch (enmPathStyle)
1634 {
1635 case PathStyle_DOS: return "DOS";
1636 case PathStyle_UNIX: return "UNIX";
1637 case PathStyle_Unknown: return "Unknown";
1638 default: break;
1639 }
1640
1641 return "<invalid>";
1642}
1643
1644GuestObject::GuestObject(void)
1645 : mSession(NULL),
1646 mObjectID(0)
1647{
1648}
1649
1650GuestObject::~GuestObject(void)
1651{
1652}
1653
1654/**
1655 * Binds this guest (control) object to a specific guest (control) session.
1656 *
1657 * @returns VBox status code.
1658 * @param pConsole Pointer to console object to use.
1659 * @param pSession Pointer to session to bind this object to.
1660 * @param uObjectID Object ID for this object to use within that specific session.
1661 * Each object ID must be unique per session.
1662 */
1663int GuestObject::bindToSession(Console *pConsole, GuestSession *pSession, uint32_t uObjectID)
1664{
1665 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
1666 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1667
1668 mConsole = pConsole;
1669 mSession = pSession;
1670 mObjectID = uObjectID;
1671
1672 return VINF_SUCCESS;
1673}
1674
1675/**
1676 * Registers (creates) a new wait event.
1677 *
1678 * @returns VBox status code.
1679 * @param lstEvents List of events which the new wait event gets triggered at.
1680 * @param ppEvent Returns the new wait event on success.
1681 */
1682int GuestObject::registerWaitEvent(const GuestEventTypes &lstEvents,
1683 GuestWaitEvent **ppEvent)
1684{
1685 AssertPtr(mSession);
1686 return GuestBase::registerWaitEventEx(mSession->i_getId(), mObjectID, lstEvents, ppEvent);
1687}
1688
1689/**
1690 * Sends a HGCM message to the guest (via the guest control host service).
1691 *
1692 * @returns VBox status code.
1693 * @param uMessage Message ID of message to send.
1694 * @param cParms Number of HGCM message parameters to send.
1695 * @param paParms Array of HGCM message parameters to send.
1696 */
1697int GuestObject::sendMessage(uint32_t uMessage, uint32_t cParms, PVBOXHGCMSVCPARM paParms)
1698{
1699#ifndef VBOX_GUESTCTRL_TEST_CASE
1700 ComObjPtr<Console> pConsole = mConsole;
1701 Assert(!pConsole.isNull());
1702
1703 int vrc = VERR_HGCM_SERVICE_NOT_FOUND;
1704
1705 /* Forward the information to the VMM device. */
1706 VMMDev *pVMMDev = pConsole->i_getVMMDev();
1707 if (pVMMDev)
1708 {
1709 /* HACK ALERT! We extend the first parameter to 64-bit and use the
1710 two topmost bits for call destination information. */
1711 Assert(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT);
1712 paParms[0].type = VBOX_HGCM_SVC_PARM_64BIT;
1713 paParms[0].u.uint64 = (uint64_t)paParms[0].u.uint32 | VBOX_GUESTCTRL_DST_SESSION;
1714
1715 /* Make the call. */
1716 LogFlowThisFunc(("uMessage=%RU32, cParms=%RU32\n", uMessage, cParms));
1717 vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uMessage, cParms, paParms);
1718 if (RT_FAILURE(vrc))
1719 {
1720 /** @todo What to do here? */
1721 }
1722 }
1723#else
1724 LogFlowThisFuncEnter();
1725
1726 /* Not needed within testcases. */
1727 RT_NOREF(uMessage, cParms, paParms);
1728 int vrc = VINF_SUCCESS;
1729#endif
1730 return vrc;
1731}
1732
1733GuestWaitEventBase::GuestWaitEventBase(void)
1734 : mfAborted(false),
1735 mCID(0),
1736 mEventSem(NIL_RTSEMEVENT),
1737 mVrc(VINF_SUCCESS),
1738 mGuestRc(VINF_SUCCESS)
1739{
1740}
1741
1742GuestWaitEventBase::~GuestWaitEventBase(void)
1743{
1744 if (mEventSem != NIL_RTSEMEVENT)
1745 {
1746 RTSemEventDestroy(mEventSem);
1747 mEventSem = NIL_RTSEMEVENT;
1748 }
1749}
1750
1751/**
1752 * Initializes a wait event with a specific context ID (CID).
1753 *
1754 * @returns VBox status code.
1755 * @param uCID Context ID (CID) to initialize wait event with.
1756 */
1757int GuestWaitEventBase::Init(uint32_t uCID)
1758{
1759 mCID = uCID;
1760
1761 return RTSemEventCreate(&mEventSem);
1762}
1763
1764/**
1765 * Signals a wait event.
1766 *
1767 * @returns VBox status code.
1768 * @param vrc Return VBox status code to set as wait result.
1769 * @param vrcGuest Guest return VBox status code to set additionally, if
1770 * @a vrc is set to VERR_GSTCTL_GUEST_ERROR.
1771 * @param pPayload Additional wait event payload data set set on return. Optional.
1772 */
1773int GuestWaitEventBase::SignalInternal(int vrc, int vrcGuest, const GuestWaitEventPayload *pPayload)
1774{
1775 if (mfAborted)
1776 return VERR_CANCELLED;
1777
1778#ifdef VBOX_STRICT
1779 if (vrc == VERR_GSTCTL_GUEST_ERROR)
1780 AssertMsg(RT_FAILURE(vrcGuest), ("Guest error indicated but no actual guest error set (%Rrc)\n", vrcGuest));
1781 else
1782 AssertMsg(RT_SUCCESS(vrcGuest), ("No guest error indicated but actual guest error set (%Rrc)\n", vrcGuest));
1783#endif
1784
1785 int vrc2;
1786 if (pPayload)
1787 vrc2 = mPayload.CopyFromDeep(*pPayload);
1788 else
1789 vrc2 = VINF_SUCCESS;
1790 if (RT_SUCCESS(vrc2))
1791 {
1792 mVrc = vrc;
1793 mGuestRc = vrcGuest;
1794
1795 vrc2 = RTSemEventSignal(mEventSem);
1796 }
1797
1798 return vrc2;
1799}
1800
1801/**
1802 * Waits for the event to get triggered. Will return success if the
1803 * wait was successufl (e.g. was being triggered), otherwise an error will be returned.
1804 *
1805 * @returns VBox status code.
1806 * @retval VERR_GSTCTL_GUEST_ERROR may be returned, call GuestResult() to get
1807 * the actual result from the guest side.
1808 *
1809 * @param msTimeout Timeout (in ms) to wait.
1810 * Specifiy 0 to wait indefinitely.
1811 */
1812int GuestWaitEventBase::Wait(RTMSINTERVAL msTimeout)
1813{
1814 int vrc;
1815 if (!mfAborted)
1816 {
1817 AssertReturn(mEventSem != NIL_RTSEMEVENT, VERR_CANCELLED);
1818
1819 vrc = RTSemEventWait(mEventSem, msTimeout ? msTimeout : RT_INDEFINITE_WAIT);
1820 if ( RT_SUCCESS(vrc)
1821 && mfAborted)
1822 vrc = VERR_CANCELLED;
1823
1824 if (RT_SUCCESS(vrc))
1825 {
1826 /* If waiting succeeded, return the overall
1827 * result code. */
1828 vrc = mVrc;
1829 }
1830 }
1831 else
1832 vrc = VERR_CANCELLED;
1833 return vrc;
1834}
1835
1836GuestWaitEvent::GuestWaitEvent(void)
1837{
1838}
1839
1840GuestWaitEvent::~GuestWaitEvent(void)
1841{
1842
1843}
1844
1845/**
1846 * Cancels the event.
1847 */
1848int GuestWaitEvent::Cancel(void)
1849{
1850 if (mfAborted) /* Already aborted? */
1851 return VINF_SUCCESS;
1852
1853 mfAborted = true;
1854
1855#ifdef DEBUG_andy
1856 LogFlowThisFunc(("Cancelling %p ...\n"));
1857#endif
1858 return RTSemEventSignal(mEventSem);
1859}
1860
1861/**
1862 * Initializes a wait event with a given context ID (CID).
1863 *
1864 * @returns VBox status code.
1865 * @param uCID Context ID to initialize wait event with.
1866 */
1867int GuestWaitEvent::Init(uint32_t uCID)
1868{
1869 return GuestWaitEventBase::Init(uCID);
1870}
1871
1872/**
1873 * Initializes a wait event with a given context ID (CID) and a list of event types to wait for.
1874 *
1875 * @returns VBox status code.
1876 * @param uCID Context ID to initialize wait event with.
1877 * @param lstEvents List of event types to wait for this wait event to get signalled.
1878 */
1879int GuestWaitEvent::Init(uint32_t uCID, const GuestEventTypes &lstEvents)
1880{
1881 int vrc = GuestWaitEventBase::Init(uCID);
1882 if (RT_SUCCESS(vrc))
1883 mEventTypes = lstEvents;
1884
1885 return vrc;
1886}
1887
1888/**
1889 * Signals the event.
1890 *
1891 * @return VBox status code.
1892 * @param pEvent Public IEvent to associate.
1893 * Optional.
1894 */
1895int GuestWaitEvent::SignalExternal(IEvent *pEvent)
1896{
1897 if (pEvent)
1898 mEvent = pEvent;
1899
1900 return RTSemEventSignal(mEventSem);
1901}
1902
1903
1904//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1905// GuestPath
1906//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1907
1908/**
1909 * Builds a (final) destination path from a given source + destination path.
1910 *
1911 * This does not utilize any file system access whatsoever. Used for guest and host paths.
1912 *
1913 * @returns VBox status code.
1914 * @param strSrcPath Source path to build destination path for.
1915 * @param enmSrcPathStyle Path style the source path is in.
1916 * @param strDstPath Destination path to use for building the (final) destination path.
1917 * @param enmDstPathStyle Path style the destination path is in.
1918 *
1919 * @note See rules within the function.
1920 */
1921/* static */
1922int GuestPath::BuildDestinationPath(const Utf8Str &strSrcPath, PathStyle_T enmSrcPathStyle,
1923 Utf8Str &strDstPath, PathStyle_T enmDstPathStyle)
1924{
1925 /*
1926 * Rules:
1927 *
1928 * # source dest final dest remarks
1929 *
1930 * 1 /src/path1/ /dst/path2/ /dst/path2/<contents of path1> Just copies contents of <contents of path1>, not the path1 itself.
1931 * 2 /src/path1 /dst/path2/ /dst/path2/path1 Copies path1 into path2.
1932 * 3 /src/path1 /dst/path2 /dst/path2 Overwrites stuff from path2 with stuff from path1.
1933 * 4 Dotdot ("..") directories are forbidden for security reasons.
1934 */
1935 const char *pszSrcName = RTPathFilenameEx(strSrcPath.c_str(),
1936 enmSrcPathStyle == PathStyle_DOS
1937 ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
1938
1939 const char *pszDstName = RTPathFilenameEx(strDstPath.c_str(),
1940 enmDstPathStyle == PathStyle_DOS
1941 ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
1942
1943 if ( (!pszSrcName && !pszDstName) /* #1 */
1944 || ( pszSrcName && pszDstName)) /* #3 */
1945 {
1946 /* Note: Must have DirectoryFlag_CopyIntoExisting + FileFlag_NoReplace *not* set. */
1947 }
1948 else if (pszSrcName && !pszDstName) /* #2 */
1949 {
1950 if (!strDstPath.endsWith(PATH_STYLE_SEP_STR(enmDstPathStyle)))
1951 strDstPath += PATH_STYLE_SEP_STR(enmDstPathStyle);
1952 strDstPath += pszSrcName;
1953 }
1954
1955 /* Translate the built destination path to a path compatible with the destination. */
1956 int vrc = GuestPath::Translate(strDstPath, enmSrcPathStyle, enmDstPathStyle);
1957 if (RT_SUCCESS(vrc))
1958 {
1959 union
1960 {
1961 RTPATHPARSED Parsed;
1962 RTPATHSPLIT Split;
1963 uint8_t ab[4096];
1964 } u;
1965 vrc = RTPathParse(strDstPath.c_str(), &u.Parsed, sizeof(u),
1966 enmDstPathStyle == PathStyle_DOS ? RTPATH_STR_F_STYLE_DOS : RTPATH_STR_F_STYLE_UNIX);
1967 if (RT_SUCCESS(vrc))
1968 {
1969 if (u.Parsed.fProps & RTPATH_PROP_DOTDOT_REFS) /* #4 */
1970 vrc = VERR_INVALID_PARAMETER;
1971 }
1972 }
1973
1974 LogRel2(("Guest Control: Building destination path for '%s' (%s) -> '%s' (%s): %Rrc\n",
1975 strSrcPath.c_str(), GuestBase::pathStyleToStr(enmSrcPathStyle),
1976 strDstPath.c_str(), GuestBase::pathStyleToStr(enmDstPathStyle), vrc));
1977
1978 return vrc;
1979}
1980
1981/**
1982 * Translates a path from a specific path style into another.
1983 *
1984 * @returns VBox status code.
1985 * @retval VERR_NOT_SUPPORTED if a conversion is not supported.
1986 * @retval VERR_NOT_IMPLEMENTED if path style conversion is not implemented yet.
1987 * @param strPath Path to translate. Will contain the translated path on success. UTF-8 only.
1988 * @param enmSrcPathStyle Source path style \a strPath is expected in.
1989 * @param enmDstPathStyle Destination path style to convert to.
1990 * @param fForce Whether to force the translation to the destination path style or not.
1991 *
1992 * @note This does NOT remove any trailing slashes and/or perform file system lookups!
1993 */
1994/* static */
1995int GuestPath::Translate(Utf8Str &strPath, PathStyle_T enmSrcPathStyle, PathStyle_T enmDstPathStyle, bool fForce /* = false */)
1996{
1997 if (strPath.isEmpty())
1998 return VINF_SUCCESS;
1999
2000 AssertReturn(RTStrIsValidEncoding(strPath.c_str()), VERR_INVALID_PARAMETER);
2001
2002 int vrc = VINF_SUCCESS;
2003
2004 Utf8Str strTranslated;
2005
2006 if ( ( enmSrcPathStyle == PathStyle_DOS
2007 && enmDstPathStyle == PathStyle_UNIX)
2008 || (fForce && enmDstPathStyle == PathStyle_UNIX))
2009 {
2010 strTranslated = strPath;
2011 RTPathChangeToUnixSlashes(strTranslated.mutableRaw(), true /* fForce */);
2012 }
2013 else if ( ( enmSrcPathStyle == PathStyle_UNIX
2014 && enmDstPathStyle == PathStyle_DOS)
2015 || (fForce && enmDstPathStyle == PathStyle_DOS))
2016
2017 {
2018 strTranslated = strPath;
2019 RTPathChangeToDosSlashes(strTranslated.mutableRaw(), true /* fForce */);
2020 }
2021
2022 if ( strTranslated.isEmpty() /* Not forced. */
2023 && enmSrcPathStyle == enmDstPathStyle)
2024 {
2025 strTranslated = strPath;
2026 }
2027
2028 if (RT_FAILURE(vrc))
2029 {
2030 LogRel(("Guest Control: Translating path '%s' (%s) -> '%s' (%s) failed, vrc=%Rrc\n",
2031 strPath.c_str(), GuestBase::pathStyleToStr(enmSrcPathStyle),
2032 strTranslated.c_str(), GuestBase::pathStyleToStr(enmDstPathStyle), vrc));
2033 return vrc;
2034 }
2035
2036 /* Cleanup. */
2037 const char *psz = strTranslated.mutableRaw();
2038 size_t const cch = strTranslated.length();
2039 size_t off = 0;
2040 while (off < cch)
2041 {
2042 if (off + 1 > cch)
2043 break;
2044 /* Remove double back slashes (DOS only). */
2045 if ( enmDstPathStyle == PathStyle_DOS
2046 && psz[off] == '\\'
2047 && psz[off + 1] == '\\')
2048 {
2049 strTranslated.erase(off + 1, 1);
2050 off++;
2051 }
2052 /* Remove double forward slashes (UNIX only). */
2053 if ( enmDstPathStyle == PathStyle_UNIX
2054 && psz[off] == '/'
2055 && psz[off + 1] == '/')
2056 {
2057 strTranslated.erase(off + 1, 1);
2058 off++;
2059 }
2060 off++;
2061 }
2062
2063 /* Note: Do not trim() paths here, as technically it's possible to create paths with trailing spaces. */
2064
2065 strTranslated.jolt();
2066
2067 LogRel2(("Guest Control: Translating '%s' (%s) -> '%s' (%s): %Rrc\n",
2068 strPath.c_str(), GuestBase::pathStyleToStr(enmSrcPathStyle),
2069 strTranslated.c_str(), GuestBase::pathStyleToStr(enmDstPathStyle), vrc));
2070
2071 if (RT_SUCCESS(vrc))
2072 strPath = strTranslated;
2073
2074 return vrc;
2075}
2076
Note: See TracBrowser for help on using the repository browser.

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