VirtualBox

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

Last change on this file since 60422 was 60025, checked in by vboxsync, 9 years ago

VBoxManage: Tweak the rawdisk fix for nvme devices to be more versatile if other devices pick up the new naming scheme. The code checks now whether the last character of the string is a digit

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