VirtualBox

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

Last change on this file since 86663 was 85889, checked in by vboxsync, 4 years ago

VDISKRAWPARTDESC: Unconst some the pointers, it makes for ugly casts in new VMDK code. bugref:9224

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