VirtualBox

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

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

VBoxManage: rc -> vrc/hrc. Make scm check. bugref:10223

  • 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 98298 2023-01-25 02:23:33Z 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 * @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 vrc = RTUtf16ToUtf8(Keys.raw(), &pszKeys);
499 if (RT_SUCCESS(vrc))
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, vrc);
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', vrc=%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', vrc=%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', vrc=%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', vrc=%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', vrc=%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', vrc=%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 vrc, 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"), vrc, 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 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */, argv[1], VDTYPE_INVALID, &pszFormat, &enmType);
804 if (RT_FAILURE(vrc))
805 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Format autodetect failed: %Rrc"), vrc);
806
807 PVDISK pDisk = NULL;
808
809 PVDINTERFACE pVDIfs = NULL;
810 VDINTERFACEERROR vdInterfaceError;
811 vdInterfaceError.pfnError = handleVDError;
812 vdInterfaceError.pfnMessage = handleVDMessage;
813
814 vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
815 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
816 AssertRC(vrc);
817
818 vrc = VDCreate(pVDIfs, enmType, &pDisk);
819 if (RT_FAILURE(vrc))
820 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot create the virtual disk container: %Rrc"), vrc);
821
822 /* Open the image */
823 vrc = VDOpen(pDisk, pszFormat, argv[1], VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_INFO, NULL);
824 if (RT_FAILURE(vrc))
825 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot open the image: %Rrc"), vrc);
826
827 if (uuidType == HDUUID)
828 vrc = VDSetUuid(pDisk, VD_LAST_IMAGE, uuid.raw());
829 else
830 vrc = VDSetParentUuid(pDisk, VD_LAST_IMAGE, uuid.raw());
831 if (RT_FAILURE(vrc))
832 RTMsgError(Internal::tr("Cannot set a new UUID: %Rrc"), vrc);
833 else
834 RTPrintf(Internal::tr("UUID changed to: %s\n"), uuid.toString().c_str());
835
836 VDCloseAll(pDisk);
837
838 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
839}
840
841
842static RTEXITCODE CmdDumpHDInfo(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
843{
844 RT_NOREF(aVirtualBox, aSession);
845
846 /* we need exactly one parameter: the image file */
847 if (argc != 1)
848 {
849 return errorSyntaxInternal(USAGE_I_DUMPHDINFO, Internal::tr("Not enough parameters"));
850 }
851
852 /* just try it */
853 char *pszFormat = NULL;
854 VDTYPE enmType = VDTYPE_INVALID;
855 int vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */, argv[0], VDTYPE_INVALID, &pszFormat, &enmType);
856 if (RT_FAILURE(vrc))
857 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Format autodetect failed: %Rrc"), vrc);
858
859 PVDISK pDisk = NULL;
860
861 PVDINTERFACE pVDIfs = NULL;
862 VDINTERFACEERROR vdInterfaceError;
863 vdInterfaceError.pfnError = handleVDError;
864 vdInterfaceError.pfnMessage = handleVDMessage;
865
866 vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
867 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
868 AssertRC(vrc);
869
870 vrc = VDCreate(pVDIfs, enmType, &pDisk);
871 if (RT_FAILURE(vrc))
872 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot create the virtual disk container: %Rrc"), vrc);
873
874 /* Open the image */
875 vrc = VDOpen(pDisk, pszFormat, argv[0], VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO, NULL);
876 if (RT_FAILURE(vrc))
877 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot open the image: %Rrc"), vrc);
878
879 VDDumpImages(pDisk);
880
881 VDCloseAll(pDisk);
882
883 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
884}
885
886static int partRead(RTFILE File, PHOSTPARTITIONS pPart)
887{
888 uint8_t aBuffer[512];
889 uint8_t partitionTableHeader[512];
890 uint32_t sector_size = 512;
891 uint64_t lastUsableLBA = 0;
892
893 VDISKPARTTYPE partitioningType;
894
895 pPart->cPartitions = 0;
896 memset(pPart->aPartitions, '\0', sizeof(pPart->aPartitions));
897
898 int vrc = RTFileReadAt(File, 0, &aBuffer, sizeof(aBuffer), NULL);
899 if (RT_FAILURE(vrc))
900 return vrc;
901
902 if (aBuffer[450] == 0xEE)/* check the sign of the GPT disk*/
903 {
904 partitioningType = VDISKPARTTYPE_GPT;
905 pPart->uPartitioningType = VDISKPARTTYPE_GPT;//partitioningType;
906
907 if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa)
908 return VERR_INVALID_PARAMETER;
909
910 vrc = RTFileReadAt(File, sector_size, &partitionTableHeader, sector_size, NULL);
911 if (RT_SUCCESS(vrc))
912 {
913 /** @todo r=bird: This is a 64-bit magic value, right... */
914 const char *l_ppth = (char *)partitionTableHeader;
915 if (strncmp(l_ppth, "EFI PART", 8))
916 return VERR_INVALID_PARAMETER;
917
918 /** @todo check GPT Version */
919
920 /** @todo r=bird: C have this handy concept called structures which
921 * greatly simplify data access... (Someone is really lazy here!) */
922#if 0 /* unused */
923 uint64_t firstUsableLBA = RT_MAKE_U64_FROM_U8(partitionTableHeader[40],
924 partitionTableHeader[41],
925 partitionTableHeader[42],
926 partitionTableHeader[43],
927 partitionTableHeader[44],
928 partitionTableHeader[45],
929 partitionTableHeader[46],
930 partitionTableHeader[47]
931 );
932#endif
933 lastUsableLBA = RT_MAKE_U64_FROM_U8(partitionTableHeader[48],
934 partitionTableHeader[49],
935 partitionTableHeader[50],
936 partitionTableHeader[51],
937 partitionTableHeader[52],
938 partitionTableHeader[53],
939 partitionTableHeader[54],
940 partitionTableHeader[55]
941 );
942 uint32_t partitionsNumber = RT_MAKE_U32_FROM_U8(partitionTableHeader[80],
943 partitionTableHeader[81],
944 partitionTableHeader[82],
945 partitionTableHeader[83]
946 );
947 uint32_t partitionEntrySize = RT_MAKE_U32_FROM_U8(partitionTableHeader[84],
948 partitionTableHeader[85],
949 partitionTableHeader[86],
950 partitionTableHeader[87]
951 );
952
953 uint32_t currentEntry = 0;
954
955 if (partitionEntrySize * partitionsNumber > 4 * _1M)
956 {
957 RTMsgError(Internal::tr("The GPT header seems corrupt because it contains too many entries"));
958 return VERR_INVALID_PARAMETER;
959 }
960
961 uint8_t *pbPartTable = (uint8_t *)RTMemAllocZ(RT_ALIGN_Z(partitionEntrySize * partitionsNumber, 512));
962 if (!pbPartTable)
963 {
964 RTMsgError(Internal::tr("Allocating memory for the GPT partitions entries failed"));
965 return VERR_NO_MEMORY;
966 }
967
968 /* partition entries begin from LBA2 */
969 /** @todo r=aeichner: Reading from LBA 2 is not always correct, the header will contain the starting LBA. */
970 vrc = RTFileReadAt(File, 1024, pbPartTable, RT_ALIGN_Z(partitionEntrySize * partitionsNumber, 512), NULL);
971 if (RT_FAILURE(vrc))
972 {
973 RTMsgError(Internal::tr("Reading the partition table failed"));
974 RTMemFree(pbPartTable);
975 return vrc;
976 }
977
978 while (currentEntry < partitionsNumber)
979 {
980 uint8_t *partitionEntry = pbPartTable + currentEntry * partitionEntrySize;
981
982 uint64_t start = RT_MAKE_U64_FROM_U8(partitionEntry[32], partitionEntry[33], partitionEntry[34], partitionEntry[35],
983 partitionEntry[36], partitionEntry[37], partitionEntry[38], partitionEntry[39]);
984 uint64_t end = RT_MAKE_U64_FROM_U8(partitionEntry[40], partitionEntry[41], partitionEntry[42], partitionEntry[43],
985 partitionEntry[44], partitionEntry[45], partitionEntry[46], partitionEntry[47]);
986
987 PHOSTPARTITION pCP = &pPart->aPartitions[pPart->cPartitions++];
988 pCP->uIndex = currentEntry + 1;
989 pCP->uIndexWin = currentEntry + 1;
990 pCP->uType = 0;
991 pCP->uStartCylinder = 0;
992 pCP->uStartHead = 0;
993 pCP->uStartSector = 0;
994 pCP->uEndCylinder = 0;
995 pCP->uEndHead = 0;
996 pCP->uEndSector = 0;
997 pCP->uPartDataStart = 0; /* will be filled out later properly. */
998 pCP->cPartDataSectors = 0;
999 if (start==0 || end==0)
1000 {
1001 pCP->uIndex = 0;
1002 pCP->uIndexWin = 0;
1003 --pPart->cPartitions;
1004 break;
1005 }
1006 else
1007 {
1008 pCP->uStart = start;
1009 pCP->uSize = (end +1) - start;/*+1 LBA because the last address is included*/
1010 }
1011
1012 ++currentEntry;
1013 }
1014
1015 RTMemFree(pbPartTable);
1016 }
1017 }
1018 else
1019 {
1020 partitioningType = VDISKPARTTYPE_MBR;
1021 pPart->uPartitioningType = VDISKPARTTYPE_MBR;//partitioningType;
1022
1023 if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa)
1024 return VERR_INVALID_PARAMETER;
1025
1026 unsigned uExtended = (unsigned)-1;
1027 unsigned uIndexWin = 1;
1028
1029 for (unsigned i = 0; i < 4; i++)
1030 {
1031 uint8_t *p = &aBuffer[0x1be + i * 16];
1032 if (p[4] == 0)
1033 continue;
1034 PHOSTPARTITION pCP = &pPart->aPartitions[pPart->cPartitions++];
1035 pCP->uIndex = i + 1;
1036 pCP->uType = p[4];
1037 pCP->uStartCylinder = (uint32_t)p[3] + ((uint32_t)(p[2] & 0xc0) << 2);
1038 pCP->uStartHead = p[1];
1039 pCP->uStartSector = p[2] & 0x3f;
1040 pCP->uEndCylinder = (uint32_t)p[7] + ((uint32_t)(p[6] & 0xc0) << 2);
1041 pCP->uEndHead = p[5];
1042 pCP->uEndSector = p[6] & 0x3f;
1043 pCP->uStart = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
1044 pCP->uSize = RT_MAKE_U32_FROM_U8(p[12], p[13], p[14], p[15]);
1045 pCP->uPartDataStart = 0; /* will be filled out later properly. */
1046 pCP->cPartDataSectors = 0;
1047
1048 if (PARTTYPE_IS_EXTENDED(p[4]))
1049 {
1050 if (uExtended == (unsigned)-1)
1051 {
1052 uExtended = (unsigned)(pCP - pPart->aPartitions);
1053 pCP->uIndexWin = 0;
1054 }
1055 else
1056 {
1057 RTMsgError(Internal::tr("More than one extended partition"));
1058 return VERR_INVALID_PARAMETER;
1059 }
1060 }
1061 else
1062 {
1063 pCP->uIndexWin = uIndexWin;
1064 uIndexWin++;
1065 }
1066 }
1067
1068 if (uExtended != (unsigned)-1)
1069 {
1070 unsigned uIndex = 5;
1071 uint64_t uStart = pPart->aPartitions[uExtended].uStart;
1072 uint64_t uOffset = 0;
1073 if (!uStart)
1074 {
1075 RTMsgError(Internal::tr("Inconsistency for logical partition start"));
1076 return VERR_INVALID_PARAMETER;
1077 }
1078
1079 do
1080 {
1081 vrc = RTFileReadAt(File, (uStart + uOffset) * 512, &aBuffer, sizeof(aBuffer), NULL);
1082 if (RT_FAILURE(vrc))
1083 return vrc;
1084
1085 if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa)
1086 {
1087 RTMsgError(Internal::tr("Logical partition without magic"));
1088 return VERR_INVALID_PARAMETER;
1089 }
1090 uint8_t *p = &aBuffer[0x1be];
1091
1092 if (p[4] == 0)
1093 {
1094 RTMsgError(Internal::tr("Logical partition with type 0 encountered"));
1095 return VERR_INVALID_PARAMETER;
1096 }
1097
1098 PHOSTPARTITION pCP = &pPart->aPartitions[pPart->cPartitions++];
1099 pCP->uIndex = uIndex;
1100 pCP->uIndexWin = uIndexWin;
1101 pCP->uType = p[4];
1102 pCP->uStartCylinder = (uint32_t)p[3] + ((uint32_t)(p[2] & 0xc0) << 2);
1103 pCP->uStartHead = p[1];
1104 pCP->uStartSector = p[2] & 0x3f;
1105 pCP->uEndCylinder = (uint32_t)p[7] + ((uint32_t)(p[6] & 0xc0) << 2);
1106 pCP->uEndHead = p[5];
1107 pCP->uEndSector = p[6] & 0x3f;
1108 uint32_t uStartOffset = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
1109 if (!uStartOffset)
1110 {
1111 RTMsgError(Internal::tr("Invalid partition start offset"));
1112 return VERR_INVALID_PARAMETER;
1113 }
1114 pCP->uStart = uStart + uOffset + uStartOffset;
1115 pCP->uSize = RT_MAKE_U32_FROM_U8(p[12], p[13], p[14], p[15]);
1116 /* Fill out partitioning location info for EBR. */
1117 pCP->uPartDataStart = uStart + uOffset;
1118 pCP->cPartDataSectors = uStartOffset;
1119 p += 16;
1120 if (p[4] == 0)
1121 uExtended = (unsigned)-1;
1122 else if (PARTTYPE_IS_EXTENDED(p[4]))
1123 {
1124 uExtended = uIndex;
1125 uIndex++;
1126 uIndexWin++;
1127 uOffset = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
1128 }
1129 else
1130 {
1131 RTMsgError(Internal::tr("Logical partition chain broken"));
1132 return VERR_INVALID_PARAMETER;
1133 }
1134 } while (uExtended != (unsigned)-1);
1135 }
1136 }
1137
1138
1139 /* Sort partitions in ascending order of start sector, plus a trivial
1140 * bit of consistency checking. */
1141 for (unsigned i = 0; i < pPart->cPartitions-1; i++)
1142 {
1143 unsigned uMinIdx = i;
1144 uint64_t uMinVal = pPart->aPartitions[i].uStart;
1145 for (unsigned j = i + 1; j < pPart->cPartitions; j++)
1146 {
1147 if (pPart->aPartitions[j].uStart < uMinVal)
1148 {
1149 uMinIdx = j;
1150 uMinVal = pPart->aPartitions[j].uStart;
1151 }
1152 else if (pPart->aPartitions[j].uStart == uMinVal)
1153 {
1154 RTMsgError(Internal::tr("Two partitions start at the same place"));
1155 return VERR_INVALID_PARAMETER;
1156 }
1157 else if (pPart->aPartitions[j].uStart == 0)
1158 {
1159 RTMsgError(Internal::tr("Partition starts at sector 0"));
1160 return VERR_INVALID_PARAMETER;
1161 }
1162 }
1163 if (uMinIdx != i)
1164 {
1165 /* Swap entries at index i and uMinIdx. */
1166 memcpy(&pPart->aPartitions[pPart->cPartitions],
1167 &pPart->aPartitions[i], sizeof(HOSTPARTITION));
1168 memcpy(&pPart->aPartitions[i],
1169 &pPart->aPartitions[uMinIdx], sizeof(HOSTPARTITION));
1170 memcpy(&pPart->aPartitions[uMinIdx],
1171 &pPart->aPartitions[pPart->cPartitions], sizeof(HOSTPARTITION));
1172 }
1173 }
1174
1175 /* Fill out partitioning location info for MBR or GPT. */
1176 pPart->aPartitions[0].uPartDataStart = 0;
1177 pPart->aPartitions[0].cPartDataSectors = pPart->aPartitions[0].uStart;
1178
1179 /* Fill out partitioning location info for backup GPT. */
1180 if (partitioningType == VDISKPARTTYPE_GPT)
1181 {
1182 pPart->aPartitions[pPart->cPartitions-1].uPartDataStart = lastUsableLBA+1;
1183 pPart->aPartitions[pPart->cPartitions-1].cPartDataSectors = 33;
1184
1185 /* Now do a some partition table consistency checking, to reject the most
1186 * obvious garbage which can lead to trouble later. */
1187 uint64_t uPrevEnd = 0;
1188 for (unsigned i = 0; i < pPart->cPartitions; i++)
1189 {
1190 if (pPart->aPartitions[i].cPartDataSectors)
1191 uPrevEnd = pPart->aPartitions[i].uPartDataStart + pPart->aPartitions[i].cPartDataSectors;
1192 if (pPart->aPartitions[i].uStart < uPrevEnd &&
1193 pPart->cPartitions-1 != i)
1194 {
1195 RTMsgError(Internal::tr("Overlapping GPT partitions"));
1196 return VERR_INVALID_PARAMETER;
1197 }
1198 }
1199 }
1200 else
1201 {
1202 /* Now do a some partition table consistency checking, to reject the most
1203 * obvious garbage which can lead to trouble later. */
1204 uint64_t uPrevEnd = 0;
1205 for (unsigned i = 0; i < pPart->cPartitions; i++)
1206 {
1207 if (pPart->aPartitions[i].cPartDataSectors)
1208 uPrevEnd = pPart->aPartitions[i].uPartDataStart + pPart->aPartitions[i].cPartDataSectors;
1209 if (pPart->aPartitions[i].uStart < uPrevEnd)
1210 {
1211 RTMsgError(Internal::tr("Overlapping MBR partitions"));
1212 return VERR_INVALID_PARAMETER;
1213 }
1214 if (!PARTTYPE_IS_EXTENDED(pPart->aPartitions[i].uType))
1215 uPrevEnd = pPart->aPartitions[i].uStart + pPart->aPartitions[i].uSize;
1216 }
1217 }
1218
1219 return VINF_SUCCESS;
1220}
1221
1222static RTEXITCODE CmdListPartitions(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1223{
1224 RT_NOREF(aVirtualBox, aSession);
1225 Utf8Str rawdisk;
1226
1227 /* let's have a closer look at the arguments */
1228 for (int i = 0; i < argc; i++)
1229 {
1230 if (strcmp(argv[i], "-rawdisk") == 0)
1231 {
1232 if (argc <= i + 1)
1233 {
1234 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1235 }
1236 i++;
1237 rawdisk = argv[i];
1238 }
1239 else
1240 {
1241 return errorSyntaxInternal(USAGE_I_LISTPARTITIONS, Internal::tr("Invalid parameter '%s'"), argv[i]);
1242 }
1243 }
1244
1245 if (rawdisk.isEmpty())
1246 return errorSyntaxInternal(USAGE_I_LISTPARTITIONS, Internal::tr("Mandatory parameter -rawdisk missing"));
1247
1248 RTFILE hRawFile;
1249 int vrc = RTFileOpen(&hRawFile, rawdisk.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1250 if (RT_FAILURE(vrc))
1251 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot open the raw disk: %Rrc"), vrc);
1252
1253 HOSTPARTITIONS partitions;
1254 vrc = partRead(hRawFile, &partitions);
1255 /* Don't bail out on errors, print the table and return the result code. */
1256
1257 RTPrintf(Internal::tr("Number Type StartCHS EndCHS Size (MiB) Start (Sect)\n"));
1258 for (unsigned i = 0; i < partitions.cPartitions; i++)
1259 {
1260 /* Don't show the extended partition, otherwise users might think they
1261 * can add it to the list of partitions for raw partition access. */
1262 if (PARTTYPE_IS_EXTENDED(partitions.aPartitions[i].uType))
1263 continue;
1264
1265 RTPrintf("%-7u %#04x %-4u/%-3u/%-2u %-4u/%-3u/%-2u %10llu %10llu\n",
1266 partitions.aPartitions[i].uIndex,
1267 partitions.aPartitions[i].uType,
1268 partitions.aPartitions[i].uStartCylinder,
1269 partitions.aPartitions[i].uStartHead,
1270 partitions.aPartitions[i].uStartSector,
1271 partitions.aPartitions[i].uEndCylinder,
1272 partitions.aPartitions[i].uEndHead,
1273 partitions.aPartitions[i].uEndSector,
1274 partitions.aPartitions[i].uSize / 2048,
1275 partitions.aPartitions[i].uStart);
1276 }
1277
1278 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1279}
1280
1281static const RTGETOPTDEF g_aCreateRawVMDKOptions[] =
1282{
1283 { "--filename", 'f', RTGETOPT_REQ_STRING },
1284 { "-filename", 'f', RTGETOPT_REQ_STRING },
1285 { "--rawdisk", 'd', RTGETOPT_REQ_STRING },
1286 { "-rawdisk", 'd', RTGETOPT_REQ_STRING },
1287 { "--partitions", 'p', RTGETOPT_REQ_STRING },
1288 { "-partitions", 'p', RTGETOPT_REQ_STRING },
1289 { "--mbr", 'm', RTGETOPT_REQ_STRING },
1290 { "-mbr", 'm', RTGETOPT_REQ_STRING },
1291#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_WINDOWS)
1292 { "--relative", 'r', RTGETOPT_REQ_NOTHING },
1293 { "-relative", 'r', RTGETOPT_REQ_NOTHING },
1294#endif /* RT_OS_LINUX || RT_OS_FREEBSD || RT_OS_WINDOWS */
1295};
1296
1297static RTEXITCODE CmdCreateRawVMDK(int argc, char **argv, HandlerArg *a)
1298{
1299 const char *pszFilename = NULL;
1300 const char *pszRawdisk = NULL;
1301 const char *pszPartitions = NULL;
1302 const char *pszMbr = NULL;
1303 bool fRelative = false;
1304 int c;
1305 RTGETOPTUNION ValueUnion;
1306 RTGETOPTSTATE GetState;
1307 RTGetOptInit(&GetState, argc, argv, g_aCreateRawVMDKOptions, RT_ELEMENTS(g_aCreateRawVMDKOptions), 0, 0);
1308 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1309 {
1310 switch (c)
1311 {
1312 case 'f': // --filename
1313 pszFilename = ValueUnion.psz;
1314 break;
1315
1316 case 'd': // --rawdisk
1317 pszRawdisk = ValueUnion.psz;
1318 break;
1319
1320 case 'p': // --partitions
1321 pszPartitions = ValueUnion.psz;
1322 break;
1323
1324 case 'm': // --mbr
1325 pszMbr = ValueUnion.psz;
1326 break;
1327#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_WINDOWS)
1328 case 'r': // --relative
1329 fRelative = true;
1330 break;
1331#endif /* RT_OS_LINUX || RT_OS_FREEBSD || RT_OS_WINDOWS */
1332
1333 default:
1334 return errorGetOptInternal(USAGE_I_CREATERAWVMDK, c, &ValueUnion);
1335 }
1336 }
1337
1338 if (!pszFilename || !*pszFilename)
1339 return errorSyntaxInternal(USAGE_I_CREATERAWVMDK, Internal::tr("Mandatory parameter --filename missing"));
1340 if (!pszRawdisk || !*pszRawdisk)
1341 return errorSyntaxInternal(USAGE_I_CREATERAWVMDK, Internal::tr("Mandatory parameter --rawdisk missing"));
1342 if (!pszPartitions && pszMbr)
1343 return errorSyntaxInternal(USAGE_I_CREATERAWVMDK,
1344 Internal::tr("The parameter --mbr is only valid when the parameter -partitions is also present"));
1345
1346 /* Construct the equivalent 'VBoxManage createmedium disk --variant RawDisk ...' command line. */
1347 size_t cMaxArgs = 9; /* all possible 'createmedium' args based on the 'createrawvmdk' options + 1 for NULL */
1348 char **papszNewArgv = (char **)RTMemAllocZ(sizeof(papszNewArgv[0]) * cMaxArgs);
1349 if (!papszNewArgv)
1350 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Failed to allocate memory for argument array"));
1351 int cArgs = 0;
1352
1353 papszNewArgv[cArgs++] = RTStrDup("disk");
1354 papszNewArgv[cArgs++] = RTStrDup("--variant=RawDisk");
1355 papszNewArgv[cArgs++] = RTStrDup("--format=VMDK");
1356
1357 for (int i = 0; i < cArgs; i++)
1358 if (!papszNewArgv[i])
1359 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Failed to allocate memory for argument array"));
1360
1361 if ( RTStrAPrintf(&papszNewArgv[cArgs++], "--filename=%s", pszFilename) == -1
1362 || RTStrAPrintf(&papszNewArgv[cArgs++], "--property=RawDrive=%s", pszRawdisk) == -1
1363 || (pszPartitions && (RTStrAPrintf(&papszNewArgv[cArgs++], "--property=Partitions=%s", pszPartitions) == -1))
1364 || (pszMbr && (RTStrAPrintf(&papszNewArgv[cArgs++], "--property-filename=%s", pszMbr) == -1))
1365 || (fRelative && (RTStrAPrintf(&papszNewArgv[cArgs++], "--property=Relative=%d", fRelative) == -1)))
1366 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Failed to allocate memory for argument array"));
1367
1368 papszNewArgv[cArgs] = NULL;
1369
1370 RTStrmPrintf(g_pStdErr,
1371 Internal::tr("\nThe 'createrawvdk' subcommand is deprecated. The equivalent functionality is\n"
1372 "available using the 'VBoxManage createmedium' command and should be used\n"
1373 "instead. See 'VBoxManage help createmedium' for details.\n\n"));
1374
1375 a->argc = cArgs;
1376 a->argv = papszNewArgv;
1377 RTEXITCODE rcExit = handleCreateMedium(a);
1378
1379 for (int i = 0; i < cArgs; i++)
1380 RTStrFree(papszNewArgv[i]);
1381 RTMemFree(papszNewArgv);
1382
1383 return rcExit;
1384}
1385
1386static RTEXITCODE CmdRenameVMDK(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1387{
1388 RT_NOREF(aVirtualBox, aSession);
1389 Utf8Str src;
1390 Utf8Str dst;
1391 /* Parse the arguments. */
1392 for (int i = 0; i < argc; i++)
1393 {
1394 if (strcmp(argv[i], "-from") == 0)
1395 {
1396 if (argc <= i + 1)
1397 {
1398 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1399 }
1400 i++;
1401 src = argv[i];
1402 }
1403 else if (strcmp(argv[i], "-to") == 0)
1404 {
1405 if (argc <= i + 1)
1406 {
1407 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1408 }
1409 i++;
1410 dst = argv[i];
1411 }
1412 else
1413 {
1414 return errorSyntaxInternal(USAGE_I_RENAMEVMDK, Internal::tr("Invalid parameter '%s'"), argv[i]);
1415 }
1416 }
1417
1418 if (src.isEmpty())
1419 return errorSyntaxInternal(USAGE_I_RENAMEVMDK, Internal::tr("Mandatory parameter -from missing"));
1420 if (dst.isEmpty())
1421 return errorSyntaxInternal(USAGE_I_RENAMEVMDK, Internal::tr("Mandatory parameter -to missing"));
1422
1423 PVDISK pDisk = NULL;
1424
1425 PVDINTERFACE pVDIfs = NULL;
1426 VDINTERFACEERROR vdInterfaceError;
1427 vdInterfaceError.pfnError = handleVDError;
1428 vdInterfaceError.pfnMessage = handleVDMessage;
1429
1430 int vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1431 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1432 AssertRC(vrc);
1433
1434 vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1435 if (RT_FAILURE(vrc))
1436 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot create the virtual disk container: %Rrc"), vrc);
1437
1438 vrc = VDOpen(pDisk, "VMDK", src.c_str(), VD_OPEN_FLAGS_NORMAL, NULL);
1439 if (RT_SUCCESS(vrc))
1440 {
1441 vrc = VDCopy(pDisk, 0, pDisk, "VMDK", dst.c_str(), true, 0,
1442 VD_IMAGE_FLAGS_NONE, NULL, VD_OPEN_FLAGS_NORMAL,
1443 NULL, NULL, NULL);
1444 if (RT_FAILURE(vrc))
1445 RTMsgError(Internal::tr("Cannot rename the image: %Rrc"), vrc);
1446 }
1447 else
1448 RTMsgError(Internal::tr("Cannot create the source image: %Rrc"), vrc);
1449 VDCloseAll(pDisk);
1450 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1451}
1452
1453static RTEXITCODE CmdConvertToRaw(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1454{
1455 RT_NOREF(aVirtualBox, aSession);
1456 Utf8Str srcformat;
1457 Utf8Str src;
1458 Utf8Str dst;
1459 bool fWriteToStdOut = false;
1460
1461 /* Parse the arguments. */
1462 for (int i = 0; i < argc; i++)
1463 {
1464 if (strcmp(argv[i], "-format") == 0)
1465 {
1466 if (argc <= i + 1)
1467 {
1468 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1469 }
1470 i++;
1471 srcformat = argv[i];
1472 }
1473 else if (src.isEmpty())
1474 {
1475 src = argv[i];
1476 }
1477 else if (dst.isEmpty())
1478 {
1479 dst = argv[i];
1480#ifdef ENABLE_CONVERT_RAW_TO_STDOUT
1481 if (!strcmp(argv[i], "stdout"))
1482 fWriteToStdOut = true;
1483#endif /* ENABLE_CONVERT_RAW_TO_STDOUT */
1484 }
1485 else
1486 {
1487 return errorSyntaxInternal(USAGE_I_CONVERTTORAW, Internal::tr("Invalid parameter '%s'"), argv[i]);
1488 }
1489 }
1490
1491 if (src.isEmpty())
1492 return errorSyntaxInternal(USAGE_I_CONVERTTORAW, Internal::tr("Mandatory filename parameter missing"));
1493 if (dst.isEmpty())
1494 return errorSyntaxInternal(USAGE_I_CONVERTTORAW, Internal::tr("Mandatory outputfile parameter missing"));
1495
1496 PVDISK pDisk = NULL;
1497
1498 PVDINTERFACE pVDIfs = NULL;
1499 VDINTERFACEERROR vdInterfaceError;
1500 vdInterfaceError.pfnError = handleVDError;
1501 vdInterfaceError.pfnMessage = handleVDMessage;
1502
1503 int vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1504 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1505 AssertRC(vrc);
1506
1507 /** @todo Support convert to raw for floppy and DVD images too. */
1508 vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1509 if (RT_FAILURE(vrc))
1510 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot create the virtual disk container: %Rrc"), vrc);
1511
1512 /* Open raw output file. */
1513 RTFILE outFile;
1514 vrc = VINF_SUCCESS;
1515 if (fWriteToStdOut)
1516 vrc = RTFileFromNative(&outFile, 1);
1517 else
1518 vrc = RTFileOpen(&outFile, dst.c_str(), RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
1519 if (RT_FAILURE(vrc))
1520 {
1521 VDCloseAll(pDisk);
1522 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot create destination file \"%s\": %Rrc"),
1523 dst.c_str(), vrc);
1524 }
1525
1526 if (srcformat.isEmpty())
1527 {
1528 char *pszFormat = NULL;
1529 VDTYPE enmType = VDTYPE_INVALID;
1530 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
1531 src.c_str(), VDTYPE_INVALID, &pszFormat, &enmType);
1532 if (RT_FAILURE(vrc) || enmType != VDTYPE_HDD)
1533 {
1534 VDCloseAll(pDisk);
1535 if (!fWriteToStdOut)
1536 {
1537 RTFileClose(outFile);
1538 RTFileDelete(dst.c_str());
1539 }
1540 if (RT_FAILURE(vrc))
1541 RTMsgError(Internal::tr("No file format specified and autodetect failed - please specify format: %Rrc"),
1542 vrc);
1543 else
1544 RTMsgError(Internal::tr("Only converting harddisk images is supported"));
1545 return RTEXITCODE_FAILURE;
1546 }
1547 srcformat = pszFormat;
1548 RTStrFree(pszFormat);
1549 }
1550 vrc = VDOpen(pDisk, srcformat.c_str(), src.c_str(), VD_OPEN_FLAGS_READONLY, NULL);
1551 if (RT_FAILURE(vrc))
1552 {
1553 VDCloseAll(pDisk);
1554 if (!fWriteToStdOut)
1555 {
1556 RTFileClose(outFile);
1557 RTFileDelete(dst.c_str());
1558 }
1559 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot open the source image: %Rrc"), vrc);
1560 }
1561
1562 uint64_t cbSize = VDGetSize(pDisk, VD_LAST_IMAGE);
1563 uint64_t offFile = 0;
1564#define RAW_BUFFER_SIZE _128K
1565 size_t cbBuf = RAW_BUFFER_SIZE;
1566 void *pvBuf = RTMemAlloc(cbBuf);
1567 if (pvBuf)
1568 {
1569 RTStrmPrintf(g_pStdErr, Internal::tr("Converting image \"%s\" with size %RU64 bytes (%RU64MB) to raw...\n", "", cbSize),
1570 src.c_str(), cbSize, (cbSize + _1M - 1) / _1M);
1571 while (offFile < cbSize)
1572 {
1573 size_t cb = (size_t)RT_MIN(cbSize - offFile, cbBuf);
1574 vrc = VDRead(pDisk, offFile, pvBuf, cb);
1575 if (RT_FAILURE(vrc))
1576 break;
1577 vrc = RTFileWrite(outFile, pvBuf, cb, NULL);
1578 if (RT_FAILURE(vrc))
1579 break;
1580 offFile += cb;
1581 }
1582 RTMemFree(pvBuf);
1583 if (RT_FAILURE(vrc))
1584 {
1585 VDCloseAll(pDisk);
1586 if (!fWriteToStdOut)
1587 {
1588 RTFileClose(outFile);
1589 RTFileDelete(dst.c_str());
1590 }
1591 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot copy image data: %Rrc"), vrc);
1592 }
1593 }
1594 else
1595 {
1596 vrc = VERR_NO_MEMORY;
1597 VDCloseAll(pDisk);
1598 if (!fWriteToStdOut)
1599 {
1600 RTFileClose(outFile);
1601 RTFileDelete(dst.c_str());
1602 }
1603 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Out of memory allocating read buffer"));
1604 }
1605
1606 if (!fWriteToStdOut)
1607 RTFileClose(outFile);
1608 VDCloseAll(pDisk);
1609 return RTEXITCODE_SUCCESS;
1610}
1611
1612static RTEXITCODE CmdConvertHardDisk(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1613{
1614 RT_NOREF(aVirtualBox, aSession);
1615 Utf8Str srcformat;
1616 Utf8Str dstformat;
1617 Utf8Str src;
1618 Utf8Str dst;
1619 int vrc;
1620 PVDISK pSrcDisk = NULL;
1621 PVDISK pDstDisk = NULL;
1622 VDTYPE enmSrcType = VDTYPE_INVALID;
1623
1624 /* Parse the arguments. */
1625 for (int i = 0; i < argc; i++)
1626 {
1627 if (strcmp(argv[i], "-srcformat") == 0)
1628 {
1629 if (argc <= i + 1)
1630 {
1631 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1632 }
1633 i++;
1634 srcformat = argv[i];
1635 }
1636 else if (strcmp(argv[i], "-dstformat") == 0)
1637 {
1638 if (argc <= i + 1)
1639 {
1640 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1641 }
1642 i++;
1643 dstformat = argv[i];
1644 }
1645 else if (src.isEmpty())
1646 {
1647 src = argv[i];
1648 }
1649 else if (dst.isEmpty())
1650 {
1651 dst = argv[i];
1652 }
1653 else
1654 {
1655 return errorSyntaxInternal(USAGE_I_CONVERTHD, Internal::tr("Invalid parameter '%s'"), argv[i]);
1656 }
1657 }
1658
1659 if (src.isEmpty())
1660 return errorSyntaxInternal(USAGE_I_CONVERTHD, Internal::tr("Mandatory input image parameter missing"));
1661 if (dst.isEmpty())
1662 return errorSyntaxInternal(USAGE_I_CONVERTHD, Internal::tr("Mandatory output image parameter missing"));
1663
1664
1665 PVDINTERFACE pVDIfs = NULL;
1666 VDINTERFACEERROR vdInterfaceError;
1667 vdInterfaceError.pfnError = handleVDError;
1668 vdInterfaceError.pfnMessage = handleVDMessage;
1669
1670 vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1671 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1672 AssertRC(vrc);
1673
1674 do
1675 {
1676 /* Try to determine input image format */
1677 if (srcformat.isEmpty())
1678 {
1679 char *pszFormat = NULL;
1680 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
1681 src.c_str(), VDTYPE_HDD, &pszFormat, &enmSrcType);
1682 if (RT_FAILURE(vrc))
1683 {
1684 RTMsgError(Internal::tr("No file format specified and autodetect failed - please specify format: %Rrc"),
1685 vrc);
1686 break;
1687 }
1688 srcformat = pszFormat;
1689 RTStrFree(pszFormat);
1690 }
1691
1692 vrc = VDCreate(pVDIfs, enmSrcType, &pSrcDisk);
1693 if (RT_FAILURE(vrc))
1694 {
1695 RTMsgError(Internal::tr("Cannot create the source virtual disk container: %Rrc"), vrc);
1696 break;
1697 }
1698
1699 /* Open the input image */
1700 vrc = VDOpen(pSrcDisk, srcformat.c_str(), src.c_str(), VD_OPEN_FLAGS_READONLY, NULL);
1701 if (RT_FAILURE(vrc))
1702 {
1703 RTMsgError(Internal::tr("Cannot open the source image: %Rrc"), vrc);
1704 break;
1705 }
1706
1707 /* Output format defaults to VDI */
1708 if (dstformat.isEmpty())
1709 dstformat = "VDI";
1710
1711 vrc = VDCreate(pVDIfs, enmSrcType, &pDstDisk);
1712 if (RT_FAILURE(vrc))
1713 {
1714 RTMsgError(Internal::tr("Cannot create the destination virtual disk container: %Rrc"), vrc);
1715 break;
1716 }
1717
1718 uint64_t cbSize = VDGetSize(pSrcDisk, VD_LAST_IMAGE);
1719 RTStrmPrintf(g_pStdErr, Internal::tr("Converting image \"%s\" with size %RU64 bytes (%RU64MB)...\n", "", cbSize),
1720 src.c_str(), cbSize, (cbSize + _1M - 1) / _1M);
1721
1722 /* Create the output image */
1723 vrc = VDCopy(pSrcDisk, VD_LAST_IMAGE, pDstDisk, dstformat.c_str(),
1724 dst.c_str(), false, 0, VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED,
1725 NULL, VD_OPEN_FLAGS_NORMAL, NULL, NULL, NULL);
1726 if (RT_FAILURE(vrc))
1727 {
1728 RTMsgError(Internal::tr("Cannot copy the image: %Rrc"), vrc);
1729 break;
1730 }
1731 }
1732 while (0);
1733 if (pDstDisk)
1734 VDCloseAll(pDstDisk);
1735 if (pSrcDisk)
1736 VDCloseAll(pSrcDisk);
1737
1738 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1739}
1740
1741/**
1742 * Tries to repair a corrupted hard disk image.
1743 *
1744 * @returns VBox status code
1745 */
1746static RTEXITCODE CmdRepairHardDisk(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1747{
1748 RT_NOREF(aVirtualBox, aSession);
1749 Utf8Str image;
1750 Utf8Str format;
1751 int vrc;
1752 bool fDryRun = false;
1753
1754 /* Parse the arguments. */
1755 for (int i = 0; i < argc; i++)
1756 {
1757 if (strcmp(argv[i], "-dry-run") == 0)
1758 {
1759 fDryRun = true;
1760 }
1761 else if (strcmp(argv[i], "-format") == 0)
1762 {
1763 if (argc <= i + 1)
1764 {
1765 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1766 }
1767 i++;
1768 format = argv[i];
1769 }
1770 else if (image.isEmpty())
1771 {
1772 image = argv[i];
1773 }
1774 else
1775 {
1776 return errorSyntaxInternal(USAGE_I_REPAIRHD, Internal::tr("Invalid parameter '%s'"), argv[i]);
1777 }
1778 }
1779
1780 if (image.isEmpty())
1781 return errorSyntaxInternal(USAGE_I_REPAIRHD, Internal::tr("Mandatory input image parameter missing"));
1782
1783 PVDINTERFACE pVDIfs = NULL;
1784 VDINTERFACEERROR vdInterfaceError;
1785 vdInterfaceError.pfnError = handleVDError;
1786 vdInterfaceError.pfnMessage = handleVDMessage;
1787
1788 vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1789 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1790 AssertRC(vrc);
1791
1792 do
1793 {
1794 /* Try to determine input image format */
1795 if (format.isEmpty())
1796 {
1797 char *pszFormat = NULL;
1798 VDTYPE enmSrcType = VDTYPE_INVALID;
1799
1800 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
1801 image.c_str(), VDTYPE_HDD, &pszFormat, &enmSrcType);
1802 if (RT_FAILURE(vrc) && (vrc != VERR_VD_IMAGE_CORRUPTED))
1803 {
1804 RTMsgError(Internal::tr("No file format specified and autodetect failed - please specify format: %Rrc"),
1805 vrc);
1806 break;
1807 }
1808 format = pszFormat;
1809 RTStrFree(pszFormat);
1810 }
1811
1812 uint32_t fFlags = 0;
1813 if (fDryRun)
1814 fFlags |= VD_REPAIR_DRY_RUN;
1815
1816 vrc = VDRepair(pVDIfs, NULL, image.c_str(), format.c_str(), fFlags);
1817 }
1818 while (0);
1819
1820 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1821}
1822
1823/**
1824 * Unloads the necessary driver.
1825 *
1826 * @returns VBox status code
1827 */
1828static RTEXITCODE CmdModUninstall(void)
1829{
1830 int vrc = SUPR3Uninstall();
1831 if (RT_SUCCESS(vrc) || vrc == VERR_NOT_IMPLEMENTED)
1832 return RTEXITCODE_SUCCESS;
1833 return RTEXITCODE_FAILURE;
1834}
1835
1836/**
1837 * Loads the necessary driver.
1838 *
1839 * @returns VBox status code
1840 */
1841static RTEXITCODE CmdModInstall(void)
1842{
1843 int vrc = SUPR3Install();
1844 if (RT_SUCCESS(vrc) || vrc == VERR_NOT_IMPLEMENTED)
1845 return RTEXITCODE_SUCCESS;
1846 return RTEXITCODE_FAILURE;
1847}
1848
1849static RTEXITCODE CmdDebugLog(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1850{
1851 /*
1852 * The first parameter is the name or UUID of a VM with a direct session
1853 * that we wish to open.
1854 */
1855 if (argc < 1)
1856 return errorSyntaxInternal(USAGE_I_DEBUGLOG, Internal::tr("Missing VM name/UUID"));
1857
1858 ComPtr<IMachine> ptrMachine;
1859 HRESULT hrc;
1860 CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
1861 ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
1862
1863 CHECK_ERROR_RET(ptrMachine, LockMachine(aSession, LockType_Shared), RTEXITCODE_FAILURE);
1864
1865 /*
1866 * Get the debugger interface.
1867 */
1868 ComPtr<IConsole> ptrConsole;
1869 CHECK_ERROR_RET(aSession, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
1870
1871 ComPtr<IMachineDebugger> ptrDebugger;
1872 CHECK_ERROR_RET(ptrConsole, COMGETTER(Debugger)(ptrDebugger.asOutParam()), RTEXITCODE_FAILURE);
1873
1874 /*
1875 * Parse the command.
1876 */
1877 bool fEnablePresent = false;
1878 bool fEnable = false;
1879 bool fFlagsPresent = false;
1880 RTCString strFlags;
1881 bool fGroupsPresent = false;
1882 RTCString strGroups;
1883 bool fDestsPresent = false;
1884 RTCString strDests;
1885
1886 static const RTGETOPTDEF s_aOptions[] =
1887 {
1888 { "--disable", 'E', RTGETOPT_REQ_NOTHING },
1889 { "--enable", 'e', RTGETOPT_REQ_NOTHING },
1890 { "--flags", 'f', RTGETOPT_REQ_STRING },
1891 { "--groups", 'g', RTGETOPT_REQ_STRING },
1892 { "--destinations", 'd', RTGETOPT_REQ_STRING }
1893 };
1894
1895 int ch;
1896 RTGETOPTUNION ValueUnion;
1897 RTGETOPTSTATE GetState;
1898 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
1899 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1900 {
1901 switch (ch)
1902 {
1903 case 'e':
1904 fEnablePresent = true;
1905 fEnable = true;
1906 break;
1907
1908 case 'E':
1909 fEnablePresent = true;
1910 fEnable = false;
1911 break;
1912
1913 case 'f':
1914 fFlagsPresent = true;
1915 if (*ValueUnion.psz)
1916 {
1917 if (strFlags.isNotEmpty())
1918 strFlags.append(' ');
1919 strFlags.append(ValueUnion.psz);
1920 }
1921 break;
1922
1923 case 'g':
1924 fGroupsPresent = true;
1925 if (*ValueUnion.psz)
1926 {
1927 if (strGroups.isNotEmpty())
1928 strGroups.append(' ');
1929 strGroups.append(ValueUnion.psz);
1930 }
1931 break;
1932
1933 case 'd':
1934 fDestsPresent = true;
1935 if (*ValueUnion.psz)
1936 {
1937 if (strDests.isNotEmpty())
1938 strDests.append(' ');
1939 strDests.append(ValueUnion.psz);
1940 }
1941 break;
1942
1943 default:
1944 return errorGetOptInternal(USAGE_I_DEBUGLOG, ch, &ValueUnion);
1945 }
1946 }
1947
1948 /*
1949 * Do the job.
1950 */
1951 if (fEnablePresent && !fEnable)
1952 CHECK_ERROR_RET(ptrDebugger, COMSETTER(LogEnabled)(FALSE), RTEXITCODE_FAILURE);
1953
1954 /** @todo flags, groups destination. */
1955 if (fFlagsPresent || fGroupsPresent || fDestsPresent)
1956 RTMsgWarning(Internal::tr("One or more of the requested features are not implemented! Feel free to do this."));
1957
1958 if (fEnablePresent && fEnable)
1959 CHECK_ERROR_RET(ptrDebugger, COMSETTER(LogEnabled)(TRUE), RTEXITCODE_FAILURE);
1960 return RTEXITCODE_SUCCESS;
1961}
1962
1963/**
1964 * Generate a SHA-256 password hash
1965 */
1966static RTEXITCODE CmdGeneratePasswordHash(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1967{
1968 RT_NOREF(aVirtualBox, aSession);
1969
1970 /* one parameter, the password to hash */
1971 if (argc != 1)
1972 return errorSyntaxInternal(USAGE_I_PASSWORDHASH, Internal::tr("password to hash required"));
1973
1974 uint8_t abDigest[RTSHA256_HASH_SIZE];
1975 RTSha256(argv[0], strlen(argv[0]), abDigest);
1976 char pszDigest[RTSHA256_DIGEST_LEN + 1];
1977 RTSha256ToString(abDigest, pszDigest, sizeof(pszDigest));
1978 RTPrintf(Internal::tr("Password hash: %s\n"), pszDigest);
1979
1980 return RTEXITCODE_SUCCESS;
1981}
1982
1983/**
1984 * Print internal guest statistics or
1985 * set internal guest statistics update interval if specified
1986 */
1987static RTEXITCODE CmdGuestStats(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1988{
1989 /* one parameter, guest name */
1990 if (argc < 1)
1991 return errorSyntaxInternal(USAGE_I_GUESTSTATS, Internal::tr("Missing VM name/UUID"));
1992
1993 /*
1994 * Parse the command.
1995 */
1996 ULONG aUpdateInterval = 0;
1997
1998 static const RTGETOPTDEF s_aOptions[] =
1999 {
2000 { "--interval", 'i', RTGETOPT_REQ_UINT32 }
2001 };
2002
2003 int ch;
2004 RTGETOPTUNION ValueUnion;
2005 RTGETOPTSTATE GetState;
2006 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
2007 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
2008 {
2009 switch (ch)
2010 {
2011 case 'i':
2012 aUpdateInterval = ValueUnion.u32;
2013 break;
2014
2015 default:
2016 return errorGetOptInternal(USAGE_I_GUESTSTATS, ch, &ValueUnion);
2017 }
2018 }
2019
2020 if (argc > 1 && aUpdateInterval == 0)
2021 return errorSyntaxInternal(USAGE_I_GUESTSTATS, Internal::tr("Invalid update interval specified"));
2022
2023 RTPrintf(Internal::tr("argc=%d interval=%u\n"), argc, aUpdateInterval);
2024
2025 ComPtr<IMachine> ptrMachine;
2026 HRESULT hrc;
2027 CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
2028 ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
2029
2030 CHECK_ERROR_RET(ptrMachine, LockMachine(aSession, LockType_Shared), RTEXITCODE_FAILURE);
2031
2032 /*
2033 * Get the guest interface.
2034 */
2035 ComPtr<IConsole> ptrConsole;
2036 CHECK_ERROR_RET(aSession, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
2037
2038 ComPtr<IGuest> ptrGuest;
2039 CHECK_ERROR_RET(ptrConsole, COMGETTER(Guest)(ptrGuest.asOutParam()), RTEXITCODE_FAILURE);
2040
2041 if (aUpdateInterval)
2042 CHECK_ERROR_RET(ptrGuest, COMSETTER(StatisticsUpdateInterval)(aUpdateInterval), RTEXITCODE_FAILURE);
2043 else
2044 {
2045 ULONG mCpuUser, mCpuKernel, mCpuIdle;
2046 ULONG mMemTotal, mMemFree, mMemBalloon, mMemShared, mMemCache, mPageTotal;
2047 ULONG ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal, ulMemSharedTotal;
2048
2049 CHECK_ERROR_RET(ptrGuest, InternalGetStatistics(&mCpuUser, &mCpuKernel, &mCpuIdle,
2050 &mMemTotal, &mMemFree, &mMemBalloon, &mMemShared, &mMemCache,
2051 &mPageTotal, &ulMemAllocTotal, &ulMemFreeTotal,
2052 &ulMemBalloonTotal, &ulMemSharedTotal),
2053 RTEXITCODE_FAILURE);
2054 RTPrintf("mCpuUser=%u mCpuKernel=%u mCpuIdle=%u\n"
2055 "mMemTotal=%u mMemFree=%u mMemBalloon=%u mMemShared=%u mMemCache=%u\n"
2056 "mPageTotal=%u ulMemAllocTotal=%u ulMemFreeTotal=%u ulMemBalloonTotal=%u ulMemSharedTotal=%u\n",
2057 mCpuUser, mCpuKernel, mCpuIdle,
2058 mMemTotal, mMemFree, mMemBalloon, mMemShared, mMemCache,
2059 mPageTotal, ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal, ulMemSharedTotal);
2060
2061 }
2062
2063 return RTEXITCODE_SUCCESS;
2064}
2065
2066
2067/**
2068 * Wrapper for handling internal commands
2069 */
2070RTEXITCODE handleInternalCommands(HandlerArg *a)
2071{
2072 /* at least a command is required */
2073 if (a->argc < 1)
2074 return errorSyntaxInternal(USAGE_I_ALL, Internal::tr("Command missing"));
2075
2076 /*
2077 * The 'string switch' on command name.
2078 */
2079 const char *pszCmd = a->argv[0];
2080 if (!strcmp(pszCmd, "loadmap"))
2081 return CmdLoadMap(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2082 if (!strcmp(pszCmd, "loadsyms"))
2083 return CmdLoadSyms(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2084 //if (!strcmp(pszCmd, "unloadsyms"))
2085 // return CmdUnloadSyms(argc - 1, &a->argv[1]);
2086 if (!strcmp(pszCmd, "sethduuid") || !strcmp(pszCmd, "sethdparentuuid"))
2087 return CmdSetHDUUID(a->argc, &a->argv[0], a->virtualBox, a->session);
2088 if (!strcmp(pszCmd, "dumphdinfo"))
2089 return CmdDumpHDInfo(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2090 if (!strcmp(pszCmd, "listpartitions"))
2091 return CmdListPartitions(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2092 if (!strcmp(pszCmd, "createrawvmdk"))
2093 return CmdCreateRawVMDK(a->argc - 1, &a->argv[1], a);
2094 if (!strcmp(pszCmd, "renamevmdk"))
2095 return CmdRenameVMDK(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2096 if (!strcmp(pszCmd, "converttoraw"))
2097 return CmdConvertToRaw(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2098 if (!strcmp(pszCmd, "converthd"))
2099 return CmdConvertHardDisk(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2100 if (!strcmp(pszCmd, "modinstall"))
2101 return CmdModInstall();
2102 if (!strcmp(pszCmd, "moduninstall"))
2103 return CmdModUninstall();
2104 if (!strcmp(pszCmd, "debuglog"))
2105 return CmdDebugLog(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2106 if (!strcmp(pszCmd, "passwordhash"))
2107 return CmdGeneratePasswordHash(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2108 if (!strcmp(pszCmd, "gueststats"))
2109 return CmdGuestStats(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2110 if (!strcmp(pszCmd, "repairhd"))
2111 return CmdRepairHardDisk(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2112
2113 /* default: */
2114 return errorSyntaxInternal(USAGE_I_ALL, Internal::tr("Invalid command '%s'"), a->argv[0]);
2115}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use