VirtualBox

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

最後變更 在這個檔案從94240是 94240,由 vboxsync 提交於 3 年 前

FE/VBoxManage: Move stuff to VBoxInternalManage.cpp because it is only used there now, ​bugref:9186

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

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