VirtualBox

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

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

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 98.1 KB
Line 
1/* $Id: VBoxInternalManage.cpp 96407 2022-08-22 17:43:14Z 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-2022 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 shall 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 access to an entire host disk (if\n"
234 " the parameter -partitions is not specified) or some partitions of a\n"
235 " host disk. If access to individual partitions is granted, then the\n"
236 " parameter -mbr can be used to specify an alternative MBR to be used\n"
237 " (the partitioning information in the MBR file is ignored).\n"
238 " The diskname is on Linux e.g. /dev/sda, and on Windows e.g.\n"
239 " \\\\.\\PhysicalDrive0).\n"
240 " On Linux or FreeBSD host the parameter -relative causes a VMDK file to\n"
241 " be created which refers to individual partitions instead to the entire\n"
242 " disk.\n"
243 " The necessary partition numbers can be queried with\n"
244 " VBoxManage internalcommands listpartitions\n"
245 "\n")
246 : "",
247 (enmCommand == USAGE_I_RENAMEVMDK || enmCommand == USAGE_I_ALL)
248 ? Internal::tr(
249 " renamevmdk -from <filename> -to <filename>\n"
250 " Renames an existing VMDK image, including the base file and all its extents.\n"
251 "\n")
252 : "",
253 (enmCommand == USAGE_I_CONVERTTORAW || enmCommand == USAGE_I_ALL)
254#ifdef ENABLE_CONVERT_RAW_TO_STDOUT
255 ? Internal::tr(
256 " converttoraw [-format <fileformat>] <filename> <outputfile>|stdout"
257 "\n"
258 " Convert image to raw, writing to file or stdout.\n"
259 "\n")
260#else
261 ? Internal::tr(
262 " converttoraw [-format <fileformat>] <filename> <outputfile>"
263 "\n"
264 " Convert image to raw, writing to file.\n"
265 "\n")
266#endif
267 : "",
268 (enmCommand == USAGE_I_CONVERTHD || enmCommand == USAGE_I_ALL)
269 ? Internal::tr(
270 " converthd [-srcformat VDI|VMDK|VHD|RAW]\n"
271 " [-dstformat VDI|VMDK|VHD|RAW]\n"
272 " <inputfile> <outputfile>\n"
273 " converts hard disk images between formats\n"
274 "\n")
275 : "",
276 (enmCommand == USAGE_I_REPAIRHD || enmCommand == USAGE_I_ALL)
277 ? Internal::tr(
278 " repairhd [-dry-run]\n"
279 " [-format VDI|VMDK|VHD|...]\n"
280 " <filename>\n"
281 " Tries to repair corrupted disk images\n"
282 "\n")
283 : "",
284#ifdef RT_OS_WINDOWS
285 (enmCommand == USAGE_I_MODINSTALL || enmCommand == USAGE_I_ALL)
286 ? Internal::tr(
287 " modinstall\n"
288 " Installs the necessary driver for the host OS\n"
289 "\n")
290 : "",
291 (enmCommand == USAGE_I_MODUNINSTALL || enmCommand == USAGE_I_ALL)
292 ? Internal::tr(
293 " moduninstall\n"
294 " Deinstalls the driver\n"
295 "\n")
296 : "",
297#else
298 "",
299 "",
300#endif
301 (enmCommand == USAGE_I_DEBUGLOG || enmCommand == USAGE_I_ALL)
302 ? Internal::tr(
303 " debuglog <vmname|uuid> [--enable|--disable] [--flags todo]\n"
304 " [--groups todo] [--destinations todo]\n"
305 " Controls debug logging.\n"
306 "\n")
307 : "",
308 (enmCommand == USAGE_I_PASSWORDHASH || enmCommand == USAGE_I_ALL)
309 ? Internal::tr(
310 " passwordhash <password>\n"
311 " Generates a password hash.\n"
312 "\n")
313 : "",
314 (enmCommand == USAGE_I_GUESTSTATS || enmCommand == USAGE_I_ALL)
315 ? Internal::tr(
316 " gueststats <vmname|uuid> [--interval <seconds>]\n"
317 " Obtains and prints internal guest statistics.\n"
318 " Sets the update interval if specified.\n"
319 "\n")
320 : ""
321 );
322}
323
324
325/**
326 * Print a usage synopsis and the syntax error message.
327 * @returns RTEXITCODE_SYNTAX.
328 */
329static RTEXITCODE errorSyntaxInternal(USAGECATEGORY enmCommand, const char *pszFormat, ...)
330{
331 va_list args;
332 showLogo(g_pStdErr); // show logo even if suppressed
333
334 printUsageInternal(enmCommand, g_pStdErr);
335
336 va_start(args, pszFormat);
337 RTStrmPrintf(g_pStdErr, Internal::tr("\nSyntax error: %N\n"), pszFormat, &args);
338 va_end(args);
339 return RTEXITCODE_SYNTAX;
340}
341
342
343/**
344 * errorSyntaxInternal for RTGetOpt users.
345 *
346 * @returns RTEXITCODE_SYNTAX.
347 *
348 * @param enmCommand The command.
349 * @param rc The RTGetOpt return code.
350 * @param pValueUnion The value union.
351 */
352static RTEXITCODE errorGetOptInternal(USAGECATEGORY enmCommand, int rc, union RTGETOPTUNION const *pValueUnion)
353{
354 /*
355 * Check if it is an unhandled standard option.
356 */
357 if (rc == 'V')
358 {
359 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
360 return RTEXITCODE_SUCCESS;
361 }
362
363 if (rc == 'h')
364 {
365 showLogo(g_pStdErr);
366 printUsageInternal(enmCommand, g_pStdOut);
367 return RTEXITCODE_SUCCESS;
368 }
369
370 /*
371 * General failure.
372 */
373 showLogo(g_pStdErr); // show logo even if suppressed
374
375 printUsageInternal(enmCommand, g_pStdErr);
376
377 if (rc == VINF_GETOPT_NOT_OPTION)
378 return RTMsgErrorExit(RTEXITCODE_SYNTAX, Internal::tr("Invalid parameter '%s'"), pValueUnion->psz);
379 if (rc > 0)
380 {
381 if (RT_C_IS_PRINT(rc))
382 return RTMsgErrorExit(RTEXITCODE_SYNTAX, Internal::tr("Invalid option -%c"), rc);
383 return RTMsgErrorExit(RTEXITCODE_SYNTAX, Internal::tr("Invalid option case %i"), rc);
384 }
385 if (rc == VERR_GETOPT_UNKNOWN_OPTION)
386 return RTMsgErrorExit(RTEXITCODE_SYNTAX, Internal::tr("Unknown option: %s"), pValueUnion->psz);
387 if (rc == VERR_GETOPT_INVALID_ARGUMENT_FORMAT)
388 return RTMsgErrorExit(RTEXITCODE_SYNTAX, Internal::tr("Invalid argument format: %s"), pValueUnion->psz);
389 if (pValueUnion->pDef)
390 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "%s: %Rrs", pValueUnion->pDef->pszLong, rc);
391 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "%Rrs", rc);
392}
393
394
395/**
396 * Externally visible wrapper around printUsageInternal() to dump the
397 * complete usage text.
398 *
399 * @returns nothing.
400 * @param pStrm The stream to dump the usage text to.
401 */
402DECLHIDDEN(void) printUsageInternalCmds(PRTSTREAM pStrm)
403{
404 printUsageInternal(USAGE_I_ALL, pStrm);
405}
406
407
408/** @todo this is no longer necessary, we can enumerate extra data */
409/**
410 * Finds a new unique key name.
411 *
412 * I don't think this is 100% race condition proof, but we assumes
413 * the user is not trying to push this point.
414 *
415 * @returns Result from the insert.
416 * @param pMachine The Machine object.
417 * @param pszKeyBase The base key.
418 * @param rKey Reference to the string object in which we will return the key.
419 */
420static HRESULT NewUniqueKey(ComPtr<IMachine> pMachine, const char *pszKeyBase, Utf8Str &rKey)
421{
422 Bstr KeyBase(pszKeyBase);
423 Bstr Keys;
424 HRESULT hrc = pMachine->GetExtraData(KeyBase.raw(), Keys.asOutParam());
425 if (FAILED(hrc))
426 return hrc;
427
428 /* if there are no keys, it's simple. */
429 if (Keys.isEmpty())
430 {
431 rKey = "1";
432 return pMachine->SetExtraData(KeyBase.raw(), Bstr(rKey).raw());
433 }
434
435 /* find a unique number - brute force rulez. */
436 Utf8Str KeysUtf8(Keys);
437 const char *pszKeys = RTStrStripL(KeysUtf8.c_str());
438 for (unsigned i = 1; i < 1000000; i++)
439 {
440 char szKey[32];
441 size_t cchKey = RTStrPrintf(szKey, sizeof(szKey), "%#x", i);
442 const char *psz = strstr(pszKeys, szKey);
443 while (psz)
444 {
445 if ( ( psz == pszKeys
446 || psz[-1] == ' ')
447 && ( psz[cchKey] == ' '
448 || !psz[cchKey])
449 )
450 break;
451 psz = strstr(psz + cchKey, szKey);
452 }
453 if (!psz)
454 {
455 rKey = szKey;
456 Utf8StrFmt NewKeysUtf8("%s %s", pszKeys, szKey);
457 return pMachine->SetExtraData(KeyBase.raw(),
458 Bstr(NewKeysUtf8).raw());
459 }
460 }
461 RTMsgError(Internal::tr("Cannot find unique key for '%s'!"), pszKeyBase);
462 return E_FAIL;
463}
464
465
466#if 0
467/**
468 * Remove a key.
469 *
470 * I don't think this isn't 100% race condition proof, but we assumes
471 * the user is not trying to push this point.
472 *
473 * @returns Result from the insert.
474 * @param pMachine The machine object.
475 * @param pszKeyBase The base key.
476 * @param pszKey The key to remove.
477 */
478static HRESULT RemoveKey(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey)
479{
480 Bstr Keys;
481 HRESULT hrc = pMachine->GetExtraData(Bstr(pszKeyBase), Keys.asOutParam());
482 if (FAILED(hrc))
483 return hrc;
484
485 /* if there are no keys, it's simple. */
486 if (Keys.isEmpty())
487 return S_OK;
488
489 char *pszKeys;
490 int rc = RTUtf16ToUtf8(Keys.raw(), &pszKeys);
491 if (RT_SUCCESS(rc))
492 {
493 /* locate it */
494 size_t cchKey = strlen(pszKey);
495 char *psz = strstr(pszKeys, pszKey);
496 while (psz)
497 {
498 if ( ( psz == pszKeys
499 || psz[-1] == ' ')
500 && ( psz[cchKey] == ' '
501 || !psz[cchKey])
502 )
503 break;
504 psz = strstr(psz + cchKey, pszKey);
505 }
506 if (psz)
507 {
508 /* remove it */
509 char *pszNext = RTStrStripL(psz + cchKey);
510 if (*pszNext)
511 memmove(psz, pszNext, strlen(pszNext) + 1);
512 else
513 *psz = '\0';
514 psz = RTStrStrip(pszKeys);
515
516 /* update */
517 hrc = pMachine->SetExtraData(Bstr(pszKeyBase), Bstr(psz));
518 }
519
520 RTStrFree(pszKeys);
521 return hrc;
522 }
523 else
524 RTMsgError(Internal::tr("Failed to delete key '%s' from '%s', string conversion error %Rrc!"),
525 pszKey, pszKeyBase, rc);
526
527 return E_FAIL;
528}
529#endif
530
531
532/**
533 * Sets a key value, does necessary error bitching.
534 *
535 * @returns COM status code.
536 * @param pMachine The Machine object.
537 * @param pszKeyBase The key base.
538 * @param pszKey The key.
539 * @param pszAttribute The attribute name.
540 * @param pszValue The string value.
541 */
542static HRESULT SetString(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey, const char *pszAttribute, const char *pszValue)
543{
544 HRESULT hrc = pMachine->SetExtraData(BstrFmt("%s/%s/%s", pszKeyBase,
545 pszKey, pszAttribute).raw(),
546 Bstr(pszValue).raw());
547 if (FAILED(hrc))
548 RTMsgError(Internal::tr("Failed to set '%s/%s/%s' to '%s'! hrc=%#x"),
549 pszKeyBase, pszKey, pszAttribute, pszValue, hrc);
550 return hrc;
551}
552
553
554/**
555 * Sets a key value, does necessary error bitching.
556 *
557 * @returns COM status code.
558 * @param pMachine The Machine object.
559 * @param pszKeyBase The key base.
560 * @param pszKey The key.
561 * @param pszAttribute The attribute name.
562 * @param u64Value The value.
563 */
564static HRESULT SetUInt64(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey, const char *pszAttribute, uint64_t u64Value)
565{
566 char szValue[64];
567 RTStrPrintf(szValue, sizeof(szValue), "%#RX64", u64Value);
568 return SetString(pMachine, pszKeyBase, pszKey, pszAttribute, szValue);
569}
570
571
572/**
573 * Sets a key value, does necessary error bitching.
574 *
575 * @returns COM status code.
576 * @param pMachine The Machine object.
577 * @param pszKeyBase The key base.
578 * @param pszKey The key.
579 * @param pszAttribute The attribute name.
580 * @param i64Value The value.
581 */
582static HRESULT SetInt64(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey, const char *pszAttribute, int64_t i64Value)
583{
584 char szValue[64];
585 RTStrPrintf(szValue, sizeof(szValue), "%RI64", i64Value);
586 return SetString(pMachine, pszKeyBase, pszKey, pszAttribute, szValue);
587}
588
589
590/**
591 * Identical to the 'loadsyms' command.
592 */
593static RTEXITCODE CmdLoadSyms(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
594{
595 RT_NOREF(aSession);
596 HRESULT hrc;
597
598 /*
599 * Get the VM
600 */
601 ComPtr<IMachine> machine;
602 CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
603 machine.asOutParam()), RTEXITCODE_FAILURE);
604
605 /*
606 * Parse the command.
607 */
608 const char *pszFilename;
609 int64_t offDelta = 0;
610 const char *pszModule = NULL;
611 uint64_t ModuleAddress = UINT64_MAX;
612 uint64_t ModuleSize = 0;
613
614 /* filename */
615 if (argc < 2)
616 return errorArgument(Internal::tr("Missing the filename argument!\n"));
617 pszFilename = argv[1];
618
619 /* offDelta */
620 if (argc >= 3)
621 {
622 int vrc = RTStrToInt64Ex(argv[2], NULL, 0, &offDelta);
623 if (RT_FAILURE(vrc))
624 return errorArgument(argv[0], Internal::tr("Failed to read delta '%s', rc=%Rrc\n"), argv[2], vrc);
625 }
626
627 /* pszModule */
628 if (argc >= 4)
629 pszModule = argv[3];
630
631 /* ModuleAddress */
632 if (argc >= 5)
633 {
634 int vrc = RTStrToUInt64Ex(argv[4], NULL, 0, &ModuleAddress);
635 if (RT_FAILURE(vrc))
636 return errorArgument(argv[0], Internal::tr("Failed to read module address '%s', rc=%Rrc\n"), argv[4], vrc);
637 }
638
639 /* ModuleSize */
640 if (argc >= 6)
641 {
642 int vrc = RTStrToUInt64Ex(argv[5], NULL, 0, &ModuleSize);
643 if (RT_FAILURE(vrc))
644 return errorArgument(argv[0], Internal::tr("Failed to read module size '%s', rc=%Rrc\n"), argv[5], vrc);
645 }
646
647 /*
648 * Add extra data.
649 */
650 Utf8Str KeyStr;
651 hrc = NewUniqueKey(machine, "VBoxInternal/DBGF/loadsyms", KeyStr);
652 if (SUCCEEDED(hrc))
653 hrc = SetString(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "Filename", pszFilename);
654 if (SUCCEEDED(hrc) && argc >= 3)
655 hrc = SetInt64(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "Delta", offDelta);
656 if (SUCCEEDED(hrc) && argc >= 4)
657 hrc = SetString(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "Module", pszModule);
658 if (SUCCEEDED(hrc) && argc >= 5)
659 hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "ModuleAddress", ModuleAddress);
660 if (SUCCEEDED(hrc) && argc >= 6)
661 hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "ModuleSize", ModuleSize);
662
663 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
664}
665
666
667/**
668 * Identical to the 'loadmap' command.
669 */
670static RTEXITCODE CmdLoadMap(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
671{
672 RT_NOREF(aSession);
673 HRESULT hrc;
674
675 /*
676 * Get the VM
677 */
678 ComPtr<IMachine> machine;
679 CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
680 machine.asOutParam()), RTEXITCODE_FAILURE);
681
682 /*
683 * Parse the command.
684 */
685 const char *pszFilename;
686 uint64_t ModuleAddress = UINT64_MAX;
687 const char *pszModule = NULL;
688 uint64_t offSubtrahend = 0;
689 uint32_t iSeg = UINT32_MAX;
690
691 /* filename */
692 if (argc < 2)
693 return errorArgument(Internal::tr("Missing the filename argument!\n"));
694 pszFilename = argv[1];
695
696 /* address */
697 if (argc < 3)
698 return errorArgument(Internal::tr("Missing the module address argument!\n"));
699 int vrc = RTStrToUInt64Ex(argv[2], NULL, 0, &ModuleAddress);
700 if (RT_FAILURE(vrc))
701 return errorArgument(argv[0], Internal::tr("Failed to read module address '%s', rc=%Rrc\n"), argv[2], vrc);
702
703 /* name (optional) */
704 if (argc > 3)
705 pszModule = argv[3];
706
707 /* subtrahend (optional) */
708 if (argc > 4)
709 {
710 vrc = RTStrToUInt64Ex(argv[4], NULL, 0, &offSubtrahend);
711 if (RT_FAILURE(vrc))
712 return errorArgument(argv[0], Internal::tr("Failed to read subtrahend '%s', rc=%Rrc\n"), argv[4], vrc);
713 }
714
715 /* segment (optional) */
716 if (argc > 5)
717 {
718 vrc = RTStrToUInt32Ex(argv[5], NULL, 0, &iSeg);
719 if (RT_FAILURE(vrc))
720 return errorArgument(argv[0], Internal::tr("Failed to read segment number '%s', rc=%Rrc\n"), argv[5], vrc);
721 }
722
723 /*
724 * Add extra data.
725 */
726 Utf8Str KeyStr;
727 hrc = NewUniqueKey(machine, "VBoxInternal/DBGF/loadmap", KeyStr);
728 if (SUCCEEDED(hrc))
729 hrc = SetString(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Filename", pszFilename);
730 if (SUCCEEDED(hrc))
731 hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Address", ModuleAddress);
732 if (SUCCEEDED(hrc) && pszModule != NULL)
733 hrc = SetString(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Name", pszModule);
734 if (SUCCEEDED(hrc) && offSubtrahend != 0)
735 hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Subtrahend", offSubtrahend);
736 if (SUCCEEDED(hrc) && iSeg != UINT32_MAX)
737 hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadmap", KeyStr.c_str(), "Segment", iSeg);
738
739 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
740}
741
742
743static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va)
744{
745 RT_NOREF(pvUser);
746 RTMsgErrorV(pszFormat, va);
747 RTMsgError(Internal::tr("Error code %Rrc at %s(%u) in function %s"), rc, RT_SRC_POS_ARGS);
748}
749
750static DECLCALLBACK(int) handleVDMessage(void *pvUser, const char *pszFormat, va_list va)
751{
752 NOREF(pvUser);
753 return RTPrintfV(pszFormat, va);
754}
755
756static RTEXITCODE CmdSetHDUUID(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
757{
758 RT_NOREF(aVirtualBox, aSession);
759 Guid uuid;
760 RTUUID rtuuid;
761 enum eUuidType {
762 HDUUID,
763 HDPARENTUUID
764 } uuidType;
765
766 if (!strcmp(argv[0], "sethduuid"))
767 {
768 uuidType = HDUUID;
769 if (argc != 3 && argc != 2)
770 return errorSyntaxInternal(USAGE_I_SETHDUUID, Internal::tr("Not enough parameters"));
771 /* if specified, take UUID, otherwise generate a new one */
772 if (argc == 3)
773 {
774 if (RT_FAILURE(RTUuidFromStr(&rtuuid, argv[2])))
775 return errorSyntaxInternal(USAGE_I_SETHDUUID, Internal::tr("Invalid UUID parameter"));
776 uuid = argv[2];
777 } else
778 uuid.create();
779 }
780 else if (!strcmp(argv[0], "sethdparentuuid"))
781 {
782 uuidType = HDPARENTUUID;
783 if (argc != 3)
784 return errorSyntaxInternal(USAGE_I_SETHDPARENTUUID, Internal::tr("Not enough parameters"));
785 if (RT_FAILURE(RTUuidFromStr(&rtuuid, argv[2])))
786 return errorSyntaxInternal(USAGE_I_SETHDPARENTUUID, Internal::tr("Invalid UUID parameter"));
787 uuid = argv[2];
788 }
789 else
790 return errorSyntaxInternal(USAGE_I_SETHDUUID, Internal::tr("Invalid invocation"));
791
792 /* just try it */
793 char *pszFormat = NULL;
794 VDTYPE enmType = VDTYPE_INVALID;
795 int rc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
796 argv[1], VDTYPE_INVALID, &pszFormat, &enmType);
797 if (RT_FAILURE(rc))
798 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Format autodetect failed: %Rrc"), rc);
799
800 PVDISK pDisk = NULL;
801
802 PVDINTERFACE pVDIfs = NULL;
803 VDINTERFACEERROR vdInterfaceError;
804 vdInterfaceError.pfnError = handleVDError;
805 vdInterfaceError.pfnMessage = handleVDMessage;
806
807 rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
808 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
809 AssertRC(rc);
810
811 rc = VDCreate(pVDIfs, enmType, &pDisk);
812 if (RT_FAILURE(rc))
813 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot create the virtual disk container: %Rrc"), rc);
814
815 /* Open the image */
816 rc = VDOpen(pDisk, pszFormat, argv[1], VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_INFO, NULL);
817 if (RT_FAILURE(rc))
818 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot open the image: %Rrc"), rc);
819
820 if (uuidType == HDUUID)
821 rc = VDSetUuid(pDisk, VD_LAST_IMAGE, uuid.raw());
822 else
823 rc = VDSetParentUuid(pDisk, VD_LAST_IMAGE, uuid.raw());
824 if (RT_FAILURE(rc))
825 RTMsgError(Internal::tr("Cannot set a new UUID: %Rrc"), rc);
826 else
827 RTPrintf(Internal::tr("UUID changed to: %s\n"), uuid.toString().c_str());
828
829 VDCloseAll(pDisk);
830
831 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
832}
833
834
835static RTEXITCODE CmdDumpHDInfo(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
836{
837 RT_NOREF(aVirtualBox, aSession);
838
839 /* we need exactly one parameter: the image file */
840 if (argc != 1)
841 {
842 return errorSyntaxInternal(USAGE_I_DUMPHDINFO, Internal::tr("Not enough parameters"));
843 }
844
845 /* just try it */
846 char *pszFormat = NULL;
847 VDTYPE enmType = VDTYPE_INVALID;
848 int rc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
849 argv[0], VDTYPE_INVALID, &pszFormat, &enmType);
850 if (RT_FAILURE(rc))
851 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Format autodetect failed: %Rrc"), rc);
852
853 PVDISK pDisk = NULL;
854
855 PVDINTERFACE pVDIfs = NULL;
856 VDINTERFACEERROR vdInterfaceError;
857 vdInterfaceError.pfnError = handleVDError;
858 vdInterfaceError.pfnMessage = handleVDMessage;
859
860 rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
861 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
862 AssertRC(rc);
863
864 rc = VDCreate(pVDIfs, enmType, &pDisk);
865 if (RT_FAILURE(rc))
866 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot create the virtual disk container: %Rrc"), rc);
867
868 /* Open the image */
869 rc = VDOpen(pDisk, pszFormat, argv[0], VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO, NULL);
870 if (RT_FAILURE(rc))
871 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot open the image: %Rrc"), rc);
872
873 VDDumpImages(pDisk);
874
875 VDCloseAll(pDisk);
876
877 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
878}
879
880static int partRead(RTFILE File, PHOSTPARTITIONS pPart)
881{
882 uint8_t aBuffer[512];
883 uint8_t partitionTableHeader[512];
884 uint32_t sector_size = 512;
885 uint64_t lastUsableLBA = 0;
886 int rc;
887
888 VDISKPARTTYPE partitioningType;
889
890 pPart->cPartitions = 0;
891 memset(pPart->aPartitions, '\0', sizeof(pPart->aPartitions));
892
893 rc = RTFileReadAt(File, 0, &aBuffer, sizeof(aBuffer), NULL);
894 if (RT_FAILURE(rc))
895 return rc;
896
897 if (aBuffer[450] == 0xEE)/* check the sign of the GPT disk*/
898 {
899 partitioningType = VDISKPARTTYPE_GPT;
900 pPart->uPartitioningType = VDISKPARTTYPE_GPT;//partitioningType;
901
902 if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa)
903 return VERR_INVALID_PARAMETER;
904
905 rc = RTFileReadAt(File, sector_size, &partitionTableHeader, sector_size, NULL);
906 if (RT_SUCCESS(rc))
907 {
908 /** @todo r=bird: This is a 64-bit magic value, right... */
909 const char *l_ppth = (char *)partitionTableHeader;
910 if (strncmp(l_ppth, "EFI PART", 8))
911 return VERR_INVALID_PARAMETER;
912
913 /** @todo check GPT Version */
914
915 /** @todo r=bird: C have this handy concept called structures which
916 * greatly simplify data access... (Someone is really lazy here!) */
917#if 0 /* unused */
918 uint64_t firstUsableLBA = RT_MAKE_U64_FROM_U8(partitionTableHeader[40],
919 partitionTableHeader[41],
920 partitionTableHeader[42],
921 partitionTableHeader[43],
922 partitionTableHeader[44],
923 partitionTableHeader[45],
924 partitionTableHeader[46],
925 partitionTableHeader[47]
926 );
927#endif
928 lastUsableLBA = RT_MAKE_U64_FROM_U8(partitionTableHeader[48],
929 partitionTableHeader[49],
930 partitionTableHeader[50],
931 partitionTableHeader[51],
932 partitionTableHeader[52],
933 partitionTableHeader[53],
934 partitionTableHeader[54],
935 partitionTableHeader[55]
936 );
937 uint32_t partitionsNumber = RT_MAKE_U32_FROM_U8(partitionTableHeader[80],
938 partitionTableHeader[81],
939 partitionTableHeader[82],
940 partitionTableHeader[83]
941 );
942 uint32_t partitionEntrySize = RT_MAKE_U32_FROM_U8(partitionTableHeader[84],
943 partitionTableHeader[85],
944 partitionTableHeader[86],
945 partitionTableHeader[87]
946 );
947
948 uint32_t currentEntry = 0;
949
950 if (partitionEntrySize * partitionsNumber > 4 * _1M)
951 {
952 RTMsgError(Internal::tr("The GPT header seems corrupt because it contains too many entries"));
953 return VERR_INVALID_PARAMETER;
954 }
955
956 uint8_t *pbPartTable = (uint8_t *)RTMemAllocZ(RT_ALIGN_Z(partitionEntrySize * partitionsNumber, 512));
957 if (!pbPartTable)
958 {
959 RTMsgError(Internal::tr("Allocating memory for the GPT partitions entries failed"));
960 return VERR_NO_MEMORY;
961 }
962
963 /* partition entries begin from LBA2 */
964 /** @todo r=aeichner: Reading from LBA 2 is not always correct, the header will contain the starting LBA. */
965 rc = RTFileReadAt(File, 1024, pbPartTable, RT_ALIGN_Z(partitionEntrySize * partitionsNumber, 512), NULL);
966 if (RT_FAILURE(rc))
967 {
968 RTMsgError(Internal::tr("Reading the partition table failed"));
969 RTMemFree(pbPartTable);
970 return rc;
971 }
972
973 while (currentEntry < partitionsNumber)
974 {
975 uint8_t *partitionEntry = pbPartTable + currentEntry * partitionEntrySize;
976
977 uint64_t start = RT_MAKE_U64_FROM_U8(partitionEntry[32], partitionEntry[33], partitionEntry[34], partitionEntry[35],
978 partitionEntry[36], partitionEntry[37], partitionEntry[38], partitionEntry[39]);
979 uint64_t end = RT_MAKE_U64_FROM_U8(partitionEntry[40], partitionEntry[41], partitionEntry[42], partitionEntry[43],
980 partitionEntry[44], partitionEntry[45], partitionEntry[46], partitionEntry[47]);
981
982 PHOSTPARTITION pCP = &pPart->aPartitions[pPart->cPartitions++];
983 pCP->uIndex = currentEntry + 1;
984 pCP->uIndexWin = currentEntry + 1;
985 pCP->uType = 0;
986 pCP->uStartCylinder = 0;
987 pCP->uStartHead = 0;
988 pCP->uStartSector = 0;
989 pCP->uEndCylinder = 0;
990 pCP->uEndHead = 0;
991 pCP->uEndSector = 0;
992 pCP->uPartDataStart = 0; /* will be filled out later properly. */
993 pCP->cPartDataSectors = 0;
994 if (start==0 || end==0)
995 {
996 pCP->uIndex = 0;
997 pCP->uIndexWin = 0;
998 --pPart->cPartitions;
999 break;
1000 }
1001 else
1002 {
1003 pCP->uStart = start;
1004 pCP->uSize = (end +1) - start;/*+1 LBA because the last address is included*/
1005 }
1006
1007 ++currentEntry;
1008 }
1009
1010 RTMemFree(pbPartTable);
1011 }
1012 }
1013 else
1014 {
1015 partitioningType = VDISKPARTTYPE_MBR;
1016 pPart->uPartitioningType = VDISKPARTTYPE_MBR;//partitioningType;
1017
1018 if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa)
1019 return VERR_INVALID_PARAMETER;
1020
1021 unsigned uExtended = (unsigned)-1;
1022 unsigned uIndexWin = 1;
1023
1024 for (unsigned i = 0; i < 4; i++)
1025 {
1026 uint8_t *p = &aBuffer[0x1be + i * 16];
1027 if (p[4] == 0)
1028 continue;
1029 PHOSTPARTITION pCP = &pPart->aPartitions[pPart->cPartitions++];
1030 pCP->uIndex = i + 1;
1031 pCP->uType = p[4];
1032 pCP->uStartCylinder = (uint32_t)p[3] + ((uint32_t)(p[2] & 0xc0) << 2);
1033 pCP->uStartHead = p[1];
1034 pCP->uStartSector = p[2] & 0x3f;
1035 pCP->uEndCylinder = (uint32_t)p[7] + ((uint32_t)(p[6] & 0xc0) << 2);
1036 pCP->uEndHead = p[5];
1037 pCP->uEndSector = p[6] & 0x3f;
1038 pCP->uStart = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
1039 pCP->uSize = RT_MAKE_U32_FROM_U8(p[12], p[13], p[14], p[15]);
1040 pCP->uPartDataStart = 0; /* will be filled out later properly. */
1041 pCP->cPartDataSectors = 0;
1042
1043 if (PARTTYPE_IS_EXTENDED(p[4]))
1044 {
1045 if (uExtended == (unsigned)-1)
1046 {
1047 uExtended = (unsigned)(pCP - pPart->aPartitions);
1048 pCP->uIndexWin = 0;
1049 }
1050 else
1051 {
1052 RTMsgError(Internal::tr("More than one extended partition"));
1053 return VERR_INVALID_PARAMETER;
1054 }
1055 }
1056 else
1057 {
1058 pCP->uIndexWin = uIndexWin;
1059 uIndexWin++;
1060 }
1061 }
1062
1063 if (uExtended != (unsigned)-1)
1064 {
1065 unsigned uIndex = 5;
1066 uint64_t uStart = pPart->aPartitions[uExtended].uStart;
1067 uint64_t uOffset = 0;
1068 if (!uStart)
1069 {
1070 RTMsgError(Internal::tr("Inconsistency for logical partition start"));
1071 return VERR_INVALID_PARAMETER;
1072 }
1073
1074 do
1075 {
1076 rc = RTFileReadAt(File, (uStart + uOffset) * 512, &aBuffer, sizeof(aBuffer), NULL);
1077 if (RT_FAILURE(rc))
1078 return rc;
1079
1080 if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa)
1081 {
1082 RTMsgError(Internal::tr("Logical partition without magic"));
1083 return VERR_INVALID_PARAMETER;
1084 }
1085 uint8_t *p = &aBuffer[0x1be];
1086
1087 if (p[4] == 0)
1088 {
1089 RTMsgError(Internal::tr("Logical partition with type 0 encountered"));
1090 return VERR_INVALID_PARAMETER;
1091 }
1092
1093 PHOSTPARTITION pCP = &pPart->aPartitions[pPart->cPartitions++];
1094 pCP->uIndex = uIndex;
1095 pCP->uIndexWin = uIndexWin;
1096 pCP->uType = p[4];
1097 pCP->uStartCylinder = (uint32_t)p[3] + ((uint32_t)(p[2] & 0xc0) << 2);
1098 pCP->uStartHead = p[1];
1099 pCP->uStartSector = p[2] & 0x3f;
1100 pCP->uEndCylinder = (uint32_t)p[7] + ((uint32_t)(p[6] & 0xc0) << 2);
1101 pCP->uEndHead = p[5];
1102 pCP->uEndSector = p[6] & 0x3f;
1103 uint32_t uStartOffset = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
1104 if (!uStartOffset)
1105 {
1106 RTMsgError(Internal::tr("Invalid partition start offset"));
1107 return VERR_INVALID_PARAMETER;
1108 }
1109 pCP->uStart = uStart + uOffset + uStartOffset;
1110 pCP->uSize = RT_MAKE_U32_FROM_U8(p[12], p[13], p[14], p[15]);
1111 /* Fill out partitioning location info for EBR. */
1112 pCP->uPartDataStart = uStart + uOffset;
1113 pCP->cPartDataSectors = uStartOffset;
1114 p += 16;
1115 if (p[4] == 0)
1116 uExtended = (unsigned)-1;
1117 else if (PARTTYPE_IS_EXTENDED(p[4]))
1118 {
1119 uExtended = uIndex;
1120 uIndex++;
1121 uIndexWin++;
1122 uOffset = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
1123 }
1124 else
1125 {
1126 RTMsgError(Internal::tr("Logical partition chain broken"));
1127 return VERR_INVALID_PARAMETER;
1128 }
1129 } while (uExtended != (unsigned)-1);
1130 }
1131 }
1132
1133
1134 /* Sort partitions in ascending order of start sector, plus a trivial
1135 * bit of consistency checking. */
1136 for (unsigned i = 0; i < pPart->cPartitions-1; i++)
1137 {
1138 unsigned uMinIdx = i;
1139 uint64_t uMinVal = pPart->aPartitions[i].uStart;
1140 for (unsigned j = i + 1; j < pPart->cPartitions; j++)
1141 {
1142 if (pPart->aPartitions[j].uStart < uMinVal)
1143 {
1144 uMinIdx = j;
1145 uMinVal = pPart->aPartitions[j].uStart;
1146 }
1147 else if (pPart->aPartitions[j].uStart == uMinVal)
1148 {
1149 RTMsgError(Internal::tr("Two partitions start at the same place"));
1150 return VERR_INVALID_PARAMETER;
1151 }
1152 else if (pPart->aPartitions[j].uStart == 0)
1153 {
1154 RTMsgError(Internal::tr("Partition starts at sector 0"));
1155 return VERR_INVALID_PARAMETER;
1156 }
1157 }
1158 if (uMinIdx != i)
1159 {
1160 /* Swap entries at index i and uMinIdx. */
1161 memcpy(&pPart->aPartitions[pPart->cPartitions],
1162 &pPart->aPartitions[i], sizeof(HOSTPARTITION));
1163 memcpy(&pPart->aPartitions[i],
1164 &pPart->aPartitions[uMinIdx], sizeof(HOSTPARTITION));
1165 memcpy(&pPart->aPartitions[uMinIdx],
1166 &pPart->aPartitions[pPart->cPartitions], sizeof(HOSTPARTITION));
1167 }
1168 }
1169
1170 /* Fill out partitioning location info for MBR or GPT. */
1171 pPart->aPartitions[0].uPartDataStart = 0;
1172 pPart->aPartitions[0].cPartDataSectors = pPart->aPartitions[0].uStart;
1173
1174 /* Fill out partitioning location info for backup GPT. */
1175 if (partitioningType == VDISKPARTTYPE_GPT)
1176 {
1177 pPart->aPartitions[pPart->cPartitions-1].uPartDataStart = lastUsableLBA+1;
1178 pPart->aPartitions[pPart->cPartitions-1].cPartDataSectors = 33;
1179
1180 /* Now do a some partition table consistency checking, to reject the most
1181 * obvious garbage which can lead to trouble later. */
1182 uint64_t uPrevEnd = 0;
1183 for (unsigned i = 0; i < pPart->cPartitions; i++)
1184 {
1185 if (pPart->aPartitions[i].cPartDataSectors)
1186 uPrevEnd = pPart->aPartitions[i].uPartDataStart + pPart->aPartitions[i].cPartDataSectors;
1187 if (pPart->aPartitions[i].uStart < uPrevEnd &&
1188 pPart->cPartitions-1 != i)
1189 {
1190 RTMsgError(Internal::tr("Overlapping GPT partitions"));
1191 return VERR_INVALID_PARAMETER;
1192 }
1193 }
1194 }
1195 else
1196 {
1197 /* Now do a some partition table consistency checking, to reject the most
1198 * obvious garbage which can lead to trouble later. */
1199 uint64_t uPrevEnd = 0;
1200 for (unsigned i = 0; i < pPart->cPartitions; i++)
1201 {
1202 if (pPart->aPartitions[i].cPartDataSectors)
1203 uPrevEnd = pPart->aPartitions[i].uPartDataStart + pPart->aPartitions[i].cPartDataSectors;
1204 if (pPart->aPartitions[i].uStart < uPrevEnd)
1205 {
1206 RTMsgError(Internal::tr("Overlapping MBR partitions"));
1207 return VERR_INVALID_PARAMETER;
1208 }
1209 if (!PARTTYPE_IS_EXTENDED(pPart->aPartitions[i].uType))
1210 uPrevEnd = pPart->aPartitions[i].uStart + pPart->aPartitions[i].uSize;
1211 }
1212 }
1213
1214 return VINF_SUCCESS;
1215}
1216
1217static RTEXITCODE CmdListPartitions(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1218{
1219 RT_NOREF(aVirtualBox, aSession);
1220 Utf8Str rawdisk;
1221
1222 /* let's have a closer look at the arguments */
1223 for (int i = 0; i < argc; i++)
1224 {
1225 if (strcmp(argv[i], "-rawdisk") == 0)
1226 {
1227 if (argc <= i + 1)
1228 {
1229 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1230 }
1231 i++;
1232 rawdisk = argv[i];
1233 }
1234 else
1235 {
1236 return errorSyntaxInternal(USAGE_I_LISTPARTITIONS, Internal::tr("Invalid parameter '%s'"), argv[i]);
1237 }
1238 }
1239
1240 if (rawdisk.isEmpty())
1241 return errorSyntaxInternal(USAGE_I_LISTPARTITIONS, Internal::tr("Mandatory parameter -rawdisk missing"));
1242
1243 RTFILE hRawFile;
1244 int vrc = RTFileOpen(&hRawFile, rawdisk.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1245 if (RT_FAILURE(vrc))
1246 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot open the raw disk: %Rrc"), vrc);
1247
1248 HOSTPARTITIONS partitions;
1249 vrc = partRead(hRawFile, &partitions);
1250 /* Don't bail out on errors, print the table and return the result code. */
1251
1252 RTPrintf(Internal::tr("Number Type StartCHS EndCHS Size (MiB) Start (Sect)\n"));
1253 for (unsigned i = 0; i < partitions.cPartitions; i++)
1254 {
1255 /* Don't show the extended partition, otherwise users might think they
1256 * can add it to the list of partitions for raw partition access. */
1257 if (PARTTYPE_IS_EXTENDED(partitions.aPartitions[i].uType))
1258 continue;
1259
1260 RTPrintf("%-7u %#04x %-4u/%-3u/%-2u %-4u/%-3u/%-2u %10llu %10llu\n",
1261 partitions.aPartitions[i].uIndex,
1262 partitions.aPartitions[i].uType,
1263 partitions.aPartitions[i].uStartCylinder,
1264 partitions.aPartitions[i].uStartHead,
1265 partitions.aPartitions[i].uStartSector,
1266 partitions.aPartitions[i].uEndCylinder,
1267 partitions.aPartitions[i].uEndHead,
1268 partitions.aPartitions[i].uEndSector,
1269 partitions.aPartitions[i].uSize / 2048,
1270 partitions.aPartitions[i].uStart);
1271 }
1272
1273 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1274}
1275
1276static PVDISKRAWPARTDESC appendPartDesc(uint32_t *pcPartDescs, PVDISKRAWPARTDESC *ppPartDescs)
1277{
1278 (*pcPartDescs)++;
1279 PVDISKRAWPARTDESC p;
1280 p = (PVDISKRAWPARTDESC)RTMemRealloc(*ppPartDescs,
1281 *pcPartDescs * sizeof(VDISKRAWPARTDESC));
1282 *ppPartDescs = p;
1283 if (p)
1284 {
1285 p = p + *pcPartDescs - 1;
1286 memset(p, '\0', sizeof(VDISKRAWPARTDESC));
1287 }
1288
1289 return p;
1290}
1291
1292static RTEXITCODE CmdCreateRawVMDK(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1293{
1294 RT_NOREF(aVirtualBox, aSession);
1295 HRESULT hrc = S_OK;
1296 Utf8Str filename;
1297 const char *pszMBRFilename = NULL;
1298 Utf8Str rawdisk;
1299 const char *pszPartitions = NULL;
1300 bool fRelative = false;
1301
1302 uint64_t cbSize = 0;
1303 PVDISK pDisk = NULL;
1304 VDISKRAW RawDescriptor;
1305 PVDINTERFACE pVDIfs = NULL;
1306
1307 /* let's have a closer look at the arguments */
1308 for (int i = 0; i < argc; i++)
1309 {
1310 if (strcmp(argv[i], "-filename") == 0)
1311 {
1312 if (argc <= i + 1)
1313 {
1314 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1315 }
1316 i++;
1317 filename = argv[i];
1318 }
1319 else if (strcmp(argv[i], "-mbr") == 0)
1320 {
1321 if (argc <= i + 1)
1322 {
1323 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1324 }
1325 i++;
1326 pszMBRFilename = argv[i];
1327 }
1328 else if (strcmp(argv[i], "-rawdisk") == 0)
1329 {
1330 if (argc <= i + 1)
1331 {
1332 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1333 }
1334 i++;
1335 rawdisk = argv[i];
1336 }
1337 else if (strcmp(argv[i], "-partitions") == 0)
1338 {
1339 if (argc <= i + 1)
1340 {
1341 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
1342 }
1343 i++;
1344 pszPartitions = argv[i];
1345 }
1346#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_WINDOWS)
1347 else if (strcmp(argv[i], "-relative") == 0)
1348 {
1349 fRelative = true;
1350 }
1351#endif /* RT_OS_LINUX || RT_OS_FREEBSD */
1352 else
1353 return errorSyntaxInternal(USAGE_I_CREATERAWVMDK, Internal::tr("Invalid parameter '%s'"), argv[i]);
1354 }
1355
1356 if (filename.isEmpty())
1357 return errorSyntaxInternal(USAGE_I_CREATERAWVMDK, Internal::tr("Mandatory parameter -filename missing"));
1358 if (rawdisk.isEmpty())
1359 return errorSyntaxInternal(USAGE_I_CREATERAWVMDK, Internal::tr("Mandatory parameter -rawdisk missing"));
1360 if (!pszPartitions && pszMBRFilename)
1361 return errorSyntaxInternal(USAGE_I_CREATERAWVMDK,
1362 Internal::tr("The parameter -mbr is only valid when the parameter -partitions is also present"));
1363
1364#ifdef RT_OS_DARWIN
1365 fRelative = true;
1366#endif /* RT_OS_DARWIN */
1367 RTFILE hRawFile;
1368 int vrc = RTFileOpen(&hRawFile, rawdisk.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1369 if (RT_FAILURE(vrc))
1370 {
1371 RTMsgError(Internal::tr("Cannot open the raw disk '%s': %Rrc"), rawdisk.c_str(), vrc);
1372 goto out;
1373 }
1374
1375#ifdef RT_OS_WINDOWS
1376 /* Windows NT has no IOCTL_DISK_GET_LENGTH_INFORMATION ioctl. This was
1377 * added to Windows XP, so we have to use the available info from DriveGeo.
1378 * Note that we cannot simply use IOCTL_DISK_GET_DRIVE_GEOMETRY as it
1379 * yields a slightly different result than IOCTL_DISK_GET_LENGTH_INFO.
1380 * We call IOCTL_DISK_GET_DRIVE_GEOMETRY first as we need to check the media
1381 * type anyway, and if IOCTL_DISK_GET_LENGTH_INFORMATION is supported
1382 * we will later override cbSize.
1383 */
1384 DISK_GEOMETRY DriveGeo;
1385 DWORD cbDriveGeo;
1386 if (DeviceIoControl((HANDLE)RTFileToNative(hRawFile),
1387 IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
1388 &DriveGeo, sizeof(DriveGeo), &cbDriveGeo, NULL))
1389 {
1390 if ( DriveGeo.MediaType == FixedMedia
1391 || DriveGeo.MediaType == RemovableMedia)
1392 {
1393 cbSize = DriveGeo.Cylinders.QuadPart
1394 * DriveGeo.TracksPerCylinder
1395 * DriveGeo.SectorsPerTrack
1396 * DriveGeo.BytesPerSector;
1397 }
1398 else
1399 {
1400 RTMsgError(Internal::tr("File '%s' is no fixed/removable medium device"), rawdisk.c_str());
1401 vrc = VERR_INVALID_PARAMETER;
1402 goto out;
1403 }
1404
1405 GET_LENGTH_INFORMATION DiskLenInfo;
1406 DWORD junk;
1407 if (DeviceIoControl((HANDLE)RTFileToNative(hRawFile),
1408 IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
1409 &DiskLenInfo, sizeof(DiskLenInfo), &junk, (LPOVERLAPPED)NULL))
1410 {
1411 /* IOCTL_DISK_GET_LENGTH_INFO is supported -- override cbSize. */
1412 cbSize = DiskLenInfo.Length.QuadPart;
1413 }
1414 if ( fRelative
1415 && !rawdisk.startsWith("\\\\.\\PhysicalDrive", Utf8Str::CaseInsensitive))
1416 {
1417 RTMsgError(Internal::tr("The -relative parameter is invalid for raw disk %s"), rawdisk.c_str());
1418 vrc = VERR_INVALID_PARAMETER;
1419 goto out;
1420 }
1421 }
1422 else
1423 {
1424 /*
1425 * Could be raw image, remember error code and try to get the size first
1426 * before failing.
1427 */
1428 vrc = RTErrConvertFromWin32(GetLastError());
1429 if (RT_FAILURE(RTFileQuerySize(hRawFile, &cbSize)))
1430 {
1431 RTMsgError(Internal::tr("Cannot get the geometry of the raw disk '%s': %Rrc"), rawdisk.c_str(), vrc);
1432 goto out;
1433 }
1434 else
1435 {
1436 if (fRelative)
1437 {
1438 RTMsgError(Internal::tr("The -relative parameter is invalid for raw images"));
1439 vrc = VERR_INVALID_PARAMETER;
1440 goto out;
1441 }
1442 vrc = VINF_SUCCESS;
1443 }
1444 }
1445#elif defined(RT_OS_LINUX)
1446 struct stat DevStat;
1447 if (!fstat(RTFileToNative(hRawFile), &DevStat))
1448 {
1449 if (S_ISBLK(DevStat.st_mode))
1450 {
1451#ifdef BLKGETSIZE64
1452 /* BLKGETSIZE64 is broken up to 2.4.17 and in many 2.5.x. In 2.6.0
1453 * it works without problems. */
1454 struct utsname utsname;
1455 if ( uname(&utsname) == 0
1456 && ( (strncmp(utsname.release, "2.5.", 4) == 0 && atoi(&utsname.release[4]) >= 18)
1457 || (strncmp(utsname.release, "2.", 2) == 0 && atoi(&utsname.release[2]) >= 6)))
1458 {
1459 uint64_t cbBlk;
1460 if (!ioctl(RTFileToNative(hRawFile), BLKGETSIZE64, &cbBlk))
1461 cbSize = cbBlk;
1462 }
1463#endif /* BLKGETSIZE64 */
1464 if (!cbSize)
1465 {
1466 long cBlocks;
1467 if (!ioctl(RTFileToNative(hRawFile), BLKGETSIZE, &cBlocks))
1468 cbSize = (uint64_t)cBlocks << 9;
1469 else
1470 {
1471 vrc = RTErrConvertFromErrno(errno);
1472 RTMsgError(Internal::tr("Cannot get the size of the raw disk '%s': %Rrc"), rawdisk.c_str(), vrc);
1473 goto out;
1474 }
1475 }
1476 }
1477 else if (S_ISREG(DevStat.st_mode))
1478 {
1479 vrc = RTFileQuerySize(hRawFile, &cbSize);
1480 if (RT_FAILURE(vrc))
1481 {
1482 RTMsgError(Internal::tr("Failed to get size of file '%s': %Rrc"), rawdisk.c_str(), vrc);
1483 goto out;
1484 }
1485 else if (fRelative)
1486 {
1487 RTMsgError(Internal::tr("The -relative parameter is invalid for raw images"));
1488 vrc = VERR_INVALID_PARAMETER;
1489 goto out;
1490 }
1491 }
1492 else
1493 {
1494 RTMsgError(Internal::tr("File '%s' is no block device"), rawdisk.c_str());
1495 vrc = VERR_INVALID_PARAMETER;
1496 goto out;
1497 }
1498 }
1499 else
1500 {
1501 vrc = RTErrConvertFromErrno(errno);
1502 RTMsgError(Internal::tr("Failed to get file informtation for raw disk '%s': %Rrc"),
1503 rawdisk.c_str(), vrc);
1504 }
1505#elif defined(RT_OS_DARWIN)
1506 struct stat DevStat;
1507 if (!fstat(RTFileToNative(hRawFile), &DevStat))
1508 {
1509 if (S_ISBLK(DevStat.st_mode))
1510 {
1511 uint64_t cBlocks;
1512 uint32_t cbBlock;
1513 if (!ioctl(RTFileToNative(hRawFile), DKIOCGETBLOCKCOUNT, &cBlocks))
1514 {
1515 if (!ioctl(RTFileToNative(hRawFile), DKIOCGETBLOCKSIZE, &cbBlock))
1516 cbSize = cBlocks * cbBlock;
1517 else
1518 {
1519 RTMsgError(Internal::tr("Cannot get the block size for file '%s': %Rrc", rawdisk.c_str()), vrc);
1520 vrc = RTErrConvertFromErrno(errno);
1521 goto out;
1522 }
1523 }
1524 else
1525 {
1526 vrc = RTErrConvertFromErrno(errno);
1527 RTMsgError(Internal::tr("Cannot get the block count for file '%s': %Rrc"), rawdisk.c_str(), vrc);
1528 goto out;
1529 }
1530 }
1531 else if (S_ISREG(DevStat.st_mode))
1532 {
1533 fRelative = false; /* Must be false for raw image files. */
1534 vrc = RTFileQuerySize(hRawFile, &cbSize);
1535 if (RT_FAILURE(vrc))
1536 {
1537 RTMsgError(Internal::tr("Failed to get size of file '%s': %Rrc"), rawdisk.c_str(), vrc);
1538 goto out;
1539 }
1540 }
1541 else
1542 {
1543 RTMsgError(Internal::tr("File '%s' is neither block device nor regular file"), rawdisk.c_str());
1544 vrc = VERR_INVALID_PARAMETER;
1545 goto out;
1546 }
1547 }
1548 else
1549 {
1550 vrc = RTErrConvertFromErrno(errno);
1551 RTMsgError(Internal::tr("Failed to get file informtation for raw disk '%s': %Rrc"),
1552 rawdisk.c_str(), vrc);
1553 }
1554#elif defined(RT_OS_SOLARIS)
1555 struct stat DevStat;
1556 if (!fstat(RTFileToNative(hRawFile), &DevStat))
1557 {
1558 if (S_ISBLK(DevStat.st_mode) || S_ISCHR(DevStat.st_mode))
1559 {
1560 struct dk_minfo mediainfo;
1561 if (!ioctl(RTFileToNative(hRawFile), DKIOCGMEDIAINFO, &mediainfo))
1562 cbSize = mediainfo.dki_capacity * mediainfo.dki_lbsize;
1563 else
1564 {
1565 vrc = RTErrConvertFromErrno(errno);
1566 RTMsgError(Internal::tr("Cannot get the size of the raw disk '%s': %Rrc"), rawdisk.c_str(), vrc);
1567 goto out;
1568 }
1569 }
1570 else if (S_ISREG(DevStat.st_mode))
1571 {
1572 vrc = RTFileQuerySize(hRawFile, &cbSize);
1573 if (RT_FAILURE(vrc))
1574 {
1575 RTMsgError(Internal::tr("Failed to get size of file '%s': %Rrc"), rawdisk.c_str(), vrc);
1576 goto out;
1577 }
1578 }
1579 else
1580 {
1581 RTMsgError(Internal::tr("File '%s' is no block or char device"), rawdisk.c_str());
1582 vrc = VERR_INVALID_PARAMETER;
1583 goto out;
1584 }
1585 }
1586 else
1587 {
1588 vrc = RTErrConvertFromErrno(errno);
1589 RTMsgError(Internal::tr("Failed to get file informtation for raw disk '%s': %Rrc"),
1590 rawdisk.c_str(), vrc);
1591 }
1592#elif defined(RT_OS_FREEBSD)
1593 struct stat DevStat;
1594 if (!fstat(RTFileToNative(hRawFile), &DevStat))
1595 {
1596 if (S_ISCHR(DevStat.st_mode))
1597 {
1598 off_t cbMedia = 0;
1599 if (!ioctl(RTFileToNative(hRawFile), DIOCGMEDIASIZE, &cbMedia))
1600 cbSize = cbMedia;
1601 else
1602 {
1603 vrc = RTErrConvertFromErrno(errno);
1604 RTMsgError(Internal::tr("Cannot get the block count for file '%s': %Rrc"), rawdisk.c_str(), vrc);
1605 goto out;
1606 }
1607 }
1608 else if (S_ISREG(DevStat.st_mode))
1609 {
1610 if (fRelative)
1611 {
1612 RTMsgError(Internal::tr("The -relative parameter is invalid for raw images"));
1613 vrc = VERR_INVALID_PARAMETER;
1614 goto out;
1615 }
1616 cbSize = DevStat.st_size;
1617 }
1618 else
1619 {
1620 RTMsgError(Internal::tr("File '%s' is neither character device nor regular file"), rawdisk.c_str());
1621 vrc = VERR_INVALID_PARAMETER;
1622 goto out;
1623 }
1624 }
1625 else
1626 {
1627 vrc = RTErrConvertFromErrno(errno);
1628 RTMsgError(Internal::tr("Failed to get file informtation for raw disk '%s': %Rrc"),
1629 rawdisk.c_str(), vrc);
1630 }
1631#else /* all unrecognized OSes */
1632 /* Hopefully this works on all other hosts. If it doesn't, it'll just fail
1633 * creating the VMDK, so no real harm done. */
1634 vrc = RTFileQuerySize(hRawFile, &cbSize);
1635 if (RT_FAILURE(vrc))
1636 {
1637 RTMsgError(Internal::tr("Cannot get the size of the raw disk '%s': %Rrc"), rawdisk.c_str(), vrc);
1638 goto out;
1639 }
1640#endif
1641
1642 /* Check whether cbSize is actually sensible. */
1643 if (!cbSize || cbSize % 512)
1644 {
1645 RTMsgError(Internal::tr("Detected size of raw disk '%s' is %RU64, an invalid value"), rawdisk.c_str(), cbSize);
1646 vrc = VERR_INVALID_PARAMETER;
1647 goto out;
1648 }
1649
1650 RawDescriptor.szSignature[0] = 'R';
1651 RawDescriptor.szSignature[1] = 'A';
1652 RawDescriptor.szSignature[2] = 'W';
1653 RawDescriptor.szSignature[3] = '\0';
1654 if (!pszPartitions)
1655 {
1656 RawDescriptor.uFlags = VDISKRAW_DISK;
1657 RawDescriptor.pszRawDisk = (char *)rawdisk.c_str();
1658 }
1659 else
1660 {
1661 RawDescriptor.uFlags = VDISKRAW_NORMAL;
1662 RawDescriptor.pszRawDisk = NULL;
1663 RawDescriptor.cPartDescs = 0;
1664 RawDescriptor.pPartDescs = NULL;
1665
1666 uint32_t uPartitions = 0;
1667 uint32_t uPartitionsRO = 0;
1668
1669 const char *p = pszPartitions;
1670 char *pszNext;
1671 uint32_t u32;
1672 while (*p != '\0')
1673 {
1674 vrc = RTStrToUInt32Ex(p, &pszNext, 0, &u32);
1675 if (RT_FAILURE(vrc))
1676 {
1677 RTMsgError(Internal::tr("Incorrect value in partitions parameter"));
1678 goto out;
1679 }
1680 uPartitions |= RT_BIT(u32);
1681 p = pszNext;
1682 if (*p == 'r')
1683 {
1684 uPartitionsRO |= RT_BIT(u32);
1685 p++;
1686 }
1687 if (*p == ',')
1688 p++;
1689 else if (*p != '\0')
1690 {
1691 RTMsgError(Internal::tr("Incorrect separator in partitions parameter"));
1692 vrc = VERR_INVALID_PARAMETER;
1693 goto out;
1694 }
1695 }
1696
1697 HOSTPARTITIONS partitions;
1698 vrc = partRead(hRawFile, &partitions);
1699 if (RT_FAILURE(vrc))
1700 {
1701 RTMsgError(Internal::tr("Cannot read the partition information from '%s'"), rawdisk.c_str());
1702 goto out;
1703 }
1704
1705 RawDescriptor.enmPartitioningType = partitions.uPartitioningType;
1706
1707 for (unsigned i = 0; i < partitions.cPartitions; i++)
1708 {
1709 if ( uPartitions & RT_BIT(partitions.aPartitions[i].uIndex)
1710 && PARTTYPE_IS_EXTENDED(partitions.aPartitions[i].uType))
1711 {
1712 /* Some ignorant user specified an extended partition.
1713 * Bad idea, as this would trigger an overlapping
1714 * partitions error later during VMDK creation. So warn
1715 * here and ignore what the user requested. */
1716 RTMsgWarning(Internal::tr(
1717 "It is not possible (and necessary) to explicitly give access to the "
1718 "extended partition %u. If required, enable access to all logical "
1719 "partitions inside this extended partition."),
1720 partitions.aPartitions[i].uIndex);
1721 uPartitions &= ~RT_BIT(partitions.aPartitions[i].uIndex);
1722 }
1723 }
1724
1725 for (unsigned i = 0; i < partitions.cPartitions; i++)
1726 {
1727 PVDISKRAWPARTDESC pPartDesc = NULL;
1728
1729 /* first dump the MBR/EPT data area */
1730 if (partitions.aPartitions[i].cPartDataSectors)
1731 {
1732 pPartDesc = appendPartDesc(&RawDescriptor.cPartDescs,
1733 &RawDescriptor.pPartDescs);
1734 if (!pPartDesc)
1735 {
1736 RTMsgError(Internal::tr("Out of memory allocating the partition list for '%s'"), rawdisk.c_str());
1737 vrc = VERR_NO_MEMORY;
1738 goto out;
1739 }
1740
1741 /** @todo the clipping below isn't 100% accurate, as it should
1742 * actually clip to the track size. However, that's easier said
1743 * than done as figuring out the track size is heuristics. In
1744 * any case the clipping is adjusted later after sorting, to
1745 * prevent overlapping data areas on the resulting image. */
1746 pPartDesc->cbData = RT_MIN(partitions.aPartitions[i].cPartDataSectors, 63) * 512;
1747 pPartDesc->offStartInVDisk = partitions.aPartitions[i].uPartDataStart * 512;
1748 Assert(pPartDesc->cbData - (size_t)pPartDesc->cbData == 0);
1749 void *pPartData = RTMemAlloc((size_t)pPartDesc->cbData);
1750 if (!pPartData)
1751 {
1752 RTMsgError(Internal::tr("Out of memory allocating the partition descriptor for '%s'"),
1753 rawdisk.c_str());
1754 vrc = VERR_NO_MEMORY;
1755 goto out;
1756 }
1757 vrc = RTFileReadAt(hRawFile, partitions.aPartitions[i].uPartDataStart * 512,
1758 pPartData, (size_t)pPartDesc->cbData, NULL);
1759 if (RT_FAILURE(vrc))
1760 {
1761 RTMsgError(Internal::tr("Cannot read partition data from raw device '%s': %Rrc"),
1762 rawdisk.c_str(), vrc);
1763 goto out;
1764 }
1765 /* Splice in the replacement MBR code if specified. */
1766 if ( partitions.aPartitions[i].uPartDataStart == 0
1767 && pszMBRFilename)
1768 {
1769 RTFILE MBRFile;
1770 vrc = RTFileOpen(&MBRFile, pszMBRFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1771 if (RT_FAILURE(vrc))
1772 {
1773 RTMsgError(Internal::tr("Cannot open replacement MBR file '%s' specified with -mbr: %Rrc"),
1774 pszMBRFilename, vrc);
1775 goto out;
1776 }
1777 vrc = RTFileReadAt(MBRFile, 0, pPartData, 0x1be, NULL);
1778 RTFileClose(MBRFile);
1779 if (RT_FAILURE(vrc))
1780 {
1781 RTMsgError(Internal::tr("Cannot read replacement MBR file '%s': %Rrc"), pszMBRFilename, vrc);
1782 goto out;
1783 }
1784 }
1785 pPartDesc->pvPartitionData = pPartData;
1786 }
1787
1788 if (PARTTYPE_IS_EXTENDED(partitions.aPartitions[i].uType))
1789 {
1790 /* Suppress exporting the actual extended partition. Only
1791 * logical partitions should be processed. However completely
1792 * ignoring it leads to leaving out the EBR data. */
1793 continue;
1794 }
1795
1796 /* set up values for non-relative device names */
1797 const char *pszRawName = rawdisk.c_str();
1798 uint64_t uStartOffset = partitions.aPartitions[i].uStart * 512;
1799
1800 pPartDesc = appendPartDesc(&RawDescriptor.cPartDescs,
1801 &RawDescriptor.pPartDescs);
1802 if (!pPartDesc)
1803 {
1804 RTMsgError(Internal::tr("Out of memory allocating the partition list for '%s'"), rawdisk.c_str());
1805 vrc = VERR_NO_MEMORY;
1806 goto out;
1807 }
1808
1809 if (uPartitions & RT_BIT(partitions.aPartitions[i].uIndex))
1810 {
1811 if (uPartitionsRO & RT_BIT(partitions.aPartitions[i].uIndex))
1812 pPartDesc->uFlags |= VDISKRAW_READONLY;
1813
1814 if (fRelative)
1815 {
1816#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
1817 /* Refer to the correct partition and use offset 0. */
1818 char *psz;
1819#if defined(RT_OS_LINUX)
1820 /*
1821 * Check whether raw disk ends with a digit. In that case
1822 * insert a p before adding the partition number.
1823 * This is used for nvme devices only currently which look like
1824 * /dev/nvme0n1p1 but might be extended to other devices in the
1825 * future.
1826 */
1827 size_t cchRawDisk = rawdisk.length();
1828 if (RT_C_IS_DIGIT(pszRawName[cchRawDisk - 1]))
1829 RTStrAPrintf(&psz,
1830 "%sp%u",
1831 rawdisk.c_str(),
1832 partitions.aPartitions[i].uIndex);
1833 else
1834 RTStrAPrintf(&psz,
1835 "%s%u",
1836 rawdisk.c_str(),
1837 partitions.aPartitions[i].uIndex);
1838#elif defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
1839 RTStrAPrintf(&psz,
1840 "%ss%u",
1841 rawdisk.c_str(),
1842 partitions.aPartitions[i].uIndex);
1843#endif
1844 if (!psz)
1845 {
1846 vrc = VERR_NO_STR_MEMORY;
1847 RTMsgError(Internal::tr("Cannot create reference to individual partition %u, rc=%Rrc"),
1848 partitions.aPartitions[i].uIndex, vrc);
1849 goto out;
1850 }
1851 pszRawName = psz;
1852 uStartOffset = 0;
1853#elif defined(RT_OS_WINDOWS)
1854 /* Refer to the correct partition and use offset 0. */
1855 char *psz;
1856 RTStrAPrintf(&psz, "\\\\.\\Harddisk%sPartition%u",
1857 rawdisk.c_str() + 17,
1858 partitions.aPartitions[i].uIndexWin);
1859 if (!psz)
1860 {
1861 vrc = VERR_NO_STR_MEMORY;
1862 RTMsgError(Internal::tr("Cannot create reference to individual partition %u (numbered %u), rc=%Rrc"),
1863 partitions.aPartitions[i].uIndex, partitions.aPartitions[i].uIndexWin, vrc);
1864 goto out;
1865 }
1866 pszRawName = psz;
1867 uStartOffset = 0;
1868#else
1869 /** @todo not implemented for other hosts. Treat just like
1870 * not specified (this code is actually never reached). */
1871#endif
1872 }
1873
1874 pPartDesc->pszRawDevice = (char *)pszRawName;
1875 pPartDesc->offStartInDevice = uStartOffset;
1876 }
1877 else
1878 {
1879 pPartDesc->pszRawDevice = NULL;
1880 pPartDesc->offStartInDevice = 0;
1881 }
1882
1883 pPartDesc->offStartInVDisk = partitions.aPartitions[i].uStart * 512;
1884 pPartDesc->cbData = partitions.aPartitions[i].uSize * 512;
1885 }
1886
1887 /* Sort data areas in ascending order of start. */
1888 for (unsigned i = 0; i < RawDescriptor.cPartDescs-1; i++)
1889 {
1890 unsigned uMinIdx = i;
1891 uint64_t uMinVal = RawDescriptor.pPartDescs[i].offStartInVDisk;
1892 for (unsigned j = i + 1; j < RawDescriptor.cPartDescs; j++)
1893 {
1894 if (RawDescriptor.pPartDescs[j].offStartInVDisk < uMinVal)
1895 {
1896 uMinIdx = j;
1897 uMinVal = RawDescriptor.pPartDescs[j].offStartInVDisk;
1898 }
1899 }
1900 if (uMinIdx != i)
1901 {
1902 /* Swap entries at index i and uMinIdx. */
1903 VDISKRAWPARTDESC tmp;
1904 memcpy(&tmp, &RawDescriptor.pPartDescs[i], sizeof(tmp));
1905 memcpy(&RawDescriptor.pPartDescs[i], &RawDescriptor.pPartDescs[uMinIdx], sizeof(tmp));
1906 memcpy(&RawDescriptor.pPartDescs[uMinIdx], &tmp, sizeof(tmp));
1907 }
1908 }
1909
1910 /* Have a second go at MBR/EPT, GPT area clipping. Now that the data areas
1911 * are sorted this is much easier to get 100% right. */
1912 //for (unsigned i = 0; i < RawDescriptor.cPartDescs-1; i++)
1913 for (unsigned i = 0; i < RawDescriptor.cPartDescs; i++)
1914 {
1915 if (RawDescriptor.pPartDescs[i].pvPartitionData)
1916 {
1917 RawDescriptor.pPartDescs[i].cbData = RT_MIN( RawDescriptor.pPartDescs[i+1].offStartInVDisk
1918 - RawDescriptor.pPartDescs[i].offStartInVDisk,
1919 RawDescriptor.pPartDescs[i].cbData);
1920 if (!RawDescriptor.pPartDescs[i].cbData)
1921 {
1922 if (RawDescriptor.enmPartitioningType == VDISKPARTTYPE_MBR)
1923 {
1924 RTMsgError(Internal::tr("MBR/EPT overlaps with data area"));
1925 vrc = VERR_INVALID_PARAMETER;
1926 goto out;
1927 }
1928 if (RawDescriptor.cPartDescs != i+1)
1929 {
1930 RTMsgError(Internal::tr("GPT overlaps with data area"));
1931 vrc = VERR_INVALID_PARAMETER;
1932 goto out;
1933 }
1934 }
1935 }
1936 }
1937 }
1938
1939 RTFileClose(hRawFile);
1940
1941#ifdef DEBUG_klaus
1942 if (!(RawDescriptor.uFlags & VDISKRAW_DISK))
1943 {
1944 RTPrintf("# start length startoffset partdataptr device\n");
1945 for (unsigned i = 0; i < RawDescriptor.cPartDescs; i++)
1946 {
1947 RTPrintf("%2u %14RU64 %14RU64 %14RU64 %#18p %s\n", i,
1948 RawDescriptor.pPartDescs[i].offStartInVDisk,
1949 RawDescriptor.pPartDescs[i].cbData,
1950 RawDescriptor.pPartDescs[i].offStartInDevice,
1951 RawDescriptor.pPartDescs[i].pvPartitionData,
1952 RawDescriptor.pPartDescs[i].pszRawDevice);
1953 }
1954 }
1955#endif
1956
1957 VDINTERFACEERROR vdInterfaceError;
1958 vdInterfaceError.pfnError = handleVDError;
1959 vdInterfaceError.pfnMessage = handleVDMessage;
1960
1961 hrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1962 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1963 AssertRC(vrc);
1964
1965 vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk); /* Raw VMDK's are harddisk only. */
1966 if (RT_FAILURE(vrc))
1967 {
1968 RTMsgError(Internal::tr("Cannot create the virtual disk container: %Rrc"), vrc);
1969 goto out;
1970 }
1971
1972 Assert(RT_MIN(cbSize / 512 / 16 / 63, 16383) -
1973 (unsigned int)RT_MIN(cbSize / 512 / 16 / 63, 16383) == 0);
1974 VDGEOMETRY PCHS, LCHS;
1975 PCHS.cCylinders = (unsigned int)RT_MIN(cbSize / 512 / 16 / 63, 16383);
1976 PCHS.cHeads = 16;
1977 PCHS.cSectors = 63;
1978 LCHS.cCylinders = 0;
1979 LCHS.cHeads = 0;
1980 LCHS.cSectors = 0;
1981 vrc = VDCreateBase(pDisk, "VMDK", filename.c_str(), cbSize,
1982 VD_IMAGE_FLAGS_FIXED | VD_VMDK_IMAGE_FLAGS_RAWDISK,
1983 (char *)&RawDescriptor, &PCHS, &LCHS, NULL,
1984 VD_OPEN_FLAGS_NORMAL, NULL, NULL);
1985 if (RT_FAILURE(vrc))
1986 {
1987 RTMsgError(Internal::tr("Cannot create the raw disk VMDK: %Rrc"), vrc);
1988 goto out;
1989 }
1990 RTPrintf(Internal::tr("RAW host disk access VMDK file %s created successfully.\n"), filename.c_str());
1991
1992 VDCloseAll(pDisk);
1993
1994 /* Clean up allocated memory etc. */
1995 if (pszPartitions)
1996 {
1997 for (unsigned i = 0; i < RawDescriptor.cPartDescs; i++)
1998 {
1999 /* Free memory allocated for relative device name. */
2000 if (fRelative && RawDescriptor.pPartDescs[i].pszRawDevice)
2001 RTStrFree((char *)(void *)RawDescriptor.pPartDescs[i].pszRawDevice);
2002 if (RawDescriptor.pPartDescs[i].pvPartitionData)
2003 RTMemFree((void *)RawDescriptor.pPartDescs[i].pvPartitionData);
2004 }
2005 if (RawDescriptor.pPartDescs)
2006 RTMemFree(RawDescriptor.pPartDescs);
2007 }
2008
2009 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2010
2011out:
2012 RTMsgError(Internal::tr("The raw disk vmdk file was not created"));
2013 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2014}
2015
2016static RTEXITCODE CmdRenameVMDK(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
2017{
2018 RT_NOREF(aVirtualBox, aSession);
2019 Utf8Str src;
2020 Utf8Str dst;
2021 /* Parse the arguments. */
2022 for (int i = 0; i < argc; i++)
2023 {
2024 if (strcmp(argv[i], "-from") == 0)
2025 {
2026 if (argc <= i + 1)
2027 {
2028 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
2029 }
2030 i++;
2031 src = argv[i];
2032 }
2033 else if (strcmp(argv[i], "-to") == 0)
2034 {
2035 if (argc <= i + 1)
2036 {
2037 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
2038 }
2039 i++;
2040 dst = argv[i];
2041 }
2042 else
2043 {
2044 return errorSyntaxInternal(USAGE_I_RENAMEVMDK, Internal::tr("Invalid parameter '%s'"), argv[i]);
2045 }
2046 }
2047
2048 if (src.isEmpty())
2049 return errorSyntaxInternal(USAGE_I_RENAMEVMDK, Internal::tr("Mandatory parameter -from missing"));
2050 if (dst.isEmpty())
2051 return errorSyntaxInternal(USAGE_I_RENAMEVMDK, Internal::tr("Mandatory parameter -to missing"));
2052
2053 PVDISK pDisk = NULL;
2054
2055 PVDINTERFACE pVDIfs = NULL;
2056 VDINTERFACEERROR vdInterfaceError;
2057 vdInterfaceError.pfnError = handleVDError;
2058 vdInterfaceError.pfnMessage = handleVDMessage;
2059
2060 int vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
2061 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
2062 AssertRC(vrc);
2063
2064 vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
2065 if (RT_FAILURE(vrc))
2066 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot create the virtual disk container: %Rrc"), vrc);
2067
2068 vrc = VDOpen(pDisk, "VMDK", src.c_str(), VD_OPEN_FLAGS_NORMAL, NULL);
2069 if (RT_SUCCESS(vrc))
2070 {
2071 vrc = VDCopy(pDisk, 0, pDisk, "VMDK", dst.c_str(), true, 0,
2072 VD_IMAGE_FLAGS_NONE, NULL, VD_OPEN_FLAGS_NORMAL,
2073 NULL, NULL, NULL);
2074 if (RT_FAILURE(vrc))
2075 RTMsgError(Internal::tr("Cannot rename the image: %Rrc"), vrc);
2076 }
2077 else
2078 RTMsgError(Internal::tr("Cannot create the source image: %Rrc"), vrc);
2079 VDCloseAll(pDisk);
2080 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2081}
2082
2083static RTEXITCODE CmdConvertToRaw(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
2084{
2085 RT_NOREF(aVirtualBox, aSession);
2086 Utf8Str srcformat;
2087 Utf8Str src;
2088 Utf8Str dst;
2089 bool fWriteToStdOut = false;
2090
2091 /* Parse the arguments. */
2092 for (int i = 0; i < argc; i++)
2093 {
2094 if (strcmp(argv[i], "-format") == 0)
2095 {
2096 if (argc <= i + 1)
2097 {
2098 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
2099 }
2100 i++;
2101 srcformat = argv[i];
2102 }
2103 else if (src.isEmpty())
2104 {
2105 src = argv[i];
2106 }
2107 else if (dst.isEmpty())
2108 {
2109 dst = argv[i];
2110#ifdef ENABLE_CONVERT_RAW_TO_STDOUT
2111 if (!strcmp(argv[i], "stdout"))
2112 fWriteToStdOut = true;
2113#endif /* ENABLE_CONVERT_RAW_TO_STDOUT */
2114 }
2115 else
2116 {
2117 return errorSyntaxInternal(USAGE_I_CONVERTTORAW, Internal::tr("Invalid parameter '%s'"), argv[i]);
2118 }
2119 }
2120
2121 if (src.isEmpty())
2122 return errorSyntaxInternal(USAGE_I_CONVERTTORAW, Internal::tr("Mandatory filename parameter missing"));
2123 if (dst.isEmpty())
2124 return errorSyntaxInternal(USAGE_I_CONVERTTORAW, Internal::tr("Mandatory outputfile parameter missing"));
2125
2126 PVDISK pDisk = NULL;
2127
2128 PVDINTERFACE pVDIfs = NULL;
2129 VDINTERFACEERROR vdInterfaceError;
2130 vdInterfaceError.pfnError = handleVDError;
2131 vdInterfaceError.pfnMessage = handleVDMessage;
2132
2133 int vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
2134 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
2135 AssertRC(vrc);
2136
2137 /** @todo Support convert to raw for floppy and DVD images too. */
2138 vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
2139 if (RT_FAILURE(vrc))
2140 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot create the virtual disk container: %Rrc"), vrc);
2141
2142 /* Open raw output file. */
2143 RTFILE outFile;
2144 vrc = VINF_SUCCESS;
2145 if (fWriteToStdOut)
2146 vrc = RTFileFromNative(&outFile, 1);
2147 else
2148 vrc = RTFileOpen(&outFile, dst.c_str(), RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
2149 if (RT_FAILURE(vrc))
2150 {
2151 VDCloseAll(pDisk);
2152 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot create destination file \"%s\": %Rrc"),
2153 dst.c_str(), vrc);
2154 }
2155
2156 if (srcformat.isEmpty())
2157 {
2158 char *pszFormat = NULL;
2159 VDTYPE enmType = VDTYPE_INVALID;
2160 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
2161 src.c_str(), VDTYPE_INVALID, &pszFormat, &enmType);
2162 if (RT_FAILURE(vrc) || enmType != VDTYPE_HDD)
2163 {
2164 VDCloseAll(pDisk);
2165 if (!fWriteToStdOut)
2166 {
2167 RTFileClose(outFile);
2168 RTFileDelete(dst.c_str());
2169 }
2170 if (RT_FAILURE(vrc))
2171 RTMsgError(Internal::tr("No file format specified and autodetect failed - please specify format: %Rrc"),
2172 vrc);
2173 else
2174 RTMsgError(Internal::tr("Only converting harddisk images is supported"));
2175 return RTEXITCODE_FAILURE;
2176 }
2177 srcformat = pszFormat;
2178 RTStrFree(pszFormat);
2179 }
2180 vrc = VDOpen(pDisk, srcformat.c_str(), src.c_str(), VD_OPEN_FLAGS_READONLY, NULL);
2181 if (RT_FAILURE(vrc))
2182 {
2183 VDCloseAll(pDisk);
2184 if (!fWriteToStdOut)
2185 {
2186 RTFileClose(outFile);
2187 RTFileDelete(dst.c_str());
2188 }
2189 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot open the source image: %Rrc"), vrc);
2190 }
2191
2192 uint64_t cbSize = VDGetSize(pDisk, VD_LAST_IMAGE);
2193 uint64_t offFile = 0;
2194#define RAW_BUFFER_SIZE _128K
2195 size_t cbBuf = RAW_BUFFER_SIZE;
2196 void *pvBuf = RTMemAlloc(cbBuf);
2197 if (pvBuf)
2198 {
2199 RTStrmPrintf(g_pStdErr, Internal::tr("Converting image \"%s\" with size %RU64 bytes (%RU64MB) to raw...\n", "", cbSize),
2200 src.c_str(), cbSize, (cbSize + _1M - 1) / _1M);
2201 while (offFile < cbSize)
2202 {
2203 size_t cb = (size_t)RT_MIN(cbSize - offFile, cbBuf);
2204 vrc = VDRead(pDisk, offFile, pvBuf, cb);
2205 if (RT_FAILURE(vrc))
2206 break;
2207 vrc = RTFileWrite(outFile, pvBuf, cb, NULL);
2208 if (RT_FAILURE(vrc))
2209 break;
2210 offFile += cb;
2211 }
2212 RTMemFree(pvBuf);
2213 if (RT_FAILURE(vrc))
2214 {
2215 VDCloseAll(pDisk);
2216 if (!fWriteToStdOut)
2217 {
2218 RTFileClose(outFile);
2219 RTFileDelete(dst.c_str());
2220 }
2221 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Cannot copy image data: %Rrc"), vrc);
2222 }
2223 }
2224 else
2225 {
2226 vrc = VERR_NO_MEMORY;
2227 VDCloseAll(pDisk);
2228 if (!fWriteToStdOut)
2229 {
2230 RTFileClose(outFile);
2231 RTFileDelete(dst.c_str());
2232 }
2233 return RTMsgErrorExit(RTEXITCODE_FAILURE, Internal::tr("Out of memory allocating read buffer"));
2234 }
2235
2236 if (!fWriteToStdOut)
2237 RTFileClose(outFile);
2238 VDCloseAll(pDisk);
2239 return RTEXITCODE_SUCCESS;
2240}
2241
2242static RTEXITCODE CmdConvertHardDisk(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
2243{
2244 RT_NOREF(aVirtualBox, aSession);
2245 Utf8Str srcformat;
2246 Utf8Str dstformat;
2247 Utf8Str src;
2248 Utf8Str dst;
2249 int vrc;
2250 PVDISK pSrcDisk = NULL;
2251 PVDISK pDstDisk = NULL;
2252 VDTYPE enmSrcType = VDTYPE_INVALID;
2253
2254 /* Parse the arguments. */
2255 for (int i = 0; i < argc; i++)
2256 {
2257 if (strcmp(argv[i], "-srcformat") == 0)
2258 {
2259 if (argc <= i + 1)
2260 {
2261 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
2262 }
2263 i++;
2264 srcformat = argv[i];
2265 }
2266 else if (strcmp(argv[i], "-dstformat") == 0)
2267 {
2268 if (argc <= i + 1)
2269 {
2270 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
2271 }
2272 i++;
2273 dstformat = argv[i];
2274 }
2275 else if (src.isEmpty())
2276 {
2277 src = argv[i];
2278 }
2279 else if (dst.isEmpty())
2280 {
2281 dst = argv[i];
2282 }
2283 else
2284 {
2285 return errorSyntaxInternal(USAGE_I_CONVERTHD, Internal::tr("Invalid parameter '%s'"), argv[i]);
2286 }
2287 }
2288
2289 if (src.isEmpty())
2290 return errorSyntaxInternal(USAGE_I_CONVERTHD, Internal::tr("Mandatory input image parameter missing"));
2291 if (dst.isEmpty())
2292 return errorSyntaxInternal(USAGE_I_CONVERTHD, Internal::tr("Mandatory output image parameter missing"));
2293
2294
2295 PVDINTERFACE pVDIfs = NULL;
2296 VDINTERFACEERROR vdInterfaceError;
2297 vdInterfaceError.pfnError = handleVDError;
2298 vdInterfaceError.pfnMessage = handleVDMessage;
2299
2300 vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
2301 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
2302 AssertRC(vrc);
2303
2304 do
2305 {
2306 /* Try to determine input image format */
2307 if (srcformat.isEmpty())
2308 {
2309 char *pszFormat = NULL;
2310 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
2311 src.c_str(), VDTYPE_HDD, &pszFormat, &enmSrcType);
2312 if (RT_FAILURE(vrc))
2313 {
2314 RTMsgError(Internal::tr("No file format specified and autodetect failed - please specify format: %Rrc"),
2315 vrc);
2316 break;
2317 }
2318 srcformat = pszFormat;
2319 RTStrFree(pszFormat);
2320 }
2321
2322 vrc = VDCreate(pVDIfs, enmSrcType, &pSrcDisk);
2323 if (RT_FAILURE(vrc))
2324 {
2325 RTMsgError(Internal::tr("Cannot create the source virtual disk container: %Rrc"), vrc);
2326 break;
2327 }
2328
2329 /* Open the input image */
2330 vrc = VDOpen(pSrcDisk, srcformat.c_str(), src.c_str(), VD_OPEN_FLAGS_READONLY, NULL);
2331 if (RT_FAILURE(vrc))
2332 {
2333 RTMsgError(Internal::tr("Cannot open the source image: %Rrc"), vrc);
2334 break;
2335 }
2336
2337 /* Output format defaults to VDI */
2338 if (dstformat.isEmpty())
2339 dstformat = "VDI";
2340
2341 vrc = VDCreate(pVDIfs, enmSrcType, &pDstDisk);
2342 if (RT_FAILURE(vrc))
2343 {
2344 RTMsgError(Internal::tr("Cannot create the destination virtual disk container: %Rrc"), vrc);
2345 break;
2346 }
2347
2348 uint64_t cbSize = VDGetSize(pSrcDisk, VD_LAST_IMAGE);
2349 RTStrmPrintf(g_pStdErr, Internal::tr("Converting image \"%s\" with size %RU64 bytes (%RU64MB)...\n", "", cbSize),
2350 src.c_str(), cbSize, (cbSize + _1M - 1) / _1M);
2351
2352 /* Create the output image */
2353 vrc = VDCopy(pSrcDisk, VD_LAST_IMAGE, pDstDisk, dstformat.c_str(),
2354 dst.c_str(), false, 0, VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED,
2355 NULL, VD_OPEN_FLAGS_NORMAL, NULL, NULL, NULL);
2356 if (RT_FAILURE(vrc))
2357 {
2358 RTMsgError(Internal::tr("Cannot copy the image: %Rrc"), vrc);
2359 break;
2360 }
2361 }
2362 while (0);
2363 if (pDstDisk)
2364 VDCloseAll(pDstDisk);
2365 if (pSrcDisk)
2366 VDCloseAll(pSrcDisk);
2367
2368 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2369}
2370
2371/**
2372 * Tries to repair a corrupted hard disk image.
2373 *
2374 * @returns VBox status code
2375 */
2376static RTEXITCODE CmdRepairHardDisk(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
2377{
2378 RT_NOREF(aVirtualBox, aSession);
2379 Utf8Str image;
2380 Utf8Str format;
2381 int vrc;
2382 bool fDryRun = false;
2383
2384 /* Parse the arguments. */
2385 for (int i = 0; i < argc; i++)
2386 {
2387 if (strcmp(argv[i], "-dry-run") == 0)
2388 {
2389 fDryRun = true;
2390 }
2391 else if (strcmp(argv[i], "-format") == 0)
2392 {
2393 if (argc <= i + 1)
2394 {
2395 return errorArgument(Internal::tr("Missing argument to '%s'"), argv[i]);
2396 }
2397 i++;
2398 format = argv[i];
2399 }
2400 else if (image.isEmpty())
2401 {
2402 image = argv[i];
2403 }
2404 else
2405 {
2406 return errorSyntaxInternal(USAGE_I_REPAIRHD, Internal::tr("Invalid parameter '%s'"), argv[i]);
2407 }
2408 }
2409
2410 if (image.isEmpty())
2411 return errorSyntaxInternal(USAGE_I_REPAIRHD, Internal::tr("Mandatory input image parameter missing"));
2412
2413 PVDINTERFACE pVDIfs = NULL;
2414 VDINTERFACEERROR vdInterfaceError;
2415 vdInterfaceError.pfnError = handleVDError;
2416 vdInterfaceError.pfnMessage = handleVDMessage;
2417
2418 vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
2419 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
2420 AssertRC(vrc);
2421
2422 do
2423 {
2424 /* Try to determine input image format */
2425 if (format.isEmpty())
2426 {
2427 char *pszFormat = NULL;
2428 VDTYPE enmSrcType = VDTYPE_INVALID;
2429
2430 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
2431 image.c_str(), VDTYPE_HDD, &pszFormat, &enmSrcType);
2432 if (RT_FAILURE(vrc) && (vrc != VERR_VD_IMAGE_CORRUPTED))
2433 {
2434 RTMsgError(Internal::tr("No file format specified and autodetect failed - please specify format: %Rrc"),
2435 vrc);
2436 break;
2437 }
2438 format = pszFormat;
2439 RTStrFree(pszFormat);
2440 }
2441
2442 uint32_t fFlags = 0;
2443 if (fDryRun)
2444 fFlags |= VD_REPAIR_DRY_RUN;
2445
2446 vrc = VDRepair(pVDIfs, NULL, image.c_str(), format.c_str(), fFlags);
2447 }
2448 while (0);
2449
2450 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2451}
2452
2453/**
2454 * Unloads the necessary driver.
2455 *
2456 * @returns VBox status code
2457 */
2458static RTEXITCODE CmdModUninstall(void)
2459{
2460 int rc = SUPR3Uninstall();
2461 if (RT_SUCCESS(rc) || rc == VERR_NOT_IMPLEMENTED)
2462 return RTEXITCODE_SUCCESS;
2463 return RTEXITCODE_FAILURE;
2464}
2465
2466/**
2467 * Loads the necessary driver.
2468 *
2469 * @returns VBox status code
2470 */
2471static RTEXITCODE CmdModInstall(void)
2472{
2473 int rc = SUPR3Install();
2474 if (RT_SUCCESS(rc) || rc == VERR_NOT_IMPLEMENTED)
2475 return RTEXITCODE_SUCCESS;
2476 return RTEXITCODE_FAILURE;
2477}
2478
2479static RTEXITCODE CmdDebugLog(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
2480{
2481 /*
2482 * The first parameter is the name or UUID of a VM with a direct session
2483 * that we wish to open.
2484 */
2485 if (argc < 1)
2486 return errorSyntaxInternal(USAGE_I_DEBUGLOG, Internal::tr("Missing VM name/UUID"));
2487
2488 ComPtr<IMachine> ptrMachine;
2489 HRESULT hrc;
2490 CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
2491 ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
2492
2493 CHECK_ERROR_RET(ptrMachine, LockMachine(aSession, LockType_Shared), RTEXITCODE_FAILURE);
2494
2495 /*
2496 * Get the debugger interface.
2497 */
2498 ComPtr<IConsole> ptrConsole;
2499 CHECK_ERROR_RET(aSession, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
2500
2501 ComPtr<IMachineDebugger> ptrDebugger;
2502 CHECK_ERROR_RET(ptrConsole, COMGETTER(Debugger)(ptrDebugger.asOutParam()), RTEXITCODE_FAILURE);
2503
2504 /*
2505 * Parse the command.
2506 */
2507 bool fEnablePresent = false;
2508 bool fEnable = false;
2509 bool fFlagsPresent = false;
2510 RTCString strFlags;
2511 bool fGroupsPresent = false;
2512 RTCString strGroups;
2513 bool fDestsPresent = false;
2514 RTCString strDests;
2515
2516 static const RTGETOPTDEF s_aOptions[] =
2517 {
2518 { "--disable", 'E', RTGETOPT_REQ_NOTHING },
2519 { "--enable", 'e', RTGETOPT_REQ_NOTHING },
2520 { "--flags", 'f', RTGETOPT_REQ_STRING },
2521 { "--groups", 'g', RTGETOPT_REQ_STRING },
2522 { "--destinations", 'd', RTGETOPT_REQ_STRING }
2523 };
2524
2525 int ch;
2526 RTGETOPTUNION ValueUnion;
2527 RTGETOPTSTATE GetState;
2528 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
2529 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
2530 {
2531 switch (ch)
2532 {
2533 case 'e':
2534 fEnablePresent = true;
2535 fEnable = true;
2536 break;
2537
2538 case 'E':
2539 fEnablePresent = true;
2540 fEnable = false;
2541 break;
2542
2543 case 'f':
2544 fFlagsPresent = true;
2545 if (*ValueUnion.psz)
2546 {
2547 if (strFlags.isNotEmpty())
2548 strFlags.append(' ');
2549 strFlags.append(ValueUnion.psz);
2550 }
2551 break;
2552
2553 case 'g':
2554 fGroupsPresent = true;
2555 if (*ValueUnion.psz)
2556 {
2557 if (strGroups.isNotEmpty())
2558 strGroups.append(' ');
2559 strGroups.append(ValueUnion.psz);
2560 }
2561 break;
2562
2563 case 'd':
2564 fDestsPresent = true;
2565 if (*ValueUnion.psz)
2566 {
2567 if (strDests.isNotEmpty())
2568 strDests.append(' ');
2569 strDests.append(ValueUnion.psz);
2570 }
2571 break;
2572
2573 default:
2574 return errorGetOptInternal(USAGE_I_DEBUGLOG, ch, &ValueUnion);
2575 }
2576 }
2577
2578 /*
2579 * Do the job.
2580 */
2581 if (fEnablePresent && !fEnable)
2582 CHECK_ERROR_RET(ptrDebugger, COMSETTER(LogEnabled)(FALSE), RTEXITCODE_FAILURE);
2583
2584 /** @todo flags, groups destination. */
2585 if (fFlagsPresent || fGroupsPresent || fDestsPresent)
2586 RTMsgWarning(Internal::tr("One or more of the requested features are not implemented! Feel free to do this."));
2587
2588 if (fEnablePresent && fEnable)
2589 CHECK_ERROR_RET(ptrDebugger, COMSETTER(LogEnabled)(TRUE), RTEXITCODE_FAILURE);
2590 return RTEXITCODE_SUCCESS;
2591}
2592
2593/**
2594 * Generate a SHA-256 password hash
2595 */
2596static RTEXITCODE CmdGeneratePasswordHash(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
2597{
2598 RT_NOREF(aVirtualBox, aSession);
2599
2600 /* one parameter, the password to hash */
2601 if (argc != 1)
2602 return errorSyntaxInternal(USAGE_I_PASSWORDHASH, Internal::tr("password to hash required"));
2603
2604 uint8_t abDigest[RTSHA256_HASH_SIZE];
2605 RTSha256(argv[0], strlen(argv[0]), abDigest);
2606 char pszDigest[RTSHA256_DIGEST_LEN + 1];
2607 RTSha256ToString(abDigest, pszDigest, sizeof(pszDigest));
2608 RTPrintf(Internal::tr("Password hash: %s\n"), pszDigest);
2609
2610 return RTEXITCODE_SUCCESS;
2611}
2612
2613/**
2614 * Print internal guest statistics or
2615 * set internal guest statistics update interval if specified
2616 */
2617static RTEXITCODE CmdGuestStats(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
2618{
2619 /* one parameter, guest name */
2620 if (argc < 1)
2621 return errorSyntaxInternal(USAGE_I_GUESTSTATS, Internal::tr("Missing VM name/UUID"));
2622
2623 /*
2624 * Parse the command.
2625 */
2626 ULONG aUpdateInterval = 0;
2627
2628 static const RTGETOPTDEF s_aOptions[] =
2629 {
2630 { "--interval", 'i', RTGETOPT_REQ_UINT32 }
2631 };
2632
2633 int ch;
2634 RTGETOPTUNION ValueUnion;
2635 RTGETOPTSTATE GetState;
2636 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
2637 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
2638 {
2639 switch (ch)
2640 {
2641 case 'i':
2642 aUpdateInterval = ValueUnion.u32;
2643 break;
2644
2645 default:
2646 return errorGetOptInternal(USAGE_I_GUESTSTATS, ch, &ValueUnion);
2647 }
2648 }
2649
2650 if (argc > 1 && aUpdateInterval == 0)
2651 return errorSyntaxInternal(USAGE_I_GUESTSTATS, Internal::tr("Invalid update interval specified"));
2652
2653 RTPrintf(Internal::tr("argc=%d interval=%u\n"), argc, aUpdateInterval);
2654
2655 ComPtr<IMachine> ptrMachine;
2656 HRESULT hrc;
2657 CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
2658 ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
2659
2660 CHECK_ERROR_RET(ptrMachine, LockMachine(aSession, LockType_Shared), RTEXITCODE_FAILURE);
2661
2662 /*
2663 * Get the guest interface.
2664 */
2665 ComPtr<IConsole> ptrConsole;
2666 CHECK_ERROR_RET(aSession, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
2667
2668 ComPtr<IGuest> ptrGuest;
2669 CHECK_ERROR_RET(ptrConsole, COMGETTER(Guest)(ptrGuest.asOutParam()), RTEXITCODE_FAILURE);
2670
2671 if (aUpdateInterval)
2672 CHECK_ERROR_RET(ptrGuest, COMSETTER(StatisticsUpdateInterval)(aUpdateInterval), RTEXITCODE_FAILURE);
2673 else
2674 {
2675 ULONG mCpuUser, mCpuKernel, mCpuIdle;
2676 ULONG mMemTotal, mMemFree, mMemBalloon, mMemShared, mMemCache, mPageTotal;
2677 ULONG ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal, ulMemSharedTotal;
2678
2679 CHECK_ERROR_RET(ptrGuest, InternalGetStatistics(&mCpuUser, &mCpuKernel, &mCpuIdle,
2680 &mMemTotal, &mMemFree, &mMemBalloon, &mMemShared, &mMemCache,
2681 &mPageTotal, &ulMemAllocTotal, &ulMemFreeTotal,
2682 &ulMemBalloonTotal, &ulMemSharedTotal),
2683 RTEXITCODE_FAILURE);
2684 RTPrintf("mCpuUser=%u mCpuKernel=%u mCpuIdle=%u\n"
2685 "mMemTotal=%u mMemFree=%u mMemBalloon=%u mMemShared=%u mMemCache=%u\n"
2686 "mPageTotal=%u ulMemAllocTotal=%u ulMemFreeTotal=%u ulMemBalloonTotal=%u ulMemSharedTotal=%u\n",
2687 mCpuUser, mCpuKernel, mCpuIdle,
2688 mMemTotal, mMemFree, mMemBalloon, mMemShared, mMemCache,
2689 mPageTotal, ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal, ulMemSharedTotal);
2690
2691 }
2692
2693 return RTEXITCODE_SUCCESS;
2694}
2695
2696
2697/**
2698 * Wrapper for handling internal commands
2699 */
2700RTEXITCODE handleInternalCommands(HandlerArg *a)
2701{
2702 /* at least a command is required */
2703 if (a->argc < 1)
2704 return errorSyntaxInternal(USAGE_I_ALL, Internal::tr("Command missing"));
2705
2706 /*
2707 * The 'string switch' on command name.
2708 */
2709 const char *pszCmd = a->argv[0];
2710 if (!strcmp(pszCmd, "loadmap"))
2711 return CmdLoadMap(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2712 if (!strcmp(pszCmd, "loadsyms"))
2713 return CmdLoadSyms(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2714 //if (!strcmp(pszCmd, "unloadsyms"))
2715 // return CmdUnloadSyms(argc - 1, &a->argv[1]);
2716 if (!strcmp(pszCmd, "sethduuid") || !strcmp(pszCmd, "sethdparentuuid"))
2717 return CmdSetHDUUID(a->argc, &a->argv[0], a->virtualBox, a->session);
2718 if (!strcmp(pszCmd, "dumphdinfo"))
2719 return CmdDumpHDInfo(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2720 if (!strcmp(pszCmd, "listpartitions"))
2721 return CmdListPartitions(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2722 if (!strcmp(pszCmd, "createrawvmdk"))
2723 return CmdCreateRawVMDK(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2724 if (!strcmp(pszCmd, "renamevmdk"))
2725 return CmdRenameVMDK(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2726 if (!strcmp(pszCmd, "converttoraw"))
2727 return CmdConvertToRaw(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2728 if (!strcmp(pszCmd, "converthd"))
2729 return CmdConvertHardDisk(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2730 if (!strcmp(pszCmd, "modinstall"))
2731 return CmdModInstall();
2732 if (!strcmp(pszCmd, "moduninstall"))
2733 return CmdModUninstall();
2734 if (!strcmp(pszCmd, "debuglog"))
2735 return CmdDebugLog(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2736 if (!strcmp(pszCmd, "passwordhash"))
2737 return CmdGeneratePasswordHash(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2738 if (!strcmp(pszCmd, "gueststats"))
2739 return CmdGuestStats(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2740 if (!strcmp(pszCmd, "repairhd"))
2741 return CmdRepairHardDisk(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2742
2743 /* default: */
2744 return errorSyntaxInternal(USAGE_I_ALL, Internal::tr("Invalid command '%s'"), a->argv[0]);
2745}
Note: See TracBrowser for help on using the repository browser.

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