VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxInternalManage.cpp

Last change on this file was 99739, checked in by vboxsync, 12 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 75.0 KB
Line 
1/* $Id: VBoxInternalManage.cpp 99739 2023-05-11 01:01:08Z vboxsync $ */
2/** @file
3 * VBoxManage - The 'internalcommands' command.
4 *
5 * VBoxInternalManage used to be a second CLI for doing special tricks,
6 * not intended for general usage, only for assisting VBox developers.
7 * It is now integrated into VBoxManage.
8 */
9
10/*
11 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
12 *
13 * This file is part of VirtualBox base platform packages, as
14 * available from https://www.virtualbox.org.
15 *
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation, in version 3 of the
19 * License.
20 *
21 * This program is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, see <https://www.gnu.org/licenses>.
28 *
29 * SPDX-License-Identifier: GPL-3.0-only
30 */
31
32
33
34/*********************************************************************************************************************************
35* Header Files *
36*********************************************************************************************************************************/
37#include <VBox/com/com.h>
38#include <VBox/com/string.h>
39#include <VBox/com/Guid.h>
40#include <VBox/com/ErrorInfo.h>
41#include <VBox/com/errorprint.h>
42
43#include <VBox/com/VirtualBox.h>
44
45#include <VBox/vd.h>
46#include <VBox/sup.h>
47#include <VBox/log.h>
48#include <VBox/version.h>
49
50#include <iprt/buildconfig.h>
51#include <iprt/ctype.h>
52#include <iprt/file.h>
53#include <iprt/getopt.h>
54#include <iprt/stream.h>
55#include <iprt/string.h>
56#include <iprt/uuid.h>
57#include <iprt/sha.h>
58
59#include "VBoxManage.h"
60
61/* Includes for the raw disk stuff. */
62#ifdef RT_OS_WINDOWS
63# include <iprt/win/windows.h>
64# include <winioctl.h>
65#elif defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) \
66 || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
67# include <errno.h>
68# include <sys/ioctl.h>
69# include <sys/types.h>
70# include <sys/stat.h>
71# include <fcntl.h>
72# include <unistd.h>
73#endif
74#ifdef RT_OS_LINUX
75# include <sys/utsname.h>
76# include <linux/hdreg.h>
77# include <linux/fs.h>
78# include <stdlib.h> /* atoi() */
79#endif /* RT_OS_LINUX */
80#ifdef RT_OS_DARWIN
81# include <sys/disk.h>
82#endif /* RT_OS_DARWIN */
83#ifdef RT_OS_SOLARIS
84# include <stropts.h>
85# include <sys/dkio.h>
86# include <sys/vtoc.h>
87#endif /* RT_OS_SOLARIS */
88#ifdef RT_OS_FREEBSD
89# include <sys/disk.h>
90#endif /* RT_OS_FREEBSD */
91
92using namespace com;
93
94
95/** Macro for checking whether a partition is of extended type or not. */
96#define PARTTYPE_IS_EXTENDED(x) ((x) == 0x05 || (x) == 0x0f || (x) == 0x85)
97
98/** Maximum number of partitions we can deal with.
99 * Ridiculously large number, but the memory consumption is rather low so who
100 * cares about never using most entries. */
101#define HOSTPARTITION_MAX 100
102
103DECLARE_TRANSLATION_CONTEXT(Internal);
104
105
106typedef struct HOSTPARTITION
107{
108 /** partition number */
109 unsigned uIndex;
110 /** partition number (internal only, windows specific numbering) */
111 unsigned uIndexWin;
112 /** partition type */
113 unsigned uType;
114 /** CHS/cylinder of the first sector */
115 unsigned uStartCylinder;
116 /** CHS/head of the first sector */
117 unsigned uStartHead;
118 /** CHS/head of the first sector */
119 unsigned uStartSector;
120 /** CHS/cylinder of the last sector */
121 unsigned uEndCylinder;
122 /** CHS/head of the last sector */
123 unsigned uEndHead;
124 /** CHS/sector of the last sector */
125 unsigned uEndSector;
126 /** start sector of this partition relative to the beginning of the hard
127 * disk or relative to the beginning of the extended partition table */
128 uint64_t uStart;
129 /** numer of sectors of the partition */
130 uint64_t uSize;
131 /** start sector of this partition _table_ */
132 uint64_t uPartDataStart;
133 /** numer of sectors of this partition _table_ */
134 uint64_t cPartDataSectors;
135} HOSTPARTITION, *PHOSTPARTITION;
136
137typedef struct HOSTPARTITIONS
138{
139 /** partitioning type - MBR or GPT */
140 VDISKPARTTYPE uPartitioningType;
141 unsigned cPartitions;
142 HOSTPARTITION aPartitions[HOSTPARTITION_MAX];
143} HOSTPARTITIONS, *PHOSTPARTITIONS;
144
145
146/** @name Syntax diagram category, i.e. the command.
147 * @{ */
148typedef enum
149{
150 USAGE_INVALID = 0,
151 USAGE_I_LOADSYMS,
152 USAGE_I_LOADMAP,
153 USAGE_I_SETHDUUID,
154 USAGE_I_LISTPARTITIONS,
155 USAGE_I_CREATERAWVMDK,
156 USAGE_I_MODINSTALL,
157 USAGE_I_MODUNINSTALL,
158 USAGE_I_RENAMEVMDK,
159 USAGE_I_CONVERTTORAW,
160 USAGE_I_CONVERTHD,
161 USAGE_I_DUMPHDINFO,
162 USAGE_I_DEBUGLOG,
163 USAGE_I_SETHDPARENTUUID,
164 USAGE_I_PASSWORDHASH,
165 USAGE_I_GUESTSTATS,
166 USAGE_I_REPAIRHD,
167 USAGE_I_ALL
168} USAGECATEGORY;
169/** @} */
170
171
172/**
173 * Print the usage info.
174 */
175static void printUsageInternal(USAGECATEGORY enmCommand, PRTSTREAM pStrm)
176{
177 Assert(enmCommand != USAGE_INVALID);
178 RTStrmPrintf(pStrm,
179 Internal::tr(
180 "Usage: VBoxManage internalcommands <command> [command arguments]\n"
181 "\n"
182 "Commands:\n"
183 "\n"
184 "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"
185 "WARNING: This is a development tool and should only be used to analyse\n"
186 " problems. It is completely unsupported and will change in\n"
187 " incompatible ways without warning.\n"),
188
189 (enmCommand == USAGE_I_LOADMAP || enmCommand == USAGE_I_ALL)
190 ? Internal::tr(
191 " loadmap <vmname|uuid> <symfile> <address> [module] [subtrahend] [segment]\n"
192 " This will instruct DBGF to load the given map file\n"
193 " during initialization. (See also loadmap in the debugger.)\n"
194 "\n")
195 : "",
196 (enmCommand == USAGE_I_LOADSYMS || enmCommand == USAGE_I_ALL)
197 ? Internal::tr(
198 " loadsyms <vmname|uuid> <symfile> [delta] [module] [module address]\n"
199 " This will instruct DBGF to load the given symbol file\n"
200 " during initialization.\n"
201 "\n")
202 : "",
203 (enmCommand == USAGE_I_SETHDUUID || enmCommand == USAGE_I_ALL)
204 ? Internal::tr(
205 " sethduuid <filepath> [<uuid>]\n"
206 " Assigns a new UUID to the given image file. This way, multiple copies\n"
207 " of a container can be registered.\n"
208 "\n")
209 : "",
210 (enmCommand == USAGE_I_SETHDPARENTUUID || enmCommand == USAGE_I_ALL)
211 ? Internal::tr(
212 " sethdparentuuid <filepath> <uuid>\n"
213 " Assigns a new parent UUID to the given image file.\n"
214 "\n")
215 : "",
216 (enmCommand == USAGE_I_DUMPHDINFO || enmCommand == USAGE_I_ALL)
217 ? Internal::tr(
218 " dumphdinfo <filepath>\n"
219 " Prints information about the image at the given location.\n"
220 "\n")
221 : "",
222 (enmCommand == USAGE_I_LISTPARTITIONS || enmCommand == USAGE_I_ALL)
223 ? Internal::tr(
224 " listpartitions -rawdisk <diskname>\n"
225 " Lists all partitions on <diskname>.\n"
226 "\n")
227 : "",
228 (enmCommand == USAGE_I_CREATERAWVMDK || enmCommand == USAGE_I_ALL)
229 ? Internal::tr(
230 " createrawvmdk --filename <filename> --rawdisk <diskname>\n"
231 " [--partitions <list of partition numbers> [--mbr <filename>] ]\n"
232 " [--relative]\n"
233 " Creates a new VMDK image which gives direct access to a physical hard\n"
234 " disk on the host. The entire disk can be presented to the guest or\n"
235 " just specific partitions specified using the --partitions parameter.\n"
236 " If access to individual partitions is granted, then the --mbr parameter\n"
237 " can be used to specify an alternative Master Boot Record (MBR) (note\n"
238 " that the partitioning information in the MBR file is ignored). The\n"
239 " format of the diskname argument for the --rawdisk parameter varies by\n"
240 " platform but can be determined using the command:\n"
241 " VBoxManage list hostdrives\n"
242 " The output lists the available drives and their partitions along with\n"
243 " their partition types and sizes.\n"
244 " On Linux, FreeBSD, and Windows hosts the --relative parameter creates a\n"
245 " VMDK image file which references the specified individual partitions\n"
246 " directly instead of referencing the partitions by their offset from\n"
247 " the start of the physical disk.\n"
248 "\n"
249 " Nota Bene: The 'createrawvdk' subcommand is deprecated. The equivalent\n"
250 " functionality is available using the 'VBoxManage createmedium' command\n"
251 " and should be used instead. See 'VBoxManage help createmedium' for\n"
252 " details.\n"
253 "\n")
254 : "",
255 (enmCommand == USAGE_I_RENAMEVMDK || enmCommand == USAGE_I_ALL)
256 ? Internal::tr(
257 " renamevmdk -from <filename> -to <filename>\n"
258 " Renames an existing VMDK image, including the base file and all its extents.\n"
259 "\n")
260 : "",
261 (enmCommand == USAGE_I_CONVERTTORAW || enmCommand == USAGE_I_ALL)
262#ifdef ENABLE_CONVERT_RAW_TO_STDOUT
263 ? Internal::tr(
264 " converttoraw [-format <fileformat>] <filename> <outputfile>|stdout"
265 "\n"
266 " Convert image to raw, writing to file or stdout.\n"
267 "\n")
268#else
269 ? Internal::tr(
270 " converttoraw [-format <fileformat>] <filename> <outputfile>"
271 "\n"
272 " Convert image to raw, writing to file.\n"
273 "\n")
274#endif
275 : "",
276 (enmCommand == USAGE_I_CONVERTHD || enmCommand == USAGE_I_ALL)
277 ? Internal::tr(
278 " converthd [-srcformat VDI|VMDK|VHD|RAW]\n"
279 " [-dstformat VDI|VMDK|VHD|RAW]\n"
280 " <inputfile> <outputfile>\n"
281 " converts hard disk images between formats\n"
282 "\n")
283 : "",
284 (enmCommand == USAGE_I_REPAIRHD || enmCommand == USAGE_I_ALL)
285 ? Internal::tr(
286 " repairhd [-dry-run]\n"
287 " [-format VDI|VMDK|VHD|...]\n"
288 " <filename>\n"
289 " Tries to repair corrupted disk images\n"
290 "\n")
291 : "",
292#ifdef RT_OS_WINDOWS
293 (enmCommand == USAGE_I_MODINSTALL || enmCommand == USAGE_I_ALL)
294 ? Internal::tr(
295 " modinstall\n"
296 " Installs the necessary driver for the host OS\n"
297 "\n")
298 : "",
299 (enmCommand == USAGE_I_MODUNINSTALL || enmCommand == USAGE_I_ALL)
300 ? Internal::tr(
301 " moduninstall\n"
302 " Deinstalls the driver\n"
303 "\n")
304 : "",
305#else
306 "",
307 "",
308#endif
309 (enmCommand == USAGE_I_DEBUGLOG || enmCommand == USAGE_I_ALL)
310 ? Internal::tr(
311 " debuglog <vmname|uuid> [--enable|--disable] [--flags todo]\n"
312 " [--groups todo] [--destinations todo]\n"
313 " Controls debug logging.\n"
314 "\n")
315 : "",
316 (enmCommand == USAGE_I_PASSWORDHASH || enmCommand == USAGE_I_ALL)
317 ? Internal::tr(
318 " passwordhash <password>\n"
319 " Generates a password hash.\n"
320 "\n")
321 : "",
322 (enmCommand == USAGE_I_GUESTSTATS || enmCommand == USAGE_I_ALL)
323 ? Internal::tr(
324 " gueststats <vmname|uuid> [--interval <seconds>]\n"
325 " Obtains and prints internal guest statistics.\n"
326 " Sets the update interval if specified.\n"
327 "\n")
328 : ""
329 );
330}
331
332
333/**
334 * Print a usage synopsis and the syntax error message.
335 * @returns RTEXITCODE_SYNTAX.
336 */
337static RTEXITCODE errorSyntaxInternal(USAGECATEGORY enmCommand, const char *pszFormat, ...)
338{
339 va_list args;
340 showLogo(g_pStdErr); // show logo even if suppressed
341
342 printUsageInternal(enmCommand, g_pStdErr);
343
344 va_start(args, pszFormat);
345 RTStrmPrintf(g_pStdErr, Internal::tr("\nSyntax error: %N\n"), pszFormat, &args);
346 va_end(args);
347 return RTEXITCODE_SYNTAX;
348}
349
350
351/**
352 * errorSyntaxInternal for RTGetOpt users.
353 *
354 * @returns RTEXITCODE_SYNTAX.
355 *
356 * @param enmCommand The command.
357 * @param vrc The RTGetOpt return code.
358 * @param pValueUnion The value union.
359 */
360static RTEXITCODE errorGetOptInternal(USAGECATEGORY enmCommand, int vrc, union RTGETOPTUNION const *pValueUnion)
361{
362 /*
363 * Check if it is an unhandled standard option.
364 */
365 if (vrc == 'V')
366 {
367 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
368 return RTEXITCODE_SUCCESS;
369 }
370
371 if (vrc == 'h')
372 {
373 showLogo(g_pStdErr);
374 printUsageInternal(enmCommand, g_pStdOut);
375 return RTEXITCODE_SUCCESS;
376 }
377
378 /*
379 * General failure.
380 */
381 showLogo(g_pStdErr); // show logo even if suppressed
382
383 printUsageInternal(enmCommand, g_pStdErr);
384
385 if (vrc == VINF_GETOPT_NOT_OPTION)
386 return RTMsgErrorExit(RTEXITCODE_SYNTAX, Internal::tr("Invalid parameter '%s'"), pValueUnion->psz);
387 if (vrc > 0)
388 {
389 if (RT_C_IS_PRINT(vrc))
390 return RTMsgErrorExit(RTEXITCODE_SYNTAX, Internal::tr("Invalid option -%c"), vrc);
391 return RTMsgErrorExit(RTEXITCODE_SYNTAX, Internal::tr("Invalid option case %i"), vrc);
392 }
393 if (vrc == VERR_GETOPT_UNKNOWN_OPTION)
394 return RTMsgErrorExit(RTEXITCODE_SYNTAX, Internal::tr("Unknown option: %s"), pValueUnion->psz);
395 if (vrc == VERR_GETOPT_INVALID_ARGUMENT_FORMAT)
396 return RTMsgErrorExit(RTEXITCODE_SYNTAX, Internal::tr("Invalid argument format: %s"), pValueUnion->psz);
397 if (pValueUnion->pDef)
398 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "%s: %Rrs", pValueUnion->pDef->pszLong, vrc);
399 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "%Rrs", vrc);
400}
401
402
403/**
404 * Externally visible wrapper around printUsageInternal() to dump the
405 * complete usage text.
406 *
407 * @param pStrm The stream to dump the usage text to.
408 */
409DECLHIDDEN(void) printUsageInternalCmds(PRTSTREAM pStrm)
410{
411 printUsageInternal(USAGE_I_ALL, pStrm);
412}
413
414
415/** @todo this is no longer necessary, we can enumerate extra data */
416/**
417 * Finds a new unique key name.
418 *
419 * I don't think this is 100% race condition proof, but we assumes
420 * the user is not trying to push this point.
421 *
422 * @returns Result from the insert.
423 * @param pMachine The Machine object.
424 * @param pszKeyBase The base key.
425 * @param rKey Reference to the string object in which we will return the key.
426 */
427static HRESULT NewUniqueKey(ComPtr<IMachine> pMachine, const char *pszKeyBase, Utf8Str &rKey)
428{
429 Bstr KeyBase(pszKeyBase);
430 Bstr Keys;
431 HRESULT hrc = pMachine->GetExtraData(KeyBase.raw(), Keys.asOutParam());
432 if (FAILED(hrc))
433 return hrc;
434
435 /* if there are no keys, it's simple. */
436 if (Keys.isEmpty())
437 {
438 rKey = "1";
439 return pMachine->SetExtraData(KeyBase.raw(), Bstr(rKey).raw());
440 }
441
442 /* find a unique number - brute force rulez. */
443 Utf8Str KeysUtf8(Keys);
444 const char *pszKeys = RTStrStripL(KeysUtf8.c_str());
445 for (unsigned i = 1; i < 1000000; i++)
446 {
447 char szKey[32];
448 size_t cchKey = RTStrPrintf(szKey, sizeof(szKey), "%#x", i);
449 const char *psz = strstr(pszKeys, szKey);
450 while (psz)
451 {
452 if ( ( psz == pszKeys
453 || psz[-1] == ' ')
454 && ( psz[cchKey] == ' '
455 || !psz[cchKey])
456 )
457 break;
458 psz = strstr(psz + cchKey, szKey);
459 }
460 if (!psz)
461 {
462 rKey = szKey;
463 Utf8StrFmt NewKeysUtf8("%s %s", pszKeys, szKey);
464 return pMachine->SetExtraData(KeyBase.raw(),
465 Bstr(NewKeysUtf8).raw());
466 }
467 }
468 RTMsgError(Internal::tr("Cannot find unique key for '%s'!"), pszKeyBase);
469 return E_FAIL;
470}
471
472
473#if 0
474/**
475 * Remove a key.
476 *
477 * I don't think this isn't 100% race condition proof, but we assumes
478 * the user is not trying to push this point.
479 *
480 * @returns Result from the insert.
481 * @param pMachine The machine object.
482 * @param pszKeyBase The base key.
483 * @param pszKey The key to remove.
484 */
485static HRESULT RemoveKey(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey)
486{
487 Bstr Keys;
488 HRESULT hrc = pMachine->GetExtraData(Bstr(pszKeyBase), Keys.asOutParam());
489 if (FAILED(hrc))
490 return hrc;
491
492 /* if there are no keys, it's simple. */
493 if (Keys.isEmpty())
494 return S_OK;
495
496 char *pszKeys;
497 int vrc = RTUtf16ToUtf8(Keys.raw(), &pszKeys);
498 if (RT_SUCCESS(vrc))
499 {
500 /* locate it */
501 size_t cchKey = strlen(pszKey);
502 char *psz = strstr(pszKeys, pszKey);
503 while (psz)
504 {
505 if ( ( psz == pszKeys
506 || psz[-1] == ' ')
507 && ( psz[cchKey] == ' '
508 || !psz[cchKey])
509 )
510 break;
511 psz = strstr(psz + cchKey, pszKey);
512 }
513 if (psz)
514 {
515 /* remove it */
516 char *pszNext = RTStrStripL(psz + cchKey);
517 if (*pszNext)
518 memmove(psz, pszNext, strlen(pszNext) + 1);
519 else
520 *psz = '\0';
521 psz = RTStrStrip(pszKeys);
522
523 /* update */
524 hrc = pMachine->SetExtraData(Bstr(pszKeyBase), Bstr(psz));
525 }
526
527 RTStrFree(pszKeys);
528 return hrc;
529 }
530 else
531 RTMsgError(Internal::tr("Failed to delete key '%s' from '%s', string conversion error %Rrc!"),
532 pszKey, pszKeyBase, vrc);
533
534 return E_FAIL;
535}
536#endif
537
538
539/**
540 * Sets a key value, does necessary error bitching.
541 *
542 * @returns COM status code.
543 * @param pMachine The Machine object.
544 * @param pszKeyBase The key base.
545 * @param pszKey The key.
546 * @param pszAttribute The attribute name.
547 * @param pszValue The string value.
548 */
549static HRESULT SetString(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey, const char *pszAttribute, const char *pszValue)
550{
551 HRESULT hrc = pMachine->SetExtraData(BstrFmt("%s/%s/%s", pszKeyBase,
552 pszKey, pszAttribute).raw(),
553 Bstr(pszValue).raw());
554 if (FAILED(hrc))
555 RTMsgError(Internal::tr("Failed to set '%s/%s/%s' to '%s'! hrc=%#x"),
556 pszKeyBase, pszKey, pszAttribute, pszValue, hrc);
557 return hrc;
558}
559
560
561/**
562 * Sets a key value, does necessary error bitching.
563 *
564 * @returns COM status code.
565 * @param pMachine The Machine object.
566 * @param pszKeyBase The key base.
567 * @param pszKey The key.
568 * @param pszAttribute The attribute name.
569 * @param u64Value The value.
570 */
571static HRESULT SetUInt64(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey, const char *pszAttribute, uint64_t u64Value)
572{
573 char szValue[64];
574 RTStrPrintf(szValue, sizeof(szValue), "%#RX64", u64Value);
575 return SetString(pMachine, pszKeyBase, pszKey, pszAttribute, szValue);
576}
577
578
579/**
580 * Sets a key value, does necessary error bitching.
581 *
582 * @returns COM status code.
583 * @param pMachine The Machine object.
584 * @param pszKeyBase The key base.
585 * @param pszKey The key.
586 * @param pszAttribute The attribute name.
587 * @param i64Value The value.
588 */
589static HRESULT SetInt64(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey, const char *pszAttribute, int64_t i64Value)
590{
591 char szValue[64];
592 RTStrPrintf(szValue, sizeof(szValue), "%RI64", i64Value);
593 return SetString(pMachine, pszKeyBase, pszKey, pszAttribute, szValue);
594}
595
596
597/**
598 * Identical to the 'loadsyms' command.
599 */
600static RTEXITCODE CmdLoadSyms(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
601{
602 RT_NOREF(aSession);
603 HRESULT hrc;
604
605 /*
606 * Get the VM
607 */
608 ComPtr<IMachine> machine;
609 CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
610 machine.asOutParam()), RTEXITCODE_FAILURE);
611
612 /*
613 * Parse the command.
614 */
615 const char *pszFilename;
616 int64_t offDelta = 0;
617 const char *pszModule = NULL;
618 uint64_t ModuleAddress = UINT64_MAX;
619 uint64_t ModuleSize = 0;
620
621 /* filename */
622 if (argc < 2)
623 return errorArgument(Internal::tr("Missing the filename argument!\n"));
624 pszFilename = argv[1];
625
626 /* offDelta */
627 if (argc >= 3)
628 {
629 int vrc = RTStrToInt64Ex(argv[2], NULL, 0, &offDelta);
630 if (RT_FAILURE(vrc))
631 return errorArgument(argv[0], Internal::tr("Failed to read delta '%s', vrc=%Rrc\n"), argv[2], vrc);
632 }
633
634 /* pszModule */
635 if (argc >= 4)
636 pszModule = argv[3];
637
638 /* ModuleAddress */
639 if (argc >= 5)
640 {
641 int vrc = RTStrToUInt64Ex(argv[4], NULL, 0, &ModuleAddress);
642 if (RT_FAILURE(vrc))
643 return errorArgument(argv[0], Internal::tr("Failed to read module address '%s', vrc=%Rrc\n"), argv[4], vrc);
644 }
645
646 /* ModuleSize */
647 if (argc >= 6)
648 {
649 int vrc = RTStrToUInt64Ex(argv[5], NULL, 0, &ModuleSize);
650 if (RT_FAILURE(vrc))
651 return errorArgument(argv[0], Internal::tr("Failed to read module size '%s', vrc=%Rrc\n"), argv[5], vrc);
652 }
653
654 /*
655 * Add extra data.
656 */
657 Utf8Str KeyStr;
658 hrc = NewUniqueKey(machine, "VBoxInternal/DBGF/loadsyms", KeyStr);
659 if (SUCCEEDED(hrc))
660 hrc = SetString(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "Filename", pszFilename);
661 if (SUCCEEDED(hrc) && argc >= 3)
662 hrc = SetInt64(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "Delta", offDelta);
663 if (SUCCEEDED(hrc) && argc >= 4)
664 hrc = SetString(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "Module", pszModule);
665 if (SUCCEEDED(hrc) && argc >= 5)
666 hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "ModuleAddress", ModuleAddress);
667 if (SUCCEEDED(hrc) && argc >= 6)
668 hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "ModuleSize", ModuleSize);
669
670 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
671}
672
673
674/**
675 * Identical to the 'loadmap' command.
676 */
677static RTEXITCODE CmdLoadMap(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
678{
679 RT_NOREF(aSession);
680 HRESULT hrc;
681
682 /*
683 * Get the VM
684 */
685 ComPtr<IMachine> machine;
686 CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
687 machine.asOutParam()), RTEXITCODE_FAILURE);
688
689 /*
690 * Parse the command.
691 */
692 const char *pszFilename;
693 uint64_t ModuleAddress = UINT64_MAX;
694 const char *pszModule = NULL;
695 uint64_t offSubtrahend = 0;
696 uint32_t iSeg = UINT32_MAX;
697
698 /* filename */
699 if (argc < 2)
700 return errorArgument(Internal::tr("Missing the filename argument!\n"));
701 pszFilename = argv[1];
702
703 /* address */
704 if (argc < 3)
705 return errorArgument(Internal::tr("Missing the module address argument!\n"));
706 int vrc = RTStrToUInt64Ex(argv[2], NULL, 0, &ModuleAddress);
707 if (RT_FAILURE(vrc))
708 return errorArgument(argv[0], Internal::tr("Failed to read module address '%s', vrc=%Rrc\n"), argv[2], vrc);
709
710 /* name (optional) */
711 if (argc > 3)
712 pszModule = argv[3];
713
714 /* subtrahend (optional) */
715 if (argc > 4)
716 {
717 vrc = RTStrToUInt64Ex(argv[4], NULL, 0, &offSubtrahend);
718 if (RT_FAILURE(vrc))
719 return errorArgument(argv[0], Internal::tr("Failed to read subtrahend '%s', vrc=%Rrc\n"), argv[4], vrc);
720 }
721
722 /* segment (optional) */
723 if (argc > 5)
724 {
725 vrc = RTStrToUInt32Ex(argv[5], NULL, 0, &iSeg);
726 if (RT_FAILURE(vrc))
727 return errorArgument(argv[0], Internal::tr("Failed to read segment number '%s', vrc=%Rrc\n"), argv[5], vrc);
728 }
729
730 /*
731 * Add extra data.
732 */
733 Utf8Str KeyStr;
734 hrc = NewUniqueKey(machine, "VBoxInternal/DBGF/loadmap", KeyStr);
735 if (SUCCEEDED(hrc))
736 hrc = SetString(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Filename", pszFilename);
737 if (SUCCEEDED(hrc))
738 hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Address", ModuleAddress);
739 if (SUCCEEDED(hrc) && pszModule != NULL)
740 hrc = SetString(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Name", pszModule);
741 if (SUCCEEDED(hrc) && offSubtrahend != 0)
742 hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Subtrahend", offSubtrahend);
743 if (SUCCEEDED(hrc) && iSeg != UINT32_MAX)
744 hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Segment", iSeg);
745
746 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
747}
748
749
750static DECLCALLBACK(void) handleVDError(void *pvUser, int vrc, RT_SRC_POS_DECL, const char *pszFormat, va_list va)
751{
752 RT_NOREF(pvUser);
753 RTMsgErrorV(pszFormat, va);
754 RTMsgError(Internal::tr("Error code %Rrc at %s(%u) in function %s"), vrc, RT_SRC_POS_ARGS);
755}
756
757static DECLCALLBACK(int) handleVDMessage(void *pvUser, const char *pszFormat, va_list va)
758{
759 NOREF(pvUser);
760 return RTPrintfV(pszFormat, va);
761}
762
763static RTEXITCODE CmdSetHDUUID(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
764{
765 RT_NOREF(aVirtualBox, aSession);
766 Guid uuid;
767 RTUUID rtuuid;
768 enum eUuidType {
769 HDUUID,
770 HDPARENTUUID
771 } uuidType;
772
773 if (!strcmp(argv[0], "sethduuid"))
774 {
775 uuidType = HDUUID;
776 if (argc != 3 && argc != 2)
777 return errorSyntaxInternal(USAGE_I_SETHDUUID, Internal::tr("Not enough parameters"));
778 /* if specified, take UUID, otherwise generate a new one */
779 if (argc == 3)
780 {
781 if (RT_FAILURE(RTUuidFromStr(&rtuuid, argv[2])))
782 return errorSyntaxInternal(USAGE_I_SETHDUUID, Internal::tr("Invalid UUID parameter"));
783 uuid = argv[2];
784 } else
785 uuid.create();
786 }
787 else if (!strcmp(argv[0], "sethdparentuuid"))
788 {
789 uuidType = HDPARENTUUID;
790 if (argc != 3)
791 return errorSyntaxInternal(USAGE_I_SETHDPARENTUUID, Internal::tr("Not enough parameters"));
792 if (RT_FAILURE(RTUuidFromStr(&rtuuid, argv[2])))
793 return errorSyntaxInternal(USAGE_I_SETHDPARENTUUID, Internal::tr("Invalid UUID parameter"));
794 uuid = argv[2];
795 }
796 else
797 return errorSyntaxInternal(USAGE_I_SETHDUUID, Internal::tr("Invalid invocation"));
798
799 /* just try it */
800 char *pszFormat = NULL;
801 VDTYPE enmType = VDTYPE_INVALID;
802 int vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */, argv[1], VDTYPE_INVALID, &pszFormat, &enmType);
803 if (RT_FAILURE(vrc))
804 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Format autodetect failed: %Rrc"), vrc);
805
806 PVDISK pDisk = NULL;
807
808 PVDINTERFACE pVDIfs = NULL;
809 VDINTERFACEERROR vdInterfaceError;
810 vdInterfaceError.pfnError = handleVDError;
811 vdInterfaceError.pfnMessage = handleVDMessage;
812
813 vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
814 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
815 AssertRC(vrc);
816
817 vrc = VDCreate(pVDIfs, enmType, &pDisk);
818 if (RT_FAILURE(vrc))
819 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot create the virtual disk container: %Rrc"), vrc);
820
821 /* Open the image */
822 vrc = VDOpen(pDisk, pszFormat, argv[1], VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_INFO, NULL);
823 if (RT_FAILURE(vrc))
824 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot open the image: %Rrc"), vrc);
825
826 if (uuidType == HDUUID)
827 vrc = VDSetUuid(pDisk, VD_LAST_IMAGE, uuid.raw());
828 else
829 vrc = VDSetParentUuid(pDisk, VD_LAST_IMAGE, uuid.raw());
830 if (RT_FAILURE(vrc))
831 RTMsgError(Internal::tr("Cannot set a new UUID: %Rrc"), vrc);
832 else
833 RTPrintf(Internal::tr("UUID changed to: %s\n"), uuid.toString().c_str());
834
835 VDCloseAll(pDisk);
836
837 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
838}
839
840
841static RTEXITCODE CmdDumpHDInfo(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
842{
843 RT_NOREF(aVirtualBox, aSession);
844
845 /* we need exactly one parameter: the image file */
846 if (argc != 1)
847 {
848 return errorSyntaxInternal(USAGE_I_DUMPHDINFO, Internal::tr("Not enough parameters"));
849 }
850
851 /* just try it */
852 char *pszFormat = NULL;
853 VDTYPE enmType = VDTYPE_INVALID;
854 int vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */, argv[0], VDTYPE_INVALID, &pszFormat, &enmType);
855 if (RT_FAILURE(vrc))
856 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Format autodetect failed: %Rrc"), vrc);
857
858 PVDISK pDisk = NULL;
859
860 PVDINTERFACE pVDIfs = NULL;
861 VDINTERFACEERROR vdInterfaceError;
862 vdInterfaceError.pfnError = handleVDError;
863 vdInterfaceError.pfnMessage = handleVDMessage;
864
865 vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
866 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
867 AssertRC(vrc);
868
869 vrc = VDCreate(pVDIfs, enmType, &pDisk);
870 if (RT_FAILURE(vrc))
871 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot create the virtual disk container: %Rrc"), vrc);
872
873 /* Open the image */
874 vrc = VDOpen(pDisk, pszFormat, argv[0], VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO, NULL);
875 if (RT_FAILURE(vrc))
876 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot open the image: %Rrc"), vrc);
877
878 VDDumpImages(pDisk);
879
880 VDCloseAll(pDisk);
881
882 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
883}
884
885static int partRead(RTFILE File, PHOSTPARTITIONS pPart)
886{
887 uint8_t aBuffer[512];
888 uint8_t partitionTableHeader[512];
889 uint32_t sector_size = 512;
890 uint64_t lastUsableLBA = 0;
891
892 VDISKPARTTYPE partitioningType;
893
894 pPart->cPartitions = 0;
895 memset(pPart->aPartitions, '\0', sizeof(pPart->aPartitions));
896
897 int vrc = RTFileReadAt(File, 0, &aBuffer, sizeof(aBuffer), NULL);
898 if (RT_FAILURE(vrc))
899 return vrc;
900
901 if (aBuffer[450] == 0xEE)/* check the sign of the GPT disk*/
902 {
903 partitioningType = VDISKPARTTYPE_GPT;
904 pPart->uPartitioningType = VDISKPARTTYPE_GPT;//partitioningType;
905
906 if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa)
907 return VERR_INVALID_PARAMETER;
908
909 vrc = RTFileReadAt(File, sector_size, &partitionTableHeader, sector_size, NULL);
910 if (RT_SUCCESS(vrc))
911 {
912 /** @todo r=bird: This is a 64-bit magic value, right... */
913 const char *l_ppth = (char *)partitionTableHeader;
914 if (strncmp(l_ppth, "EFI PART", 8))
915 return VERR_INVALID_PARAMETER;
916
917 /** @todo check GPT Version */
918
919 /** @todo r=bird: C have this handy concept called structures which
920 * greatly simplify data access... (Someone is really lazy here!) */
921#if 0 /* unused */
922 uint64_t firstUsableLBA = RT_MAKE_U64_FROM_U8(partitionTableHeader[40],
923 partitionTableHeader[41],
924 partitionTableHeader[42],
925 partitionTableHeader[43],
926 partitionTableHeader[44],
927 partitionTableHeader[45],
928 partitionTableHeader[46],
929 partitionTableHeader[47]
930 );
931#endif
932 lastUsableLBA = RT_MAKE_U64_FROM_U8(partitionTableHeader[48],
933 partitionTableHeader[49],
934 partitionTableHeader[50],
935 partitionTableHeader[51],
936 partitionTableHeader[52],
937 partitionTableHeader[53],
938 partitionTableHeader[54],
939 partitionTableHeader[55]
940 );
941 uint32_t partitionsNumber = RT_MAKE_U32_FROM_U8(partitionTableHeader[80],
942 partitionTableHeader[81],
943 partitionTableHeader[82],
944 partitionTableHeader[83]
945 );
946 uint32_t partitionEntrySize = RT_MAKE_U32_FROM_U8(partitionTableHeader[84],
947 partitionTableHeader[85],
948 partitionTableHeader[86],
949 partitionTableHeader[87]
950 );
951
952 uint32_t currentEntry = 0;
953
954 if (partitionEntrySize * partitionsNumber > 4 * _1M)
955 {
956 RTMsgError(Internal::tr("The GPT header seems corrupt because it contains too many entries"));
957 return VERR_INVALID_PARAMETER;
958 }
959
960 uint8_t *pbPartTable = (uint8_t *)RTMemAllocZ(RT_ALIGN_Z(partitionEntrySize * partitionsNumber, 512));
961 if (!pbPartTable)
962 {
963 RTMsgError(Internal::tr("Allocating memory for the GPT partitions entries failed"));
964 return VERR_NO_MEMORY;
965 }
966
967 /* partition entries begin from LBA2 */
968 /** @todo r=aeichner: Reading from LBA 2 is not always correct, the header will contain the starting LBA. */
969 vrc = RTFileReadAt(File, 1024, pbPartTable, RT_ALIGN_Z(partitionEntrySize * partitionsNumber, 512), NULL);
970 if (RT_FAILURE(vrc))
971 {
972 RTMsgError(Internal::tr("Reading the partition table failed"));
973 RTMemFree(pbPartTable);
974 return vrc;
975 }
976
977 while (currentEntry < partitionsNumber)
978 {
979 uint8_t *partitionEntry = pbPartTable + currentEntry * partitionEntrySize;
980
981 uint64_t start = RT_MAKE_U64_FROM_U8(partitionEntry[32], partitionEntry[33], partitionEntry[34], partitionEntry[35],
982 partitionEntry[36], partitionEntry[37], partitionEntry[38], partitionEntry[39]);
983 uint64_t end = RT_MAKE_U64_FROM_U8(partitionEntry[40], partitionEntry[41], partitionEntry[42], partitionEntry[43],
984 partitionEntry[44], partitionEntry[45], partitionEntry[46], partitionEntry[47]);
985
986 PHOSTPARTITION pCP = &pPart->aPartitions[pPart->cPartitions++];
987 pCP->uIndex = currentEntry + 1;
988 pCP->uIndexWin = currentEntry + 1;
989 pCP->uType = 0;
990 pCP->uStartCylinder = 0;
991 pCP->uStartHead = 0;
992 pCP->uStartSector = 0;
993 pCP->uEndCylinder = 0;
994 pCP->uEndHead = 0;
995 pCP->uEndSector = 0;
996 pCP->uPartDataStart = 0; /* will be filled out later properly. */
997 pCP->cPartDataSectors = 0;
998 if (start==0 || end==0)
999 {
1000 pCP->uIndex = 0;
1001 pCP->uIndexWin = 0;
1002 --pPart->cPartitions;
1003 break;
1004 }
1005 else
1006 {
1007 pCP->uStart = start;
1008 pCP->uSize = (end +1) - start;/*+1 LBA because the last address is included*/
1009 }
1010
1011 ++currentEntry;
1012 }
1013
1014 RTMemFree(pbPartTable);
1015 }
1016 }
1017 else
1018 {
1019 partitioningType = VDISKPARTTYPE_MBR;
1020 pPart->uPartitioningType = VDISKPARTTYPE_MBR;//partitioningType;
1021
1022 if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa)
1023 return VERR_INVALID_PARAMETER;
1024
1025 unsigned uExtended = (unsigned)-1;
1026 unsigned uIndexWin = 1;
1027
1028 for (unsigned i = 0; i < 4; i++)
1029 {
1030 uint8_t *p = &aBuffer[0x1be + i * 16];
1031 if (p[4] == 0)
1032 continue;
1033 PHOSTPARTITION pCP = &pPart->aPartitions[pPart->cPartitions++];
1034 pCP->uIndex = i + 1;
1035 pCP->uType = p[4];
1036 pCP->uStartCylinder = (uint32_t)p[3] + ((uint32_t)(p[2] & 0xc0) << 2);
1037 pCP->uStartHead = p[1];
1038 pCP->uStartSector = p[2] & 0x3f;
1039 pCP->uEndCylinder = (uint32_t)p[7] + ((uint32_t)(p[6] & 0xc0) << 2);
1040 pCP->uEndHead = p[5];
1041 pCP->uEndSector = p[6] & 0x3f;
1042 pCP->uStart = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
1043 pCP->uSize = RT_MAKE_U32_FROM_U8(p[12], p[13], p[14], p[15]);
1044 pCP->uPartDataStart = 0; /* will be filled out later properly. */
1045 pCP->cPartDataSectors = 0;
1046
1047 if (PARTTYPE_IS_EXTENDED(p[4]))
1048 {
1049 if (uExtended == (unsigned)-1)
1050 {
1051 uExtended = (unsigned)(pCP - pPart->aPartitions);
1052 pCP->uIndexWin = 0;
1053 }
1054 else
1055 {
1056 RTMsgError(Internal::tr("More than one extended partition"));
1057 return VERR_INVALID_PARAMETER;
1058 }
1059 }
1060 else
1061 {
1062 pCP->uIndexWin = uIndexWin;
1063 uIndexWin++;
1064 }
1065 }
1066
1067 if (uExtended != (unsigned)-1)
1068 {
1069 unsigned uIndex = 5;
1070 uint64_t uStart = pPart->aPartitions[uExtended].uStart;
1071 uint64_t uOffset = 0;
1072 if (!uStart)
1073 {
1074 RTMsgError(Internal::tr("Inconsistency for logical partition start"));
1075 return VERR_INVALID_PARAMETER;
1076 }
1077
1078 do
1079 {
1080 vrc = RTFileReadAt(File, (uStart + uOffset) * 512, &aBuffer, sizeof(aBuffer), NULL);
1081 if (RT_FAILURE(vrc))
1082 return vrc;
1083
1084 if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa)
1085 {
1086 RTMsgError(Internal::tr("Logical partition without magic"));
1087 return VERR_INVALID_PARAMETER;
1088 }
1089 uint8_t *p = &aBuffer[0x1be];
1090
1091 if (p[4] == 0)
1092 {
1093 RTMsgError(Internal::tr("Logical partition with type 0 encountered"));
1094 return VERR_INVALID_PARAMETER;
1095 }
1096
1097 PHOSTPARTITION pCP = &pPart->aPartitions[pPart->cPartitions++];
1098 pCP->uIndex = uIndex;
1099 pCP->uIndexWin = uIndexWin;
1100 pCP->uType = p[4];
1101 pCP->uStartCylinder = (uint32_t)p[3] + ((uint32_t)(p[2] & 0xc0) << 2);
1102 pCP->uStartHead = p[1];
1103 pCP->uStartSector = p[2] & 0x3f;
1104 pCP->uEndCylinder = (uint32_t)p[7] + ((uint32_t)(p[6] & 0xc0) << 2);
1105 pCP->uEndHead = p[5];
1106 pCP->uEndSector = p[6] & 0x3f;
1107 uint32_t uStartOffset = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
1108 if (!uStartOffset)
1109 {
1110 RTMsgError(Internal::tr("Invalid partition start offset"));
1111 return VERR_INVALID_PARAMETER;
1112 }
1113 pCP->uStart = uStart + uOffset + uStartOffset;
1114 pCP->uSize = RT_MAKE_U32_FROM_U8(p[12], p[13], p[14], p[15]);
1115 /* Fill out partitioning location info for EBR. */
1116 pCP->uPartDataStart = uStart + uOffset;
1117 pCP->cPartDataSectors = uStartOffset;
1118 p += 16;
1119 if (p[4] == 0)
1120 uExtended = (unsigned)-1;
1121 else if (PARTTYPE_IS_EXTENDED(p[4]))
1122 {
1123 uExtended = uIndex;
1124 uIndex++;
1125 uIndexWin++;
1126 uOffset = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
1127 }
1128 else
1129 {
1130 RTMsgError(Internal::tr("Logical partition chain broken"));
1131 return VERR_INVALID_PARAMETER;
1132 }
1133 } while (uExtended != (unsigned)-1);
1134 }
1135 }
1136
1137
1138 /* Sort partitions in ascending order of start sector, plus a trivial
1139 * bit of consistency checking. */
1140 for (unsigned i = 0; i < pPart->cPartitions-1; i++)
1141 {
1142 unsigned uMinIdx = i;
1143 uint64_t uMinVal = pPart->aPartitions[i].uStart;
1144 for (unsigned j = i + 1; j < pPart->cPartitions; j++)
1145 {
1146 if (pPart->aPartitions[j].uStart < uMinVal)
1147 {
1148 uMinIdx = j;
1149 uMinVal = pPart->aPartitions[j].uStart;
1150 }
1151 else if (pPart->aPartitions[j].uStart == uMinVal)
1152 {
1153 RTMsgError(Internal::tr("Two partitions start at the same place"));
1154 return VERR_INVALID_PARAMETER;
1155 }
1156 else if (pPart->aPartitions[j].uStart == 0)
1157 {
1158 RTMsgError(Internal::tr("Partition starts at sector 0"));
1159 return VERR_INVALID_PARAMETER;
1160 }
1161 }
1162 if (uMinIdx != i)
1163 {
1164 /* Swap entries at index i and uMinIdx. */
1165 memcpy(&pPart->aPartitions[pPart->cPartitions],
1166 &pPart->aPartitions[i], sizeof(HOSTPARTITION));
1167 memcpy(&pPart->aPartitions[i],
1168 &pPart->aPartitions[uMinIdx], sizeof(HOSTPARTITION));
1169 memcpy(&pPart->aPartitions[uMinIdx],
1170 &pPart->aPartitions[pPart->cPartitions], sizeof(HOSTPARTITION));
1171 }
1172 }
1173
1174 /* Fill out partitioning location info for MBR or GPT. */
1175 pPart->aPartitions[0].uPartDataStart = 0;
1176 pPart->aPartitions[0].cPartDataSectors = pPart->aPartitions[0].uStart;
1177
1178 /* Fill out partitioning location info for backup GPT. */
1179 if (partitioningType == VDISKPARTTYPE_GPT)
1180 {
1181 pPart->aPartitions[pPart->cPartitions-1].uPartDataStart = lastUsableLBA+1;
1182 pPart->aPartitions[pPart->cPartitions-1].cPartDataSectors = 33;
1183
1184 /* Now do a some partition table consistency checking, to reject the most
1185 * obvious garbage which can lead to trouble later. */
1186 uint64_t uPrevEnd = 0;
1187 for (unsigned i = 0; i < pPart->cPartitions; i++)
1188 {
1189 if (pPart->aPartitions[i].cPartDataSectors)
1190 uPrevEnd = pPart->aPartitions[i].uPartDataStart + pPart->aPartitions[i].cPartDataSectors;
1191 if (pPart->aPartitions[i].uStart < uPrevEnd &&
1192 pPart->cPartitions-1 != i)
1193 {
1194 RTMsgError(Internal::tr("Overlapping GPT partitions"));
1195 return VERR_INVALID_PARAMETER;
1196 }
1197 }
1198 }
1199 else
1200 {
1201 /* Now do a some partition table consistency checking, to reject the most
1202 * obvious garbage which can lead to trouble later. */
1203 uint64_t uPrevEnd = 0;
1204 for (unsigned i = 0; i < pPart->cPartitions; i++)
1205 {
1206 if (pPart->aPartitions[i].cPartDataSectors)
1207 uPrevEnd = pPart->aPartitions[i].uPartDataStart + pPart->aPartitions[i].cPartDataSectors;
1208 if (pPart->aPartitions[i].uStart < uPrevEnd)
1209 {
1210 RTMsgError(Internal::tr("Overlapping MBR partitions"));
1211 return VERR_INVALID_PARAMETER;
1212 }
1213 if (!PARTTYPE_IS_EXTENDED(pPart->aPartitions[i].uType))
1214 uPrevEnd = pPart->aPartitions[i].uStart + pPart->aPartitions[i].uSize;
1215 }
1216 }
1217
1218 return VINF_SUCCESS;
1219}
1220
1221static RTEXITCODE CmdListPartitions(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1222{
1223 RT_NOREF(aVirtualBox, aSession);
1224 Utf8Str rawdisk;
1225
1226 /* let's have a closer look at the arguments */
1227 for (int i = 0; i < argc; i++)
1228 {
1229 if (strcmp(argv[i], "-rawdisk") == 0)
1230 {
1231 if (argc <= i + 1)
1232 {
1233 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1234 }
1235 i++;
1236 rawdisk = argv[i];
1237 }
1238 else
1239 {
1240 return errorSyntaxInternal(USAGE_I_LISTPARTITIONS, Internal::tr("Invalid parameter '%s'"), argv[i]);
1241 }
1242 }
1243
1244 if (rawdisk.isEmpty())
1245 return errorSyntaxInternal(USAGE_I_LISTPARTITIONS, Internal::tr("Mandatory parameter -rawdisk missing"));
1246
1247 RTFILE hRawFile;
1248 int vrc = RTFileOpen(&hRawFile, rawdisk.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1249 if (RT_FAILURE(vrc))
1250 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot open the raw disk: %Rrc"), vrc);
1251
1252 HOSTPARTITIONS partitions;
1253 vrc = partRead(hRawFile, &partitions);
1254 /* Don't bail out on errors, print the table and return the result code. */
1255
1256 RTPrintf(Internal::tr("Number Type StartCHS EndCHS Size (MiB) Start (Sect)\n"));
1257 for (unsigned i = 0; i < partitions.cPartitions; i++)
1258 {
1259 /* Don't show the extended partition, otherwise users might think they
1260 * can add it to the list of partitions for raw partition access. */
1261 if (PARTTYPE_IS_EXTENDED(partitions.aPartitions[i].uType))
1262 continue;
1263
1264 RTPrintf("%-7u %#04x %-4u/%-3u/%-2u %-4u/%-3u/%-2u %10llu %10llu\n",
1265 partitions.aPartitions[i].uIndex,
1266 partitions.aPartitions[i].uType,
1267 partitions.aPartitions[i].uStartCylinder,
1268 partitions.aPartitions[i].uStartHead,
1269 partitions.aPartitions[i].uStartSector,
1270 partitions.aPartitions[i].uEndCylinder,
1271 partitions.aPartitions[i].uEndHead,
1272 partitions.aPartitions[i].uEndSector,
1273 partitions.aPartitions[i].uSize / 2048,
1274 partitions.aPartitions[i].uStart);
1275 }
1276
1277 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1278}
1279
1280static const RTGETOPTDEF g_aCreateRawVMDKOptions[] =
1281{
1282 { "--filename", 'f', RTGETOPT_REQ_STRING },
1283 { "-filename", 'f', RTGETOPT_REQ_STRING },
1284 { "--rawdisk", 'd', RTGETOPT_REQ_STRING },
1285 { "-rawdisk", 'd', RTGETOPT_REQ_STRING },
1286 { "--partitions", 'p', RTGETOPT_REQ_STRING },
1287 { "-partitions", 'p', RTGETOPT_REQ_STRING },
1288 { "--mbr", 'm', RTGETOPT_REQ_STRING },
1289 { "-mbr", 'm', RTGETOPT_REQ_STRING },
1290#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_WINDOWS)
1291 { "--relative", 'r', RTGETOPT_REQ_NOTHING },
1292 { "-relative", 'r', RTGETOPT_REQ_NOTHING },
1293#endif /* RT_OS_LINUX || RT_OS_FREEBSD || RT_OS_WINDOWS */
1294};
1295
1296static RTEXITCODE CmdCreateRawVMDK(int argc, char **argv, HandlerArg *a)
1297{
1298 const char *pszFilename = NULL;
1299 const char *pszRawdisk = NULL;
1300 const char *pszPartitions = NULL;
1301 const char *pszMbr = NULL;
1302 bool fRelative = false;
1303 int c;
1304 RTGETOPTUNION ValueUnion;
1305 RTGETOPTSTATE GetState;
1306 RTGetOptInit(&GetState, argc, argv, g_aCreateRawVMDKOptions, RT_ELEMENTS(g_aCreateRawVMDKOptions), 0, 0);
1307 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1308 {
1309 switch (c)
1310 {
1311 case 'f': // --filename
1312 pszFilename = ValueUnion.psz;
1313 break;
1314
1315 case 'd': // --rawdisk
1316 pszRawdisk = ValueUnion.psz;
1317 break;
1318
1319 case 'p': // --partitions
1320 pszPartitions = ValueUnion.psz;
1321 break;
1322
1323 case 'm': // --mbr
1324 pszMbr = ValueUnion.psz;
1325 break;
1326#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_WINDOWS)
1327 case 'r': // --relative
1328 fRelative = true;
1329 break;
1330#endif /* RT_OS_LINUX || RT_OS_FREEBSD || RT_OS_WINDOWS */
1331
1332 default:
1333 return errorGetOptInternal(USAGE_I_CREATERAWVMDK, c, &ValueUnion);
1334 }
1335 }
1336
1337 if (!pszFilename || !*pszFilename)
1338 return errorSyntaxInternal(USAGE_I_CREATERAWVMDK, Internal::tr("Mandatory parameter --filename missing"));
1339 if (!pszRawdisk || !*pszRawdisk)
1340 return errorSyntaxInternal(USAGE_I_CREATERAWVMDK, Internal::tr("Mandatory parameter --rawdisk missing"));
1341 if (!pszPartitions && pszMbr)
1342 return errorSyntaxInternal(USAGE_I_CREATERAWVMDK,
1343 Internal::tr("The parameter --mbr is only valid when the parameter -partitions is also present"));
1344
1345 /* Construct the equivalent 'VBoxManage createmedium disk --variant RawDisk ...' command line. */
1346 size_t cMaxArgs = 9; /* all possible 'createmedium' args based on the 'createrawvmdk' options + 1 for NULL */
1347 char **papszNewArgv = (char **)RTMemAllocZ(sizeof(papszNewArgv[0]) * cMaxArgs);
1348 if (!papszNewArgv)
1349 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Failed to allocate memory for argument array"));
1350 int cArgs = 0;
1351
1352 papszNewArgv[cArgs++] = RTStrDup("disk");
1353 papszNewArgv[cArgs++] = RTStrDup("--variant=RawDisk");
1354 papszNewArgv[cArgs++] = RTStrDup("--format=VMDK");
1355
1356 for (int i = 0; i < cArgs; i++)
1357 if (!papszNewArgv[i])
1358 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Failed to allocate memory for argument array"));
1359
1360 if ( RTStrAPrintf(&papszNewArgv[cArgs++], "--filename=%s", pszFilename) == -1
1361 || RTStrAPrintf(&papszNewArgv[cArgs++], "--property=RawDrive=%s", pszRawdisk) == -1
1362 || (pszPartitions && (RTStrAPrintf(&papszNewArgv[cArgs++], "--property=Partitions=%s", pszPartitions) == -1))
1363 || (pszMbr && (RTStrAPrintf(&papszNewArgv[cArgs++], "--property-filename=%s", pszMbr) == -1))
1364 || (fRelative && (RTStrAPrintf(&papszNewArgv[cArgs++], "--property=Relative=%d", fRelative) == -1)))
1365 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Failed to allocate memory for argument array"));
1366
1367 papszNewArgv[cArgs] = NULL;
1368
1369 RTStrmPrintf(g_pStdErr,
1370 Internal::tr("\nThe 'createrawvdk' subcommand is deprecated. The equivalent functionality is\n"
1371 "available using the 'VBoxManage createmedium' command and should be used\n"
1372 "instead. See 'VBoxManage help createmedium' for details.\n\n"));
1373
1374 a->argc = cArgs;
1375 a->argv = papszNewArgv;
1376 RTEXITCODE rcExit = handleCreateMedium(a);
1377
1378 for (int i = 0; i < cArgs; i++)
1379 RTStrFree(papszNewArgv[i]);
1380 RTMemFree(papszNewArgv);
1381
1382 return rcExit;
1383}
1384
1385static RTEXITCODE CmdRenameVMDK(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1386{
1387 RT_NOREF(aVirtualBox, aSession);
1388 Utf8Str src;
1389 Utf8Str dst;
1390 /* Parse the arguments. */
1391 for (int i = 0; i < argc; i++)
1392 {
1393 if (strcmp(argv[i], "-from") == 0)
1394 {
1395 if (argc <= i + 1)
1396 {
1397 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1398 }
1399 i++;
1400 src = argv[i];
1401 }
1402 else if (strcmp(argv[i], "-to") == 0)
1403 {
1404 if (argc <= i + 1)
1405 {
1406 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1407 }
1408 i++;
1409 dst = argv[i];
1410 }
1411 else
1412 {
1413 return errorSyntaxInternal(USAGE_I_RENAMEVMDK, Internal::tr("Invalid parameter '%s'"), argv[i]);
1414 }
1415 }
1416
1417 if (src.isEmpty())
1418 return errorSyntaxInternal(USAGE_I_RENAMEVMDK, Internal::tr("Mandatory parameter -from missing"));
1419 if (dst.isEmpty())
1420 return errorSyntaxInternal(USAGE_I_RENAMEVMDK, Internal::tr("Mandatory parameter -to missing"));
1421
1422 PVDISK pDisk = NULL;
1423
1424 PVDINTERFACE pVDIfs = NULL;
1425 VDINTERFACEERROR vdInterfaceError;
1426 vdInterfaceError.pfnError = handleVDError;
1427 vdInterfaceError.pfnMessage = handleVDMessage;
1428
1429 int vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1430 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1431 AssertRC(vrc);
1432
1433 vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1434 if (RT_FAILURE(vrc))
1435 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot create the virtual disk container: %Rrc"), vrc);
1436
1437 vrc = VDOpen(pDisk, "VMDK", src.c_str(), VD_OPEN_FLAGS_NORMAL, NULL);
1438 if (RT_SUCCESS(vrc))
1439 {
1440 vrc = VDCopy(pDisk, 0, pDisk, "VMDK", dst.c_str(), true, 0,
1441 VD_IMAGE_FLAGS_NONE, NULL, VD_OPEN_FLAGS_NORMAL,
1442 NULL, NULL, NULL);
1443 if (RT_FAILURE(vrc))
1444 RTMsgError(Internal::tr("Cannot rename the image: %Rrc"), vrc);
1445 }
1446 else
1447 RTMsgError(Internal::tr("Cannot create the source image: %Rrc"), vrc);
1448 VDCloseAll(pDisk);
1449 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1450}
1451
1452static RTEXITCODE CmdConvertToRaw(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1453{
1454 RT_NOREF(aVirtualBox, aSession);
1455 Utf8Str srcformat;
1456 Utf8Str src;
1457 Utf8Str dst;
1458 bool fWriteToStdOut = false;
1459
1460 /* Parse the arguments. */
1461 for (int i = 0; i < argc; i++)
1462 {
1463 if (strcmp(argv[i], "-format") == 0)
1464 {
1465 if (argc <= i + 1)
1466 {
1467 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1468 }
1469 i++;
1470 srcformat = argv[i];
1471 }
1472 else if (src.isEmpty())
1473 {
1474 src = argv[i];
1475 }
1476 else if (dst.isEmpty())
1477 {
1478 dst = argv[i];
1479#ifdef ENABLE_CONVERT_RAW_TO_STDOUT
1480 if (!strcmp(argv[i], "stdout"))
1481 fWriteToStdOut = true;
1482#endif /* ENABLE_CONVERT_RAW_TO_STDOUT */
1483 }
1484 else
1485 {
1486 return errorSyntaxInternal(USAGE_I_CONVERTTORAW, Internal::tr("Invalid parameter '%s'"), argv[i]);
1487 }
1488 }
1489
1490 if (src.isEmpty())
1491 return errorSyntaxInternal(USAGE_I_CONVERTTORAW, Internal::tr("Mandatory filename parameter missing"));
1492 if (dst.isEmpty())
1493 return errorSyntaxInternal(USAGE_I_CONVERTTORAW, Internal::tr("Mandatory outputfile parameter missing"));
1494
1495 PVDISK pDisk = NULL;
1496
1497 PVDINTERFACE pVDIfs = NULL;
1498 VDINTERFACEERROR vdInterfaceError;
1499 vdInterfaceError.pfnError = handleVDError;
1500 vdInterfaceError.pfnMessage = handleVDMessage;
1501
1502 int vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1503 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1504 AssertRC(vrc);
1505
1506 /** @todo Support convert to raw for floppy and DVD images too. */
1507 vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1508 if (RT_FAILURE(vrc))
1509 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot create the virtual disk container: %Rrc"), vrc);
1510
1511 /* Open raw output file. */
1512 RTFILE outFile;
1513 vrc = VINF_SUCCESS;
1514 if (fWriteToStdOut)
1515 vrc = RTFileFromNative(&outFile, 1);
1516 else
1517 vrc = RTFileOpen(&outFile, dst.c_str(), RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
1518 if (RT_FAILURE(vrc))
1519 {
1520 VDCloseAll(pDisk);
1521 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot create destination file \"%s\": %Rrc"),
1522 dst.c_str(), vrc);
1523 }
1524
1525 if (srcformat.isEmpty())
1526 {
1527 char *pszFormat = NULL;
1528 VDTYPE enmType = VDTYPE_INVALID;
1529 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
1530 src.c_str(), VDTYPE_INVALID, &pszFormat, &enmType);
1531 if (RT_FAILURE(vrc) || enmType != VDTYPE_HDD)
1532 {
1533 VDCloseAll(pDisk);
1534 if (!fWriteToStdOut)
1535 {
1536 RTFileClose(outFile);
1537 RTFileDelete(dst.c_str());
1538 }
1539 if (RT_FAILURE(vrc))
1540 RTMsgError(Internal::tr("No file format specified and autodetect failed - please specify format: %Rrc"),
1541 vrc);
1542 else
1543 RTMsgError(Internal::tr("Only converting harddisk images is supported"));
1544 return RTEXITCODE_FAILURE;
1545 }
1546 srcformat = pszFormat;
1547 RTStrFree(pszFormat);
1548 }
1549 vrc = VDOpen(pDisk, srcformat.c_str(), src.c_str(), VD_OPEN_FLAGS_READONLY, NULL);
1550 if (RT_FAILURE(vrc))
1551 {
1552 VDCloseAll(pDisk);
1553 if (!fWriteToStdOut)
1554 {
1555 RTFileClose(outFile);
1556 RTFileDelete(dst.c_str());
1557 }
1558 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot open the source image: %Rrc"), vrc);
1559 }
1560
1561 uint64_t cbSize = VDGetSize(pDisk, VD_LAST_IMAGE);
1562 uint64_t offFile = 0;
1563#define RAW_BUFFER_SIZE _128K
1564 size_t cbBuf = RAW_BUFFER_SIZE;
1565 void *pvBuf = RTMemAlloc(cbBuf);
1566 if (pvBuf)
1567 {
1568 RTStrmPrintf(g_pStdErr, Internal::tr("Converting image \"%s\" with size %RU64 bytes (%RU64MB) to raw...\n", "", cbSize),
1569 src.c_str(), cbSize, (cbSize + _1M - 1) / _1M);
1570 while (offFile < cbSize)
1571 {
1572 size_t cb = (size_t)RT_MIN(cbSize - offFile, cbBuf);
1573 vrc = VDRead(pDisk, offFile, pvBuf, cb);
1574 if (RT_FAILURE(vrc))
1575 break;
1576 vrc = RTFileWrite(outFile, pvBuf, cb, NULL);
1577 if (RT_FAILURE(vrc))
1578 break;
1579 offFile += cb;
1580 }
1581 RTMemFree(pvBuf);
1582 if (RT_FAILURE(vrc))
1583 {
1584 VDCloseAll(pDisk);
1585 if (!fWriteToStdOut)
1586 {
1587 RTFileClose(outFile);
1588 RTFileDelete(dst.c_str());
1589 }
1590 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot copy image data: %Rrc"), vrc);
1591 }
1592 }
1593 else
1594 {
1595 vrc = VERR_NO_MEMORY;
1596 VDCloseAll(pDisk);
1597 if (!fWriteToStdOut)
1598 {
1599 RTFileClose(outFile);
1600 RTFileDelete(dst.c_str());
1601 }
1602 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Out of memory allocating read buffer"));
1603 }
1604
1605 if (!fWriteToStdOut)
1606 RTFileClose(outFile);
1607 VDCloseAll(pDisk);
1608 return RTEXITCODE_SUCCESS;
1609}
1610
1611static RTEXITCODE CmdConvertHardDisk(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1612{
1613 RT_NOREF(aVirtualBox, aSession);
1614 Utf8Str srcformat;
1615 Utf8Str dstformat;
1616 Utf8Str src;
1617 Utf8Str dst;
1618 int vrc;
1619 PVDISK pSrcDisk = NULL;
1620 PVDISK pDstDisk = NULL;
1621 VDTYPE enmSrcType = VDTYPE_INVALID;
1622
1623 /* Parse the arguments. */
1624 for (int i = 0; i < argc; i++)
1625 {
1626 if (strcmp(argv[i], "-srcformat") == 0)
1627 {
1628 if (argc <= i + 1)
1629 {
1630 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1631 }
1632 i++;
1633 srcformat = argv[i];
1634 }
1635 else if (strcmp(argv[i], "-dstformat") == 0)
1636 {
1637 if (argc <= i + 1)
1638 {
1639 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1640 }
1641 i++;
1642 dstformat = argv[i];
1643 }
1644 else if (src.isEmpty())
1645 {
1646 src = argv[i];
1647 }
1648 else if (dst.isEmpty())
1649 {
1650 dst = argv[i];
1651 }
1652 else
1653 {
1654 return errorSyntaxInternal(USAGE_I_CONVERTHD, Internal::tr("Invalid parameter '%s'"), argv[i]);
1655 }
1656 }
1657
1658 if (src.isEmpty())
1659 return errorSyntaxInternal(USAGE_I_CONVERTHD, Internal::tr("Mandatory input image parameter missing"));
1660 if (dst.isEmpty())
1661 return errorSyntaxInternal(USAGE_I_CONVERTHD, Internal::tr("Mandatory output image parameter missing"));
1662
1663
1664 PVDINTERFACE pVDIfs = NULL;
1665 VDINTERFACEERROR vdInterfaceError;
1666 vdInterfaceError.pfnError = handleVDError;
1667 vdInterfaceError.pfnMessage = handleVDMessage;
1668
1669 vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1670 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1671 AssertRC(vrc);
1672
1673 do
1674 {
1675 /* Try to determine input image format */
1676 if (srcformat.isEmpty())
1677 {
1678 char *pszFormat = NULL;
1679 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
1680 src.c_str(), VDTYPE_HDD, &pszFormat, &enmSrcType);
1681 if (RT_FAILURE(vrc))
1682 {
1683 RTMsgError(Internal::tr("No file format specified and autodetect failed - please specify format: %Rrc"),
1684 vrc);
1685 break;
1686 }
1687 srcformat = pszFormat;
1688 RTStrFree(pszFormat);
1689 }
1690
1691 vrc = VDCreate(pVDIfs, enmSrcType, &pSrcDisk);
1692 if (RT_FAILURE(vrc))
1693 {
1694 RTMsgError(Internal::tr("Cannot create the source virtual disk container: %Rrc"), vrc);
1695 break;
1696 }
1697
1698 /* Open the input image */
1699 vrc = VDOpen(pSrcDisk, srcformat.c_str(), src.c_str(), VD_OPEN_FLAGS_READONLY, NULL);
1700 if (RT_FAILURE(vrc))
1701 {
1702 RTMsgError(Internal::tr("Cannot open the source image: %Rrc"), vrc);
1703 break;
1704 }
1705
1706 /* Output format defaults to VDI */
1707 if (dstformat.isEmpty())
1708 dstformat = "VDI";
1709
1710 vrc = VDCreate(pVDIfs, enmSrcType, &pDstDisk);
1711 if (RT_FAILURE(vrc))
1712 {
1713 RTMsgError(Internal::tr("Cannot create the destination virtual disk container: %Rrc"), vrc);
1714 break;
1715 }
1716
1717 uint64_t cbSize = VDGetSize(pSrcDisk, VD_LAST_IMAGE);
1718 RTStrmPrintf(g_pStdErr, Internal::tr("Converting image \"%s\" with size %RU64 bytes (%RU64MB)...\n", "", cbSize),
1719 src.c_str(), cbSize, (cbSize + _1M - 1) / _1M);
1720
1721 /* Create the output image */
1722 vrc = VDCopy(pSrcDisk, VD_LAST_IMAGE, pDstDisk, dstformat.c_str(),
1723 dst.c_str(), false, 0, VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED,
1724 NULL, VD_OPEN_FLAGS_NORMAL, NULL, NULL, NULL);
1725 if (RT_FAILURE(vrc))
1726 {
1727 RTMsgError(Internal::tr("Cannot copy the image: %Rrc"), vrc);
1728 break;
1729 }
1730 }
1731 while (0);
1732 if (pDstDisk)
1733 VDCloseAll(pDstDisk);
1734 if (pSrcDisk)
1735 VDCloseAll(pSrcDisk);
1736
1737 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1738}
1739
1740/**
1741 * Tries to repair a corrupted hard disk image.
1742 *
1743 * @returns VBox status code
1744 */
1745static RTEXITCODE CmdRepairHardDisk(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1746{
1747 RT_NOREF(aVirtualBox, aSession);
1748 Utf8Str image;
1749 Utf8Str format;
1750 int vrc;
1751 bool fDryRun = false;
1752
1753 /* Parse the arguments. */
1754 for (int i = 0; i < argc; i++)
1755 {
1756 if (strcmp(argv[i], "-dry-run") == 0)
1757 {
1758 fDryRun = true;
1759 }
1760 else if (strcmp(argv[i], "-format") == 0)
1761 {
1762 if (argc <= i + 1)
1763 {
1764 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1765 }
1766 i++;
1767 format = argv[i];
1768 }
1769 else if (image.isEmpty())
1770 {
1771 image = argv[i];
1772 }
1773 else
1774 {
1775 return errorSyntaxInternal(USAGE_I_REPAIRHD, Internal::tr("Invalid parameter '%s'"), argv[i]);
1776 }
1777 }
1778
1779 if (image.isEmpty())
1780 return errorSyntaxInternal(USAGE_I_REPAIRHD, Internal::tr("Mandatory input image parameter missing"));
1781
1782 PVDINTERFACE pVDIfs = NULL;
1783 VDINTERFACEERROR vdInterfaceError;
1784 vdInterfaceError.pfnError = handleVDError;
1785 vdInterfaceError.pfnMessage = handleVDMessage;
1786
1787 vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1788 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1789 AssertRC(vrc);
1790
1791 do
1792 {
1793 /* Try to determine input image format */
1794 if (format.isEmpty())
1795 {
1796 char *pszFormat = NULL;
1797 VDTYPE enmSrcType = VDTYPE_INVALID;
1798
1799 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
1800 image.c_str(), VDTYPE_HDD, &pszFormat, &enmSrcType);
1801 if (RT_FAILURE(vrc) && (vrc != VERR_VD_IMAGE_CORRUPTED))
1802 {
1803 RTMsgError(Internal::tr("No file format specified and autodetect failed - please specify format: %Rrc"),
1804 vrc);
1805 break;
1806 }
1807 format = pszFormat;
1808 RTStrFree(pszFormat);
1809 }
1810
1811 uint32_t fFlags = 0;
1812 if (fDryRun)
1813 fFlags |= VD_REPAIR_DRY_RUN;
1814
1815 vrc = VDRepair(pVDIfs, NULL, image.c_str(), format.c_str(), fFlags);
1816 }
1817 while (0);
1818
1819 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1820}
1821
1822/**
1823 * Unloads the necessary driver.
1824 *
1825 * @returns VBox status code
1826 */
1827static RTEXITCODE CmdModUninstall(void)
1828{
1829 int vrc = SUPR3Uninstall();
1830 if (RT_SUCCESS(vrc) || vrc == VERR_NOT_IMPLEMENTED)
1831 return RTEXITCODE_SUCCESS;
1832 return RTEXITCODE_FAILURE;
1833}
1834
1835/**
1836 * Loads the necessary driver.
1837 *
1838 * @returns VBox status code
1839 */
1840static RTEXITCODE CmdModInstall(void)
1841{
1842 int vrc = SUPR3Install();
1843 if (RT_SUCCESS(vrc) || vrc == VERR_NOT_IMPLEMENTED)
1844 return RTEXITCODE_SUCCESS;
1845 return RTEXITCODE_FAILURE;
1846}
1847
1848static RTEXITCODE CmdDebugLog(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1849{
1850 /*
1851 * The first parameter is the name or UUID of a VM with a direct session
1852 * that we wish to open.
1853 */
1854 if (argc < 1)
1855 return errorSyntaxInternal(USAGE_I_DEBUGLOG, Internal::tr("Missing VM name/UUID"));
1856
1857 ComPtr<IMachine> ptrMachine;
1858 HRESULT hrc;
1859 CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
1860 ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1861
1862 CHECK_ERROR_RET(ptrMachine, LockMachine(aSession, LockType_Shared), RTEXITCODE_FAILURE);
1863
1864 /*
1865 * Get the debugger interface.
1866 */
1867 ComPtr<IConsole> ptrConsole;
1868 CHECK_ERROR_RET(aSession, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
1869
1870 ComPtr<IMachineDebugger> ptrDebugger;
1871 CHECK_ERROR_RET(ptrConsole, COMGETTER(Debugger)(ptrDebugger.asOutParam()), RTEXITCODE_FAILURE);
1872
1873 /*
1874 * Parse the command.
1875 */
1876 bool fEnablePresent = false;
1877 bool fEnable = false;
1878 bool fFlagsPresent = false;
1879 RTCString strFlags;
1880 bool fGroupsPresent = false;
1881 RTCString strGroups;
1882 bool fDestsPresent = false;
1883 RTCString strDests;
1884
1885 static const RTGETOPTDEF s_aOptions[] =
1886 {
1887 { "--disable", 'E', RTGETOPT_REQ_NOTHING },
1888 { "--enable", 'e', RTGETOPT_REQ_NOTHING },
1889 { "--flags", 'f', RTGETOPT_REQ_STRING },
1890 { "--groups", 'g', RTGETOPT_REQ_STRING },
1891 { "--destinations", 'd', RTGETOPT_REQ_STRING }
1892 };
1893
1894 int ch;
1895 RTGETOPTUNION ValueUnion;
1896 RTGETOPTSTATE GetState;
1897 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
1898 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1899 {
1900 switch (ch)
1901 {
1902 case 'e':
1903 fEnablePresent = true;
1904 fEnable = true;
1905 break;
1906
1907 case 'E':
1908 fEnablePresent = true;
1909 fEnable = false;
1910 break;
1911
1912 case 'f':
1913 fFlagsPresent = true;
1914 if (*ValueUnion.psz)
1915 {
1916 if (strFlags.isNotEmpty())
1917 strFlags.append(' ');
1918 strFlags.append(ValueUnion.psz);
1919 }
1920 break;
1921
1922 case 'g':
1923 fGroupsPresent = true;
1924 if (*ValueUnion.psz)
1925 {
1926 if (strGroups.isNotEmpty())
1927 strGroups.append(' ');
1928 strGroups.append(ValueUnion.psz);
1929 }
1930 break;
1931
1932 case 'd':
1933 fDestsPresent = true;
1934 if (*ValueUnion.psz)
1935 {
1936 if (strDests.isNotEmpty())
1937 strDests.append(' ');
1938 strDests.append(ValueUnion.psz);
1939 }
1940 break;
1941
1942 default:
1943 return errorGetOptInternal(USAGE_I_DEBUGLOG, ch, &ValueUnion);
1944 }
1945 }
1946
1947 /*
1948 * Do the job.
1949 */
1950 if (fEnablePresent && !fEnable)
1951 CHECK_ERROR_RET(ptrDebugger, COMSETTER(LogEnabled)(FALSE), RTEXITCODE_FAILURE);
1952
1953 /** @todo flags, groups destination. */
1954 if (fFlagsPresent || fGroupsPresent || fDestsPresent)
1955 RTMsgWarning(Internal::tr("One or more of the requested features are not implemented! Feel free to do this."));
1956
1957 if (fEnablePresent && fEnable)
1958 CHECK_ERROR_RET(ptrDebugger, COMSETTER(LogEnabled)(TRUE), RTEXITCODE_FAILURE);
1959 return RTEXITCODE_SUCCESS;
1960}
1961
1962/**
1963 * Generate a SHA-256 password hash
1964 */
1965static RTEXITCODE CmdGeneratePasswordHash(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1966{
1967 RT_NOREF(aVirtualBox, aSession);
1968
1969 /* one parameter, the password to hash */
1970 if (argc != 1)
1971 return errorSyntaxInternal(USAGE_I_PASSWORDHASH, Internal::tr("password to hash required"));
1972
1973 uint8_t abDigest[RTSHA256_HASH_SIZE];
1974 RTSha256(argv[0], strlen(argv[0]), abDigest);
1975 char pszDigest[RTSHA256_DIGEST_LEN + 1];
1976 RTSha256ToString(abDigest, pszDigest, sizeof(pszDigest));
1977 RTPrintf(Internal::tr("Password hash: %s\n"), pszDigest);
1978
1979 return RTEXITCODE_SUCCESS;
1980}
1981
1982/**
1983 * Print internal guest statistics or
1984 * set internal guest statistics update interval if specified
1985 */
1986static RTEXITCODE CmdGuestStats(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1987{
1988 /* one parameter, guest name */
1989 if (argc < 1)
1990 return errorSyntaxInternal(USAGE_I_GUESTSTATS, Internal::tr("Missing VM name/UUID"));
1991
1992 /*
1993 * Parse the command.
1994 */
1995 ULONG aUpdateInterval = 0;
1996
1997 static const RTGETOPTDEF s_aOptions[] =
1998 {
1999 { "--interval", 'i', RTGETOPT_REQ_UINT32 }
2000 };
2001
2002 int ch;
2003 RTGETOPTUNION ValueUnion;
2004 RTGETOPTSTATE GetState;
2005 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
2006 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
2007 {
2008 switch (ch)
2009 {
2010 case 'i':
2011 aUpdateInterval = ValueUnion.u32;
2012 break;
2013
2014 default:
2015 return errorGetOptInternal(USAGE_I_GUESTSTATS, ch, &ValueUnion);
2016 }
2017 }
2018
2019 if (argc > 1 && aUpdateInterval == 0)
2020 return errorSyntaxInternal(USAGE_I_GUESTSTATS, Internal::tr("Invalid update interval specified"));
2021
2022 RTPrintf(Internal::tr("argc=%d interval=%u\n"), argc, aUpdateInterval);
2023
2024 ComPtr<IMachine> ptrMachine;
2025 HRESULT hrc;
2026 CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
2027 ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
2028
2029 CHECK_ERROR_RET(ptrMachine, LockMachine(aSession, LockType_Shared), RTEXITCODE_FAILURE);
2030
2031 /*
2032 * Get the guest interface.
2033 */
2034 ComPtr<IConsole> ptrConsole;
2035 CHECK_ERROR_RET(aSession, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
2036
2037 ComPtr<IGuest> ptrGuest;
2038 CHECK_ERROR_RET(ptrConsole, COMGETTER(Guest)(ptrGuest.asOutParam()), RTEXITCODE_FAILURE);
2039
2040 if (aUpdateInterval)
2041 CHECK_ERROR_RET(ptrGuest, COMSETTER(StatisticsUpdateInterval)(aUpdateInterval), RTEXITCODE_FAILURE);
2042 else
2043 {
2044 ULONG mCpuUser, mCpuKernel, mCpuIdle;
2045 ULONG mMemTotal, mMemFree, mMemBalloon, mMemShared, mMemCache, mPageTotal;
2046 ULONG ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal, ulMemSharedTotal;
2047
2048 CHECK_ERROR_RET(ptrGuest, InternalGetStatistics(&mCpuUser, &mCpuKernel, &mCpuIdle,
2049 &mMemTotal, &mMemFree, &mMemBalloon, &mMemShared, &mMemCache,
2050 &mPageTotal, &ulMemAllocTotal, &ulMemFreeTotal,
2051 &ulMemBalloonTotal, &ulMemSharedTotal),
2052 RTEXITCODE_FAILURE);
2053 RTPrintf("mCpuUser=%u mCpuKernel=%u mCpuIdle=%u\n"
2054 "mMemTotal=%u mMemFree=%u mMemBalloon=%u mMemShared=%u mMemCache=%u\n"
2055 "mPageTotal=%u ulMemAllocTotal=%u ulMemFreeTotal=%u ulMemBalloonTotal=%u ulMemSharedTotal=%u\n",
2056 mCpuUser, mCpuKernel, mCpuIdle,
2057 mMemTotal, mMemFree, mMemBalloon, mMemShared, mMemCache,
2058 mPageTotal, ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal, ulMemSharedTotal);
2059
2060 }
2061
2062 return RTEXITCODE_SUCCESS;
2063}
2064
2065
2066/**
2067 * Wrapper for handling internal commands
2068 */
2069RTEXITCODE handleInternalCommands(HandlerArg *a)
2070{
2071 /* at least a command is required */
2072 if (a->argc < 1)
2073 return errorSyntaxInternal(USAGE_I_ALL, Internal::tr("Command missing"));
2074
2075 /*
2076 * The 'string switch' on command name.
2077 */
2078 const char *pszCmd = a->argv[0];
2079 if (!strcmp(pszCmd, "loadmap"))
2080 return CmdLoadMap(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2081 if (!strcmp(pszCmd, "loadsyms"))
2082 return CmdLoadSyms(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2083 //if (!strcmp(pszCmd, "unloadsyms"))
2084 // return CmdUnloadSyms(argc - 1, &a->argv[1]);
2085 if (!strcmp(pszCmd, "sethduuid") || !strcmp(pszCmd, "sethdparentuuid"))
2086 return CmdSetHDUUID(a->argc, &a->argv[0], a->virtualBox, a->session);
2087 if (!strcmp(pszCmd, "dumphdinfo"))
2088 return CmdDumpHDInfo(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2089 if (!strcmp(pszCmd, "listpartitions"))
2090 return CmdListPartitions(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2091 if (!strcmp(pszCmd, "createrawvmdk"))
2092 return CmdCreateRawVMDK(a->argc - 1, &a->argv[1], a);
2093 if (!strcmp(pszCmd, "renamevmdk"))
2094 return CmdRenameVMDK(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2095 if (!strcmp(pszCmd, "converttoraw"))
2096 return CmdConvertToRaw(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2097 if (!strcmp(pszCmd, "converthd"))
2098 return CmdConvertHardDisk(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2099 if (!strcmp(pszCmd, "modinstall"))
2100 return CmdModInstall();
2101 if (!strcmp(pszCmd, "moduninstall"))
2102 return CmdModUninstall();
2103 if (!strcmp(pszCmd, "debuglog"))
2104 return CmdDebugLog(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2105 if (!strcmp(pszCmd, "passwordhash"))
2106 return CmdGeneratePasswordHash(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2107 if (!strcmp(pszCmd, "gueststats"))
2108 return CmdGuestStats(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2109 if (!strcmp(pszCmd, "repairhd"))
2110 return CmdRepairHardDisk(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2111
2112 /* default: */
2113 return errorSyntaxInternal(USAGE_I_ALL, Internal::tr("Invalid command '%s'"), a->argv[0]);
2114}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use