VirtualBox

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

Last change on this file since 61864 was 61674, checked in by vboxsync, 9 years ago

FE/VBoxManage: Committed too much

  • 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 61674 2016-06-13 12:31:37Z 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