VirtualBox

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

Last change on this file since 79965 was 79965, checked in by vboxsync, 5 years ago

Storage: Added a desired format parameter to VDGetFormat() so Main can pass along the device type. Bumped the RAW backend down after the CUE and VISO to prevent (impossible) mixups of tiny files. Extended rawProbe() to look for a valid ISO-9660/UDF descriptor sequence on the image if the caller doesn't say it should be a floppy or hdd, only if that fail go by the extension. Note! the pfnProbe callback should probably get a score return parameter to indicate how confient the backend is about the probe result, as this would prevent the RAW backend from accidentally grabbing images which belongs to a plug-in. bugref:9151

  • 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 79965 2019-07-24 20:32:32Z 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-2019 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 = GPT;
762 pPart->uPartitioningType = 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 = MBR;
878 pPart->uPartitioningType = 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 == 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(RTFileGetSize(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 = RTFileGetSize(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 = RTFileGetSize(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 = RTFileGetSize(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 = RTFileGetSize(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 == MBR)
1778 {
1779 RTMsgError("MBR/EPT overlaps with data area");
1780 vrc = VERR_INVALID_PARAMETER;
1781 goto out;
1782 }
1783 else
1784 {
1785 if (RawDescriptor.cPartDescs != i+1)
1786 {
1787 RTMsgError("GPT overlaps with data area");
1788 vrc = VERR_INVALID_PARAMETER;
1789 goto out;
1790 }
1791 }
1792 }
1793 }
1794 }
1795 }
1796
1797 RTFileClose(hRawFile);
1798
1799#ifdef DEBUG_klaus
1800 if (!(RawDescriptor.uFlags & VDISKRAW_DISK))
1801 {
1802 RTPrintf("# start length startoffset partdataptr device\n");
1803 for (unsigned i = 0; i < RawDescriptor.cPartDescs; i++)
1804 {
1805 RTPrintf("%2u %14RU64 %14RU64 %14RU64 %#18p %s\n", i,
1806 RawDescriptor.pPartDescs[i].uStart,
1807 RawDescriptor.pPartDescs[i].cbData,
1808 RawDescriptor.pPartDescs[i].uStartOffset,
1809 RawDescriptor.pPartDescs[i].pvPartitionData,
1810 RawDescriptor.pPartDescs[i].pszRawDevice);
1811 }
1812 }
1813#endif
1814
1815 VDINTERFACEERROR vdInterfaceError;
1816 vdInterfaceError.pfnError = handleVDError;
1817 vdInterfaceError.pfnMessage = handleVDMessage;
1818
1819 rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1820 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1821 AssertRC(vrc);
1822
1823 vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk); /* Raw VMDK's are harddisk only. */
1824 if (RT_FAILURE(vrc))
1825 {
1826 RTMsgError("Cannot create the virtual disk container: %Rrc", vrc);
1827 goto out;
1828 }
1829
1830 Assert(RT_MIN(cbSize / 512 / 16 / 63, 16383) -
1831 (unsigned int)RT_MIN(cbSize / 512 / 16 / 63, 16383) == 0);
1832 VDGEOMETRY PCHS, LCHS;
1833 PCHS.cCylinders = (unsigned int)RT_MIN(cbSize / 512 / 16 / 63, 16383);
1834 PCHS.cHeads = 16;
1835 PCHS.cSectors = 63;
1836 LCHS.cCylinders = 0;
1837 LCHS.cHeads = 0;
1838 LCHS.cSectors = 0;
1839 vrc = VDCreateBase(pDisk, "VMDK", filename.c_str(), cbSize,
1840 VD_IMAGE_FLAGS_FIXED | VD_VMDK_IMAGE_FLAGS_RAWDISK,
1841 (char *)&RawDescriptor, &PCHS, &LCHS, NULL,
1842 VD_OPEN_FLAGS_NORMAL, NULL, NULL);
1843 if (RT_FAILURE(vrc))
1844 {
1845 RTMsgError("Cannot create the raw disk VMDK: %Rrc", vrc);
1846 goto out;
1847 }
1848 RTPrintf("RAW host disk access VMDK file %s created successfully.\n", filename.c_str());
1849
1850 VDCloseAll(pDisk);
1851
1852 /* Clean up allocated memory etc. */
1853 if (pszPartitions)
1854 {
1855 for (unsigned i = 0; i < RawDescriptor.cPartDescs; i++)
1856 {
1857 /* Free memory allocated for relative device name. */
1858 if (fRelative && RawDescriptor.pPartDescs[i].pszRawDevice)
1859 RTStrFree((char *)(void *)RawDescriptor.pPartDescs[i].pszRawDevice);
1860 if (RawDescriptor.pPartDescs[i].pvPartitionData)
1861 RTMemFree((void *)RawDescriptor.pPartDescs[i].pvPartitionData);
1862 }
1863 if (RawDescriptor.pPartDescs)
1864 RTMemFree(RawDescriptor.pPartDescs);
1865 }
1866
1867 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1868
1869out:
1870 RTMsgError("The raw disk vmdk file was not created");
1871 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1872}
1873
1874static RTEXITCODE CmdRenameVMDK(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1875{
1876 RT_NOREF(aVirtualBox, aSession);
1877 Utf8Str src;
1878 Utf8Str dst;
1879 /* Parse the arguments. */
1880 for (int i = 0; i < argc; i++)
1881 {
1882 if (strcmp(argv[i], "-from") == 0)
1883 {
1884 if (argc <= i + 1)
1885 {
1886 return errorArgument("Missing argument to '%s'", argv[i]);
1887 }
1888 i++;
1889 src = argv[i];
1890 }
1891 else if (strcmp(argv[i], "-to") == 0)
1892 {
1893 if (argc <= i + 1)
1894 {
1895 return errorArgument("Missing argument to '%s'", argv[i]);
1896 }
1897 i++;
1898 dst = argv[i];
1899 }
1900 else
1901 {
1902 return errorSyntax(USAGE_I_RENAMEVMDK, "Invalid parameter '%s'", argv[i]);
1903 }
1904 }
1905
1906 if (src.isEmpty())
1907 return errorSyntax(USAGE_I_RENAMEVMDK, "Mandatory parameter -from missing");
1908 if (dst.isEmpty())
1909 return errorSyntax(USAGE_I_RENAMEVMDK, "Mandatory parameter -to missing");
1910
1911 PVDISK pDisk = NULL;
1912
1913 PVDINTERFACE pVDIfs = NULL;
1914 VDINTERFACEERROR vdInterfaceError;
1915 vdInterfaceError.pfnError = handleVDError;
1916 vdInterfaceError.pfnMessage = handleVDMessage;
1917
1918 int vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1919 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1920 AssertRC(vrc);
1921
1922 vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1923 if (RT_FAILURE(vrc))
1924 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot create the virtual disk container: %Rrc", vrc);
1925
1926 vrc = VDOpen(pDisk, "VMDK", src.c_str(), VD_OPEN_FLAGS_NORMAL, NULL);
1927 if (RT_SUCCESS(vrc))
1928 {
1929 vrc = VDCopy(pDisk, 0, pDisk, "VMDK", dst.c_str(), true, 0,
1930 VD_IMAGE_FLAGS_NONE, NULL, VD_OPEN_FLAGS_NORMAL,
1931 NULL, NULL, NULL);
1932 if (RT_FAILURE(vrc))
1933 RTMsgError("Cannot rename the image: %Rrc", vrc);
1934 }
1935 else
1936 RTMsgError("Cannot create the source image: %Rrc", vrc);
1937 VDCloseAll(pDisk);
1938 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1939}
1940
1941static RTEXITCODE CmdConvertToRaw(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1942{
1943 RT_NOREF(aVirtualBox, aSession);
1944 Utf8Str srcformat;
1945 Utf8Str src;
1946 Utf8Str dst;
1947 bool fWriteToStdOut = false;
1948
1949 /* Parse the arguments. */
1950 for (int i = 0; i < argc; i++)
1951 {
1952 if (strcmp(argv[i], "-format") == 0)
1953 {
1954 if (argc <= i + 1)
1955 {
1956 return errorArgument("Missing argument to '%s'", argv[i]);
1957 }
1958 i++;
1959 srcformat = argv[i];
1960 }
1961 else if (src.isEmpty())
1962 {
1963 src = argv[i];
1964 }
1965 else if (dst.isEmpty())
1966 {
1967 dst = argv[i];
1968#ifdef ENABLE_CONVERT_RAW_TO_STDOUT
1969 if (!strcmp(argv[i], "stdout"))
1970 fWriteToStdOut = true;
1971#endif /* ENABLE_CONVERT_RAW_TO_STDOUT */
1972 }
1973 else
1974 {
1975 return errorSyntax(USAGE_I_CONVERTTORAW, "Invalid parameter '%s'", argv[i]);
1976 }
1977 }
1978
1979 if (src.isEmpty())
1980 return errorSyntax(USAGE_I_CONVERTTORAW, "Mandatory filename parameter missing");
1981 if (dst.isEmpty())
1982 return errorSyntax(USAGE_I_CONVERTTORAW, "Mandatory outputfile parameter missing");
1983
1984 PVDISK pDisk = NULL;
1985
1986 PVDINTERFACE pVDIfs = NULL;
1987 VDINTERFACEERROR vdInterfaceError;
1988 vdInterfaceError.pfnError = handleVDError;
1989 vdInterfaceError.pfnMessage = handleVDMessage;
1990
1991 int vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1992 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1993 AssertRC(vrc);
1994
1995 /** @todo Support convert to raw for floppy and DVD images too. */
1996 vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1997 if (RT_FAILURE(vrc))
1998 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot create the virtual disk container: %Rrc", vrc);
1999
2000 /* Open raw output file. */
2001 RTFILE outFile;
2002 vrc = VINF_SUCCESS;
2003 if (fWriteToStdOut)
2004 vrc = RTFileFromNative(&outFile, 1);
2005 else
2006 vrc = RTFileOpen(&outFile, dst.c_str(), RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
2007 if (RT_FAILURE(vrc))
2008 {
2009 VDCloseAll(pDisk);
2010 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot create destination file \"%s\": %Rrc", dst.c_str(), vrc);
2011 }
2012
2013 if (srcformat.isEmpty())
2014 {
2015 char *pszFormat = NULL;
2016 VDTYPE enmType = VDTYPE_INVALID;
2017 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
2018 src.c_str(), VDTYPE_INVALID, &pszFormat, &enmType);
2019 if (RT_FAILURE(vrc) || enmType != VDTYPE_HDD)
2020 {
2021 VDCloseAll(pDisk);
2022 if (!fWriteToStdOut)
2023 {
2024 RTFileClose(outFile);
2025 RTFileDelete(dst.c_str());
2026 }
2027 if (RT_FAILURE(vrc))
2028 RTMsgError("No file format specified and autodetect failed - please specify format: %Rrc", vrc);
2029 else
2030 RTMsgError("Only converting harddisk images is supported");
2031 return RTEXITCODE_FAILURE;
2032 }
2033 srcformat = pszFormat;
2034 RTStrFree(pszFormat);
2035 }
2036 vrc = VDOpen(pDisk, srcformat.c_str(), src.c_str(), VD_OPEN_FLAGS_READONLY, NULL);
2037 if (RT_FAILURE(vrc))
2038 {
2039 VDCloseAll(pDisk);
2040 if (!fWriteToStdOut)
2041 {
2042 RTFileClose(outFile);
2043 RTFileDelete(dst.c_str());
2044 }
2045 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot open the source image: %Rrc", vrc);
2046 }
2047
2048 uint64_t cbSize = VDGetSize(pDisk, VD_LAST_IMAGE);
2049 uint64_t offFile = 0;
2050#define RAW_BUFFER_SIZE _128K
2051 size_t cbBuf = RAW_BUFFER_SIZE;
2052 void *pvBuf = RTMemAlloc(cbBuf);
2053 if (pvBuf)
2054 {
2055 RTStrmPrintf(g_pStdErr, "Converting image \"%s\" with size %RU64 bytes (%RU64MB) to raw...\n", src.c_str(), cbSize, (cbSize + _1M - 1) / _1M);
2056 while (offFile < cbSize)
2057 {
2058 size_t cb = (size_t)RT_MIN(cbSize - offFile, cbBuf);
2059 vrc = VDRead(pDisk, offFile, pvBuf, cb);
2060 if (RT_FAILURE(vrc))
2061 break;
2062 vrc = RTFileWrite(outFile, pvBuf, cb, NULL);
2063 if (RT_FAILURE(vrc))
2064 break;
2065 offFile += cb;
2066 }
2067 RTMemFree(pvBuf);
2068 if (RT_FAILURE(vrc))
2069 {
2070 VDCloseAll(pDisk);
2071 if (!fWriteToStdOut)
2072 {
2073 RTFileClose(outFile);
2074 RTFileDelete(dst.c_str());
2075 }
2076 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Cannot copy image data: %Rrc", vrc);
2077 }
2078 }
2079 else
2080 {
2081 vrc = VERR_NO_MEMORY;
2082 VDCloseAll(pDisk);
2083 if (!fWriteToStdOut)
2084 {
2085 RTFileClose(outFile);
2086 RTFileDelete(dst.c_str());
2087 }
2088 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory allocating read buffer");
2089 }
2090
2091 if (!fWriteToStdOut)
2092 RTFileClose(outFile);
2093 VDCloseAll(pDisk);
2094 return RTEXITCODE_SUCCESS;
2095}
2096
2097static RTEXITCODE CmdConvertHardDisk(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
2098{
2099 RT_NOREF(aVirtualBox, aSession);
2100 Utf8Str srcformat;
2101 Utf8Str dstformat;
2102 Utf8Str src;
2103 Utf8Str dst;
2104 int vrc;
2105 PVDISK pSrcDisk = NULL;
2106 PVDISK pDstDisk = NULL;
2107 VDTYPE enmSrcType = VDTYPE_INVALID;
2108
2109 /* Parse the arguments. */
2110 for (int i = 0; i < argc; i++)
2111 {
2112 if (strcmp(argv[i], "-srcformat") == 0)
2113 {
2114 if (argc <= i + 1)
2115 {
2116 return errorArgument("Missing argument to '%s'", argv[i]);
2117 }
2118 i++;
2119 srcformat = argv[i];
2120 }
2121 else if (strcmp(argv[i], "-dstformat") == 0)
2122 {
2123 if (argc <= i + 1)
2124 {
2125 return errorArgument("Missing argument to '%s'", argv[i]);
2126 }
2127 i++;
2128 dstformat = argv[i];
2129 }
2130 else if (src.isEmpty())
2131 {
2132 src = argv[i];
2133 }
2134 else if (dst.isEmpty())
2135 {
2136 dst = argv[i];
2137 }
2138 else
2139 {
2140 return errorSyntax(USAGE_I_CONVERTHD, "Invalid parameter '%s'", argv[i]);
2141 }
2142 }
2143
2144 if (src.isEmpty())
2145 return errorSyntax(USAGE_I_CONVERTHD, "Mandatory input image parameter missing");
2146 if (dst.isEmpty())
2147 return errorSyntax(USAGE_I_CONVERTHD, "Mandatory output image parameter missing");
2148
2149
2150 PVDINTERFACE pVDIfs = NULL;
2151 VDINTERFACEERROR vdInterfaceError;
2152 vdInterfaceError.pfnError = handleVDError;
2153 vdInterfaceError.pfnMessage = handleVDMessage;
2154
2155 vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
2156 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
2157 AssertRC(vrc);
2158
2159 do
2160 {
2161 /* Try to determine input image format */
2162 if (srcformat.isEmpty())
2163 {
2164 char *pszFormat = NULL;
2165 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
2166 src.c_str(), VDTYPE_HDD, &pszFormat, &enmSrcType);
2167 if (RT_FAILURE(vrc))
2168 {
2169 RTMsgError("No file format specified and autodetect failed - please specify format: %Rrc", vrc);
2170 break;
2171 }
2172 srcformat = pszFormat;
2173 RTStrFree(pszFormat);
2174 }
2175
2176 vrc = VDCreate(pVDIfs, enmSrcType, &pSrcDisk);
2177 if (RT_FAILURE(vrc))
2178 {
2179 RTMsgError("Cannot create the source virtual disk container: %Rrc", vrc);
2180 break;
2181 }
2182
2183 /* Open the input image */
2184 vrc = VDOpen(pSrcDisk, srcformat.c_str(), src.c_str(), VD_OPEN_FLAGS_READONLY, NULL);
2185 if (RT_FAILURE(vrc))
2186 {
2187 RTMsgError("Cannot open the source image: %Rrc", vrc);
2188 break;
2189 }
2190
2191 /* Output format defaults to VDI */
2192 if (dstformat.isEmpty())
2193 dstformat = "VDI";
2194
2195 vrc = VDCreate(pVDIfs, enmSrcType, &pDstDisk);
2196 if (RT_FAILURE(vrc))
2197 {
2198 RTMsgError("Cannot create the destination virtual disk container: %Rrc", vrc);
2199 break;
2200 }
2201
2202 uint64_t cbSize = VDGetSize(pSrcDisk, VD_LAST_IMAGE);
2203 RTStrmPrintf(g_pStdErr, "Converting image \"%s\" with size %RU64 bytes (%RU64MB)...\n", src.c_str(), cbSize, (cbSize + _1M - 1) / _1M);
2204
2205 /* Create the output image */
2206 vrc = VDCopy(pSrcDisk, VD_LAST_IMAGE, pDstDisk, dstformat.c_str(),
2207 dst.c_str(), false, 0, VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED,
2208 NULL, VD_OPEN_FLAGS_NORMAL, NULL, NULL, NULL);
2209 if (RT_FAILURE(vrc))
2210 {
2211 RTMsgError("Cannot copy the image: %Rrc", vrc);
2212 break;
2213 }
2214 }
2215 while (0);
2216 if (pDstDisk)
2217 VDCloseAll(pDstDisk);
2218 if (pSrcDisk)
2219 VDCloseAll(pSrcDisk);
2220
2221 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2222}
2223
2224/**
2225 * Tries to repair a corrupted hard disk image.
2226 *
2227 * @returns VBox status code
2228 */
2229static RTEXITCODE CmdRepairHardDisk(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
2230{
2231 RT_NOREF(aVirtualBox, aSession);
2232 Utf8Str image;
2233 Utf8Str format;
2234 int vrc;
2235 bool fDryRun = false;
2236
2237 /* Parse the arguments. */
2238 for (int i = 0; i < argc; i++)
2239 {
2240 if (strcmp(argv[i], "-dry-run") == 0)
2241 {
2242 fDryRun = true;
2243 }
2244 else if (strcmp(argv[i], "-format") == 0)
2245 {
2246 if (argc <= i + 1)
2247 {
2248 return errorArgument("Missing argument to '%s'", argv[i]);
2249 }
2250 i++;
2251 format = argv[i];
2252 }
2253 else if (image.isEmpty())
2254 {
2255 image = argv[i];
2256 }
2257 else
2258 {
2259 return errorSyntax(USAGE_I_REPAIRHD, "Invalid parameter '%s'", argv[i]);
2260 }
2261 }
2262
2263 if (image.isEmpty())
2264 return errorSyntax(USAGE_I_REPAIRHD, "Mandatory input image parameter missing");
2265
2266 PVDINTERFACE pVDIfs = NULL;
2267 VDINTERFACEERROR vdInterfaceError;
2268 vdInterfaceError.pfnError = handleVDError;
2269 vdInterfaceError.pfnMessage = handleVDMessage;
2270
2271 vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
2272 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
2273 AssertRC(vrc);
2274
2275 do
2276 {
2277 /* Try to determine input image format */
2278 if (format.isEmpty())
2279 {
2280 char *pszFormat = NULL;
2281 VDTYPE enmSrcType = VDTYPE_INVALID;
2282
2283 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
2284 image.c_str(), VDTYPE_HDD, &pszFormat, &enmSrcType);
2285 if (RT_FAILURE(vrc) && (vrc != VERR_VD_IMAGE_CORRUPTED))
2286 {
2287 RTMsgError("No file format specified and autodetect failed - please specify format: %Rrc", vrc);
2288 break;
2289 }
2290 format = pszFormat;
2291 RTStrFree(pszFormat);
2292 }
2293
2294 uint32_t fFlags = 0;
2295 if (fDryRun)
2296 fFlags |= VD_REPAIR_DRY_RUN;
2297
2298 vrc = VDRepair(pVDIfs, NULL, image.c_str(), format.c_str(), fFlags);
2299 }
2300 while (0);
2301
2302 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2303}
2304
2305/**
2306 * Unloads the necessary driver.
2307 *
2308 * @returns VBox status code
2309 */
2310static RTEXITCODE CmdModUninstall(void)
2311{
2312 int rc = SUPR3Uninstall();
2313 if (RT_SUCCESS(rc) || rc == VERR_NOT_IMPLEMENTED)
2314 return RTEXITCODE_SUCCESS;
2315 return RTEXITCODE_FAILURE;
2316}
2317
2318/**
2319 * Loads the necessary driver.
2320 *
2321 * @returns VBox status code
2322 */
2323static RTEXITCODE CmdModInstall(void)
2324{
2325 int rc = SUPR3Install();
2326 if (RT_SUCCESS(rc) || rc == VERR_NOT_IMPLEMENTED)
2327 return RTEXITCODE_SUCCESS;
2328 return RTEXITCODE_FAILURE;
2329}
2330
2331static RTEXITCODE CmdDebugLog(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
2332{
2333 /*
2334 * The first parameter is the name or UUID of a VM with a direct session
2335 * that we wish to open.
2336 */
2337 if (argc < 1)
2338 return errorSyntax(USAGE_I_DEBUGLOG, "Missing VM name/UUID");
2339
2340 ComPtr<IMachine> ptrMachine;
2341 HRESULT rc;
2342 CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
2343 ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
2344
2345 CHECK_ERROR_RET(ptrMachine, LockMachine(aSession, LockType_Shared), RTEXITCODE_FAILURE);
2346
2347 /*
2348 * Get the debugger interface.
2349 */
2350 ComPtr<IConsole> ptrConsole;
2351 CHECK_ERROR_RET(aSession, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
2352
2353 ComPtr<IMachineDebugger> ptrDebugger;
2354 CHECK_ERROR_RET(ptrConsole, COMGETTER(Debugger)(ptrDebugger.asOutParam()), RTEXITCODE_FAILURE);
2355
2356 /*
2357 * Parse the command.
2358 */
2359 bool fEnablePresent = false;
2360 bool fEnable = false;
2361 bool fFlagsPresent = false;
2362 RTCString strFlags;
2363 bool fGroupsPresent = false;
2364 RTCString strGroups;
2365 bool fDestsPresent = false;
2366 RTCString strDests;
2367
2368 static const RTGETOPTDEF s_aOptions[] =
2369 {
2370 { "--disable", 'E', RTGETOPT_REQ_NOTHING },
2371 { "--enable", 'e', RTGETOPT_REQ_NOTHING },
2372 { "--flags", 'f', RTGETOPT_REQ_STRING },
2373 { "--groups", 'g', RTGETOPT_REQ_STRING },
2374 { "--destinations", 'd', RTGETOPT_REQ_STRING }
2375 };
2376
2377 int ch;
2378 RTGETOPTUNION ValueUnion;
2379 RTGETOPTSTATE GetState;
2380 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
2381 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
2382 {
2383 switch (ch)
2384 {
2385 case 'e':
2386 fEnablePresent = true;
2387 fEnable = true;
2388 break;
2389
2390 case 'E':
2391 fEnablePresent = true;
2392 fEnable = false;
2393 break;
2394
2395 case 'f':
2396 fFlagsPresent = true;
2397 if (*ValueUnion.psz)
2398 {
2399 if (strFlags.isNotEmpty())
2400 strFlags.append(' ');
2401 strFlags.append(ValueUnion.psz);
2402 }
2403 break;
2404
2405 case 'g':
2406 fGroupsPresent = true;
2407 if (*ValueUnion.psz)
2408 {
2409 if (strGroups.isNotEmpty())
2410 strGroups.append(' ');
2411 strGroups.append(ValueUnion.psz);
2412 }
2413 break;
2414
2415 case 'd':
2416 fDestsPresent = true;
2417 if (*ValueUnion.psz)
2418 {
2419 if (strDests.isNotEmpty())
2420 strDests.append(' ');
2421 strDests.append(ValueUnion.psz);
2422 }
2423 break;
2424
2425 default:
2426 return errorGetOpt(USAGE_I_DEBUGLOG, ch, &ValueUnion);
2427 }
2428 }
2429
2430 /*
2431 * Do the job.
2432 */
2433 if (fEnablePresent && !fEnable)
2434 CHECK_ERROR_RET(ptrDebugger, COMSETTER(LogEnabled)(FALSE), RTEXITCODE_FAILURE);
2435
2436 /** @todo flags, groups destination. */
2437 if (fFlagsPresent || fGroupsPresent || fDestsPresent)
2438 RTMsgWarning("One or more of the requested features are not implemented! Feel free to do this.");
2439
2440 if (fEnablePresent && fEnable)
2441 CHECK_ERROR_RET(ptrDebugger, COMSETTER(LogEnabled)(TRUE), RTEXITCODE_FAILURE);
2442 return RTEXITCODE_SUCCESS;
2443}
2444
2445/**
2446 * Generate a SHA-256 password hash
2447 */
2448static RTEXITCODE CmdGeneratePasswordHash(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
2449{
2450 RT_NOREF(aVirtualBox, aSession);
2451
2452 /* one parameter, the password to hash */
2453 if (argc != 1)
2454 return errorSyntax(USAGE_I_PASSWORDHASH, "password to hash required");
2455
2456 uint8_t abDigest[RTSHA256_HASH_SIZE];
2457 RTSha256(argv[0], strlen(argv[0]), abDigest);
2458 char pszDigest[RTSHA256_DIGEST_LEN + 1];
2459 RTSha256ToString(abDigest, pszDigest, sizeof(pszDigest));
2460 RTPrintf("Password hash: %s\n", pszDigest);
2461
2462 return RTEXITCODE_SUCCESS;
2463}
2464
2465/**
2466 * Print internal guest statistics or
2467 * set internal guest statistics update interval if specified
2468 */
2469static RTEXITCODE CmdGuestStats(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
2470{
2471 /* one parameter, guest name */
2472 if (argc < 1)
2473 return errorSyntax(USAGE_I_GUESTSTATS, "Missing VM name/UUID");
2474
2475 /*
2476 * Parse the command.
2477 */
2478 ULONG aUpdateInterval = 0;
2479
2480 static const RTGETOPTDEF s_aOptions[] =
2481 {
2482 { "--interval", 'i', RTGETOPT_REQ_UINT32 }
2483 };
2484
2485 int ch;
2486 RTGETOPTUNION ValueUnion;
2487 RTGETOPTSTATE GetState;
2488 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
2489 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
2490 {
2491 switch (ch)
2492 {
2493 case 'i':
2494 aUpdateInterval = ValueUnion.u32;
2495 break;
2496
2497 default:
2498 return errorGetOpt(USAGE_I_GUESTSTATS, ch, &ValueUnion);
2499 }
2500 }
2501
2502 if (argc > 1 && aUpdateInterval == 0)
2503 return errorSyntax(USAGE_I_GUESTSTATS, "Invalid update interval specified");
2504
2505 RTPrintf("argc=%d interval=%u\n", argc, aUpdateInterval);
2506
2507 ComPtr<IMachine> ptrMachine;
2508 HRESULT rc;
2509 CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
2510 ptrMachine.asOutParam()), RTEXITCODE_FAILURE);
2511
2512 CHECK_ERROR_RET(ptrMachine, LockMachine(aSession, LockType_Shared), RTEXITCODE_FAILURE);
2513
2514 /*
2515 * Get the guest interface.
2516 */
2517 ComPtr<IConsole> ptrConsole;
2518 CHECK_ERROR_RET(aSession, COMGETTER(Console)(ptrConsole.asOutParam()), RTEXITCODE_FAILURE);
2519
2520 ComPtr<IGuest> ptrGuest;
2521 CHECK_ERROR_RET(ptrConsole, COMGETTER(Guest)(ptrGuest.asOutParam()), RTEXITCODE_FAILURE);
2522
2523 if (aUpdateInterval)
2524 CHECK_ERROR_RET(ptrGuest, COMSETTER(StatisticsUpdateInterval)(aUpdateInterval), RTEXITCODE_FAILURE);
2525 else
2526 {
2527 ULONG mCpuUser, mCpuKernel, mCpuIdle;
2528 ULONG mMemTotal, mMemFree, mMemBalloon, mMemShared, mMemCache, mPageTotal;
2529 ULONG ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal, ulMemSharedTotal;
2530
2531 CHECK_ERROR_RET(ptrGuest, InternalGetStatistics(&mCpuUser, &mCpuKernel, &mCpuIdle,
2532 &mMemTotal, &mMemFree, &mMemBalloon, &mMemShared, &mMemCache,
2533 &mPageTotal, &ulMemAllocTotal, &ulMemFreeTotal,
2534 &ulMemBalloonTotal, &ulMemSharedTotal),
2535 RTEXITCODE_FAILURE);
2536 RTPrintf("mCpuUser=%u mCpuKernel=%u mCpuIdle=%u\n"
2537 "mMemTotal=%u mMemFree=%u mMemBalloon=%u mMemShared=%u mMemCache=%u\n"
2538 "mPageTotal=%u ulMemAllocTotal=%u ulMemFreeTotal=%u ulMemBalloonTotal=%u ulMemSharedTotal=%u\n",
2539 mCpuUser, mCpuKernel, mCpuIdle,
2540 mMemTotal, mMemFree, mMemBalloon, mMemShared, mMemCache,
2541 mPageTotal, ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal, ulMemSharedTotal);
2542
2543 }
2544
2545 return RTEXITCODE_SUCCESS;
2546}
2547
2548
2549/**
2550 * Wrapper for handling internal commands
2551 */
2552RTEXITCODE handleInternalCommands(HandlerArg *a)
2553{
2554 g_fInternalMode = true;
2555
2556 /* at least a command is required */
2557 if (a->argc < 1)
2558 return errorSyntax(USAGE_S_ALL, "Command missing");
2559
2560 /*
2561 * The 'string switch' on command name.
2562 */
2563 const char *pszCmd = a->argv[0];
2564 if (!strcmp(pszCmd, "loadmap"))
2565 return CmdLoadMap(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2566 if (!strcmp(pszCmd, "loadsyms"))
2567 return CmdLoadSyms(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2568 //if (!strcmp(pszCmd, "unloadsyms"))
2569 // return CmdUnloadSyms(argc - 1, &a->argv[1]);
2570 if (!strcmp(pszCmd, "sethduuid") || !strcmp(pszCmd, "sethdparentuuid"))
2571 return CmdSetHDUUID(a->argc, &a->argv[0], a->virtualBox, a->session);
2572 if (!strcmp(pszCmd, "dumphdinfo"))
2573 return CmdDumpHDInfo(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2574 if (!strcmp(pszCmd, "listpartitions"))
2575 return CmdListPartitions(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2576 if (!strcmp(pszCmd, "createrawvmdk"))
2577 return CmdCreateRawVMDK(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2578 if (!strcmp(pszCmd, "renamevmdk"))
2579 return CmdRenameVMDK(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2580 if (!strcmp(pszCmd, "converttoraw"))
2581 return CmdConvertToRaw(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2582 if (!strcmp(pszCmd, "converthd"))
2583 return CmdConvertHardDisk(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2584 if (!strcmp(pszCmd, "modinstall"))
2585 return CmdModInstall();
2586 if (!strcmp(pszCmd, "moduninstall"))
2587 return CmdModUninstall();
2588 if (!strcmp(pszCmd, "debuglog"))
2589 return CmdDebugLog(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2590 if (!strcmp(pszCmd, "passwordhash"))
2591 return CmdGeneratePasswordHash(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2592 if (!strcmp(pszCmd, "gueststats"))
2593 return CmdGuestStats(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2594 if (!strcmp(pszCmd, "repairhd"))
2595 return CmdRepairHardDisk(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2596
2597 /* default: */
2598 return errorSyntax(USAGE_S_ALL, "Invalid command '%s'", a->argv[0]);
2599}
2600
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