VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/ConsoleImplConfigCommon.cpp@ 101381

Last change on this file since 101381 was 101318, checked in by vboxsync, 8 months ago

Main: Move findEfiRom to the common code and add a PlatformArchitecture_T parameter to IVirtualBox::CheckFirmwarePresent to differentiate between x86 and ARM, bugref:10528

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 148.4 KB
Line 
1/* $Id: ConsoleImplConfigCommon.cpp 101318 2023-09-29 15:13:07Z vboxsync $ */
2/** @file
3 * VBox Console COM Class implementation - VM Configuration Bits.
4 *
5 * @remark We've split out the code that the 64-bit VC++ v8 compiler finds
6 * problematic to optimize so we can disable optimizations and later,
7 * perhaps, find a real solution for it (like rewriting the code and
8 * to stop resemble a tonne of spaghetti).
9 */
10
11/*
12 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
13 *
14 * This file is part of VirtualBox base platform packages, as
15 * available from https://www.virtualbox.org.
16 *
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License
19 * as published by the Free Software Foundation, in version 3 of the
20 * License.
21 *
22 * This program is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, see <https://www.gnu.org/licenses>.
29 *
30 * SPDX-License-Identifier: GPL-3.0-only
31 */
32
33
34/*********************************************************************************************************************************
35* Header Files *
36*********************************************************************************************************************************/
37#define LOG_GROUP LOG_GROUP_MAIN_CONSOLE
38#include "LoggingNew.h"
39
40// VBoxNetCfg-win.h needs winsock2.h and thus MUST be included before any other
41// header file includes Windows.h.
42#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_NETFLT)
43# include <VBox/VBoxNetCfg-win.h>
44#endif
45
46#include "ConsoleImpl.h"
47#include "DisplayImpl.h"
48#include "NvramStoreImpl.h"
49#ifdef VBOX_WITH_DRAG_AND_DROP
50# include "GuestImpl.h"
51# include "GuestDnDPrivate.h"
52#endif
53#include "VMMDev.h"
54#include "Global.h"
55#ifdef VBOX_WITH_PCI_PASSTHROUGH
56# include "PCIRawDevImpl.h"
57#endif
58
59// generated header
60#include "SchemaDefs.h"
61
62#include "AutoCaller.h"
63
64#include <iprt/base64.h>
65#include <iprt/buildconfig.h>
66#include <iprt/ctype.h>
67#include <iprt/dir.h>
68#include <iprt/file.h>
69#include <iprt/param.h>
70#include <iprt/path.h>
71#include <iprt/string.h>
72#include <iprt/system.h>
73#if 0 /* enable to play with lots of memory. */
74# include <iprt/env.h>
75#endif
76#include <iprt/stream.h>
77
78#include <iprt/http.h>
79#include <iprt/socket.h>
80#include <iprt/uri.h>
81
82#include <VBox/vmm/vmmr3vtable.h>
83#include <VBox/vmm/vmapi.h>
84#include <VBox/err.h>
85#include <VBox/param.h>
86#include <VBox/settings.h> /* For MachineConfigFile::getHostDefaultAudioDriver(). */
87#include <VBox/vmm/pdmapi.h> /* For PDMR3DriverAttach/PDMR3DriverDetach. */
88#include <VBox/vmm/pdmusb.h> /* For PDMR3UsbCreateEmulatedDevice. */
89#include <VBox/vmm/pdmdev.h> /* For PDMAPICMODE enum. */
90#include <VBox/vmm/pdmstorageifs.h>
91#include <VBox/version.h>
92#ifdef VBOX_WITH_SHARED_CLIPBOARD
93# include <VBox/HostServices/VBoxClipboardSvc.h>
94#endif
95#ifdef VBOX_WITH_GUEST_PROPS
96# include <VBox/HostServices/GuestPropertySvc.h>
97# include <VBox/com/defs.h>
98# include <VBox/com/array.h>
99# include <vector>
100#endif /* VBOX_WITH_GUEST_PROPS */
101#include <VBox/intnet.h>
102
103#include <VBox/com/com.h>
104#include <VBox/com/string.h>
105#include <VBox/com/array.h>
106
107#ifdef VBOX_WITH_NETFLT
108# if defined(RT_OS_SOLARIS)
109# include <zone.h>
110# elif defined(RT_OS_LINUX)
111# include <unistd.h>
112# include <sys/ioctl.h>
113# include <sys/socket.h>
114# include <linux/types.h>
115# include <linux/if.h>
116# elif defined(RT_OS_FREEBSD)
117# include <unistd.h>
118# include <sys/types.h>
119# include <sys/ioctl.h>
120# include <sys/socket.h>
121# include <net/if.h>
122# include <net80211/ieee80211_ioctl.h>
123# endif
124# if defined(RT_OS_WINDOWS)
125# include <iprt/win/ntddndis.h>
126# include <devguid.h>
127# else
128# include <HostNetworkInterfaceImpl.h>
129# include <netif.h>
130# include <stdlib.h>
131# endif
132#endif /* VBOX_WITH_NETFLT */
133
134#ifdef VBOX_WITH_AUDIO_VRDE
135# include "DrvAudioVRDE.h"
136#endif
137#ifdef VBOX_WITH_AUDIO_RECORDING
138# include "DrvAudioRec.h"
139#endif
140#include "NetworkServiceRunner.h"
141#ifdef VBOX_WITH_EXTPACK
142# include "ExtPackManagerImpl.h"
143#endif
144
145
146/*********************************************************************************************************************************
147* Internal Functions *
148*********************************************************************************************************************************/
149
150/* Darwin compile kludge */
151#undef PVM
152
153/**
154 * Helper that calls CFGMR3InsertString and throws an RTCError if that
155 * fails (C-string variant).
156 * @param pNode See CFGMR3InsertString.
157 * @param pcszName See CFGMR3InsertString.
158 * @param pcszValue The string value.
159 */
160void Console::InsertConfigString(PCFGMNODE pNode, const char *pcszName, const char *pcszValue)
161{
162 int vrc = mpVMM->pfnCFGMR3InsertString(pNode, pcszName, pcszValue);
163 if (RT_FAILURE(vrc))
164 throw ConfigError("CFGMR3InsertString", vrc, pcszName);
165}
166
167/**
168 * Helper that calls CFGMR3InsertString and throws an RTCError if that
169 * fails (Utf8Str variant).
170 * @param pNode See CFGMR3InsertStringN.
171 * @param pcszName See CFGMR3InsertStringN.
172 * @param rStrValue The string value.
173 */
174void Console::InsertConfigString(PCFGMNODE pNode, const char *pcszName, const Utf8Str &rStrValue)
175{
176 int vrc = mpVMM->pfnCFGMR3InsertStringN(pNode, pcszName, rStrValue.c_str(), rStrValue.length());
177 if (RT_FAILURE(vrc))
178 throw ConfigError("CFGMR3InsertStringLengthKnown", vrc, pcszName);
179}
180
181/**
182 * Helper that calls CFGMR3InsertString and throws an RTCError if that
183 * fails (Bstr variant).
184 *
185 * @param pNode See CFGMR3InsertStringN.
186 * @param pcszName See CFGMR3InsertStringN.
187 * @param rBstrValue The string value.
188 */
189void Console::InsertConfigString(PCFGMNODE pNode, const char *pcszName, const Bstr &rBstrValue)
190{
191 InsertConfigString(pNode, pcszName, Utf8Str(rBstrValue));
192}
193
194/**
195 * Helper that calls CFGMR3InsertStringFV and throws an RTCError if that fails.
196 * @param pNode See CFGMR3InsertStringF.
197 * @param pcszName See CFGMR3InsertStringF.
198 * @param pszFormat See CFGMR3InsertStringF.
199 * @param ... See CFGMR3InsertStringF.
200 */
201void Console::InsertConfigStringF(PCFGMNODE pNode, const char *pcszName, const char *pszFormat, ...)
202{
203 va_list va;
204 va_start(va, pszFormat);
205 int vrc = mpVMM->pfnCFGMR3InsertStringFV(pNode, pcszName, pszFormat, va);
206 va_end(va);
207 if (RT_FAILURE(vrc))
208 throw ConfigError("CFGMR3InsertStringFV", vrc, pcszName);
209}
210
211
212/**
213 * Helper that calls CFGMR3InsertPassword and throws an RTCError if that
214 * fails (Utf8Str variant).
215 * @param pNode See CFGMR3InsertPasswordN.
216 * @param pcszName See CFGMR3InsertPasswordN.
217 * @param rStrValue The string value.
218 */
219void Console::InsertConfigPassword(PCFGMNODE pNode, const char *pcszName, const Utf8Str &rStrValue)
220{
221 int vrc = mpVMM->pfnCFGMR3InsertPasswordN(pNode, pcszName, rStrValue.c_str(), rStrValue.length());
222 if (RT_FAILURE(vrc))
223 throw ConfigError("CFGMR3InsertPasswordLengthKnown", vrc, pcszName);
224}
225
226/**
227 * Helper that calls CFGMR3InsertBytes and throws an RTCError if that fails.
228 *
229 * @param pNode See CFGMR3InsertBytes.
230 * @param pcszName See CFGMR3InsertBytes.
231 * @param pvBytes See CFGMR3InsertBytes.
232 * @param cbBytes See CFGMR3InsertBytes.
233 */
234void Console::InsertConfigBytes(PCFGMNODE pNode, const char *pcszName, const void *pvBytes, size_t cbBytes)
235{
236 int vrc = mpVMM->pfnCFGMR3InsertBytes(pNode, pcszName, pvBytes, cbBytes);
237 if (RT_FAILURE(vrc))
238 throw ConfigError("CFGMR3InsertBytes", vrc, pcszName);
239}
240
241/**
242 * Helper that calls CFGMR3InsertInteger and throws an RTCError if that
243 * fails.
244 *
245 * @param pNode See CFGMR3InsertInteger.
246 * @param pcszName See CFGMR3InsertInteger.
247 * @param u64Integer See CFGMR3InsertInteger.
248 */
249void Console::InsertConfigInteger(PCFGMNODE pNode, const char *pcszName, uint64_t u64Integer)
250{
251 int vrc = mpVMM->pfnCFGMR3InsertInteger(pNode, pcszName, u64Integer);
252 if (RT_FAILURE(vrc))
253 throw ConfigError("CFGMR3InsertInteger", vrc, pcszName);
254}
255
256/**
257 * Helper that calls CFGMR3InsertNode and throws an RTCError if that fails.
258 *
259 * @param pNode See CFGMR3InsertNode.
260 * @param pcszName See CFGMR3InsertNode.
261 * @param ppChild See CFGMR3InsertNode.
262 */
263void Console::InsertConfigNode(PCFGMNODE pNode, const char *pcszName, PCFGMNODE *ppChild)
264{
265 int vrc = mpVMM->pfnCFGMR3InsertNode(pNode, pcszName, ppChild);
266 if (RT_FAILURE(vrc))
267 throw ConfigError("CFGMR3InsertNode", vrc, pcszName);
268}
269
270/**
271 * Helper that calls CFGMR3InsertNodeF and throws an RTCError if that fails.
272 *
273 * @param pNode See CFGMR3InsertNodeF.
274 * @param ppChild See CFGMR3InsertNodeF.
275 * @param pszNameFormat Name format string, see CFGMR3InsertNodeF.
276 * @param ... Format arguments.
277 */
278void Console::InsertConfigNodeF(PCFGMNODE pNode, PCFGMNODE *ppChild, const char *pszNameFormat, ...)
279{
280 va_list va;
281 va_start(va, pszNameFormat);
282 int vrc = mpVMM->pfnCFGMR3InsertNodeF(pNode, ppChild, "%N", pszNameFormat, &va);
283 va_end(va);
284 if (RT_FAILURE(vrc))
285 throw ConfigError("CFGMR3InsertNodeF", vrc, pszNameFormat);
286}
287
288/**
289 * Helper that calls CFGMR3RemoveValue and throws an RTCError if that fails.
290 *
291 * @param pNode See CFGMR3RemoveValue.
292 * @param pcszName See CFGMR3RemoveValue.
293 */
294void Console::RemoveConfigValue(PCFGMNODE pNode, const char *pcszName)
295{
296 int vrc = mpVMM->pfnCFGMR3RemoveValue(pNode, pcszName);
297 if (RT_FAILURE(vrc))
298 throw ConfigError("CFGMR3RemoveValue", vrc, pcszName);
299}
300
301/**
302 * Gets an extra data value, consulting both machine and global extra data.
303 *
304 * @throws HRESULT on failure
305 * @returns pStrValue for the callers convenience.
306 * @param pVirtualBox Pointer to the IVirtualBox interface.
307 * @param pMachine Pointer to the IMachine interface.
308 * @param pszName The value to get.
309 * @param pStrValue Where to return it's value (empty string if not
310 * found).
311 */
312DECL_HIDDEN_THROW(Utf8Str *) GetExtraDataBoth(IVirtualBox *pVirtualBox, IMachine *pMachine, const char *pszName, Utf8Str *pStrValue)
313{
314 pStrValue->setNull();
315
316 Bstr bstrName(pszName);
317 Bstr bstrValue;
318 HRESULT hrc = pMachine->GetExtraData(bstrName.raw(), bstrValue.asOutParam());
319 if (FAILED(hrc))
320 throw hrc;
321 if (bstrValue.isEmpty())
322 {
323 hrc = pVirtualBox->GetExtraData(bstrName.raw(), bstrValue.asOutParam());
324 if (FAILED(hrc))
325 throw hrc;
326 }
327
328 if (bstrValue.isNotEmpty())
329 *pStrValue = bstrValue;
330 return pStrValue;
331}
332
333
334/**
335 * Updates the device type for a LED.
336 *
337 * @param penmSubTypeEntry The sub-type entry to update.
338 * @param enmNewType The new type.
339 */
340void Console::i_setLedType(DeviceType_T *penmSubTypeEntry, DeviceType_T enmNewType)
341{
342 /*
343 * ASSUMES no race conditions here wrt concurrent type updating.
344 */
345 if (*penmSubTypeEntry != enmNewType)
346 {
347 *penmSubTypeEntry = enmNewType;
348 ASMAtomicIncU32(&muLedGen);
349 }
350}
351
352
353/**
354 * Allocate a set of LEDs.
355 *
356 * This grabs a maLedSets entry and populates it with @a cLeds.
357 *
358 * @returns Index into maLedSets.
359 * @param cLeds The number of LEDs in the set.
360 * @param fTypes Bitmask of DeviceType_T values, e.g.
361 * RT_BIT_32(DeviceType_Network).
362 * @param ppaSubTypes When not NULL, subtypes for each LED and return the
363 * array pointer here.
364 */
365uint32_t Console::i_allocateDriverLeds(uint32_t cLeds, uint32_t fTypes, DeviceType_T **ppaSubTypes)
366{
367 Assert(cLeds > 0);
368 Assert(cLeds < 1024); /* Adjust if any driver supports >=1024 units! */
369 Assert(!(fTypes & (RT_BIT_32(DeviceType_Null) | ~(RT_BIT_32(DeviceType_End) - 1))));
370
371 /* Preallocate the arrays we need, bunching them together. */
372 AssertCompile((unsigned)DeviceType_Null == 0);
373 PPDMLED volatile *papLeds = (PPDMLED volatile *)RTMemAllocZ( (sizeof(PPDMLED) + (ppaSubTypes ? sizeof(**ppaSubTypes) : 0))
374 * cLeds);
375 AssertStmt(papLeds, throw E_OUTOFMEMORY);
376
377 /* Take the LED lock in allocation mode and see if there are more LED set entries availalbe. */
378 {
379 AutoWriteLock alock(mLedLock COMMA_LOCKVAL_SRC_POS);
380 uint32_t const idxLedSet = mcLedSets;
381 if (idxLedSet < RT_ELEMENTS(maLedSets))
382 {
383 /* Initialize the set and return the index. */
384 PLEDSET pLS = &maLedSets[idxLedSet];
385 pLS->papLeds = papLeds;
386 pLS->cLeds = cLeds;
387 pLS->fTypes = fTypes;
388 if (ppaSubTypes)
389 *ppaSubTypes = pLS->paSubTypes = (DeviceType_T *)&papLeds[cLeds];
390 else
391 pLS->paSubTypes = NULL;
392
393 mcLedSets = idxLedSet + 1;
394 LogRel2(("return idxLedSet=%d (mcLedSets=%u out of max %zu)\n", idxLedSet, mcLedSets, RT_ELEMENTS(maLedSets)));
395 return idxLedSet;
396 }
397 }
398
399 RTMemFree((void *)papLeds);
400 AssertFailed();
401 throw ConfigError("AllocateDriverPapLeds", VERR_OUT_OF_RANGE, "Too many LED sets");
402}
403
404
405/**
406 * @throws ConfigError and std::bad_alloc.
407 */
408void Console::i_attachStatusDriver(PCFGMNODE pCtlInst, uint32_t fTypes, uint32_t cLeds, DeviceType_T **ppaSubTypes,
409 Console::MediumAttachmentMap *pmapMediumAttachments,
410 const char *pcszDevice, unsigned uInstance)
411{
412 PCFGMNODE pLunL0;
413 InsertConfigNode(pCtlInst, "LUN#999", &pLunL0);
414 InsertConfigString(pLunL0, "Driver", "MainStatus");
415 PCFGMNODE pCfg;
416 InsertConfigNode(pLunL0, "Config", &pCfg);
417 uint32_t const iLedSet = i_allocateDriverLeds(cLeds, fTypes, ppaSubTypes);
418 InsertConfigInteger(pCfg, "iLedSet", iLedSet);
419
420 InsertConfigInteger(pCfg, "HasMediumAttachments", pmapMediumAttachments != NULL);
421 if (pmapMediumAttachments)
422 {
423 AssertPtr(pcszDevice);
424 InsertConfigStringF(pCfg, "DeviceInstance", "%s/%u", pcszDevice, uInstance);
425 }
426 InsertConfigInteger(pCfg, "First", 0);
427 InsertConfigInteger(pCfg, "Last", cLeds - 1);
428}
429
430
431/**
432 * @throws ConfigError and std::bad_alloc.
433 */
434void Console::i_attachStatusDriver(PCFGMNODE pCtlInst, DeviceType_T enmType, uint32_t cLeds /*= 1*/)
435{
436 Assert(enmType > DeviceType_Null && enmType < DeviceType_End);
437 i_attachStatusDriver(pCtlInst, RT_BIT_32(enmType), cLeds, NULL, NULL, NULL, 0);
438}
439
440
441/**
442 * Construct the VM configuration tree (CFGM).
443 *
444 * This is a callback for VMR3Create() call. It is called from CFGMR3Init() in
445 * the emulation thread (EMT). Any per thread COM/XPCOM initialization is done
446 * here.
447 *
448 * @returns VBox status code.
449 * @param pUVM The user mode VM handle.
450 * @param pVM The cross context VM handle.
451 * @param pVMM The VMM ring-3 vtable.
452 * @param pvConsole Pointer to the VMPowerUpTask object.
453 *
454 * @note Locks the Console object for writing.
455 */
456/*static*/ DECLCALLBACK(int)
457Console::i_configConstructor(PUVM pUVM, PVM pVM, PCVMMR3VTABLE pVMM, void *pvConsole)
458{
459 LogFlowFuncEnter();
460
461 AssertReturn(pvConsole, VERR_INVALID_POINTER);
462 ComObjPtr<Console> pConsole = static_cast<Console *>(pvConsole);
463
464 AutoCaller autoCaller(pConsole);
465 AssertComRCReturn(autoCaller.hrc(), VERR_ACCESS_DENIED);
466
467 /* lock the console because we widely use internal fields and methods */
468 AutoWriteLock alock(pConsole COMMA_LOCKVAL_SRC_POS);
469
470 /*
471 * Set the VM handle and do the rest of the job in an worker method so we
472 * can easily reset the VM handle on failure.
473 */
474 pConsole->mpUVM = pUVM;
475 pVMM->pfnVMR3RetainUVM(pUVM);
476 int vrc;
477 try
478 {
479 vrc = pConsole->i_configConstructorInner(pUVM, pVM, pVMM, &alock);
480 }
481 catch (...)
482 {
483 vrc = VERR_UNEXPECTED_EXCEPTION;
484 }
485 if (RT_FAILURE(vrc))
486 {
487 pConsole->mpUVM = NULL;
488 pVMM->pfnVMR3ReleaseUVM(pUVM);
489 }
490
491 return vrc;
492}
493
494
495/**
496 * Worker for configConstructor.
497 *
498 * @return VBox status code.
499 * @return VERR_PLATFORM_ARCH_NOT_SUPPORTED if the machine's platform architecture is not supported.
500 * @param pUVM The user mode VM handle.
501 * @param pVM The cross context VM handle.
502 * @param pVMM The VMM vtable.
503 * @param pAlock The automatic lock instance. This is for when we have
504 * to leave it in order to avoid deadlocks (ext packs and
505 * more).
506 */
507int Console::i_configConstructorInner(PUVM pUVM, PVM pVM, PCVMMR3VTABLE pVMM, AutoWriteLock *pAlock)
508{
509 ComPtr<IMachine> const pMachine = i_machine();
510
511 ComPtr<IPlatform> pPlatform;
512 HRESULT hrc = pMachine->COMGETTER(Platform)(pPlatform.asOutParam());
513 AssertComRCReturn(hrc, VERR_COM_VM_ERROR);
514
515 PlatformArchitecture_T platformArch;
516 hrc = pPlatform->COMGETTER(Architecture)(&platformArch);
517 AssertComRCReturn(hrc, VERR_COM_VM_ERROR);
518
519 switch (platformArch)
520 {
521 case PlatformArchitecture_x86:
522 return i_configConstructorX86(pUVM, pVM, pVMM, pAlock);
523
524#ifdef VBOX_WITH_VIRT_ARMV8
525 case PlatformArchitecture_ARM:
526 return i_configConstructorArmV8(pUVM, pVM, pVMM, pAlock);
527#endif
528 default:
529 break;
530 }
531
532 return VERR_PLATFORM_ARCH_NOT_SUPPORTED;
533}
534
535
536/**
537 * Configures an audio driver via CFGM by getting (optional) values from extra data.
538 *
539 * @param pVirtualBox Pointer to IVirtualBox instance.
540 * @param pMachine Pointer to IMachine instance.
541 * @param pLUN Pointer to CFGM node of LUN (the driver) to configure.
542 * @param pszDrvName Name of the driver to configure.
543 * @param fAudioEnabledIn IAudioAdapter::enabledIn value.
544 * @param fAudioEnabledOut IAudioAdapter::enabledOut value.
545 *
546 * @throws ConfigError or HRESULT on if there is trouble.
547 */
548void Console::i_configAudioDriver(IVirtualBox *pVirtualBox, IMachine *pMachine, PCFGMNODE pLUN, const char *pszDrvName,
549 bool fAudioEnabledIn, bool fAudioEnabledOut)
550{
551#define H() AssertLogRelMsgStmt(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), \
552 throw ConfigError(__FUNCTION__, VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR, "line: " RT_XSTR(__LINE__)))
553
554 InsertConfigString(pLUN, "Driver", "AUDIO");
555
556 PCFGMNODE pCfg;
557 InsertConfigNode(pLUN, "Config", &pCfg);
558 InsertConfigString(pCfg, "DriverName", pszDrvName);
559 InsertConfigInteger(pCfg, "InputEnabled", fAudioEnabledIn);
560 InsertConfigInteger(pCfg, "OutputEnabled", fAudioEnabledOut);
561
562 Utf8Str strTmp;
563 GetExtraDataBoth(pVirtualBox, pMachine, "VBoxInternal2/Audio/Debug/Enabled", &strTmp);
564 const uint64_t fDebugEnabled = strTmp.equalsIgnoreCase("true") || strTmp.equalsIgnoreCase("1");
565 if (fDebugEnabled)
566 {
567 InsertConfigInteger(pCfg, "DebugEnabled", fDebugEnabled);
568
569 Utf8Str strDebugPathOut;
570 GetExtraDataBoth(pVirtualBox, pMachine, "VBoxInternal2/Audio/Debug/PathOut", &strDebugPathOut);
571 InsertConfigString(pCfg, "DebugPathOut", strDebugPathOut.c_str());
572 }
573
574 /*
575 * PCM input parameters (playback + recording).
576 * We have host driver specific ones as: VBoxInternal2/Audio/<DrvName>/<Value>
577 * And global ones for all host drivers: VBoxInternal2/Audio/<Value>
578 */
579 for (unsigned iDir = 0; iDir < 2; iDir++)
580 {
581 static const struct
582 {
583 const char *pszExtraName;
584 const char *pszCfgmName;
585 } s_aToCopy[] =
586 { /* PCM parameters: */
587 { "PCMSampleBit", "PCMSampleBit" },
588 { "PCMSampleHz", "PCMSampleHz" },
589 { "PCMSampleSigned", "PCMSampleSigned" },
590 { "PCMSampleSwapEndian", "PCMSampleSwapEndian" },
591 { "PCMSampleChannels", "PCMSampleChannels" },
592 /* Buffering stuff: */
593 { "PeriodSizeMs", "PeriodSizeMs" },
594 { "BufferSizeMs", "BufferSizeMs" },
595 { "PreBufferSizeMs", "PreBufferSizeMs" },
596 };
597
598 PCFGMNODE pDirNode = NULL;
599 const char *pszDir = iDir == 0 ? "In" : "Out";
600 for (size_t i = 0; i < RT_ELEMENTS(s_aToCopy); i++)
601 {
602 char szExtra[128];
603 RTStrPrintf(szExtra, sizeof(szExtra), "VBoxInternal2/Audio/%s/%s%s", pszDrvName, s_aToCopy[i].pszExtraName, pszDir);
604 GetExtraDataBoth(pVirtualBox, pMachine, szExtra, &strTmp); /* throws hrc */
605 if (strTmp.isEmpty())
606 {
607 RTStrPrintf(szExtra, sizeof(szExtra), "VBoxInternal2/Audio/%s%s", s_aToCopy[i].pszExtraName, pszDir);
608 GetExtraDataBoth(pVirtualBox, pMachine, szExtra, &strTmp);
609 if (strTmp.isEmpty())
610 continue;
611 }
612
613 uint32_t uValue;
614 int vrc = RTStrToUInt32Full(strTmp.c_str(), 0, &uValue);
615 if (RT_SUCCESS(vrc))
616 {
617 if (!pDirNode)
618 InsertConfigNode(pCfg, pszDir, &pDirNode);
619 InsertConfigInteger(pDirNode, s_aToCopy[i].pszCfgmName, uValue);
620 }
621 else
622 LogRel(("Ignoring malformed 32-bit unsigned integer config value '%s' = '%s': %Rrc\n", szExtra, strTmp.c_str(), vrc));
623 }
624 }
625
626 PCFGMNODE pLunL1;
627 InsertConfigNode(pLUN, "AttachedDriver", &pLunL1);
628 InsertConfigString(pLunL1, "Driver", pszDrvName);
629 InsertConfigNode(pLunL1, "Config", &pCfg);
630
631#ifdef RT_OS_WINDOWS
632 if (strcmp(pszDrvName, "HostAudioWas") == 0)
633 {
634 Bstr bstrTmp;
635 HRESULT hrc = pMachine->COMGETTER(Id)(bstrTmp.asOutParam()); H();
636 InsertConfigString(pCfg, "VmUuid", bstrTmp);
637 }
638#endif
639
640#if defined(RT_OS_WINDOWS) || defined(RT_OS_LINUX)
641 if ( strcmp(pszDrvName, "HostAudioWas") == 0
642 || strcmp(pszDrvName, "PulseAudio") == 0)
643 {
644 Bstr bstrTmp;
645 HRESULT hrc = pMachine->COMGETTER(Name)(bstrTmp.asOutParam()); H();
646 InsertConfigString(pCfg, "VmName", bstrTmp);
647 }
648#endif
649
650 LogFlowFunc(("szDrivName=%s\n", pszDrvName));
651
652#undef H
653}
654
655/**
656 * Applies the CFGM overlay as specified by VBoxInternal/XXX extra data
657 * values.
658 *
659 * @returns VBox status code.
660 * @param pRoot The root of the configuration tree.
661 * @param pVirtualBox Pointer to the IVirtualBox interface.
662 * @param pMachine Pointer to the IMachine interface.
663 */
664/* static */
665int Console::i_configCfgmOverlay(PCFGMNODE pRoot, IVirtualBox *pVirtualBox, IMachine *pMachine)
666{
667 /*
668 * CFGM overlay handling.
669 *
670 * Here we check the extra data entries for CFGM values
671 * and create the nodes and insert the values on the fly. Existing
672 * values will be removed and reinserted. CFGM is typed, so by default
673 * we will guess whether it's a string or an integer (byte arrays are
674 * not currently supported). It's possible to override this autodetection
675 * by adding "string:", "integer:" or "bytes:" (future).
676 *
677 * We first perform a run on global extra data, then on the machine
678 * extra data to support global settings with local overrides.
679 */
680 int vrc = VINF_SUCCESS;
681 bool fFirst = true;
682 try
683 {
684 /** @todo add support for removing nodes and byte blobs. */
685 /*
686 * Get the next key
687 */
688 SafeArray<BSTR> aGlobalExtraDataKeys;
689 SafeArray<BSTR> aMachineExtraDataKeys;
690 HRESULT hrc = pVirtualBox->GetExtraDataKeys(ComSafeArrayAsOutParam(aGlobalExtraDataKeys));
691 AssertMsg(SUCCEEDED(hrc), ("VirtualBox::GetExtraDataKeys failed with %Rhrc\n", hrc));
692
693 // remember the no. of global values so we can call the correct method below
694 size_t cGlobalValues = aGlobalExtraDataKeys.size();
695
696 hrc = pMachine->GetExtraDataKeys(ComSafeArrayAsOutParam(aMachineExtraDataKeys));
697 AssertMsg(SUCCEEDED(hrc), ("Machine::GetExtraDataKeys failed with %Rhrc\n", hrc));
698
699 // build a combined list from global keys...
700 std::list<Utf8Str> llExtraDataKeys;
701
702 for (size_t i = 0; i < aGlobalExtraDataKeys.size(); ++i)
703 llExtraDataKeys.push_back(Utf8Str(aGlobalExtraDataKeys[i]));
704 // ... and machine keys
705 for (size_t i = 0; i < aMachineExtraDataKeys.size(); ++i)
706 llExtraDataKeys.push_back(Utf8Str(aMachineExtraDataKeys[i]));
707
708 size_t i2 = 0;
709 for (std::list<Utf8Str>::const_iterator it = llExtraDataKeys.begin();
710 it != llExtraDataKeys.end();
711 ++it, ++i2)
712 {
713 const Utf8Str &strKey = *it;
714
715 /*
716 * We only care about keys starting with "VBoxInternal/" (skip "G:" or "M:")
717 */
718 if (!strKey.startsWith("VBoxInternal/"))
719 continue;
720
721 const char *pszExtraDataKey = strKey.c_str() + sizeof("VBoxInternal/") - 1;
722
723 // get the value
724 Bstr bstrExtraDataValue;
725 if (i2 < cGlobalValues)
726 // this is still one of the global values:
727 hrc = pVirtualBox->GetExtraData(Bstr(strKey).raw(), bstrExtraDataValue.asOutParam());
728 else
729 hrc = pMachine->GetExtraData(Bstr(strKey).raw(), bstrExtraDataValue.asOutParam());
730 if (FAILED(hrc))
731 LogRel(("Warning: Cannot get extra data key %s, hrc = %Rhrc\n", strKey.c_str(), hrc));
732
733 if (fFirst)
734 {
735 fFirst = false;
736 LogRel(("Extradata overrides:\n"));
737 }
738 LogRel((" %s=\"%ls\"%s\n", strKey.c_str(), bstrExtraDataValue.raw(), i2 < cGlobalValues ? " (global)" : ""));
739
740 /*
741 * The key will be in the format "Node1/Node2/Value" or simply "Value".
742 * Split the two and get the node, delete the value and create the node
743 * if necessary.
744 */
745 PCFGMNODE pNode;
746 const char *pszCFGMValueName = strrchr(pszExtraDataKey, '/');
747 if (pszCFGMValueName)
748 {
749 /* terminate the node and advance to the value (Utf8Str might not
750 offically like this but wtf) */
751 *(char *)pszCFGMValueName = '\0';
752 ++pszCFGMValueName;
753
754 /* does the node already exist? */
755 pNode = mpVMM->pfnCFGMR3GetChild(pRoot, pszExtraDataKey);
756 if (pNode)
757 mpVMM->pfnCFGMR3RemoveValue(pNode, pszCFGMValueName);
758 else
759 {
760 /* create the node */
761 vrc = mpVMM->pfnCFGMR3InsertNode(pRoot, pszExtraDataKey, &pNode);
762 if (RT_FAILURE(vrc))
763 {
764 AssertLogRelMsgRC(vrc, ("failed to insert node '%s'\n", pszExtraDataKey));
765 continue;
766 }
767 Assert(pNode);
768 }
769 }
770 else
771 {
772 /* root value (no node path). */
773 pNode = pRoot;
774 pszCFGMValueName = pszExtraDataKey;
775 pszExtraDataKey--;
776 mpVMM->pfnCFGMR3RemoveValue(pNode, pszCFGMValueName);
777 }
778
779 /*
780 * Now let's have a look at the value.
781 * Empty strings means that we should remove the value, which we've
782 * already done above.
783 */
784 Utf8Str strCFGMValueUtf8(bstrExtraDataValue);
785 if (strCFGMValueUtf8.isNotEmpty())
786 {
787 uint64_t u64Value;
788
789 /* check for type prefix first. */
790 if (!strncmp(strCFGMValueUtf8.c_str(), RT_STR_TUPLE("string:")))
791 vrc = mpVMM->pfnCFGMR3InsertString(pNode, pszCFGMValueName, strCFGMValueUtf8.c_str() + sizeof("string:") - 1);
792 else if (!strncmp(strCFGMValueUtf8.c_str(), RT_STR_TUPLE("integer:")))
793 {
794 vrc = RTStrToUInt64Full(strCFGMValueUtf8.c_str() + sizeof("integer:") - 1, 0, &u64Value);
795 if (RT_SUCCESS(vrc))
796 vrc = mpVMM->pfnCFGMR3InsertInteger(pNode, pszCFGMValueName, u64Value);
797 }
798 else if (!strncmp(strCFGMValueUtf8.c_str(), RT_STR_TUPLE("bytes:")))
799 {
800 char const *pszBase64 = strCFGMValueUtf8.c_str() + sizeof("bytes:") - 1;
801 ssize_t cbValue = RTBase64DecodedSize(pszBase64, NULL);
802 if (cbValue > 0)
803 {
804 void *pvBytes = RTMemTmpAlloc(cbValue);
805 if (pvBytes)
806 {
807 vrc = RTBase64Decode(pszBase64, pvBytes, cbValue, NULL, NULL);
808 if (RT_SUCCESS(vrc))
809 vrc = mpVMM->pfnCFGMR3InsertBytes(pNode, pszCFGMValueName, pvBytes, cbValue);
810 RTMemTmpFree(pvBytes);
811 }
812 else
813 vrc = VERR_NO_TMP_MEMORY;
814 }
815 else if (cbValue == 0)
816 vrc = mpVMM->pfnCFGMR3InsertBytes(pNode, pszCFGMValueName, NULL, 0);
817 else
818 vrc = VERR_INVALID_BASE64_ENCODING;
819 }
820 /* auto detect type. */
821 else if (RT_SUCCESS(RTStrToUInt64Full(strCFGMValueUtf8.c_str(), 0, &u64Value)))
822 vrc = mpVMM->pfnCFGMR3InsertInteger(pNode, pszCFGMValueName, u64Value);
823 else
824 vrc = mpVMM->pfnCFGMR3InsertString(pNode, pszCFGMValueName, strCFGMValueUtf8.c_str());
825 AssertLogRelMsgRCBreak(vrc, ("failed to insert CFGM value '%s' to key '%s'\n",
826 strCFGMValueUtf8.c_str(), pszExtraDataKey));
827 }
828 }
829 }
830 catch (ConfigError &x)
831 {
832 // InsertConfig threw something:
833 return x.m_vrc;
834 }
835 return vrc;
836}
837
838/**
839 * Dumps the API settings tweaks as specified by VBoxInternal2/XXX extra data
840 * values.
841 *
842 * @returns VBox status code.
843 * @param pVirtualBox Pointer to the IVirtualBox interface.
844 * @param pMachine Pointer to the IMachine interface.
845 */
846/* static */
847int Console::i_configDumpAPISettingsTweaks(IVirtualBox *pVirtualBox, IMachine *pMachine)
848{
849 {
850 SafeArray<BSTR> aGlobalExtraDataKeys;
851 HRESULT hrc = pVirtualBox->GetExtraDataKeys(ComSafeArrayAsOutParam(aGlobalExtraDataKeys));
852 AssertMsg(SUCCEEDED(hrc), ("VirtualBox::GetExtraDataKeys failed with %Rhrc\n", hrc));
853 bool hasKey = false;
854 for (size_t i = 0; i < aGlobalExtraDataKeys.size(); i++)
855 {
856 Utf8Str strKey(aGlobalExtraDataKeys[i]);
857 if (!strKey.startsWith("VBoxInternal2/"))
858 continue;
859
860 Bstr bstrValue;
861 hrc = pVirtualBox->GetExtraData(Bstr(strKey).raw(),
862 bstrValue.asOutParam());
863 if (FAILED(hrc))
864 continue;
865 if (!hasKey)
866 LogRel(("Global extradata API settings:\n"));
867 LogRel((" %s=\"%ls\"\n", strKey.c_str(), bstrValue.raw()));
868 hasKey = true;
869 }
870 }
871
872 {
873 SafeArray<BSTR> aMachineExtraDataKeys;
874 HRESULT hrc = pMachine->GetExtraDataKeys(ComSafeArrayAsOutParam(aMachineExtraDataKeys));
875 AssertMsg(SUCCEEDED(hrc), ("Machine::GetExtraDataKeys failed with %Rhrc\n", hrc));
876 bool hasKey = false;
877 for (size_t i = 0; i < aMachineExtraDataKeys.size(); i++)
878 {
879 Utf8Str strKey(aMachineExtraDataKeys[i]);
880 if (!strKey.startsWith("VBoxInternal2/"))
881 continue;
882
883 Bstr bstrValue;
884 hrc = pMachine->GetExtraData(Bstr(strKey).raw(),
885 bstrValue.asOutParam());
886 if (FAILED(hrc))
887 continue;
888 if (!hasKey)
889 LogRel(("Per-VM extradata API settings:\n"));
890 LogRel((" %s=\"%ls\"\n", strKey.c_str(), bstrValue.raw()));
891 hasKey = true;
892 }
893 }
894
895 return VINF_SUCCESS;
896}
897
898
899/**
900 * Ellipsis to va_list wrapper for calling setVMRuntimeErrorCallback.
901 */
902void Console::i_atVMRuntimeErrorCallbackF(uint32_t fFlags, const char *pszErrorId, const char *pszFormat, ...)
903{
904 va_list va;
905 va_start(va, pszFormat);
906 i_atVMRuntimeErrorCallback(NULL, this, fFlags, pszErrorId, pszFormat, va);
907 va_end(va);
908}
909
910/* XXX introduce RT format specifier */
911static uint64_t formatDiskSize(uint64_t u64Size, const char **pszUnit)
912{
913 if (u64Size > INT64_C(5000)*_1G)
914 {
915 *pszUnit = "TB";
916 return u64Size / _1T;
917 }
918 else if (u64Size > INT64_C(5000)*_1M)
919 {
920 *pszUnit = "GB";
921 return u64Size / _1G;
922 }
923 else
924 {
925 *pszUnit = "MB";
926 return u64Size / _1M;
927 }
928}
929
930/**
931 * Checks the location of the given medium for known bugs affecting the usage
932 * of the host I/O cache setting.
933 *
934 * @returns VBox status code.
935 * @param pMedium The medium to check.
936 * @param pfUseHostIOCache Where to store the suggested host I/O cache setting.
937 */
938int Console::i_checkMediumLocation(IMedium *pMedium, bool *pfUseHostIOCache)
939{
940#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR)
941 /*
942 * Some sanity checks.
943 */
944 RT_NOREF(pfUseHostIOCache);
945 ComPtr<IMediumFormat> pMediumFormat;
946 HRESULT hrc = pMedium->COMGETTER(MediumFormat)(pMediumFormat.asOutParam()); H();
947 ULONG uCaps = 0;
948 com::SafeArray <MediumFormatCapabilities_T> mediumFormatCap;
949 hrc = pMediumFormat->COMGETTER(Capabilities)(ComSafeArrayAsOutParam(mediumFormatCap)); H();
950
951 for (ULONG j = 0; j < mediumFormatCap.size(); j++)
952 uCaps |= mediumFormatCap[j];
953
954 if (uCaps & MediumFormatCapabilities_File)
955 {
956 Bstr bstrFile;
957 hrc = pMedium->COMGETTER(Location)(bstrFile.asOutParam()); H();
958 Utf8Str const strFile(bstrFile);
959
960 Bstr bstrSnap;
961 ComPtr<IMachine> pMachine = i_machine();
962 hrc = pMachine->COMGETTER(SnapshotFolder)(bstrSnap.asOutParam()); H();
963 Utf8Str const strSnap(bstrSnap);
964
965 RTFSTYPE enmFsTypeFile = RTFSTYPE_UNKNOWN;
966 int vrc2 = RTFsQueryType(strFile.c_str(), &enmFsTypeFile);
967 AssertMsgRCReturn(vrc2, ("Querying the file type of '%s' failed!\n", strFile.c_str()), vrc2);
968
969 /* Any VM which hasn't created a snapshot or saved the current state of the VM
970 * won't have a Snapshot folder yet so no need to log anything about the file system
971 * type of the non-existent directory in such cases. */
972 RTFSTYPE enmFsTypeSnap = RTFSTYPE_UNKNOWN;
973 vrc2 = RTFsQueryType(strSnap.c_str(), &enmFsTypeSnap);
974 if (RT_SUCCESS(vrc2) && !mfSnapshotFolderDiskTypeShown)
975 {
976 LogRel(("File system of '%s' (snapshots) is %s\n", strSnap.c_str(), RTFsTypeName(enmFsTypeSnap)));
977 mfSnapshotFolderDiskTypeShown = true;
978 }
979 LogRel(("File system of '%s' is %s\n", strFile.c_str(), RTFsTypeName(enmFsTypeFile)));
980 LONG64 i64Size;
981 hrc = pMedium->COMGETTER(LogicalSize)(&i64Size); H();
982#ifdef RT_OS_WINDOWS
983 if ( enmFsTypeFile == RTFSTYPE_FAT
984 && i64Size >= _4G)
985 {
986 const char *pszUnit;
987 uint64_t u64Print = formatDiskSize((uint64_t)i64Size, &pszUnit);
988 i_atVMRuntimeErrorCallbackF(0, "FatPartitionDetected",
989 N_("The medium '%s' has a logical size of %RU64%s "
990 "but the file system the medium is located on seems "
991 "to be FAT(32) which cannot handle files bigger than 4GB.\n"
992 "We strongly recommend to put all your virtual disk images and "
993 "the snapshot folder onto an NTFS partition"),
994 strFile.c_str(), u64Print, pszUnit);
995 }
996#else /* !RT_OS_WINDOWS */
997 if ( enmFsTypeFile == RTFSTYPE_FAT
998 || enmFsTypeFile == RTFSTYPE_EXT
999 || enmFsTypeFile == RTFSTYPE_EXT2
1000 || enmFsTypeFile == RTFSTYPE_EXT3
1001 || enmFsTypeFile == RTFSTYPE_EXT4)
1002 {
1003 RTFILE file;
1004 int vrc = RTFileOpen(&file, strFile.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
1005 if (RT_SUCCESS(vrc))
1006 {
1007 RTFOFF maxSize;
1008 /* Careful: This function will work only on selected local file systems! */
1009 vrc = RTFileQueryMaxSizeEx(file, &maxSize);
1010 RTFileClose(file);
1011 if ( RT_SUCCESS(vrc)
1012 && maxSize > 0
1013 && i64Size > (LONG64)maxSize)
1014 {
1015 const char *pszUnitSiz;
1016 const char *pszUnitMax;
1017 uint64_t u64PrintSiz = formatDiskSize((LONG64)i64Size, &pszUnitSiz);
1018 uint64_t u64PrintMax = formatDiskSize(maxSize, &pszUnitMax);
1019 i_atVMRuntimeErrorCallbackF(0, "FatPartitionDetected", /* <= not exact but ... */
1020 N_("The medium '%s' has a logical size of %RU64%s "
1021 "but the file system the medium is located on can "
1022 "only handle files up to %RU64%s in theory.\n"
1023 "We strongly recommend to put all your virtual disk "
1024 "images and the snapshot folder onto a proper "
1025 "file system (e.g. ext3) with a sufficient size"),
1026 strFile.c_str(), u64PrintSiz, pszUnitSiz, u64PrintMax, pszUnitMax);
1027 }
1028 }
1029 }
1030#endif /* !RT_OS_WINDOWS */
1031
1032 /*
1033 * Snapshot folder:
1034 * Here we test only for a FAT partition as we had to create a dummy file otherwise
1035 */
1036 if ( enmFsTypeSnap == RTFSTYPE_FAT
1037 && i64Size >= _4G
1038 && !mfSnapshotFolderSizeWarningShown)
1039 {
1040 const char *pszUnit;
1041 uint64_t u64Print = formatDiskSize(i64Size, &pszUnit);
1042 i_atVMRuntimeErrorCallbackF(0, "FatPartitionDetected",
1043#ifdef RT_OS_WINDOWS
1044 N_("The snapshot folder of this VM '%s' seems to be located on "
1045 "a FAT(32) file system. The logical size of the medium '%s' "
1046 "(%RU64%s) is bigger than the maximum file size this file "
1047 "system can handle (4GB).\n"
1048 "We strongly recommend to put all your virtual disk images and "
1049 "the snapshot folder onto an NTFS partition"),
1050#else
1051 N_("The snapshot folder of this VM '%s' seems to be located on "
1052 "a FAT(32) file system. The logical size of the medium '%s' "
1053 "(%RU64%s) is bigger than the maximum file size this file "
1054 "system can handle (4GB).\n"
1055 "We strongly recommend to put all your virtual disk images and "
1056 "the snapshot folder onto a proper file system (e.g. ext3)"),
1057#endif
1058 strSnap.c_str(), strFile.c_str(), u64Print, pszUnit);
1059 /* Show this particular warning only once */
1060 mfSnapshotFolderSizeWarningShown = true;
1061 }
1062
1063#ifdef RT_OS_LINUX
1064 /*
1065 * Ext4 bug: Check if the host I/O cache is disabled and the disk image is located
1066 * on an ext4 partition.
1067 * This bug apparently applies to the XFS file system as well.
1068 * Linux 2.6.36 is known to be fixed (tested with 2.6.36-rc4).
1069 */
1070
1071 char szOsRelease[128];
1072 int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szOsRelease, sizeof(szOsRelease));
1073 bool fKernelHasODirectBug = RT_FAILURE(vrc)
1074 || (RTStrVersionCompare(szOsRelease, "2.6.36-rc4") < 0);
1075
1076 if ( (uCaps & MediumFormatCapabilities_Asynchronous)
1077 && !*pfUseHostIOCache
1078 && fKernelHasODirectBug)
1079 {
1080 if ( enmFsTypeFile == RTFSTYPE_EXT4
1081 || enmFsTypeFile == RTFSTYPE_XFS)
1082 {
1083 i_atVMRuntimeErrorCallbackF(0, "Ext4PartitionDetected",
1084 N_("The host I/O cache for at least one controller is disabled "
1085 "and the medium '%s' for this VM "
1086 "is located on an %s partition. There is a known Linux "
1087 "kernel bug which can lead to the corruption of the virtual "
1088 "disk image under these conditions.\n"
1089 "Either enable the host I/O cache permanently in the VM "
1090 "settings or put the disk image and the snapshot folder "
1091 "onto a different file system.\n"
1092 "The host I/O cache will now be enabled for this medium"),
1093 strFile.c_str(), enmFsTypeFile == RTFSTYPE_EXT4 ? "ext4" : "xfs");
1094 *pfUseHostIOCache = true;
1095 }
1096 else if ( ( enmFsTypeSnap == RTFSTYPE_EXT4
1097 || enmFsTypeSnap == RTFSTYPE_XFS)
1098 && !mfSnapshotFolderExt4WarningShown)
1099 {
1100 i_atVMRuntimeErrorCallbackF(0, "Ext4PartitionDetected",
1101 N_("The host I/O cache for at least one controller is disabled "
1102 "and the snapshot folder for this VM "
1103 "is located on an %s partition. There is a known Linux "
1104 "kernel bug which can lead to the corruption of the virtual "
1105 "disk image under these conditions.\n"
1106 "Either enable the host I/O cache permanently in the VM "
1107 "settings or put the disk image and the snapshot folder "
1108 "onto a different file system.\n"
1109 "The host I/O cache will now be enabled for this medium"),
1110 enmFsTypeSnap == RTFSTYPE_EXT4 ? "ext4" : "xfs");
1111 *pfUseHostIOCache = true;
1112 mfSnapshotFolderExt4WarningShown = true;
1113 }
1114 }
1115
1116 /*
1117 * 2.6.18 bug: Check if the host I/O cache is disabled and the host is running
1118 * Linux 2.6.18. See @bugref{8690}. Apparently the same problem as
1119 * documented in https://lkml.org/lkml/2007/2/1/14. We saw such
1120 * kernel oopses on Linux 2.6.18-416.el5. We don't know when this
1121 * was fixed but we _know_ that 2.6.18 EL5 kernels are affected.
1122 */
1123 bool fKernelAsyncUnreliable = RT_FAILURE(vrc)
1124 || (RTStrVersionCompare(szOsRelease, "2.6.19") < 0);
1125 if ( (uCaps & MediumFormatCapabilities_Asynchronous)
1126 && !*pfUseHostIOCache
1127 && fKernelAsyncUnreliable)
1128 {
1129 i_atVMRuntimeErrorCallbackF(0, "Linux2618TooOld",
1130 N_("The host I/O cache for at least one controller is disabled. "
1131 "There is a known Linux kernel bug which can lead to kernel "
1132 "oopses under heavy load. To our knowledge this bug affects "
1133 "all 2.6.18 kernels.\n"
1134 "Either enable the host I/O cache permanently in the VM "
1135 "settings or switch to a newer host kernel.\n"
1136 "The host I/O cache will now be enabled for this medium"));
1137 *pfUseHostIOCache = true;
1138 }
1139#endif
1140 }
1141#undef H
1142
1143 return VINF_SUCCESS;
1144}
1145
1146/**
1147 * Unmounts the specified medium from the specified device.
1148 *
1149 * @returns VBox status code.
1150 * @param pUVM The usermode VM handle.
1151 * @param pVMM The VMM vtable.
1152 * @param enmBus The storage bus.
1153 * @param enmDevType The device type.
1154 * @param pcszDevice The device emulation.
1155 * @param uInstance Instance of the device.
1156 * @param uLUN The LUN on the device.
1157 * @param fForceUnmount Whether to force unmounting.
1158 */
1159int Console::i_unmountMediumFromGuest(PUVM pUVM, PCVMMR3VTABLE pVMM, StorageBus_T enmBus, DeviceType_T enmDevType,
1160 const char *pcszDevice, unsigned uInstance, unsigned uLUN,
1161 bool fForceUnmount) RT_NOEXCEPT
1162{
1163 /* Unmount existing media only for floppy and DVD drives. */
1164 int vrc = VINF_SUCCESS;
1165 PPDMIBASE pBase;
1166 if (enmBus == StorageBus_USB)
1167 vrc = pVMM->pfnPDMR3UsbQueryDriverOnLun(pUVM, pcszDevice, uInstance, uLUN, "SCSI", &pBase);
1168 else if ( (enmBus == StorageBus_SAS || enmBus == StorageBus_SCSI || enmBus == StorageBus_VirtioSCSI)
1169 || (enmBus == StorageBus_SATA && enmDevType == DeviceType_DVD))
1170 vrc = pVMM->pfnPDMR3QueryDriverOnLun(pUVM, pcszDevice, uInstance, uLUN, "SCSI", &pBase);
1171 else /* IDE or Floppy */
1172 vrc = pVMM->pfnPDMR3QueryLun(pUVM, pcszDevice, uInstance, uLUN, &pBase);
1173
1174 if (RT_FAILURE(vrc))
1175 {
1176 if (vrc == VERR_PDM_LUN_NOT_FOUND || vrc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN)
1177 vrc = VINF_SUCCESS;
1178 AssertRC(vrc);
1179 }
1180 else
1181 {
1182 PPDMIMOUNT pIMount = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMOUNT);
1183 AssertReturn(pIMount, VERR_INVALID_POINTER);
1184
1185 /* Unmount the media (but do not eject the medium!) */
1186 vrc = pIMount->pfnUnmount(pIMount, fForceUnmount, false /*=fEject*/);
1187 if (vrc == VERR_PDM_MEDIA_NOT_MOUNTED)
1188 vrc = VINF_SUCCESS;
1189 /* for example if the medium is locked */
1190 else if (RT_FAILURE(vrc))
1191 return vrc;
1192 }
1193
1194 return vrc;
1195}
1196
1197/**
1198 * Removes the currently attached medium driver form the specified device
1199 * taking care of the controlelr specific configs wrt. to the attached driver chain.
1200 *
1201 * @returns VBox status code.
1202 * @param pCtlInst The controler instance node in the CFGM tree.
1203 * @param pcszDevice The device name.
1204 * @param uInstance The device instance.
1205 * @param uLUN The device LUN.
1206 * @param enmBus The storage bus.
1207 * @param fAttachDetach Flag whether this is a change while the VM is running
1208 * @param fHotplug Flag whether the guest should be notified about the device change.
1209 * @param fForceUnmount Flag whether to force unmounting the medium even if it is locked.
1210 * @param pUVM The usermode VM handle.
1211 * @param pVMM The VMM vtable.
1212 * @param enmDevType The device type.
1213 * @param ppLunL0 Where to store the node to attach the new config to on success.
1214 */
1215int Console::i_removeMediumDriverFromVm(PCFGMNODE pCtlInst,
1216 const char *pcszDevice,
1217 unsigned uInstance,
1218 unsigned uLUN,
1219 StorageBus_T enmBus,
1220 bool fAttachDetach,
1221 bool fHotplug,
1222 bool fForceUnmount,
1223 PUVM pUVM,
1224 PCVMMR3VTABLE pVMM,
1225 DeviceType_T enmDevType,
1226 PCFGMNODE *ppLunL0)
1227{
1228 int vrc = VINF_SUCCESS;
1229 bool fAddLun = false;
1230
1231 /* First check if the LUN already exists. */
1232 PCFGMNODE pLunL0 = pVMM->pfnCFGMR3GetChildF(pCtlInst, "LUN#%u", uLUN);
1233 AssertReturn(!RT_VALID_PTR(pLunL0) || fAttachDetach, VERR_INTERNAL_ERROR);
1234
1235 if (pLunL0)
1236 {
1237 /*
1238 * Unmount the currently mounted medium if we don't just hot remove the
1239 * complete device (SATA) and it supports unmounting (DVD).
1240 */
1241 if ( (enmDevType != DeviceType_HardDisk)
1242 && !fHotplug)
1243 {
1244 vrc = i_unmountMediumFromGuest(pUVM, pVMM, enmBus, enmDevType, pcszDevice, uInstance, uLUN, fForceUnmount);
1245 if (RT_FAILURE(vrc))
1246 return vrc;
1247 }
1248
1249 /*
1250 * Don't detach the SCSI driver when unmounting the current medium
1251 * (we are not ripping out the device but only eject the medium).
1252 */
1253 char *pszDriverDetach = NULL;
1254 if ( !fHotplug
1255 && ( (enmBus == StorageBus_SATA && enmDevType == DeviceType_DVD)
1256 || enmBus == StorageBus_SAS
1257 || enmBus == StorageBus_SCSI
1258 || enmBus == StorageBus_VirtioSCSI
1259 || enmBus == StorageBus_USB))
1260 {
1261 /* Get the current attached driver we have to detach. */
1262 PCFGMNODE pDrvLun = pVMM->pfnCFGMR3GetChildF(pCtlInst, "LUN#%u/AttachedDriver/", uLUN);
1263 if (pDrvLun)
1264 {
1265 char szDriver[128];
1266 RT_ZERO(szDriver);
1267 vrc = pVMM->pfnCFGMR3QueryString(pDrvLun, "Driver", &szDriver[0], sizeof(szDriver));
1268 if (RT_SUCCESS(vrc))
1269 pszDriverDetach = RTStrDup(&szDriver[0]);
1270
1271 pLunL0 = pDrvLun;
1272 }
1273 }
1274
1275 if (enmBus == StorageBus_USB)
1276 vrc = pVMM->pfnPDMR3UsbDriverDetach(pUVM, pcszDevice, uInstance, uLUN, pszDriverDetach,
1277 0 /* iOccurence */, fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG);
1278 else
1279 vrc = pVMM->pfnPDMR3DriverDetach(pUVM, pcszDevice, uInstance, uLUN, pszDriverDetach,
1280 0 /* iOccurence */, fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG);
1281
1282 if (pszDriverDetach)
1283 {
1284 RTStrFree(pszDriverDetach);
1285 /* Remove the complete node and create new for the new config. */
1286 pVMM->pfnCFGMR3RemoveNode(pLunL0);
1287 pLunL0 = pVMM->pfnCFGMR3GetChildF(pCtlInst, "LUN#%u", uLUN);
1288 if (pLunL0)
1289 {
1290 try
1291 {
1292 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0);
1293 }
1294 catch (ConfigError &x)
1295 {
1296 // InsertConfig threw something:
1297 return x.m_vrc;
1298 }
1299 }
1300 }
1301 if (vrc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN)
1302 vrc = VINF_SUCCESS;
1303 AssertRCReturn(vrc, vrc);
1304
1305 /*
1306 * Don't remove the LUN except for IDE/floppy/NVMe (which connects directly to the medium driver
1307 * even for DVD devices) or if there is a hotplug event which rips out the complete device.
1308 */
1309 if ( fHotplug
1310 || enmBus == StorageBus_IDE
1311 || enmBus == StorageBus_Floppy
1312 || enmBus == StorageBus_PCIe
1313 || (enmBus == StorageBus_SATA && enmDevType != DeviceType_DVD))
1314 {
1315 fAddLun = true;
1316 pVMM->pfnCFGMR3RemoveNode(pLunL0);
1317 }
1318 }
1319 else
1320 fAddLun = true;
1321
1322 try
1323 {
1324 if (fAddLun)
1325 InsertConfigNodeF(pCtlInst, &pLunL0, "LUN#%u", uLUN);
1326 }
1327 catch (ConfigError &x)
1328 {
1329 // InsertConfig threw something:
1330 return x.m_vrc;
1331 }
1332
1333 if (ppLunL0)
1334 *ppLunL0 = pLunL0;
1335
1336 return vrc;
1337}
1338
1339int Console::i_configMediumAttachment(const char *pcszDevice,
1340 unsigned uInstance,
1341 StorageBus_T enmBus,
1342 bool fUseHostIOCache,
1343 bool fBuiltinIOCache,
1344 bool fInsertDiskIntegrityDrv,
1345 bool fSetupMerge,
1346 unsigned uMergeSource,
1347 unsigned uMergeTarget,
1348 IMediumAttachment *pMediumAtt,
1349 MachineState_T aMachineState,
1350 HRESULT *phrc,
1351 bool fAttachDetach,
1352 bool fForceUnmount,
1353 bool fHotplug,
1354 PUVM pUVM,
1355 PCVMMR3VTABLE pVMM,
1356 DeviceType_T *paLedDevType,
1357 PCFGMNODE *ppLunL0)
1358{
1359 // InsertConfig* throws
1360 try
1361 {
1362 int vrc = VINF_SUCCESS;
1363 HRESULT hrc;
1364 Bstr bstr;
1365 PCFGMNODE pCtlInst = NULL;
1366
1367// #define RC_CHECK() AssertMsgReturn(RT_SUCCESS(vrc), ("vrc=%Rrc\n", vrc), vrc)
1368#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR)
1369
1370 LONG lDev;
1371 hrc = pMediumAtt->COMGETTER(Device)(&lDev); H();
1372 LONG lPort;
1373 hrc = pMediumAtt->COMGETTER(Port)(&lPort); H();
1374 DeviceType_T enmType;
1375 hrc = pMediumAtt->COMGETTER(Type)(&enmType); H();
1376 BOOL fNonRotational;
1377 hrc = pMediumAtt->COMGETTER(NonRotational)(&fNonRotational); H();
1378 BOOL fDiscard;
1379 hrc = pMediumAtt->COMGETTER(Discard)(&fDiscard); H();
1380
1381 if (enmType == DeviceType_DVD)
1382 fInsertDiskIntegrityDrv = false;
1383
1384 unsigned uLUN;
1385 PCFGMNODE pLunL0 = NULL;
1386 hrc = Console::i_storageBusPortDeviceToLun(enmBus, lPort, lDev, uLUN); H();
1387
1388 /* Determine the base path for the device instance. */
1389 if (enmBus != StorageBus_USB)
1390 pCtlInst = pVMM->pfnCFGMR3GetChildF(pVMM->pfnCFGMR3GetRootU(pUVM), "Devices/%s/%u/", pcszDevice, uInstance);
1391 else
1392 {
1393 /* If we hotplug a USB device create a new CFGM tree. */
1394 if (!fHotplug)
1395 pCtlInst = pVMM->pfnCFGMR3GetChildF(pVMM->pfnCFGMR3GetRootU(pUVM), "USB/%s/", pcszDevice);
1396 else
1397 pCtlInst = pVMM->pfnCFGMR3CreateTree(pUVM); /** @todo r=bird: Leaked in error paths! */
1398 }
1399 AssertReturn(pCtlInst, VERR_INTERNAL_ERROR);
1400
1401 if (enmBus == StorageBus_USB)
1402 {
1403 PCFGMNODE pCfg = NULL;
1404
1405 /* Create correct instance. */
1406 if (!fHotplug)
1407 {
1408 if (!fAttachDetach)
1409 InsertConfigNodeF(pCtlInst, &pCtlInst, "%d", lPort);
1410 else
1411 pCtlInst = pVMM->pfnCFGMR3GetChildF(pCtlInst, "%d/", lPort);
1412 }
1413
1414 if (!fAttachDetach)
1415 InsertConfigNode(pCtlInst, "Config", &pCfg);
1416
1417 uInstance = lPort; /* Overwrite uInstance with the correct one. */
1418
1419 /** @todo No LED after hotplugging. */
1420 if (!fHotplug && !fAttachDetach)
1421 {
1422 USBStorageDevice UsbMsd;
1423 UsbMsd.iPort = uInstance;
1424 vrc = RTUuidCreate(&UsbMsd.mUuid);
1425 AssertRCReturn(vrc, vrc);
1426
1427 InsertConfigStringF(pCtlInst, "UUID", "%RTuuid", &UsbMsd.mUuid);
1428
1429 mUSBStorageDevices.push_back(UsbMsd);
1430
1431 /** @todo This LED set is not freed if the device is unplugged. We could
1432 * keep the LED set index in the UsbMsd structure and clean it up in
1433 * i_detachStorageDevice. */
1434 /* Attach the status driver */
1435 i_attachStatusDriver(pCtlInst, RT_BIT_32(DeviceType_HardDisk),
1436 8, &paLedDevType, &mapMediumAttachments, pcszDevice, 0);
1437 }
1438 }
1439
1440 vrc = i_removeMediumDriverFromVm(pCtlInst, pcszDevice, uInstance, uLUN, enmBus, fAttachDetach,
1441 fHotplug, fForceUnmount, pUVM, pVMM, enmType, &pLunL0);
1442 if (RT_FAILURE(vrc))
1443 return vrc;
1444 if (ppLunL0)
1445 *ppLunL0 = pLunL0;
1446
1447 Utf8StrFmt devicePath("%s/%u/LUN#%u", pcszDevice, uInstance, uLUN);
1448 mapMediumAttachments[devicePath] = pMediumAtt;
1449
1450 ComPtr<IMedium> ptrMedium;
1451 hrc = pMediumAtt->COMGETTER(Medium)(ptrMedium.asOutParam()); H();
1452
1453 /*
1454 * 1. Only check this for hard disk images.
1455 * 2. Only check during VM creation and not later, especially not during
1456 * taking an online snapshot!
1457 */
1458 if ( enmType == DeviceType_HardDisk
1459 && ( aMachineState == MachineState_Starting
1460 || aMachineState == MachineState_Restoring))
1461 {
1462 vrc = i_checkMediumLocation(ptrMedium, &fUseHostIOCache);
1463 if (RT_FAILURE(vrc))
1464 return vrc;
1465 }
1466
1467 BOOL fPassthrough = FALSE;
1468 if (ptrMedium.isNotNull())
1469 {
1470 BOOL fHostDrive;
1471 hrc = ptrMedium->COMGETTER(HostDrive)(&fHostDrive); H();
1472 if ( ( enmType == DeviceType_DVD
1473 || enmType == DeviceType_Floppy)
1474 && !fHostDrive)
1475 {
1476 /*
1477 * Informative logging.
1478 */
1479 Bstr bstrFile;
1480 hrc = ptrMedium->COMGETTER(Location)(bstrFile.asOutParam()); H();
1481 Utf8Str strFile(bstrFile);
1482 RTFSTYPE enmFsTypeFile = RTFSTYPE_UNKNOWN;
1483 (void)RTFsQueryType(strFile.c_str(), &enmFsTypeFile);
1484 LogRel(("File system of '%s' (%s) is %s\n",
1485 strFile.c_str(), enmType == DeviceType_DVD ? "DVD" : "Floppy", RTFsTypeName(enmFsTypeFile)));
1486 }
1487
1488 if (fHostDrive)
1489 {
1490 hrc = pMediumAtt->COMGETTER(Passthrough)(&fPassthrough); H();
1491 }
1492 }
1493
1494 ComObjPtr<IBandwidthGroup> pBwGroup;
1495 Bstr bstrBwGroup;
1496 hrc = pMediumAtt->COMGETTER(BandwidthGroup)(pBwGroup.asOutParam()); H();
1497
1498 if (!pBwGroup.isNull())
1499 {
1500 hrc = pBwGroup->COMGETTER(Name)(bstrBwGroup.asOutParam()); H();
1501 }
1502
1503 /*
1504 * Insert the SCSI driver for hotplug events on the SCSI/USB based storage controllers
1505 * or for SATA if the new device is a CD/DVD drive.
1506 */
1507 if ( (fHotplug || !fAttachDetach)
1508 && ( enmBus == StorageBus_SCSI
1509 || enmBus == StorageBus_SAS
1510 || enmBus == StorageBus_USB
1511 || enmBus == StorageBus_VirtioSCSI
1512 || (enmBus == StorageBus_SATA && enmType == DeviceType_DVD && !fPassthrough)))
1513 {
1514 InsertConfigString(pLunL0, "Driver", "SCSI");
1515 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0);
1516 }
1517
1518 vrc = i_configMedium(pLunL0,
1519 !!fPassthrough,
1520 enmType,
1521 fUseHostIOCache,
1522 fBuiltinIOCache,
1523 fInsertDiskIntegrityDrv,
1524 fSetupMerge,
1525 uMergeSource,
1526 uMergeTarget,
1527 bstrBwGroup.isEmpty() ? NULL : Utf8Str(bstrBwGroup).c_str(),
1528 !!fDiscard,
1529 !!fNonRotational,
1530 ptrMedium,
1531 aMachineState,
1532 phrc);
1533 if (RT_FAILURE(vrc))
1534 return vrc;
1535
1536 if (fAttachDetach)
1537 {
1538 /* Attach the new driver. */
1539 if (enmBus == StorageBus_USB)
1540 {
1541 if (fHotplug)
1542 {
1543 USBStorageDevice UsbMsd;
1544 RTUuidCreate(&UsbMsd.mUuid);
1545 UsbMsd.iPort = uInstance;
1546 vrc = pVMM->pfnPDMR3UsbCreateEmulatedDevice(pUVM, pcszDevice, pCtlInst, &UsbMsd.mUuid, NULL);
1547 if (RT_SUCCESS(vrc))
1548 mUSBStorageDevices.push_back(UsbMsd);
1549 }
1550 else
1551 vrc = pVMM->pfnPDMR3UsbDriverAttach(pUVM, pcszDevice, uInstance, uLUN,
1552 fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/);
1553 }
1554 else if ( !fHotplug
1555 && ( (enmBus == StorageBus_SAS || enmBus == StorageBus_SCSI || enmBus == StorageBus_VirtioSCSI)
1556 || (enmBus == StorageBus_SATA && enmType == DeviceType_DVD)))
1557 vrc = pVMM->pfnPDMR3DriverAttach(pUVM, pcszDevice, uInstance, uLUN,
1558 fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/);
1559 else
1560 vrc = pVMM->pfnPDMR3DeviceAttach(pUVM, pcszDevice, uInstance, uLUN,
1561 fHotplug ? 0 : PDM_TACH_FLAGS_NOT_HOT_PLUG, NULL /*ppBase*/);
1562 AssertRCReturn(vrc, vrc);
1563
1564 /*
1565 * Make the secret key helper interface known to the VD driver if it is attached,
1566 * so we can get notified about missing keys.
1567 */
1568 PPDMIBASE pIBase = NULL;
1569 vrc = pVMM->pfnPDMR3QueryDriverOnLun(pUVM, pcszDevice, uInstance, uLUN, "VD", &pIBase);
1570 if (RT_SUCCESS(vrc) && pIBase)
1571 {
1572 PPDMIMEDIA pIMedium = (PPDMIMEDIA)pIBase->pfnQueryInterface(pIBase, PDMIMEDIA_IID);
1573 if (pIMedium)
1574 {
1575 vrc = pIMedium->pfnSetSecKeyIf(pIMedium, mpIfSecKey, mpIfSecKeyHlp);
1576 Assert(RT_SUCCESS(vrc) || vrc == VERR_NOT_SUPPORTED);
1577 }
1578 }
1579
1580 /* There is no need to handle removable medium mounting, as we
1581 * unconditionally replace everthing including the block driver level.
1582 * This means the new medium will be picked up automatically. */
1583 }
1584
1585 if (paLedDevType)
1586 i_setLedType(&paLedDevType[uLUN], enmType);
1587
1588 /* Dump the changed LUN if possible, dump the complete device otherwise */
1589 if ( aMachineState != MachineState_Starting
1590 && aMachineState != MachineState_Restoring)
1591 pVMM->pfnCFGMR3Dump(pLunL0 ? pLunL0 : pCtlInst);
1592 }
1593 catch (ConfigError &x)
1594 {
1595 // InsertConfig threw something:
1596 return x.m_vrc;
1597 }
1598
1599#undef H
1600
1601 return VINF_SUCCESS;
1602}
1603
1604int Console::i_configMedium(PCFGMNODE pLunL0,
1605 bool fPassthrough,
1606 DeviceType_T enmType,
1607 bool fUseHostIOCache,
1608 bool fBuiltinIOCache,
1609 bool fInsertDiskIntegrityDrv,
1610 bool fSetupMerge,
1611 unsigned uMergeSource,
1612 unsigned uMergeTarget,
1613 const char *pcszBwGroup,
1614 bool fDiscard,
1615 bool fNonRotational,
1616 ComPtr<IMedium> ptrMedium,
1617 MachineState_T aMachineState,
1618 HRESULT *phrc)
1619{
1620 // InsertConfig* throws
1621 try
1622 {
1623 HRESULT hrc;
1624 Bstr bstr;
1625 PCFGMNODE pCfg = NULL;
1626
1627#define H() \
1628 AssertMsgReturnStmt(SUCCEEDED(hrc), ("hrc=%Rhrc\n", hrc), if (phrc) *phrc = hrc, Global::vboxStatusCodeFromCOM(hrc))
1629
1630
1631 BOOL fHostDrive = FALSE;
1632 MediumType_T mediumType = MediumType_Normal;
1633 if (ptrMedium.isNotNull())
1634 {
1635 hrc = ptrMedium->COMGETTER(HostDrive)(&fHostDrive); H();
1636 hrc = ptrMedium->COMGETTER(Type)(&mediumType); H();
1637 }
1638
1639 if (fHostDrive)
1640 {
1641 Assert(ptrMedium.isNotNull());
1642 if (enmType == DeviceType_DVD)
1643 {
1644 InsertConfigString(pLunL0, "Driver", "HostDVD");
1645 InsertConfigNode(pLunL0, "Config", &pCfg);
1646
1647 hrc = ptrMedium->COMGETTER(Location)(bstr.asOutParam()); H();
1648 InsertConfigString(pCfg, "Path", bstr);
1649
1650 InsertConfigInteger(pCfg, "Passthrough", fPassthrough);
1651 }
1652 else if (enmType == DeviceType_Floppy)
1653 {
1654 InsertConfigString(pLunL0, "Driver", "HostFloppy");
1655 InsertConfigNode(pLunL0, "Config", &pCfg);
1656
1657 hrc = ptrMedium->COMGETTER(Location)(bstr.asOutParam()); H();
1658 InsertConfigString(pCfg, "Path", bstr);
1659 }
1660 }
1661 else
1662 {
1663 if (fInsertDiskIntegrityDrv)
1664 {
1665 /*
1666 * The actual configuration is done through CFGM extra data
1667 * for each inserted driver separately.
1668 */
1669 InsertConfigString(pLunL0, "Driver", "DiskIntegrity");
1670 InsertConfigNode(pLunL0, "Config", &pCfg);
1671 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0);
1672 }
1673
1674 InsertConfigString(pLunL0, "Driver", "VD");
1675 InsertConfigNode(pLunL0, "Config", &pCfg);
1676 switch (enmType)
1677 {
1678 case DeviceType_DVD:
1679 InsertConfigString(pCfg, "Type", "DVD");
1680 InsertConfigInteger(pCfg, "Mountable", 1);
1681 break;
1682 case DeviceType_Floppy:
1683 InsertConfigString(pCfg, "Type", "Floppy 1.44");
1684 InsertConfigInteger(pCfg, "Mountable", 1);
1685 break;
1686 case DeviceType_HardDisk:
1687 default:
1688 InsertConfigString(pCfg, "Type", "HardDisk");
1689 InsertConfigInteger(pCfg, "Mountable", 0);
1690 }
1691
1692 if ( ptrMedium.isNotNull()
1693 && ( enmType == DeviceType_DVD
1694 || enmType == DeviceType_Floppy)
1695 )
1696 {
1697 // if this medium represents an ISO image and this image is inaccessible,
1698 // the ignore it instead of causing a failure; this can happen when we
1699 // restore a VM state and the ISO has disappeared, e.g. because the Guest
1700 // Additions were mounted and the user upgraded VirtualBox. Previously
1701 // we failed on startup, but that's not good because the only way out then
1702 // would be to discard the VM state...
1703 MediumState_T mediumState;
1704 hrc = ptrMedium->RefreshState(&mediumState); H();
1705 if (mediumState == MediumState_Inaccessible)
1706 {
1707 Bstr loc;
1708 hrc = ptrMedium->COMGETTER(Location)(loc.asOutParam()); H();
1709 i_atVMRuntimeErrorCallbackF(0, "DvdOrFloppyImageInaccessible",
1710 N_("The image file '%ls' is inaccessible and is being ignored. "
1711 "Please select a different image file for the virtual %s drive."),
1712 loc.raw(),
1713 enmType == DeviceType_DVD ? "DVD" : "floppy");
1714 ptrMedium.setNull();
1715 }
1716 }
1717
1718 if (ptrMedium.isNotNull())
1719 {
1720 /* Start with length of parent chain, as the list is reversed */
1721 unsigned uImage = 0;
1722 ComPtr<IMedium> ptrTmp = ptrMedium;
1723 while (ptrTmp.isNotNull())
1724 {
1725 uImage++;
1726 ComPtr<IMedium> ptrParent;
1727 hrc = ptrTmp->COMGETTER(Parent)(ptrParent.asOutParam()); H();
1728 ptrTmp = ptrParent;
1729 }
1730 /* Index of last image */
1731 uImage--;
1732
1733# ifdef VBOX_WITH_EXTPACK
1734 if (mptrExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
1735 {
1736 /* Configure loading the VDPlugin. */
1737 static const char s_szVDPlugin[] = "VDPluginCrypt";
1738 PCFGMNODE pCfgPlugins = NULL;
1739 PCFGMNODE pCfgPlugin = NULL;
1740 Utf8Str strPlugin;
1741 hrc = mptrExtPackManager->i_getLibraryPathForExtPack(s_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
1742 // Don't fail, this is optional!
1743 if (SUCCEEDED(hrc))
1744 {
1745 InsertConfigNode(pCfg, "Plugins", &pCfgPlugins);
1746 InsertConfigNode(pCfgPlugins, s_szVDPlugin, &pCfgPlugin);
1747 InsertConfigString(pCfgPlugin, "Path", strPlugin);
1748 }
1749 }
1750# endif
1751
1752 hrc = ptrMedium->COMGETTER(Location)(bstr.asOutParam()); H();
1753 InsertConfigString(pCfg, "Path", bstr);
1754
1755 hrc = ptrMedium->COMGETTER(Format)(bstr.asOutParam()); H();
1756 InsertConfigString(pCfg, "Format", bstr);
1757
1758 if (mediumType == MediumType_Readonly)
1759 InsertConfigInteger(pCfg, "ReadOnly", 1);
1760 else if (enmType == DeviceType_Floppy)
1761 InsertConfigInteger(pCfg, "MaybeReadOnly", 1);
1762
1763 /* Start without exclusive write access to the images. */
1764 /** @todo Live Migration: I don't quite like this, we risk screwing up when
1765 * we're resuming the VM if some 3rd dude have any of the VDIs open
1766 * with write sharing denied. However, if the two VMs are sharing a
1767 * image it really is necessary....
1768 *
1769 * So, on the "lock-media" command, the target teleporter should also
1770 * make DrvVD undo TempReadOnly. It gets interesting if we fail after
1771 * that. Grumble. */
1772 if ( enmType == DeviceType_HardDisk
1773 && aMachineState == MachineState_TeleportingIn)
1774 InsertConfigInteger(pCfg, "TempReadOnly", 1);
1775
1776 /* Flag for opening the medium for sharing between VMs. This
1777 * is done at the moment only for the first (and only) medium
1778 * in the chain, as shared media can have no diffs. */
1779 if (mediumType == MediumType_Shareable)
1780 InsertConfigInteger(pCfg, "Shareable", 1);
1781
1782 if (!fUseHostIOCache)
1783 {
1784 InsertConfigInteger(pCfg, "UseNewIo", 1);
1785 /*
1786 * Activate the builtin I/O cache for harddisks only.
1787 * It caches writes only which doesn't make sense for DVD drives
1788 * and just increases the overhead.
1789 */
1790 if ( fBuiltinIOCache
1791 && (enmType == DeviceType_HardDisk))
1792 InsertConfigInteger(pCfg, "BlockCache", 1);
1793 }
1794
1795 if (fSetupMerge)
1796 {
1797 InsertConfigInteger(pCfg, "SetupMerge", 1);
1798 if (uImage == uMergeSource)
1799 InsertConfigInteger(pCfg, "MergeSource", 1);
1800 else if (uImage == uMergeTarget)
1801 InsertConfigInteger(pCfg, "MergeTarget", 1);
1802 }
1803
1804 if (pcszBwGroup)
1805 InsertConfigString(pCfg, "BwGroup", pcszBwGroup);
1806
1807 if (fDiscard)
1808 InsertConfigInteger(pCfg, "Discard", 1);
1809
1810 if (fNonRotational)
1811 InsertConfigInteger(pCfg, "NonRotationalMedium", 1);
1812
1813 /* Pass all custom parameters. */
1814 bool fHostIP = true;
1815 bool fEncrypted = false;
1816 hrc = i_configMediumProperties(pCfg, ptrMedium, &fHostIP, &fEncrypted); H();
1817
1818 /* Create an inverted list of parents. */
1819 uImage--;
1820 ComPtr<IMedium> ptrParentMedium = ptrMedium;
1821 for (PCFGMNODE pParent = pCfg;; uImage--)
1822 {
1823 ComPtr<IMedium> ptrCurMedium;
1824 hrc = ptrParentMedium->COMGETTER(Parent)(ptrCurMedium.asOutParam()); H();
1825 if (ptrCurMedium.isNull())
1826 break;
1827
1828 PCFGMNODE pCur;
1829 InsertConfigNode(pParent, "Parent", &pCur);
1830 hrc = ptrCurMedium->COMGETTER(Location)(bstr.asOutParam()); H();
1831 InsertConfigString(pCur, "Path", bstr);
1832
1833 hrc = ptrCurMedium->COMGETTER(Format)(bstr.asOutParam()); H();
1834 InsertConfigString(pCur, "Format", bstr);
1835
1836 if (fSetupMerge)
1837 {
1838 if (uImage == uMergeSource)
1839 InsertConfigInteger(pCur, "MergeSource", 1);
1840 else if (uImage == uMergeTarget)
1841 InsertConfigInteger(pCur, "MergeTarget", 1);
1842 }
1843
1844 /* Configure medium properties. */
1845 hrc = i_configMediumProperties(pCur, ptrCurMedium, &fHostIP, &fEncrypted); H();
1846
1847 /* next */
1848 pParent = pCur;
1849 ptrParentMedium = ptrCurMedium;
1850 }
1851
1852 /* Custom code: put marker to not use host IP stack to driver
1853 * configuration node. Simplifies life of DrvVD a bit. */
1854 if (!fHostIP)
1855 InsertConfigInteger(pCfg, "HostIPStack", 0);
1856
1857 if (fEncrypted)
1858 m_cDisksEncrypted++;
1859 }
1860 else
1861 {
1862 /* Set empty drive flag for DVD or floppy without media. */
1863 if ( enmType == DeviceType_DVD
1864 || enmType == DeviceType_Floppy)
1865 InsertConfigInteger(pCfg, "EmptyDrive", 1);
1866 }
1867 }
1868#undef H
1869 }
1870 catch (ConfigError &x)
1871 {
1872 // InsertConfig threw something:
1873 return x.m_vrc;
1874 }
1875
1876 return VINF_SUCCESS;
1877}
1878
1879/**
1880 * Adds the medium properties to the CFGM tree.
1881 *
1882 * @returns VBox status code.
1883 * @param pCur The current CFGM node.
1884 * @param pMedium The medium object to configure.
1885 * @param pfHostIP Where to return the value of the \"HostIPStack\" property if found.
1886 * @param pfEncrypted Where to return whether the medium is encrypted.
1887 */
1888int Console::i_configMediumProperties(PCFGMNODE pCur, IMedium *pMedium, bool *pfHostIP, bool *pfEncrypted)
1889{
1890 /* Pass all custom parameters. */
1891 SafeArray<BSTR> aNames;
1892 SafeArray<BSTR> aValues;
1893 HRESULT hrc = pMedium->GetProperties(NULL, ComSafeArrayAsOutParam(aNames), ComSafeArrayAsOutParam(aValues));
1894 if ( SUCCEEDED(hrc)
1895 && aNames.size() != 0)
1896 {
1897 PCFGMNODE pVDC;
1898 InsertConfigNode(pCur, "VDConfig", &pVDC);
1899 for (size_t ii = 0; ii < aNames.size(); ++ii)
1900 {
1901 if (aValues[ii] && *aValues[ii])
1902 {
1903 Utf8Str const strName = aNames[ii];
1904 Utf8Str const strValue = aValues[ii];
1905 size_t offSlash = strName.find("/", 0);
1906 if ( offSlash != strName.npos
1907 && !strName.startsWith("Special/"))
1908 {
1909 com::Utf8Str strFilter;
1910 hrc = strFilter.assignEx(strName, 0, offSlash);
1911 if (FAILED(hrc))
1912 break;
1913
1914 com::Utf8Str strKey;
1915 hrc = strKey.assignEx(strName, offSlash + 1, strName.length() - offSlash - 1); /* Skip slash */
1916 if (FAILED(hrc))
1917 break;
1918
1919 PCFGMNODE pCfgFilterConfig = mpVMM->pfnCFGMR3GetChild(pVDC, strFilter.c_str());
1920 if (!pCfgFilterConfig)
1921 InsertConfigNode(pVDC, strFilter.c_str(), &pCfgFilterConfig);
1922
1923 InsertConfigString(pCfgFilterConfig, strKey.c_str(), strValue);
1924 }
1925 else
1926 {
1927 InsertConfigString(pVDC, strName.c_str(), strValue);
1928 if ( strName.compare("HostIPStack") == 0
1929 && strValue.compare("0") == 0)
1930 *pfHostIP = false;
1931 }
1932
1933 if ( strName.compare("CRYPT/KeyId") == 0
1934 && pfEncrypted)
1935 *pfEncrypted = true;
1936 }
1937 }
1938 }
1939
1940 return hrc;
1941}
1942
1943
1944/**
1945 * Configure proxy parameters the Network configuration tree.
1946 *
1947 * Parameters may differ depending on the IP address being accessed.
1948 *
1949 * @returns VBox status code.
1950 *
1951 * @param virtualBox The VirtualBox object.
1952 * @param pCfg Configuration node for the driver.
1953 * @param pcszPrefix The prefix for CFGM parameters: "Primary" or "Secondary".
1954 * @param strIpAddr The public IP address to be accessed via a proxy.
1955 *
1956 * @thread EMT
1957 */
1958int Console::i_configProxy(ComPtr<IVirtualBox> virtualBox, PCFGMNODE pCfg, const char *pcszPrefix, const com::Utf8Str &strIpAddr)
1959{
1960/** @todo r=bird: This code doesn't handle cleanup correctly and may leak
1961 * when hitting errors or throwing exceptions (bad_alloc). */
1962 RTHTTPPROXYINFO ProxyInfo;
1963 ComPtr<ISystemProperties> systemProperties;
1964 ProxyMode_T enmProxyMode;
1965 HRESULT hrc = virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam());
1966 if (FAILED(hrc))
1967 {
1968 LogRel(("CLOUD-NET: Failed to obtain system properties. hrc=%x\n", hrc));
1969 return false;
1970 }
1971 hrc = systemProperties->COMGETTER(ProxyMode)(&enmProxyMode);
1972 if (FAILED(hrc))
1973 {
1974 LogRel(("CLOUD-NET: Failed to obtain default machine folder. hrc=%x\n", hrc));
1975 return VERR_INTERNAL_ERROR;
1976 }
1977
1978 RTHTTP hHttp;
1979 int vrc = RTHttpCreate(&hHttp);
1980 if (RT_FAILURE(vrc))
1981 {
1982 LogRel(("CLOUD-NET: Failed to create HTTP context (vrc=%Rrc)\n", vrc));
1983 return vrc;
1984 }
1985
1986 char *pszProxyType = NULL;
1987
1988 if (enmProxyMode == ProxyMode_Manual)
1989 {
1990 /*
1991 * Unfortunately we cannot simply call RTHttpSetProxyByUrl because it never
1992 * exposes proxy settings. Calling RTHttpQueryProxyInfoForUrl afterward
1993 * won't help either as it uses system-wide proxy settings instead of
1994 * parameters we would have set with RTHttpSetProxyByUrl. Hence we parse
1995 * proxy URL ourselves here.
1996 */
1997 Bstr proxyUrl;
1998 hrc = systemProperties->COMGETTER(ProxyURL)(proxyUrl.asOutParam());
1999 if (FAILED(hrc))
2000 {
2001 LogRel(("CLOUD-NET: Failed to obtain proxy URL. hrc=%x\n", hrc));
2002 return false;
2003 }
2004 Utf8Str strProxyUrl = proxyUrl;
2005 if (!strProxyUrl.contains("://"))
2006 strProxyUrl = "http://" + strProxyUrl;
2007 const char *pcszProxyUrl = strProxyUrl.c_str();
2008 RTURIPARSED Parsed;
2009 vrc = RTUriParse(pcszProxyUrl, &Parsed);
2010 if (RT_FAILURE(vrc))
2011 {
2012 LogRel(("CLOUD-NET: Failed to parse proxy URL: %ls (vrc=%Rrc)\n", proxyUrl.raw(), vrc));
2013 return false;
2014 }
2015
2016 pszProxyType = RTUriParsedScheme(pcszProxyUrl, &Parsed);
2017 if (!pszProxyType)
2018 {
2019 LogRel(("CLOUD-NET: Failed to get proxy scheme from proxy URL: %s\n", pcszProxyUrl));
2020 return false;
2021 }
2022 RTStrToUpper(pszProxyType);
2023
2024 ProxyInfo.pszProxyHost = RTUriParsedAuthorityHost(pcszProxyUrl, &Parsed);
2025 if (!ProxyInfo.pszProxyHost)
2026 {
2027 LogRel(("CLOUD-NET: Failed to get proxy host name from proxy URL: %s\n", pcszProxyUrl));
2028 return false;
2029 }
2030 ProxyInfo.uProxyPort = RTUriParsedAuthorityPort(pcszProxyUrl, &Parsed);
2031 if (ProxyInfo.uProxyPort == UINT32_MAX)
2032 {
2033 LogRel(("CLOUD-NET: Failed to get proxy port from proxy URL: %s\n", pcszProxyUrl));
2034 return false;
2035 }
2036 ProxyInfo.pszProxyUsername = RTUriParsedAuthorityUsername(pcszProxyUrl, &Parsed);
2037 ProxyInfo.pszProxyPassword = RTUriParsedAuthorityPassword(pcszProxyUrl, &Parsed);
2038 }
2039 else if (enmProxyMode == ProxyMode_System)
2040 {
2041 vrc = RTHttpUseSystemProxySettings(hHttp);
2042 if (RT_FAILURE(vrc))
2043 {
2044 LogRel(("%s: RTHttpUseSystemProxySettings() failed: %Rrc", __FUNCTION__, vrc));
2045 RTHttpDestroy(hHttp);
2046 return vrc;
2047 }
2048 vrc = RTHttpQueryProxyInfoForUrl(hHttp, ("http://" + strIpAddr).c_str(), &ProxyInfo);
2049 RTHttpDestroy(hHttp);
2050 if (RT_FAILURE(vrc))
2051 {
2052 LogRel(("CLOUD-NET: Failed to get proxy for %s (vrc=%Rrc)\n", strIpAddr.c_str(), vrc));
2053 return vrc;
2054 }
2055
2056 switch (ProxyInfo.enmProxyType)
2057 {
2058 case RTHTTPPROXYTYPE_NOPROXY:
2059 /* Nothing to do */
2060 return VINF_SUCCESS;
2061 case RTHTTPPROXYTYPE_HTTP:
2062 pszProxyType = RTStrDup("HTTP");
2063 break;
2064 case RTHTTPPROXYTYPE_HTTPS:
2065 case RTHTTPPROXYTYPE_SOCKS4:
2066 case RTHTTPPROXYTYPE_SOCKS5:
2067 /* break; -- Fall through until support is implemented */
2068 case RTHTTPPROXYTYPE_UNKNOWN:
2069 case RTHTTPPROXYTYPE_INVALID:
2070 case RTHTTPPROXYTYPE_END:
2071 case RTHTTPPROXYTYPE_32BIT_HACK:
2072 LogRel(("CLOUD-NET: Unsupported proxy type %u\n", ProxyInfo.enmProxyType));
2073 RTHttpFreeProxyInfo(&ProxyInfo);
2074 return VERR_INVALID_PARAMETER;
2075 }
2076 }
2077 else
2078 {
2079 Assert(enmProxyMode == ProxyMode_NoProxy);
2080 return VINF_SUCCESS;
2081 }
2082
2083 /* Resolve proxy host name to IP address if necessary */
2084 RTNETADDR addr;
2085 RTSocketParseInetAddress(ProxyInfo.pszProxyHost, ProxyInfo.uProxyPort, &addr);
2086 if (addr.enmType != RTNETADDRTYPE_IPV4)
2087 {
2088 LogRel(("CLOUD-NET: Unsupported address type %u\n", addr.enmType));
2089 RTHttpFreeProxyInfo(&ProxyInfo);
2090 return VERR_INVALID_PARAMETER;
2091 }
2092
2093 InsertConfigString( pCfg, Utf8StrFmt("%sProxyType", pcszPrefix).c_str(), pszProxyType);
2094 InsertConfigInteger( pCfg, Utf8StrFmt("%sProxyPort", pcszPrefix).c_str(), ProxyInfo.uProxyPort);
2095 if (ProxyInfo.pszProxyHost)
2096 InsertConfigStringF( pCfg, Utf8StrFmt("%sProxyHost", pcszPrefix).c_str(), "%RTnaipv4", addr.uAddr.IPv4);
2097 if (ProxyInfo.pszProxyUsername)
2098 InsertConfigString( pCfg, Utf8StrFmt("%sProxyUser", pcszPrefix).c_str(), ProxyInfo.pszProxyUsername);
2099 if (ProxyInfo.pszProxyPassword)
2100 InsertConfigPassword(pCfg, Utf8StrFmt("%sProxyPassword", pcszPrefix).c_str(), ProxyInfo.pszProxyPassword);
2101
2102 RTHttpFreeProxyInfo(&ProxyInfo);
2103 RTStrFree(pszProxyType);
2104 return vrc;
2105}
2106
2107
2108/**
2109 * Construct the Network configuration tree
2110 *
2111 * @returns VBox status code.
2112 *
2113 * @param pszDevice The PDM device name.
2114 * @param uInstance The PDM device instance.
2115 * @param uLun The PDM LUN number of the drive.
2116 * @param aNetworkAdapter The network adapter whose attachment needs to be changed
2117 * @param pCfg Configuration node for the device
2118 * @param pLunL0 To store the pointer to the LUN#0.
2119 * @param pInst The instance CFGM node
2120 * @param fAttachDetach To determine if the network attachment should
2121 * be attached/detached after/before
2122 * configuration.
2123 * @param fIgnoreConnectFailure
2124 * True if connection failures should be ignored
2125 * (makes only sense for bridged/host-only networks).
2126 * @param pUVM The usermode VM handle.
2127 * @param pVMM The VMM vtable.
2128 *
2129 * @note Locks this object for writing.
2130 * @thread EMT
2131 */
2132int Console::i_configNetwork(const char *pszDevice,
2133 unsigned uInstance,
2134 unsigned uLun,
2135 INetworkAdapter *aNetworkAdapter,
2136 PCFGMNODE pCfg,
2137 PCFGMNODE pLunL0,
2138 PCFGMNODE pInst,
2139 bool fAttachDetach,
2140 bool fIgnoreConnectFailure,
2141 PUVM pUVM,
2142 PCVMMR3VTABLE pVMM)
2143{
2144 RT_NOREF(fIgnoreConnectFailure);
2145 AutoCaller autoCaller(this);
2146 AssertComRCReturn(autoCaller.hrc(), VERR_ACCESS_DENIED);
2147
2148 // InsertConfig* throws
2149 try
2150 {
2151 int vrc = VINF_SUCCESS;
2152 HRESULT hrc;
2153 Bstr bstr;
2154
2155#ifdef VBOX_WITH_CLOUD_NET
2156 /* We'll need device's pCfg for cloud attachments */
2157 PCFGMNODE pDevCfg = pCfg;
2158#endif /* VBOX_WITH_CLOUD_NET */
2159
2160#define H() AssertLogRelMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR)
2161
2162 /*
2163 * Locking the object before doing VMR3* calls is quite safe here, since
2164 * we're on EMT. Write lock is necessary because we indirectly modify the
2165 * meAttachmentType member.
2166 */
2167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2168
2169 ComPtr<IMachine> pMachine = i_machine();
2170
2171 ComPtr<IVirtualBox> virtualBox;
2172 hrc = pMachine->COMGETTER(Parent)(virtualBox.asOutParam()); H();
2173
2174 ComPtr<IHost> host;
2175 hrc = virtualBox->COMGETTER(Host)(host.asOutParam()); H();
2176
2177 BOOL fSniffer;
2178 hrc = aNetworkAdapter->COMGETTER(TraceEnabled)(&fSniffer); H();
2179
2180 NetworkAdapterPromiscModePolicy_T enmPromiscModePolicy;
2181 hrc = aNetworkAdapter->COMGETTER(PromiscModePolicy)(&enmPromiscModePolicy); H();
2182 const char *pszPromiscuousGuestPolicy;
2183 switch (enmPromiscModePolicy)
2184 {
2185 case NetworkAdapterPromiscModePolicy_Deny: pszPromiscuousGuestPolicy = "deny"; break;
2186 case NetworkAdapterPromiscModePolicy_AllowNetwork: pszPromiscuousGuestPolicy = "allow-network"; break;
2187 case NetworkAdapterPromiscModePolicy_AllowAll: pszPromiscuousGuestPolicy = "allow-all"; break;
2188 default: AssertFailedReturn(VERR_INTERNAL_ERROR_4);
2189 }
2190
2191 if (fAttachDetach)
2192 {
2193 vrc = pVMM->pfnPDMR3DeviceDetach(pUVM, pszDevice, uInstance, uLun, 0 /*fFlags*/);
2194 if (vrc == VINF_PDM_NO_DRIVER_ATTACHED_TO_LUN)
2195 vrc = VINF_SUCCESS;
2196 AssertLogRelRCReturn(vrc, vrc);
2197
2198 /* Nuke anything which might have been left behind. */
2199 pVMM->pfnCFGMR3RemoveNode(pVMM->pfnCFGMR3GetChildF(pInst, "LUN#%u", uLun));
2200 }
2201
2202 Bstr networkName, trunkName, trunkType;
2203 NetworkAttachmentType_T eAttachmentType;
2204 hrc = aNetworkAdapter->COMGETTER(AttachmentType)(&eAttachmentType); H();
2205
2206#ifdef VBOX_WITH_NETSHAPER
2207 ComObjPtr<IBandwidthGroup> pBwGroup;
2208 Bstr bstrBwGroup;
2209 hrc = aNetworkAdapter->COMGETTER(BandwidthGroup)(pBwGroup.asOutParam()); H();
2210
2211 if (!pBwGroup.isNull())
2212 {
2213 hrc = pBwGroup->COMGETTER(Name)(bstrBwGroup.asOutParam()); H();
2214 }
2215#endif /* VBOX_WITH_NETSHAPER */
2216
2217 AssertMsg(uLun == 0, ("Network attachments with LUN > 0 are not supported yet\n"));
2218 InsertConfigNodeF(pInst, &pLunL0, "LUN#%u", uLun);
2219
2220 /*
2221 * Do not insert neither a shaper nor a sniffer if we are not attached to anything.
2222 * This way we can easily detect if we are attached to anything at the device level.
2223 */
2224#ifdef VBOX_WITH_NETSHAPER
2225 if (bstrBwGroup.isNotEmpty() && eAttachmentType != NetworkAttachmentType_Null)
2226 {
2227 InsertConfigString(pLunL0, "Driver", "NetShaper");
2228 InsertConfigNode(pLunL0, "Config", &pCfg);
2229 InsertConfigString(pCfg, "BwGroup", bstrBwGroup);
2230 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0);
2231 }
2232#endif /* VBOX_WITH_NETSHAPER */
2233
2234 if (fSniffer && eAttachmentType != NetworkAttachmentType_Null)
2235 {
2236 InsertConfigString(pLunL0, "Driver", "NetSniffer");
2237 InsertConfigNode(pLunL0, "Config", &pCfg);
2238 hrc = aNetworkAdapter->COMGETTER(TraceFile)(bstr.asOutParam()); H();
2239 if (!bstr.isEmpty()) /* check convention for indicating default file. */
2240 InsertConfigString(pCfg, "File", bstr);
2241 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL0);
2242 }
2243
2244 switch (eAttachmentType)
2245 {
2246 case NetworkAttachmentType_Null:
2247 break;
2248
2249 case NetworkAttachmentType_NAT:
2250 {
2251 ComPtr<INATEngine> natEngine;
2252 hrc = aNetworkAdapter->COMGETTER(NATEngine)(natEngine.asOutParam()); H();
2253 InsertConfigString(pLunL0, "Driver", "NAT");
2254 InsertConfigNode(pLunL0, "Config", &pCfg);
2255
2256 /* Configure TFTP prefix and boot filename. */
2257 hrc = virtualBox->COMGETTER(HomeFolder)(bstr.asOutParam()); H();
2258 if (!bstr.isEmpty())
2259 InsertConfigStringF(pCfg, "TFTPPrefix", "%ls%c%s", bstr.raw(), RTPATH_DELIMITER, "TFTP");
2260 hrc = pMachine->COMGETTER(Name)(bstr.asOutParam()); H();
2261 InsertConfigStringF(pCfg, "BootFile", "%ls.pxe", bstr.raw());
2262
2263 hrc = natEngine->COMGETTER(Network)(bstr.asOutParam()); H();
2264 if (!bstr.isEmpty())
2265 InsertConfigString(pCfg, "Network", bstr);
2266 else
2267 {
2268 ULONG uSlot;
2269 hrc = aNetworkAdapter->COMGETTER(Slot)(&uSlot); H();
2270 InsertConfigStringF(pCfg, "Network", "10.0.%d.0/24", uSlot+2);
2271 }
2272 hrc = natEngine->COMGETTER(HostIP)(bstr.asOutParam()); H();
2273 if (!bstr.isEmpty())
2274 InsertConfigString(pCfg, "BindIP", bstr);
2275 ULONG mtu = 0;
2276 ULONG sockSnd = 0;
2277 ULONG sockRcv = 0;
2278 ULONG tcpSnd = 0;
2279 ULONG tcpRcv = 0;
2280 hrc = natEngine->GetNetworkSettings(&mtu, &sockSnd, &sockRcv, &tcpSnd, &tcpRcv); H();
2281 if (mtu)
2282 InsertConfigInteger(pCfg, "SlirpMTU", mtu);
2283 if (sockRcv)
2284 InsertConfigInteger(pCfg, "SockRcv", sockRcv);
2285 if (sockSnd)
2286 InsertConfigInteger(pCfg, "SockSnd", sockSnd);
2287 if (tcpRcv)
2288 InsertConfigInteger(pCfg, "TcpRcv", tcpRcv);
2289 if (tcpSnd)
2290 InsertConfigInteger(pCfg, "TcpSnd", tcpSnd);
2291 hrc = natEngine->COMGETTER(TFTPPrefix)(bstr.asOutParam()); H();
2292 if (!bstr.isEmpty())
2293 {
2294 RemoveConfigValue(pCfg, "TFTPPrefix");
2295 InsertConfigString(pCfg, "TFTPPrefix", bstr);
2296 }
2297 hrc = natEngine->COMGETTER(TFTPBootFile)(bstr.asOutParam()); H();
2298 if (!bstr.isEmpty())
2299 {
2300 RemoveConfigValue(pCfg, "BootFile");
2301 InsertConfigString(pCfg, "BootFile", bstr);
2302 }
2303 hrc = natEngine->COMGETTER(TFTPNextServer)(bstr.asOutParam()); H();
2304 if (!bstr.isEmpty())
2305 InsertConfigString(pCfg, "NextServer", bstr);
2306 BOOL fDNSFlag;
2307 hrc = natEngine->COMGETTER(DNSPassDomain)(&fDNSFlag); H();
2308 InsertConfigInteger(pCfg, "PassDomain", fDNSFlag);
2309 hrc = natEngine->COMGETTER(DNSProxy)(&fDNSFlag); H();
2310 InsertConfigInteger(pCfg, "DNSProxy", fDNSFlag);
2311 hrc = natEngine->COMGETTER(DNSUseHostResolver)(&fDNSFlag); H();
2312 InsertConfigInteger(pCfg, "UseHostResolver", fDNSFlag);
2313
2314 ULONG aliasMode;
2315 hrc = natEngine->COMGETTER(AliasMode)(&aliasMode); H();
2316 InsertConfigInteger(pCfg, "AliasMode", aliasMode);
2317
2318 BOOL fLocalhostReachable;
2319 hrc = natEngine->COMGETTER(LocalhostReachable)(&fLocalhostReachable); H();
2320 InsertConfigInteger(pCfg, "LocalhostReachable", fLocalhostReachable);
2321
2322 /* port-forwarding */
2323 SafeArray<BSTR> pfs;
2324 hrc = natEngine->COMGETTER(Redirects)(ComSafeArrayAsOutParam(pfs)); H();
2325
2326 PCFGMNODE pPFTree = NULL;
2327 if (pfs.size() > 0)
2328 InsertConfigNode(pCfg, "PortForwarding", &pPFTree);
2329
2330 for (unsigned int i = 0; i < pfs.size(); ++i)
2331 {
2332 PCFGMNODE pPF = NULL; /* /Devices/Dev/.../Config/PortForwarding/$n/ */
2333
2334 uint16_t port = 0;
2335 Utf8Str utf = pfs[i];
2336 Utf8Str strName;
2337 Utf8Str strProto;
2338 Utf8Str strHostPort;
2339 Utf8Str strHostIP;
2340 Utf8Str strGuestPort;
2341 Utf8Str strGuestIP;
2342 size_t pos, ppos;
2343 pos = ppos = 0;
2344#define ITERATE_TO_NEXT_TERM(res, str, pos, ppos) \
2345 { \
2346 pos = str.find(",", ppos); \
2347 if (pos == Utf8Str::npos) \
2348 { \
2349 Log(( #res " extracting from %s is failed\n", str.c_str())); \
2350 continue; \
2351 } \
2352 res = str.substr(ppos, pos - ppos); \
2353 Log2((#res " %s pos:%d, ppos:%d\n", res.c_str(), pos, ppos)); \
2354 ppos = pos + 1; \
2355 } /* no do { ... } while because of 'continue' */
2356 ITERATE_TO_NEXT_TERM(strName, utf, pos, ppos);
2357 ITERATE_TO_NEXT_TERM(strProto, utf, pos, ppos);
2358 ITERATE_TO_NEXT_TERM(strHostIP, utf, pos, ppos);
2359 ITERATE_TO_NEXT_TERM(strHostPort, utf, pos, ppos);
2360 ITERATE_TO_NEXT_TERM(strGuestIP, utf, pos, ppos);
2361 strGuestPort = utf.substr(ppos, utf.length() - ppos);
2362#undef ITERATE_TO_NEXT_TERM
2363
2364 uint32_t proto = strProto.toUInt32();
2365 bool fValid = true;
2366 switch (proto)
2367 {
2368 case NATProtocol_UDP:
2369 strProto = "UDP";
2370 break;
2371 case NATProtocol_TCP:
2372 strProto = "TCP";
2373 break;
2374 default:
2375 fValid = false;
2376 }
2377 /* continue with next rule if no valid proto was passed */
2378 if (!fValid)
2379 continue;
2380
2381 InsertConfigNodeF(pPFTree, &pPF, "%u", i);
2382
2383 if (!strName.isEmpty())
2384 InsertConfigString(pPF, "Name", strName);
2385
2386 InsertConfigString(pPF, "Protocol", strProto);
2387
2388 if (!strHostIP.isEmpty())
2389 InsertConfigString(pPF, "BindIP", strHostIP);
2390
2391 if (!strGuestIP.isEmpty())
2392 InsertConfigString(pPF, "GuestIP", strGuestIP);
2393
2394 port = RTStrToUInt16(strHostPort.c_str());
2395 if (port)
2396 InsertConfigInteger(pPF, "HostPort", port);
2397
2398 port = RTStrToUInt16(strGuestPort.c_str());
2399 if (port)
2400 InsertConfigInteger(pPF, "GuestPort", port);
2401 }
2402 break;
2403 }
2404
2405 case NetworkAttachmentType_Bridged:
2406 {
2407#if (defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) && !defined(VBOX_WITH_NETFLT)
2408 hrc = i_attachToTapInterface(aNetworkAdapter);
2409 if (FAILED(hrc))
2410 {
2411 switch (hrc)
2412 {
2413 case E_ACCESSDENIED:
2414 return pVMM->pfnVMR3SetError(pUVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_(
2415 "Failed to open '/dev/net/tun' for read/write access. Please check the "
2416 "permissions of that node. Either run 'chmod 0666 /dev/net/tun' or "
2417 "change the group of that node and make yourself a member of that group. "
2418 "Make sure that these changes are permanent, especially if you are "
2419 "using udev"));
2420 default:
2421 AssertMsgFailed(("Could not attach to host interface! Bad!\n"));
2422 return pVMM->pfnVMR3SetError(pUVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
2423 N_("Failed to initialize Host Interface Networking"));
2424 }
2425 }
2426
2427 Assert((intptr_t)maTapFD[uInstance] >= 0);
2428 if ((intptr_t)maTapFD[uInstance] >= 0)
2429 {
2430 InsertConfigString(pLunL0, "Driver", "HostInterface");
2431 InsertConfigNode(pLunL0, "Config", &pCfg);
2432 InsertConfigInteger(pCfg, "FileHandle", (intptr_t)maTapFD[uInstance]);
2433 }
2434
2435#elif defined(VBOX_WITH_NETFLT)
2436 /*
2437 * This is the new VBoxNetFlt+IntNet stuff.
2438 */
2439 Bstr BridgedIfName;
2440 hrc = aNetworkAdapter->COMGETTER(BridgedInterface)(BridgedIfName.asOutParam());
2441 if (FAILED(hrc))
2442 {
2443 LogRel(("NetworkAttachmentType_Bridged: COMGETTER(BridgedInterface) failed, hrc (0x%x)\n", hrc));
2444 H();
2445 }
2446
2447 Utf8Str BridgedIfNameUtf8(BridgedIfName);
2448 const char *pszBridgedIfName = BridgedIfNameUtf8.c_str();
2449
2450 ComPtr<IHostNetworkInterface> hostInterface;
2451 hrc = host->FindHostNetworkInterfaceByName(BridgedIfName.raw(),
2452 hostInterface.asOutParam());
2453 if (!SUCCEEDED(hrc))
2454 {
2455 AssertLogRelMsgFailed(("NetworkAttachmentType_Bridged: FindByName failed, hrc=%Rhrc (0x%x)\n", hrc, hrc));
2456 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
2457 N_("Nonexistent host networking interface, name '%ls'"),
2458 BridgedIfName.raw());
2459 }
2460
2461# if defined(RT_OS_DARWIN)
2462 /* The name is in the format 'ifX: long name', chop it off at the colon. */
2463 char szTrunk[INTNET_MAX_TRUNK_NAME];
2464 RTStrCopy(szTrunk, sizeof(szTrunk), pszBridgedIfName);
2465 char *pszColon = (char *)memchr(szTrunk, ':', sizeof(szTrunk));
2466// Quick fix for @bugref{5633}
2467// if (!pszColon)
2468// {
2469// /*
2470// * Dynamic changing of attachment causes an attempt to configure
2471// * network with invalid host adapter (as it is must be changed before
2472// * the attachment), calling Detach here will cause a deadlock.
2473// * See @bugref{4750}.
2474// * hrc = aNetworkAdapter->Detach(); H();
2475// */
2476// return VMSetError(VMR3GetVM(mpUVM), VERR_INTERNAL_ERROR, RT_SRC_POS,
2477// N_("Malformed host interface networking name '%ls'"),
2478// BridgedIfName.raw());
2479// }
2480 if (pszColon)
2481 *pszColon = '\0';
2482 const char *pszTrunk = szTrunk;
2483
2484# elif defined(RT_OS_SOLARIS)
2485 /* The name is in the format 'ifX[:1] - long name, chop it off at space. */
2486 char szTrunk[256];
2487 strlcpy(szTrunk, pszBridgedIfName, sizeof(szTrunk));
2488 char *pszSpace = (char *)memchr(szTrunk, ' ', sizeof(szTrunk));
2489
2490 /*
2491 * Currently don't bother about malformed names here for the sake of people using
2492 * VBoxManage and setting only the NIC name from there. If there is a space we
2493 * chop it off and proceed, otherwise just use whatever we've got.
2494 */
2495 if (pszSpace)
2496 *pszSpace = '\0';
2497
2498 /* Chop it off at the colon (zone naming eg: e1000g:1 we need only the e1000g) */
2499 char *pszColon = (char *)memchr(szTrunk, ':', sizeof(szTrunk));
2500 if (pszColon)
2501 *pszColon = '\0';
2502
2503 const char *pszTrunk = szTrunk;
2504
2505# elif defined(RT_OS_WINDOWS)
2506 HostNetworkInterfaceType_T eIfType;
2507 hrc = hostInterface->COMGETTER(InterfaceType)(&eIfType);
2508 if (FAILED(hrc))
2509 {
2510 LogRel(("NetworkAttachmentType_Bridged: COMGETTER(InterfaceType) failed, hrc (0x%x)\n", hrc));
2511 H();
2512 }
2513
2514 if (eIfType != HostNetworkInterfaceType_Bridged)
2515 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
2516 N_("Interface ('%ls') is not a Bridged Adapter interface"),
2517 BridgedIfName.raw());
2518
2519 hrc = hostInterface->COMGETTER(Id)(bstr.asOutParam());
2520 if (FAILED(hrc))
2521 {
2522 LogRel(("NetworkAttachmentType_Bridged: COMGETTER(Id) failed, hrc (0x%x)\n", hrc));
2523 H();
2524 }
2525 Guid hostIFGuid(bstr);
2526
2527 INetCfg *pNc;
2528 ComPtr<INetCfgComponent> pAdaptorComponent;
2529 LPWSTR pszApp;
2530
2531 hrc = VBoxNetCfgWinQueryINetCfg(&pNc, FALSE, L"VirtualBox", 10, &pszApp);
2532 Assert(hrc == S_OK);
2533 if (hrc != S_OK)
2534 {
2535 LogRel(("NetworkAttachmentType_Bridged: Failed to get NetCfg, hrc=%Rhrc (0x%x)\n", hrc, hrc));
2536 H();
2537 }
2538
2539 /* get the adapter's INetCfgComponent*/
2540 hrc = VBoxNetCfgWinGetComponentByGuid(pNc, &GUID_DEVCLASS_NET, (GUID*)hostIFGuid.raw(),
2541 pAdaptorComponent.asOutParam());
2542 if (hrc != S_OK)
2543 {
2544 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
2545 LogRel(("NetworkAttachmentType_Bridged: VBoxNetCfgWinGetComponentByGuid failed, hrc (0x%x)\n", hrc));
2546 H();
2547 }
2548# define VBOX_WIN_BINDNAME_PREFIX "\\DEVICE\\"
2549 char szTrunkName[INTNET_MAX_TRUNK_NAME];
2550 char *pszTrunkName = szTrunkName;
2551 wchar_t * pswzBindName;
2552 hrc = pAdaptorComponent->GetBindName(&pswzBindName);
2553 Assert(hrc == S_OK);
2554 if (hrc == S_OK)
2555 {
2556 int cwBindName = (int)wcslen(pswzBindName) + 1;
2557 int cbFullBindNamePrefix = sizeof(VBOX_WIN_BINDNAME_PREFIX);
2558 if (sizeof(szTrunkName) > cbFullBindNamePrefix + cwBindName)
2559 {
2560 strcpy(szTrunkName, VBOX_WIN_BINDNAME_PREFIX);
2561 pszTrunkName += cbFullBindNamePrefix-1;
2562 if (!WideCharToMultiByte(CP_ACP, 0, pswzBindName, cwBindName, pszTrunkName,
2563 sizeof(szTrunkName) - cbFullBindNamePrefix + 1, NULL, NULL))
2564 {
2565 DWORD err = GetLastError();
2566 hrc = HRESULT_FROM_WIN32(err);
2567 AssertMsgFailed(("hrc=%Rhrc %#x\n", hrc, hrc));
2568 AssertLogRelMsgFailed(("NetworkAttachmentType_Bridged: WideCharToMultiByte failed, hr=%Rhrc (0x%x) err=%u\n",
2569 hrc, hrc, err));
2570 }
2571 }
2572 else
2573 {
2574 AssertLogRelMsgFailed(("NetworkAttachmentType_Bridged: insufficient szTrunkName buffer space\n"));
2575 /** @todo set appropriate error code */
2576 hrc = E_FAIL;
2577 }
2578
2579 if (hrc != S_OK)
2580 {
2581 AssertFailed();
2582 CoTaskMemFree(pswzBindName);
2583 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
2584 H();
2585 }
2586
2587 /* we're not freeing the bind name since we'll use it later for detecting wireless*/
2588 }
2589 else
2590 {
2591 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
2592 AssertLogRelMsgFailed(("NetworkAttachmentType_Bridged: VBoxNetCfgWinGetComponentByGuid failed, hrc (0x%x)",
2593 hrc));
2594 H();
2595 }
2596
2597 const char *pszTrunk = szTrunkName;
2598 /* we're not releasing the INetCfg stuff here since we use it later to figure out whether it is wireless */
2599
2600# elif defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
2601# if defined(RT_OS_FREEBSD)
2602 /*
2603 * If we bridge to a tap interface open it the `old' direct way.
2604 * This works and performs better than bridging a physical
2605 * interface via the current FreeBSD vboxnetflt implementation.
2606 */
2607 if (!strncmp(pszBridgedIfName, RT_STR_TUPLE("tap"))) {
2608 hrc = i_attachToTapInterface(aNetworkAdapter);
2609 if (FAILED(hrc))
2610 {
2611 switch (hrc)
2612 {
2613 case E_ACCESSDENIED:
2614 return pVMM->pfnVMR3SetError(pUVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_(
2615 "Failed to open '/dev/%s' for read/write access. Please check the "
2616 "permissions of that node, and that the net.link.tap.user_open "
2617 "sysctl is set. Either run 'chmod 0666 /dev/%s' or change the "
2618 "group of that node to vboxusers and make yourself a member of "
2619 "that group. Make sure that these changes are permanent."),
2620 pszBridgedIfName, pszBridgedIfName);
2621 default:
2622 AssertMsgFailed(("Could not attach to tap interface! Bad!\n"));
2623 return pVMM->pfnVMR3SetError(pUVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
2624 N_("Failed to initialize Host Interface Networking"));
2625 }
2626 }
2627
2628 Assert((intptr_t)maTapFD[uInstance] >= 0);
2629 if ((intptr_t)maTapFD[uInstance] >= 0)
2630 {
2631 InsertConfigString(pLunL0, "Driver", "HostInterface");
2632 InsertConfigNode(pLunL0, "Config", &pCfg);
2633 InsertConfigInteger(pCfg, "FileHandle", (intptr_t)maTapFD[uInstance]);
2634 }
2635 break;
2636 }
2637# endif
2638 /** @todo Check for malformed names. */
2639 const char *pszTrunk = pszBridgedIfName;
2640
2641 /* Issue a warning if the interface is down */
2642 {
2643 int iSock = socket(AF_INET, SOCK_DGRAM, 0);
2644 if (iSock >= 0)
2645 {
2646 struct ifreq Req;
2647 RT_ZERO(Req);
2648 RTStrCopy(Req.ifr_name, sizeof(Req.ifr_name), pszBridgedIfName);
2649 if (ioctl(iSock, SIOCGIFFLAGS, &Req) >= 0)
2650 if ((Req.ifr_flags & IFF_UP) == 0)
2651 i_atVMRuntimeErrorCallbackF(0, "BridgedInterfaceDown",
2652 N_("Bridged interface %s is down. Guest will not be able to use this interface"),
2653 pszBridgedIfName);
2654
2655 close(iSock);
2656 }
2657 }
2658
2659# else
2660# error "PORTME (VBOX_WITH_NETFLT)"
2661# endif
2662
2663# if defined(RT_OS_DARWIN) && defined(VBOX_WITH_VMNET)
2664 InsertConfigString(pLunL0, "Driver", "VMNet");
2665 InsertConfigNode(pLunL0, "Config", &pCfg);
2666 InsertConfigString(pCfg, "Trunk", pszTrunk);
2667 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetFlt);
2668# else
2669 InsertConfigString(pLunL0, "Driver", "IntNet");
2670 InsertConfigNode(pLunL0, "Config", &pCfg);
2671 InsertConfigString(pCfg, "Trunk", pszTrunk);
2672 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetFlt);
2673 InsertConfigInteger(pCfg, "IgnoreConnectFailure", (uint64_t)fIgnoreConnectFailure);
2674 InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy);
2675 char szNetwork[INTNET_MAX_NETWORK_NAME];
2676
2677# if defined(RT_OS_SOLARIS) || defined(RT_OS_DARWIN)
2678 /*
2679 * 'pszTrunk' contains just the interface name required in ring-0, while 'pszBridgedIfName' contains
2680 * interface name + optional description. We must not pass any description to the VM as it can differ
2681 * for the same interface name, eg: "nge0 - ethernet" (GUI) vs "nge0" (VBoxManage).
2682 */
2683 RTStrPrintf(szNetwork, sizeof(szNetwork), "HostInterfaceNetworking-%s", pszTrunk);
2684# else
2685 RTStrPrintf(szNetwork, sizeof(szNetwork), "HostInterfaceNetworking-%s", pszBridgedIfName);
2686# endif
2687 InsertConfigString(pCfg, "Network", szNetwork);
2688 networkName = Bstr(szNetwork);
2689 trunkName = Bstr(pszTrunk);
2690 trunkType = Bstr(TRUNKTYPE_NETFLT);
2691
2692 BOOL fSharedMacOnWire = false;
2693 hrc = hostInterface->COMGETTER(Wireless)(&fSharedMacOnWire);
2694 if (FAILED(hrc))
2695 {
2696 LogRel(("NetworkAttachmentType_Bridged: COMGETTER(Wireless) failed, hrc (0x%x)\n", hrc));
2697 H();
2698 }
2699 else if (fSharedMacOnWire)
2700 {
2701 InsertConfigInteger(pCfg, "SharedMacOnWire", true);
2702 Log(("Set SharedMacOnWire\n"));
2703 }
2704
2705# if defined(RT_OS_SOLARIS)
2706# if 0 /* bird: this is a bit questionable and might cause more trouble than its worth. */
2707 /* Zone access restriction, don't allow snooping the global zone. */
2708 zoneid_t ZoneId = getzoneid();
2709 if (ZoneId != GLOBAL_ZONEID)
2710 {
2711 InsertConfigInteger(pCfg, "IgnoreAllPromisc", true);
2712 }
2713# endif
2714# endif
2715# endif
2716
2717#elif defined(RT_OS_WINDOWS) /* not defined NetFlt */
2718 /* NOTHING TO DO HERE */
2719#elif defined(RT_OS_LINUX)
2720/// @todo aleksey: is there anything to be done here?
2721#elif defined(RT_OS_FREEBSD)
2722/** @todo FreeBSD: Check out this later (HIF networking). */
2723#else
2724# error "Port me"
2725#endif
2726 break;
2727 }
2728
2729 case NetworkAttachmentType_Internal:
2730 {
2731 hrc = aNetworkAdapter->COMGETTER(InternalNetwork)(bstr.asOutParam()); H();
2732 if (!bstr.isEmpty())
2733 {
2734 InsertConfigString(pLunL0, "Driver", "IntNet");
2735 InsertConfigNode(pLunL0, "Config", &pCfg);
2736 InsertConfigString(pCfg, "Network", bstr);
2737 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_WhateverNone);
2738 InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy);
2739 networkName = bstr;
2740 trunkType = Bstr(TRUNKTYPE_WHATEVER);
2741 }
2742 break;
2743 }
2744
2745 case NetworkAttachmentType_HostOnly:
2746 {
2747 InsertConfigString(pLunL0, "Driver", "IntNet");
2748 InsertConfigNode(pLunL0, "Config", &pCfg);
2749
2750 Bstr HostOnlyName;
2751 hrc = aNetworkAdapter->COMGETTER(HostOnlyInterface)(HostOnlyName.asOutParam());
2752 if (FAILED(hrc))
2753 {
2754 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(HostOnlyInterface) failed, hrc (0x%x)\n", hrc));
2755 H();
2756 }
2757
2758 Utf8Str HostOnlyNameUtf8(HostOnlyName);
2759 const char *pszHostOnlyName = HostOnlyNameUtf8.c_str();
2760#ifdef VBOX_WITH_VMNET
2761 /* Check if the matching host-only network has already been created. */
2762 Bstr bstrLowerIP, bstrUpperIP, bstrNetworkMask;
2763 BstrFmt bstrNetworkName("Legacy %s Network", pszHostOnlyName);
2764 ComPtr<IHostOnlyNetwork> hostOnlyNetwork;
2765 hrc = virtualBox->FindHostOnlyNetworkByName(bstrNetworkName.raw(), hostOnlyNetwork.asOutParam());
2766 if (FAILED(hrc))
2767 {
2768 /*
2769 * With VMNET there is no VBoxNetAdp to create vboxnetX adapters,
2770 * which means that the Host object won't be able to re-create
2771 * them from extra data. Go through existing DHCP/adapter config
2772 * to derive the parameters for the new network.
2773 */
2774 BstrFmt bstrOldNetworkName("HostInterfaceNetworking-%s", pszHostOnlyName);
2775 ComPtr<IDHCPServer> dhcpServer;
2776 hrc = virtualBox->FindDHCPServerByNetworkName(bstrOldNetworkName.raw(),
2777 dhcpServer.asOutParam());
2778 if (SUCCEEDED(hrc))
2779 {
2780 /* There is a DHCP server available for this network. */
2781 hrc = dhcpServer->COMGETTER(LowerIP)(bstrLowerIP.asOutParam());
2782 if (FAILED(hrc))
2783 {
2784 LogRel(("Console::i_configNetwork: COMGETTER(LowerIP) failed, hrc (%Rhrc)\n", hrc));
2785 H();
2786 }
2787 hrc = dhcpServer->COMGETTER(UpperIP)(bstrUpperIP.asOutParam());
2788 if (FAILED(hrc))
2789 {
2790 LogRel(("Console::i_configNetwork: COMGETTER(UpperIP) failed, hrc (%Rhrc)\n", hrc));
2791 H();
2792 }
2793 hrc = dhcpServer->COMGETTER(NetworkMask)(bstrNetworkMask.asOutParam());
2794 if (FAILED(hrc))
2795 {
2796 LogRel(("Console::i_configNetwork: COMGETTER(NetworkMask) failed, hrc (%Rhrc)\n", hrc));
2797 H();
2798 }
2799 }
2800 else
2801 {
2802 /* No DHCP server for this hostonly interface, let's look at extra data */
2803 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPAddress",
2804 pszHostOnlyName).raw(),
2805 bstrLowerIP.asOutParam());
2806 if (SUCCEEDED(hrc) && !bstrLowerIP.isEmpty())
2807 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPNetMask",
2808 pszHostOnlyName).raw(),
2809 bstrNetworkMask.asOutParam());
2810
2811 }
2812 RTNETADDRIPV4 ipAddr, ipMask;
2813 vrc = bstrLowerIP.isEmpty() ? VERR_MISSING : RTNetStrToIPv4Addr(Utf8Str(bstrLowerIP).c_str(), &ipAddr);
2814 if (RT_FAILURE(vrc))
2815 {
2816 /* We failed to locate any valid config of this vboxnetX interface, assume defaults. */
2817 LogRel(("NetworkAttachmentType_HostOnly: Invalid or missing lower IP '%ls', using '%ls' instead.\n",
2818 bstrLowerIP.raw(), getDefaultIPv4Address(Bstr(pszHostOnlyName)).raw()));
2819 bstrLowerIP = getDefaultIPv4Address(Bstr(pszHostOnlyName));
2820 bstrNetworkMask.setNull();
2821 bstrUpperIP.setNull();
2822 vrc = RTNetStrToIPv4Addr(Utf8Str(bstrLowerIP).c_str(), &ipAddr);
2823 AssertLogRelMsgReturn(RT_SUCCESS(vrc), ("RTNetStrToIPv4Addr(%ls) failed, vrc=%Rrc\n", bstrLowerIP.raw(), vrc),
2824 VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR);
2825 }
2826 vrc = bstrNetworkMask.isEmpty() ? VERR_MISSING : RTNetStrToIPv4Addr(Utf8Str(bstrNetworkMask).c_str(), &ipMask);
2827 if (RT_FAILURE(vrc))
2828 {
2829 LogRel(("NetworkAttachmentType_HostOnly: Invalid or missing network mask '%ls', using '%s' instead.\n",
2830 bstrNetworkMask.raw(), VBOXNET_IPV4MASK_DEFAULT));
2831 bstrNetworkMask = VBOXNET_IPV4MASK_DEFAULT;
2832 vrc = RTNetStrToIPv4Addr(Utf8Str(bstrNetworkMask).c_str(), &ipMask);
2833 AssertLogRelMsgReturn(RT_SUCCESS(vrc), ("RTNetStrToIPv4Addr(%ls) failed, vrc=%Rrc\n", bstrNetworkMask.raw(), vrc),
2834 VERR_MAIN_CONFIG_CONSTRUCTOR_COM_ERROR);
2835 }
2836 vrc = bstrUpperIP.isEmpty() ? VERR_MISSING : RTNetStrToIPv4Addr(Utf8Str(bstrUpperIP).c_str(), &ipAddr);
2837 if (RT_FAILURE(vrc))
2838 {
2839 ipAddr.au32[0] = RT_H2N_U32((RT_N2H_U32(ipAddr.au32[0]) | ~RT_N2H_U32(ipMask.au32[0])) - 1); /* Do we need to exlude the last IP? */
2840 LogRel(("NetworkAttachmentType_HostOnly: Invalid or missing upper IP '%ls', using '%RTnaipv4' instead.\n",
2841 bstrUpperIP.raw(), ipAddr));
2842 bstrUpperIP = BstrFmt("%RTnaipv4", ipAddr);
2843 }
2844
2845 /* All parameters are set, create the new network. */
2846 hrc = virtualBox->CreateHostOnlyNetwork(bstrNetworkName.raw(), hostOnlyNetwork.asOutParam());
2847 if (FAILED(hrc))
2848 {
2849 LogRel(("NetworkAttachmentType_HostOnly: failed to create host-only network, hrc (0x%x)\n", hrc));
2850 H();
2851 }
2852 hrc = hostOnlyNetwork->COMSETTER(NetworkMask)(bstrNetworkMask.raw());
2853 if (FAILED(hrc))
2854 {
2855 LogRel(("NetworkAttachmentType_HostOnly: COMSETTER(NetworkMask) failed, hrc (0x%x)\n", hrc));
2856 H();
2857 }
2858 hrc = hostOnlyNetwork->COMSETTER(LowerIP)(bstrLowerIP.raw());
2859 if (FAILED(hrc))
2860 {
2861 LogRel(("NetworkAttachmentType_HostOnly: COMSETTER(LowerIP) failed, hrc (0x%x)\n", hrc));
2862 H();
2863 }
2864 hrc = hostOnlyNetwork->COMSETTER(UpperIP)(bstrUpperIP.raw());
2865 if (FAILED(hrc))
2866 {
2867 LogRel(("NetworkAttachmentType_HostOnly: COMSETTER(UpperIP) failed, hrc (0x%x)\n", hrc));
2868 H();
2869 }
2870 LogRel(("Console: created host-only network '%ls' with mask '%ls' and range '%ls'-'%ls'\n",
2871 bstrNetworkName.raw(), bstrNetworkMask.raw(), bstrLowerIP.raw(), bstrUpperIP.raw()));
2872 }
2873 else
2874 {
2875 /* The matching host-only network already exists. Tell the user to switch to it. */
2876 hrc = hostOnlyNetwork->COMGETTER(NetworkMask)(bstrNetworkMask.asOutParam());
2877 if (FAILED(hrc))
2878 {
2879 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(NetworkMask) failed, hrc (0x%x)\n", hrc));
2880 H();
2881 }
2882 hrc = hostOnlyNetwork->COMGETTER(LowerIP)(bstrLowerIP.asOutParam());
2883 if (FAILED(hrc))
2884 {
2885 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(LowerIP) failed, hrc (0x%x)\n", hrc));
2886 H();
2887 }
2888 hrc = hostOnlyNetwork->COMGETTER(UpperIP)(bstrUpperIP.asOutParam());
2889 if (FAILED(hrc))
2890 {
2891 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(UpperIP) failed, hrc (0x%x)\n", hrc));
2892 H();
2893 }
2894 }
2895 return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS,
2896 N_("Host-only adapters are no longer supported!\n"
2897 "For your convenience a host-only network named '%ls' has been "
2898 "created with network mask '%ls' and IP address range '%ls' - '%ls'.\n"
2899 "To fix this problem, switch to 'Host-only Network' "
2900 "attachment type in the VM settings.\n"),
2901 bstrNetworkName.raw(), bstrNetworkMask.raw(),
2902 bstrLowerIP.raw(), bstrUpperIP.raw());
2903#endif /* VBOX_WITH_VMNET */
2904 ComPtr<IHostNetworkInterface> hostInterface;
2905 hrc = host->FindHostNetworkInterfaceByName(HostOnlyName.raw(),
2906 hostInterface.asOutParam());
2907 if (!SUCCEEDED(hrc))
2908 {
2909 LogRel(("NetworkAttachmentType_HostOnly: FindByName failed, vrc=%Rrc\n", vrc));
2910 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
2911 N_("Nonexistent host networking interface, name '%ls'"), HostOnlyName.raw());
2912 }
2913
2914 char szNetwork[INTNET_MAX_NETWORK_NAME];
2915 RTStrPrintf(szNetwork, sizeof(szNetwork), "HostInterfaceNetworking-%s", pszHostOnlyName);
2916
2917#if defined(RT_OS_WINDOWS)
2918# ifndef VBOX_WITH_NETFLT
2919 hrc = E_NOTIMPL;
2920 LogRel(("NetworkAttachmentType_HostOnly: Not Implemented\n"));
2921 H();
2922# else /* defined VBOX_WITH_NETFLT*/
2923 /** @todo r=bird: Put this in a function. */
2924
2925 HostNetworkInterfaceType_T eIfType;
2926 hrc = hostInterface->COMGETTER(InterfaceType)(&eIfType);
2927 if (FAILED(hrc))
2928 {
2929 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(InterfaceType) failed, hrc (0x%x)\n", hrc));
2930 H();
2931 }
2932
2933 if (eIfType != HostNetworkInterfaceType_HostOnly)
2934 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
2935 N_("Interface ('%ls') is not a Host-Only Adapter interface"),
2936 HostOnlyName.raw());
2937
2938 hrc = hostInterface->COMGETTER(Id)(bstr.asOutParam());
2939 if (FAILED(hrc))
2940 {
2941 LogRel(("NetworkAttachmentType_HostOnly: COMGETTER(Id) failed, hrc (0x%x)\n", hrc));
2942 H();
2943 }
2944 Guid hostIFGuid(bstr);
2945
2946 INetCfg *pNc;
2947 ComPtr<INetCfgComponent> pAdaptorComponent;
2948 LPWSTR pszApp;
2949 hrc = VBoxNetCfgWinQueryINetCfg(&pNc, FALSE, L"VirtualBox", 10, &pszApp);
2950 Assert(hrc == S_OK);
2951 if (hrc != S_OK)
2952 {
2953 LogRel(("NetworkAttachmentType_HostOnly: Failed to get NetCfg, hrc=%Rhrc (0x%x)\n", hrc, hrc));
2954 H();
2955 }
2956
2957 /* get the adapter's INetCfgComponent*/
2958 hrc = VBoxNetCfgWinGetComponentByGuid(pNc, &GUID_DEVCLASS_NET, (GUID*)hostIFGuid.raw(),
2959 pAdaptorComponent.asOutParam());
2960 if (hrc != S_OK)
2961 {
2962 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
2963 LogRel(("NetworkAttachmentType_HostOnly: VBoxNetCfgWinGetComponentByGuid failed, hrc=%Rhrc (0x%x)\n", hrc, hrc));
2964 H();
2965 }
2966# define VBOX_WIN_BINDNAME_PREFIX "\\DEVICE\\"
2967 char szTrunkName[INTNET_MAX_TRUNK_NAME];
2968 bool fNdis6 = false;
2969 wchar_t * pwszHelpText;
2970 hrc = pAdaptorComponent->GetHelpText(&pwszHelpText);
2971 Assert(hrc == S_OK);
2972 if (hrc == S_OK)
2973 {
2974 Log(("help-text=%ls\n", pwszHelpText));
2975 if (!wcscmp(pwszHelpText, L"VirtualBox NDIS 6.0 Miniport Driver"))
2976 fNdis6 = true;
2977 CoTaskMemFree(pwszHelpText);
2978 }
2979 if (fNdis6)
2980 {
2981 strncpy(szTrunkName, pszHostOnlyName, sizeof(szTrunkName) - 1);
2982 Log(("trunk=%s\n", szTrunkName));
2983 }
2984 else
2985 {
2986 char *pszTrunkName = szTrunkName;
2987 wchar_t * pswzBindName;
2988 hrc = pAdaptorComponent->GetBindName(&pswzBindName);
2989 Assert(hrc == S_OK);
2990 if (hrc == S_OK)
2991 {
2992 int cwBindName = (int)wcslen(pswzBindName) + 1;
2993 int cbFullBindNamePrefix = sizeof(VBOX_WIN_BINDNAME_PREFIX);
2994 if (sizeof(szTrunkName) > cbFullBindNamePrefix + cwBindName)
2995 {
2996 strcpy(szTrunkName, VBOX_WIN_BINDNAME_PREFIX);
2997 pszTrunkName += cbFullBindNamePrefix-1;
2998 if (!WideCharToMultiByte(CP_ACP, 0, pswzBindName, cwBindName, pszTrunkName,
2999 sizeof(szTrunkName) - cbFullBindNamePrefix + 1, NULL, NULL))
3000 {
3001 DWORD err = GetLastError();
3002 hrc = HRESULT_FROM_WIN32(err);
3003 AssertLogRelMsgFailed(("NetworkAttachmentType_HostOnly: WideCharToMultiByte failed, hr=%Rhrc (0x%x) err=%u\n",
3004 hrc, hrc, err));
3005 }
3006 }
3007 else
3008 {
3009 AssertLogRelMsgFailed(("NetworkAttachmentType_HostOnly: insufficient szTrunkName buffer space\n"));
3010 /** @todo set appropriate error code */
3011 hrc = E_FAIL;
3012 }
3013
3014 if (hrc != S_OK)
3015 {
3016 AssertFailed();
3017 CoTaskMemFree(pswzBindName);
3018 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
3019 H();
3020 }
3021 }
3022 else
3023 {
3024 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
3025 AssertLogRelMsgFailed(("NetworkAttachmentType_HostOnly: VBoxNetCfgWinGetComponentByGuid failed, hrc=%Rhrc (0x%x)\n",
3026 hrc, hrc));
3027 H();
3028 }
3029
3030
3031 CoTaskMemFree(pswzBindName);
3032 }
3033
3034 trunkType = TRUNKTYPE_NETADP;
3035 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetAdp);
3036
3037 pAdaptorComponent.setNull();
3038 /* release the pNc finally */
3039 VBoxNetCfgWinReleaseINetCfg(pNc, FALSE /*fHasWriteLock*/);
3040
3041 const char *pszTrunk = szTrunkName;
3042
3043 InsertConfigString(pCfg, "Trunk", pszTrunk);
3044 InsertConfigString(pCfg, "Network", szNetwork);
3045 InsertConfigInteger(pCfg, "IgnoreConnectFailure", (uint64_t)fIgnoreConnectFailure); /** @todo why is this
3046 windows only?? */
3047 networkName = Bstr(szNetwork);
3048 trunkName = Bstr(pszTrunk);
3049# endif /* defined VBOX_WITH_NETFLT*/
3050#elif defined(RT_OS_DARWIN)
3051 InsertConfigString(pCfg, "Trunk", pszHostOnlyName);
3052 InsertConfigString(pCfg, "Network", szNetwork);
3053 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetAdp);
3054 networkName = Bstr(szNetwork);
3055 trunkName = Bstr(pszHostOnlyName);
3056 trunkType = TRUNKTYPE_NETADP;
3057#else
3058 InsertConfigString(pCfg, "Trunk", pszHostOnlyName);
3059 InsertConfigString(pCfg, "Network", szNetwork);
3060 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetFlt);
3061 networkName = Bstr(szNetwork);
3062 trunkName = Bstr(pszHostOnlyName);
3063 trunkType = TRUNKTYPE_NETFLT;
3064#endif
3065 InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy);
3066
3067#if !defined(RT_OS_WINDOWS) && defined(VBOX_WITH_NETFLT)
3068
3069 Bstr tmpAddr, tmpMask;
3070
3071 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPAddress",
3072 pszHostOnlyName).raw(),
3073 tmpAddr.asOutParam());
3074 if (SUCCEEDED(hrc) && !tmpAddr.isEmpty())
3075 {
3076 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPNetMask",
3077 pszHostOnlyName).raw(),
3078 tmpMask.asOutParam());
3079 if (SUCCEEDED(hrc) && !tmpMask.isEmpty())
3080 hrc = hostInterface->EnableStaticIPConfig(tmpAddr.raw(),
3081 tmpMask.raw());
3082 else
3083 hrc = hostInterface->EnableStaticIPConfig(tmpAddr.raw(),
3084 Bstr(VBOXNET_IPV4MASK_DEFAULT).raw());
3085 }
3086 else
3087 {
3088 /* Grab the IP number from the 'vboxnetX' instance number (see netif.h) */
3089 hrc = hostInterface->EnableStaticIPConfig(getDefaultIPv4Address(Bstr(pszHostOnlyName)).raw(),
3090 Bstr(VBOXNET_IPV4MASK_DEFAULT).raw());
3091 }
3092
3093 ComAssertComRC(hrc); /** @todo r=bird: Why this isn't fatal? (H()) */
3094
3095 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPV6Address",
3096 pszHostOnlyName).raw(),
3097 tmpAddr.asOutParam());
3098 if (SUCCEEDED(hrc))
3099 hrc = virtualBox->GetExtraData(BstrFmt("HostOnly/%s/IPV6NetMask", pszHostOnlyName).raw(),
3100 tmpMask.asOutParam());
3101 if (SUCCEEDED(hrc) && !tmpAddr.isEmpty() && !tmpMask.isEmpty())
3102 {
3103 hrc = hostInterface->EnableStaticIPConfigV6(tmpAddr.raw(),
3104 Utf8Str(tmpMask).toUInt32());
3105 ComAssertComRC(hrc); /** @todo r=bird: Why this isn't fatal? (H()) */
3106 }
3107#endif
3108 break;
3109 }
3110
3111 case NetworkAttachmentType_Generic:
3112 {
3113 hrc = aNetworkAdapter->COMGETTER(GenericDriver)(bstr.asOutParam()); H();
3114 SafeArray<BSTR> names;
3115 SafeArray<BSTR> values;
3116 hrc = aNetworkAdapter->GetProperties(Bstr().raw(),
3117 ComSafeArrayAsOutParam(names),
3118 ComSafeArrayAsOutParam(values)); H();
3119
3120 InsertConfigString(pLunL0, "Driver", bstr);
3121 InsertConfigNode(pLunL0, "Config", &pCfg);
3122 for (size_t ii = 0; ii < names.size(); ++ii)
3123 {
3124 if (values[ii] && *values[ii])
3125 {
3126 Utf8Str const strName(names[ii]);
3127 Utf8Str const strValue(values[ii]);
3128 InsertConfigString(pCfg, strName.c_str(), strValue);
3129 }
3130 }
3131 break;
3132 }
3133
3134 case NetworkAttachmentType_NATNetwork:
3135 {
3136 hrc = aNetworkAdapter->COMGETTER(NATNetwork)(bstr.asOutParam()); H();
3137 if (!bstr.isEmpty())
3138 {
3139 /** @todo add intnet prefix to separate namespaces, and add trunk if dealing with vboxnatX */
3140 InsertConfigString(pLunL0, "Driver", "IntNet");
3141 InsertConfigNode(pLunL0, "Config", &pCfg);
3142 InsertConfigString(pCfg, "Network", bstr);
3143 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_WhateverNone);
3144 InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy);
3145 networkName = bstr;
3146 trunkType = Bstr(TRUNKTYPE_WHATEVER);
3147 }
3148 break;
3149 }
3150
3151#ifdef VBOX_WITH_CLOUD_NET
3152 case NetworkAttachmentType_Cloud:
3153 {
3154 static const char *s_pszCloudExtPackName = "Oracle VM VirtualBox Extension Pack";
3155 /*
3156 * Cloud network attachments do not work wihout installed extpack.
3157 * Without extpack support they won't work either.
3158 */
3159# ifdef VBOX_WITH_EXTPACK
3160 if (!mptrExtPackManager->i_isExtPackUsable(s_pszCloudExtPackName))
3161# endif
3162 {
3163 return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS,
3164 N_("Implementation of the cloud network attachment not found!\n"
3165 "To fix this problem, either install the '%s' or switch to "
3166 "another network attachment type in the VM settings."),
3167 s_pszCloudExtPackName);
3168 }
3169
3170 ComPtr<ICloudNetwork> network;
3171 hrc = aNetworkAdapter->COMGETTER(CloudNetwork)(bstr.asOutParam()); H();
3172 hrc = pMachine->COMGETTER(Name)(mGateway.mTargetVM.asOutParam()); H();
3173 hrc = virtualBox->FindCloudNetworkByName(bstr.raw(), network.asOutParam()); H();
3174 hrc = generateKeys(mGateway);
3175 if (FAILED(hrc))
3176 {
3177 if (hrc == E_NOTIMPL)
3178 return pVMM->pfnVMR3SetError(pUVM, VERR_NOT_FOUND, RT_SRC_POS,
3179 N_("Failed to generate a key pair due to missing libssh\n"
3180 "To fix this problem, either build VirtualBox with libssh "
3181 "support or switch to another network attachment type in "
3182 "the VM settings."));
3183 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
3184 N_("Failed to generate a key pair due to libssh error!"));
3185 }
3186 hrc = startCloudGateway(virtualBox, network, mGateway);
3187 if (FAILED(hrc))
3188 {
3189 if (hrc == VBOX_E_OBJECT_NOT_FOUND)
3190 return pVMM->pfnVMR3SetError(pUVM, hrc, RT_SRC_POS,
3191 N_("Failed to start cloud gateway instance.\nCould not find suitable "
3192 "standard cloud images. Make sure you ran 'VBoxManage cloud network setup' "
3193 "with correct '--gateway-os-name' and '--gateway-os-version' parameters. "
3194 "Check VBoxSVC.log for actual values used to look up cloud images."));
3195 return pVMM->pfnVMR3SetError(pUVM, hrc, RT_SRC_POS,
3196 N_("Failed to start cloud gateway instance.\nMake sure you set up "
3197 "cloud networking properly with 'VBoxManage cloud network setup'. "
3198 "Check VBoxSVC.log for details."));
3199 }
3200 InsertConfigBytes(pDevCfg, "MAC", &mGateway.mCloudMacAddress, sizeof(mGateway.mCloudMacAddress));
3201 if (!bstr.isEmpty())
3202 {
3203 InsertConfigString(pLunL0, "Driver", "CloudTunnel");
3204 InsertConfigNode(pLunL0, "Config", &pCfg);
3205 InsertConfigPassword(pCfg, "SshKey", mGateway.mPrivateSshKey);
3206 InsertConfigString(pCfg, "PrimaryIP", mGateway.mCloudPublicIp);
3207 InsertConfigString(pCfg, "SecondaryIP", mGateway.mCloudSecondaryPublicIp);
3208 InsertConfigBytes(pCfg, "TargetMAC", &mGateway.mLocalMacAddress, sizeof(mGateway.mLocalMacAddress));
3209 hrc = i_configProxy(virtualBox, pCfg, "Primary", mGateway.mCloudPublicIp);
3210 if (FAILED(hrc))
3211 {
3212 return pVMM->pfnVMR3SetError(pUVM, hrc, RT_SRC_POS,
3213 N_("Failed to configure proxy for accessing cloud gateway instance via primary VNIC.\n"
3214 "Check VirtualBox.log for details."));
3215 }
3216 hrc = i_configProxy(virtualBox, pCfg, "Secondary", mGateway.mCloudSecondaryPublicIp);
3217 if (FAILED(hrc))
3218 {
3219 return pVMM->pfnVMR3SetError(pUVM, hrc, RT_SRC_POS,
3220 N_("Failed to configure proxy for accessing cloud gateway instance via secondary VNIC.\n"
3221 "Check VirtualBox.log for details."));
3222 }
3223 networkName = bstr;
3224 trunkType = Bstr(TRUNKTYPE_WHATEVER);
3225 }
3226 break;
3227 }
3228#endif /* VBOX_WITH_CLOUD_NET */
3229
3230#ifdef VBOX_WITH_VMNET
3231 case NetworkAttachmentType_HostOnlyNetwork:
3232 {
3233 Bstr bstrId, bstrNetMask, bstrLowerIP, bstrUpperIP;
3234 ComPtr<IHostOnlyNetwork> network;
3235 hrc = aNetworkAdapter->COMGETTER(HostOnlyNetwork)(bstr.asOutParam()); H();
3236 hrc = virtualBox->FindHostOnlyNetworkByName(bstr.raw(), network.asOutParam());
3237 if (FAILED(hrc))
3238 {
3239 LogRel(("NetworkAttachmentType_HostOnlyNetwork: FindByName failed, hrc (0x%x)\n", hrc));
3240 return pVMM->pfnVMR3SetError(pUVM, VERR_INTERNAL_ERROR, RT_SRC_POS,
3241 N_("Nonexistent host-only network '%ls'"), bstr.raw());
3242 }
3243 hrc = network->COMGETTER(Id)(bstrId.asOutParam()); H();
3244 hrc = network->COMGETTER(NetworkMask)(bstrNetMask.asOutParam()); H();
3245 hrc = network->COMGETTER(LowerIP)(bstrLowerIP.asOutParam()); H();
3246 hrc = network->COMGETTER(UpperIP)(bstrUpperIP.asOutParam()); H();
3247 if (!bstr.isEmpty())
3248 {
3249 InsertConfigString(pLunL0, "Driver", "VMNet");
3250 InsertConfigNode(pLunL0, "Config", &pCfg);
3251 // InsertConfigString(pCfg, "Trunk", bstr);
3252 // InsertConfigStringF(pCfg, "Network", "HostOnlyNetworking-%ls", bstr.raw());
3253 InsertConfigInteger(pCfg, "TrunkType", kIntNetTrunkType_NetAdp);
3254 InsertConfigString(pCfg, "Id", bstrId);
3255 InsertConfigString(pCfg, "NetworkMask", bstrNetMask);
3256 InsertConfigString(pCfg, "LowerIP", bstrLowerIP);
3257 InsertConfigString(pCfg, "UpperIP", bstrUpperIP);
3258 // InsertConfigString(pCfg, "IfPolicyPromisc", pszPromiscuousGuestPolicy);
3259 networkName.setNull(); // We do not want DHCP server on our network!
3260 // trunkType = Bstr(TRUNKTYPE_WHATEVER);
3261 }
3262 break;
3263 }
3264#endif /* VBOX_WITH_VMNET */
3265
3266 default:
3267 AssertMsgFailed(("should not get here!\n"));
3268 break;
3269 }
3270
3271 /*
3272 * Attempt to attach the driver.
3273 */
3274 switch (eAttachmentType)
3275 {
3276 case NetworkAttachmentType_Null:
3277 break;
3278
3279 case NetworkAttachmentType_Bridged:
3280 case NetworkAttachmentType_Internal:
3281 case NetworkAttachmentType_HostOnly:
3282#ifdef VBOX_WITH_VMNET
3283 case NetworkAttachmentType_HostOnlyNetwork:
3284#endif /* VBOX_WITH_VMNET */
3285 case NetworkAttachmentType_NAT:
3286 case NetworkAttachmentType_Generic:
3287 case NetworkAttachmentType_NATNetwork:
3288#ifdef VBOX_WITH_CLOUD_NET
3289 case NetworkAttachmentType_Cloud:
3290#endif /* VBOX_WITH_CLOUD_NET */
3291 {
3292 if (SUCCEEDED(hrc) && RT_SUCCESS(vrc))
3293 {
3294 if (fAttachDetach)
3295 {
3296 vrc = pVMM->pfnPDMR3DriverAttach(mpUVM, pszDevice, uInstance, uLun, 0 /*fFlags*/, NULL /* ppBase */);
3297 //AssertRC(vrc);
3298 }
3299
3300 {
3301 /** @todo pritesh: get the dhcp server name from the
3302 * previous network configuration and then stop the server
3303 * else it may conflict with the dhcp server running with
3304 * the current attachment type
3305 */
3306 /* Stop the hostonly DHCP Server */
3307 }
3308
3309 /*
3310 * NAT networks start their DHCP server theirself, see NATNetwork::Start()
3311 */
3312 if ( !networkName.isEmpty()
3313 && eAttachmentType != NetworkAttachmentType_NATNetwork)
3314 {
3315 /*
3316 * Until we implement service reference counters DHCP Server will be stopped
3317 * by DHCPServerRunner destructor.
3318 */
3319 ComPtr<IDHCPServer> dhcpServer;
3320 hrc = virtualBox->FindDHCPServerByNetworkName(networkName.raw(), dhcpServer.asOutParam());
3321 if (SUCCEEDED(hrc))
3322 {
3323 /* there is a DHCP server available for this network */
3324 BOOL fEnabledDhcp;
3325 hrc = dhcpServer->COMGETTER(Enabled)(&fEnabledDhcp);
3326 if (FAILED(hrc))
3327 {
3328 LogRel(("DHCP svr: COMGETTER(Enabled) failed, hrc (%Rhrc)\n", hrc));
3329 H();
3330 }
3331
3332 if (fEnabledDhcp)
3333 hrc = dhcpServer->Start(trunkName.raw(), trunkType.raw());
3334 }
3335 else
3336 hrc = S_OK;
3337 }
3338 }
3339
3340 break;
3341 }
3342
3343 default:
3344 AssertMsgFailed(("should not get here!\n"));
3345 break;
3346 }
3347
3348 meAttachmentType[uInstance] = eAttachmentType;
3349 }
3350 catch (ConfigError &x)
3351 {
3352 // InsertConfig threw something:
3353 return x.m_vrc;
3354 }
3355
3356#undef H
3357
3358 return VINF_SUCCESS;
3359}
3360
3361
3362/**
3363 * Configures the serial port at the given CFGM node with the supplied parameters.
3364 *
3365 * @returns VBox status code.
3366 * @param pInst The instance CFGM node.
3367 * @param ePortMode The port mode to sue.
3368 * @param pszPath The serial port path.
3369 * @param fServer Flag whether the port should act as a server
3370 * for the pipe and TCP mode or connect as a client.
3371 */
3372int Console::i_configSerialPort(PCFGMNODE pInst, PortMode_T ePortMode, const char *pszPath, bool fServer)
3373{
3374 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */
3375 PCFGMNODE pLunL1 = NULL; /* /Devices/Dev/0/LUN#0/AttachedDriver/ */
3376 PCFGMNODE pLunL1Cfg = NULL; /* /Devices/Dev/0/LUN#0/AttachedDriver/Config */
3377
3378 try
3379 {
3380 InsertConfigNode(pInst, "LUN#0", &pLunL0);
3381 if (ePortMode == PortMode_HostPipe)
3382 {
3383 InsertConfigString(pLunL0, "Driver", "Char");
3384 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
3385 InsertConfigString(pLunL1, "Driver", "NamedPipe");
3386 InsertConfigNode(pLunL1, "Config", &pLunL1Cfg);
3387 InsertConfigString(pLunL1Cfg, "Location", pszPath);
3388 InsertConfigInteger(pLunL1Cfg, "IsServer", fServer);
3389 }
3390 else if (ePortMode == PortMode_HostDevice)
3391 {
3392 InsertConfigString(pLunL0, "Driver", "Host Serial");
3393 InsertConfigNode(pLunL0, "Config", &pLunL1);
3394 InsertConfigString(pLunL1, "DevicePath", pszPath);
3395 }
3396 else if (ePortMode == PortMode_TCP)
3397 {
3398 InsertConfigString(pLunL0, "Driver", "Char");
3399 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
3400 InsertConfigString(pLunL1, "Driver", "TCP");
3401 InsertConfigNode(pLunL1, "Config", &pLunL1Cfg);
3402 InsertConfigString(pLunL1Cfg, "Location", pszPath);
3403 InsertConfigInteger(pLunL1Cfg, "IsServer", fServer);
3404 }
3405 else if (ePortMode == PortMode_RawFile)
3406 {
3407 InsertConfigString(pLunL0, "Driver", "Char");
3408 InsertConfigNode(pLunL0, "AttachedDriver", &pLunL1);
3409 InsertConfigString(pLunL1, "Driver", "RawFile");
3410 InsertConfigNode(pLunL1, "Config", &pLunL1Cfg);
3411 InsertConfigString(pLunL1Cfg, "Location", pszPath);
3412 }
3413 }
3414 catch (ConfigError &x)
3415 {
3416 /* InsertConfig threw something */
3417 return x.m_vrc;
3418 }
3419
3420 return VINF_SUCCESS;
3421}
3422
3423
3424#ifndef VBOX_WITH_EFI_IN_DD2
3425DECLHIDDEN(int) findEfiRom(IVirtualBox* vbox, PlatformArchitecture_T aPlatformArchitecture, FirmwareType_T aFirmwareType, Utf8Str *pEfiRomFile)
3426{
3427 Bstr aFilePath, empty;
3428 BOOL fPresent = FALSE;
3429 HRESULT hrc = vbox->CheckFirmwarePresent(aPlatformArchitecture, aFirmwareType, empty.raw(),
3430 empty.asOutParam(), aFilePath.asOutParam(), &fPresent);
3431 AssertComRCReturn(hrc, Global::vboxStatusCodeFromCOM(hrc));
3432
3433 if (!fPresent)
3434 {
3435 LogRel(("Failed to find an EFI ROM file.\n"));
3436 return VERR_FILE_NOT_FOUND;
3437 }
3438
3439 *pEfiRomFile = Utf8Str(aFilePath);
3440
3441 return VINF_SUCCESS;
3442}
3443#endif
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use