VirtualBox

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

Last change on this file since 85550 was 85221, checked in by vboxsync, 4 years ago

vd.h,VBoxInternalManager.cpp: It is certifiably insane to use 3 letter value names for a C enum like VDISKPARTTYPE. Clang 11 complains that it clashes with the MBR and EPT types in the Main API when vd.h is included together with it. bugref:9790

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