VirtualBox

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

Last change on this file since 39477 was 39477, checked in by vboxsync, 13 years ago

VMM,VBoxManage,Main: Automatic map file loading for simplifying debugging.

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