VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use