VirtualBox

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

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

Runtime: Some renaming to stay consistent (*Get* always returns what is asked for while *Query* returns a status code and where to store the value on success is given as a pointer)

  • RTVfsFileGetSize -> RTVfsFileQuerySize
  • RTFileQuerySize -> RTFileQuerySizeByPath
  • RTFileGetSize -> RTFileQuerySize
  • RTFileGetSizeMaxEx -> RTFileQuerySizeMaxEx
  • 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 80585 2019-09-04 14:05:50Z 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(RTFileQuerySize(hRawFile, &cbSize)))
1291 {
1292 RTMsgError("Cannot get the geometry of the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
1293 goto out;
1294 }
1295 else
1296 {
1297 if (fRelative)
1298 {
1299 RTMsgError("The -relative parameter is invalid for raw images");
1300 vrc = VERR_INVALID_PARAMETER;
1301 goto out;
1302 }
1303 vrc = VINF_SUCCESS;
1304 }
1305 }
1306#elif defined(RT_OS_LINUX)
1307 struct stat DevStat;
1308 if (!fstat(RTFileToNative(hRawFile), &DevStat))
1309 {
1310 if (S_ISBLK(DevStat.st_mode))
1311 {
1312#ifdef BLKGETSIZE64
1313 /* BLKGETSIZE64 is broken up to 2.4.17 and in many 2.5.x. In 2.6.0
1314 * it works without problems. */
1315 struct utsname utsname;
1316 if ( uname(&utsname) == 0
1317 && ( (strncmp(utsname.release, "2.5.", 4) == 0 && atoi(&utsname.release[4]) >= 18)
1318 || (strncmp(utsname.release, "2.", 2) == 0 && atoi(&utsname.release[2]) >= 6)))
1319 {
1320 uint64_t cbBlk;
1321 if (!ioctl(RTFileToNative(hRawFile), BLKGETSIZE64, &cbBlk))
1322 cbSize = cbBlk;
1323 }
1324#endif /* BLKGETSIZE64 */
1325 if (!cbSize)
1326 {
1327 long cBlocks;
1328 if (!ioctl(RTFileToNative(hRawFile), BLKGETSIZE, &cBlocks))
1329 cbSize = (uint64_t)cBlocks << 9;
1330 else
1331 {
1332 vrc = RTErrConvertFromErrno(errno);
1333 RTMsgError("Cannot get the size of the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
1334 goto out;
1335 }
1336 }
1337 }
1338 else if (S_ISREG(DevStat.st_mode))
1339 {
1340 vrc = RTFileQuerySize(hRawFile, &cbSize);
1341 if (RT_FAILURE(vrc))
1342 {
1343 RTMsgError("Failed to get size of file '%s': %Rrc", rawdisk.c_str(), vrc);
1344 goto out;
1345 }
1346 else if (fRelative)
1347 {
1348 RTMsgError("The -relative parameter is invalid for raw images");
1349 vrc = VERR_INVALID_PARAMETER;
1350 goto out;
1351 }
1352 }
1353 else
1354 {
1355 RTMsgError("File '%s' is no block device", rawdisk.c_str());
1356 vrc = VERR_INVALID_PARAMETER;
1357 goto out;
1358 }
1359 }
1360 else
1361 {
1362 vrc = RTErrConvertFromErrno(errno);
1363 RTMsgError("Failed to get file informtation for raw disk '%s': %Rrc",
1364 rawdisk.c_str(), vrc);
1365 }
1366#elif defined(RT_OS_DARWIN)
1367 struct stat DevStat;
1368 if (!fstat(RTFileToNative(hRawFile), &DevStat))
1369 {
1370 if (S_ISBLK(DevStat.st_mode))
1371 {
1372 uint64_t cBlocks;
1373 uint32_t cbBlock;
1374 if (!ioctl(RTFileToNative(hRawFile), DKIOCGETBLOCKCOUNT, &cBlocks))
1375 {
1376 if (!ioctl(RTFileToNative(hRawFile), DKIOCGETBLOCKSIZE, &cbBlock))
1377 cbSize = cBlocks * cbBlock;
1378 else
1379 {
1380 RTMsgError("Cannot get the block size for file '%s': %Rrc", rawdisk.c_str(), vrc);
1381 vrc = RTErrConvertFromErrno(errno);
1382 goto out;
1383 }
1384 }
1385 else
1386 {
1387 vrc = RTErrConvertFromErrno(errno);
1388 RTMsgError("Cannot get the block count for file '%s': %Rrc", rawdisk.c_str(), vrc);
1389 goto out;
1390 }
1391 }
1392 else if (S_ISREG(DevStat.st_mode))
1393 {
1394 fRelative = false; /* Must be false for raw image files. */
1395 vrc = RTFileQuerySize(hRawFile, &cbSize);
1396 if (RT_FAILURE(vrc))
1397 {
1398 RTMsgError("Failed to get size of file '%s': %Rrc", rawdisk.c_str(), vrc);
1399 goto out;
1400 }
1401 }
1402 else
1403 {
1404 RTMsgError("File '%s' is neither block device nor regular file", rawdisk.c_str());
1405 vrc = VERR_INVALID_PARAMETER;
1406 goto out;
1407 }
1408 }
1409 else
1410 {
1411 vrc = RTErrConvertFromErrno(errno);
1412 RTMsgError("Failed to get file informtation for raw disk '%s': %Rrc",
1413 rawdisk.c_str(), vrc);
1414 }
1415#elif defined(RT_OS_SOLARIS)
1416 struct stat DevStat;
1417 if (!fstat(RTFileToNative(hRawFile), &DevStat))
1418 {
1419 if (S_ISBLK(DevStat.st_mode) || S_ISCHR(DevStat.st_mode))
1420 {
1421 struct dk_minfo mediainfo;
1422 if (!ioctl(RTFileToNative(hRawFile), DKIOCGMEDIAINFO, &mediainfo))
1423 cbSize = mediainfo.dki_capacity * mediainfo.dki_lbsize;
1424 else
1425 {
1426 vrc = RTErrConvertFromErrno(errno);
1427 RTMsgError("Cannot get the size of the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
1428 goto out;
1429 }
1430 }
1431 else if (S_ISREG(DevStat.st_mode))
1432 {
1433 vrc = RTFileQuerySize(hRawFile, &cbSize);
1434 if (RT_FAILURE(vrc))
1435 {
1436 RTMsgError("Failed to get size of file '%s': %Rrc", rawdisk.c_str(), vrc);
1437 goto out;
1438 }
1439 }
1440 else
1441 {
1442 RTMsgError("File '%s' is no block or char device", rawdisk.c_str());
1443 vrc = VERR_INVALID_PARAMETER;
1444 goto out;
1445 }
1446 }
1447 else
1448 {
1449 vrc = RTErrConvertFromErrno(errno);
1450 RTMsgError("Failed to get file informtation for raw disk '%s': %Rrc",
1451 rawdisk.c_str(), vrc);
1452 }
1453#elif defined(RT_OS_FREEBSD)
1454 struct stat DevStat;
1455 if (!fstat(RTFileToNative(hRawFile), &DevStat))
1456 {
1457 if (S_ISCHR(DevStat.st_mode))
1458 {
1459 off_t cbMedia = 0;
1460 if (!ioctl(RTFileToNative(hRawFile), DIOCGMEDIASIZE, &cbMedia))
1461 cbSize = cbMedia;
1462 else
1463 {
1464 vrc = RTErrConvertFromErrno(errno);
1465 RTMsgError("Cannot get the block count for file '%s': %Rrc", rawdisk.c_str(), vrc);
1466 goto out;
1467 }
1468 }
1469 else if (S_ISREG(DevStat.st_mode))
1470 {
1471 if (fRelative)
1472 {
1473 RTMsgError("The -relative parameter is invalid for raw images");
1474 vrc = VERR_INVALID_PARAMETER;
1475 goto out;
1476 }
1477 cbSize = DevStat.st_size;
1478 }
1479 else
1480 {
1481 RTMsgError("File '%s' is neither character device nor regular file", rawdisk.c_str());
1482 vrc = VERR_INVALID_PARAMETER;
1483 goto out;
1484 }
1485 }
1486 else
1487 {
1488 vrc = RTErrConvertFromErrno(errno);
1489 RTMsgError("Failed to get file informtation for raw disk '%s': %Rrc",
1490 rawdisk.c_str(), vrc);
1491 }
1492#else /* all unrecognized OSes */
1493 /* Hopefully this works on all other hosts. If it doesn't, it'll just fail
1494 * creating the VMDK, so no real harm done. */
1495 vrc = RTFileQuerySize(hRawFile, &cbSize);
1496 if (RT_FAILURE(vrc))
1497 {
1498 RTMsgError("Cannot get the size of the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
1499 goto out;
1500 }
1501#endif
1502
1503 /* Check whether cbSize is actually sensible. */
1504 if (!cbSize || cbSize % 512)
1505 {
1506 RTMsgError("Detected size of raw disk '%s' is %RU64, an invalid value", rawdisk.c_str(), cbSize);
1507 vrc = VERR_INVALID_PARAMETER;
1508 goto out;
1509 }
1510
1511 RawDescriptor.szSignature[0] = 'R';
1512 RawDescriptor.szSignature[1] = 'A';
1513 RawDescriptor.szSignature[2] = 'W';
1514 RawDescriptor.szSignature[3] = '\0';
1515 if (!pszPartitions)
1516 {
1517 RawDescriptor.uFlags = VDISKRAW_DISK;
1518 RawDescriptor.pszRawDisk = rawdisk.c_str();
1519 }
1520 else
1521 {
1522 RawDescriptor.uFlags = VDISKRAW_NORMAL;
1523 RawDescriptor.pszRawDisk = NULL;
1524 RawDescriptor.cPartDescs = 0;
1525 RawDescriptor.pPartDescs = NULL;
1526
1527 uint32_t uPartitions = 0;
1528 uint32_t uPartitionsRO = 0;
1529
1530 const char *p = pszPartitions;
1531 char *pszNext;
1532 uint32_t u32;
1533 while (*p != '\0')
1534 {
1535 vrc = RTStrToUInt32Ex(p, &pszNext, 0, &u32);
1536 if (RT_FAILURE(vrc))
1537 {
1538 RTMsgError("Incorrect value in partitions parameter");
1539 goto out;
1540 }
1541 uPartitions |= RT_BIT(u32);
1542 p = pszNext;
1543 if (*p == 'r')
1544 {
1545 uPartitionsRO |= RT_BIT(u32);
1546 p++;
1547 }
1548 if (*p == ',')
1549 p++;
1550 else if (*p != '\0')
1551 {
1552 RTMsgError("Incorrect separator in partitions parameter");
1553 vrc = VERR_INVALID_PARAMETER;
1554 goto out;
1555 }
1556 }
1557
1558 HOSTPARTITIONS partitions;
1559 vrc = partRead(hRawFile, &partitions);
1560 if (RT_FAILURE(vrc))
1561 {
1562 RTMsgError("Cannot read the partition information from '%s'", rawdisk.c_str());
1563 goto out;
1564 }
1565
1566 RawDescriptor.uPartitioningType = partitions.uPartitioningType;
1567
1568 for (unsigned i = 0; i < partitions.cPartitions; i++)
1569 {
1570 if ( uPartitions & RT_BIT(partitions.aPartitions[i].uIndex)
1571 && PARTTYPE_IS_EXTENDED(partitions.aPartitions[i].uType))
1572 {
1573 /* Some ignorant user specified an extended partition.
1574 * Bad idea, as this would trigger an overlapping
1575 * partitions error later during VMDK creation. So warn
1576 * here and ignore what the user requested. */
1577 RTMsgWarning("It is not possible (and necessary) to explicitly give access to the "
1578 "extended partition %u. If required, enable access to all logical "
1579 "partitions inside this extended partition.",
1580 partitions.aPartitions[i].uIndex);
1581 uPartitions &= ~RT_BIT(partitions.aPartitions[i].uIndex);
1582 }
1583 }
1584
1585 for (unsigned i = 0; i < partitions.cPartitions; i++)
1586 {
1587 PVDISKRAWPARTDESC pPartDesc = NULL;
1588
1589 /* first dump the MBR/EPT data area */
1590 if (partitions.aPartitions[i].cPartDataSectors)
1591 {
1592 pPartDesc = appendPartDesc(&RawDescriptor.cPartDescs,
1593 &RawDescriptor.pPartDescs);
1594 if (!pPartDesc)
1595 {
1596 RTMsgError("Out of memory allocating the partition list for '%s'", rawdisk.c_str());
1597 vrc = VERR_NO_MEMORY;
1598 goto out;
1599 }
1600
1601 /** @todo the clipping below isn't 100% accurate, as it should
1602 * actually clip to the track size. However, that's easier said
1603 * than done as figuring out the track size is heuristics. In
1604 * any case the clipping is adjusted later after sorting, to
1605 * prevent overlapping data areas on the resulting image. */
1606 pPartDesc->cbData = RT_MIN(partitions.aPartitions[i].cPartDataSectors, 63) * 512;
1607 pPartDesc->uStart = partitions.aPartitions[i].uPartDataStart * 512;
1608 Assert(pPartDesc->cbData - (size_t)pPartDesc->cbData == 0);
1609 void *pPartData = RTMemAlloc((size_t)pPartDesc->cbData);
1610 if (!pPartData)
1611 {
1612 RTMsgError("Out of memory allocating the partition descriptor for '%s'", rawdisk.c_str());
1613 vrc = VERR_NO_MEMORY;
1614 goto out;
1615 }
1616 vrc = RTFileReadAt(hRawFile, partitions.aPartitions[i].uPartDataStart * 512,
1617 pPartData, (size_t)pPartDesc->cbData, NULL);
1618 if (RT_FAILURE(vrc))
1619 {
1620 RTMsgError("Cannot read partition data from raw device '%s': %Rrc", rawdisk.c_str(), vrc);
1621 goto out;
1622 }
1623 /* Splice in the replacement MBR code if specified. */
1624 if ( partitions.aPartitions[i].uPartDataStart == 0
1625 && pszMBRFilename)
1626 {
1627 RTFILE MBRFile;
1628 vrc = RTFileOpen(&MBRFile, pszMBRFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1629 if (RT_FAILURE(vrc))
1630 {
1631 RTMsgError("Cannot open replacement MBR file '%s' specified with -mbr: %Rrc", pszMBRFilename, vrc);
1632 goto out;
1633 }
1634 vrc = RTFileReadAt(MBRFile, 0, pPartData, 0x1be, NULL);
1635 RTFileClose(MBRFile);
1636 if (RT_FAILURE(vrc))
1637 {
1638 RTMsgError("Cannot read replacement MBR file '%s': %Rrc", pszMBRFilename, vrc);
1639 goto out;
1640 }
1641 }
1642 pPartDesc->pvPartitionData = pPartData;
1643 }
1644
1645 if (PARTTYPE_IS_EXTENDED(partitions.aPartitions[i].uType))
1646 {
1647 /* Suppress exporting the actual extended partition. Only
1648 * logical partitions should be processed. However completely
1649 * ignoring it leads to leaving out the EBR data. */
1650 continue;
1651 }
1652
1653 /* set up values for non-relative device names */
1654 const char *pszRawName = rawdisk.c_str();
1655 uint64_t uStartOffset = partitions.aPartitions[i].uStart * 512;
1656
1657 pPartDesc = appendPartDesc(&RawDescriptor.cPartDescs,
1658 &RawDescriptor.pPartDescs);
1659 if (!pPartDesc)
1660 {
1661 RTMsgError("Out of memory allocating the partition list for '%s'", rawdisk.c_str());
1662 vrc = VERR_NO_MEMORY;
1663 goto out;
1664 }
1665
1666 if (uPartitions & RT_BIT(partitions.aPartitions[i].uIndex))
1667 {
1668 if (uPartitionsRO & RT_BIT(partitions.aPartitions[i].uIndex))
1669 pPartDesc->uFlags |= VDISKRAW_READONLY;
1670
1671 if (fRelative)
1672 {
1673#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
1674 /* Refer to the correct partition and use offset 0. */
1675 char *psz;
1676#if defined(RT_OS_LINUX)
1677 /*
1678 * Check whether raw disk ends with a digit. In that case
1679 * insert a p before adding the partition number.
1680 * This is used for nvme devices only currently which look like
1681 * /dev/nvme0n1p1 but might be extended to other devices in the
1682 * future.
1683 */
1684 size_t cchRawDisk = rawdisk.length();
1685 if (RT_C_IS_DIGIT(pszRawName[cchRawDisk - 1]))
1686 RTStrAPrintf(&psz,
1687 "%sp%u",
1688 rawdisk.c_str(),
1689 partitions.aPartitions[i].uIndex);
1690 else
1691 RTStrAPrintf(&psz,
1692 "%s%u",
1693 rawdisk.c_str(),
1694 partitions.aPartitions[i].uIndex);
1695#elif defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD)
1696 RTStrAPrintf(&psz,
1697 "%ss%u",
1698 rawdisk.c_str(),
1699 partitions.aPartitions[i].uIndex);
1700#endif
1701 if (!psz)
1702 {
1703 vrc = VERR_NO_STR_MEMORY;
1704 RTMsgError("Cannot create reference to individual partition %u, rc=%Rrc",
1705 partitions.aPartitions[i].uIndex, vrc);
1706 goto out;
1707 }
1708 pszRawName = psz;
1709 uStartOffset = 0;
1710#elif defined(RT_OS_WINDOWS)
1711 /* Refer to the correct partition and use offset 0. */
1712 char *psz;
1713 RTStrAPrintf(&psz, "\\\\.\\Harddisk%sPartition%u",
1714 rawdisk.c_str() + 17,
1715 partitions.aPartitions[i].uIndexWin);
1716 if (!psz)
1717 {
1718 vrc = VERR_NO_STR_MEMORY;
1719 RTMsgError("Cannot create reference to individual partition %u (numbered %u), rc=%Rrc",
1720 partitions.aPartitions[i].uIndex, partitions.aPartitions[i].uIndexWin, vrc);
1721 goto out;
1722 }
1723 pszRawName = psz;
1724 uStartOffset = 0;
1725#else
1726 /** @todo not implemented for other hosts. Treat just like
1727 * not specified (this code is actually never reached). */
1728#endif
1729 }
1730
1731 pPartDesc->pszRawDevice = pszRawName;
1732 pPartDesc->uStartOffset = uStartOffset;
1733 }
1734 else
1735 {
1736 pPartDesc->pszRawDevice = NULL;
1737 pPartDesc->uStartOffset = 0;
1738 }
1739
1740 pPartDesc->uStart = partitions.aPartitions[i].uStart * 512;
1741 pPartDesc->cbData = partitions.aPartitions[i].uSize * 512;
1742 }
1743
1744 /* Sort data areas in ascending order of start. */
1745 for (unsigned i = 0; i < RawDescriptor.cPartDescs-1; i++)
1746 {
1747 unsigned uMinIdx = i;
1748 uint64_t uMinVal = RawDescriptor.pPartDescs[i].uStart;
1749 for (unsigned j = i + 1; j < RawDescriptor.cPartDescs; j++)
1750 {
1751 if (RawDescriptor.pPartDescs[j].uStart < uMinVal)
1752 {
1753 uMinIdx = j;
1754 uMinVal = RawDescriptor.pPartDescs[j].uStart;
1755 }
1756 }
1757 if (uMinIdx != i)
1758 {
1759 /* Swap entries at index i and uMinIdx. */
1760 VDISKRAWPARTDESC tmp;
1761 memcpy(&tmp, &RawDescriptor.pPartDescs[i], sizeof(tmp));
1762 memcpy(&RawDescriptor.pPartDescs[i], &RawDescriptor.pPartDescs[uMinIdx], sizeof(tmp));
1763 memcpy(&RawDescriptor.pPartDescs[uMinIdx], &tmp, sizeof(tmp));
1764 }
1765 }
1766
1767 /* Have a second go at MBR/EPT, GPT area clipping. Now that the data areas
1768 * are sorted this is much easier to get 100% right. */
1769 //for (unsigned i = 0; i < RawDescriptor.cPartDescs-1; i++)
1770 for (unsigned i = 0; i < RawDescriptor.cPartDescs; i++)
1771 {
1772 if (RawDescriptor.pPartDescs[i].pvPartitionData)
1773 {
1774 RawDescriptor.pPartDescs[i].cbData = RT_MIN(RawDescriptor.pPartDescs[i+1].uStart - RawDescriptor.pPartDescs[i].uStart, RawDescriptor.pPartDescs[i].cbData);
1775 if (!RawDescriptor.pPartDescs[i].cbData)
1776 {
1777 if (RawDescriptor.uPartitioningType == 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