VirtualBox

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

Last change on this file since 96313 was 95140, checked in by vboxsync, 3 years ago

Frontends + Main: Adjust to the new rules wrt. to rc -> hrc,vrc usage. This also fixes quite a few bugs wrt shadow variables, wrong return values and output error translations / exit codes. Also see @todos added. ​​bugref:10223

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