VirtualBox

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

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

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use