VirtualBox

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

Last change on this file since 38469 was 38469, checked in by vboxsync, 14 years ago

VD: Interface cleanup. Merge the two involved structures (generic interface descriptor and callback table) into one, remove the duplicated interface wrappers in the backends and move the interface definitions into separate headers separating public and private interfaces.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 70.8 KB
Line 
1/* $Id: VBoxInternalManage.cpp 38469 2011-08-16 10:34:32Z 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_LOADSYMS)
145 ? " loadsyms <vmname>|<uuid> <symfile> [delta] [module] [module address]\n"
146 " This will instruct DBGF to load the given symbolfile\n"
147 " during initialization.\n"
148 "\n"
149 : "",
150 (u64Cmd & USAGE_UNLOADSYMS)
151 ? " unloadsyms <vmname>|<uuid> <symfile>\n"
152 " Removes <symfile> from the list of symbol files that\n"
153 " should be loaded during DBF 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
513static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va)
514{
515 RTMsgErrorV(pszFormat, va);
516 RTMsgError("Error code %Rrc at %s(%u) in function %s", rc, RT_SRC_POS_ARGS);
517}
518
519static int handleVDMessage(void *pvUser, const char *pszFormat, va_list va)
520{
521 NOREF(pvUser);
522 return RTPrintfV(pszFormat, va);
523}
524
525static int CmdSetHDUUID(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
526{
527 Guid uuid;
528 RTUUID rtuuid;
529 enum eUuidType {
530 HDUUID,
531 HDPARENTUUID
532 } uuidType;
533
534 if (!strcmp(argv[0], "sethduuid"))
535 {
536 uuidType = HDUUID;
537 if (argc != 3 && argc != 2)
538 return errorSyntax(USAGE_SETHDUUID, "Not enough parameters");
539 /* if specified, take UUID, otherwise generate a new one */
540 if (argc == 3)
541 {
542 if (RT_FAILURE(RTUuidFromStr(&rtuuid, argv[2])))
543 return errorSyntax(USAGE_SETHDUUID, "Invalid UUID parameter");
544 uuid = argv[2];
545 } else
546 uuid.create();
547 }
548 else if (!strcmp(argv[0], "sethdparentuuid"))
549 {
550 uuidType = HDPARENTUUID;
551 if (argc != 3)
552 return errorSyntax(USAGE_SETHDPARENTUUID, "Not enough parameters");
553 if (RT_FAILURE(RTUuidFromStr(&rtuuid, argv[2])))
554 return errorSyntax(USAGE_SETHDPARENTUUID, "Invalid UUID parameter");
555 uuid = argv[2];
556 }
557 else
558 return errorSyntax(USAGE_SETHDUUID, "Invalid invocation");
559
560 /* just try it */
561 char *pszFormat = NULL;
562 VDTYPE enmType = VDTYPE_INVALID;
563 int rc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
564 argv[1], &pszFormat, &enmType);
565 if (RT_FAILURE(rc))
566 {
567 RTMsgError("Format autodetect failed: %Rrc", rc);
568 return 1;
569 }
570
571 PVBOXHDD pDisk = NULL;
572
573 PVDINTERFACE pVDIfs = NULL;
574 VDINTERFACEERROR vdInterfaceError;
575 vdInterfaceError.pfnError = handleVDError;
576 vdInterfaceError.pfnMessage = handleVDMessage;
577
578 rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
579 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
580 AssertRC(rc);
581
582 rc = VDCreate(pVDIfs, enmType, &pDisk);
583 if (RT_FAILURE(rc))
584 {
585 RTMsgError("Cannot create the virtual disk container: %Rrc", rc);
586 return 1;
587 }
588
589 /* Open the image */
590 rc = VDOpen(pDisk, pszFormat, argv[1], VD_OPEN_FLAGS_NORMAL, NULL);
591 if (RT_FAILURE(rc))
592 {
593 RTMsgError("Cannot open the image: %Rrc", rc);
594 return 1;
595 }
596
597 if (uuidType == HDUUID)
598 rc = VDSetUuid(pDisk, VD_LAST_IMAGE, uuid.raw());
599 else
600 rc = VDSetParentUuid(pDisk, VD_LAST_IMAGE, uuid.raw());
601 if (RT_FAILURE(rc))
602 RTMsgError("Cannot set a new UUID: %Rrc", rc);
603 else
604 RTPrintf("UUID changed to: %s\n", uuid.toString().c_str());
605
606 VDCloseAll(pDisk);
607
608 return RT_FAILURE(rc);
609}
610
611
612static int CmdDumpHDInfo(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
613{
614 /* we need exactly one parameter: the image file */
615 if (argc != 1)
616 {
617 return errorSyntax(USAGE_DUMPHDINFO, "Not enough parameters");
618 }
619
620 /* just try it */
621 char *pszFormat = NULL;
622 VDTYPE enmType = VDTYPE_INVALID;
623 int rc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
624 argv[0], &pszFormat, &enmType);
625 if (RT_FAILURE(rc))
626 {
627 RTMsgError("Format autodetect failed: %Rrc", rc);
628 return 1;
629 }
630
631 PVBOXHDD pDisk = NULL;
632
633 PVDINTERFACE pVDIfs = NULL;
634 VDINTERFACEERROR vdInterfaceError;
635 vdInterfaceError.pfnError = handleVDError;
636 vdInterfaceError.pfnMessage = handleVDMessage;
637
638 rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
639 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
640 AssertRC(rc);
641
642 rc = VDCreate(pVDIfs, enmType, &pDisk);
643 if (RT_FAILURE(rc))
644 {
645 RTMsgError("Cannot create the virtual disk container: %Rrc", rc);
646 return 1;
647 }
648
649 /* Open the image */
650 rc = VDOpen(pDisk, pszFormat, argv[0], VD_OPEN_FLAGS_INFO, NULL);
651 if (RT_FAILURE(rc))
652 {
653 RTMsgError("Cannot open the image: %Rrc", rc);
654 return 1;
655 }
656
657 VDDumpImages(pDisk);
658
659 VDCloseAll(pDisk);
660
661 return RT_FAILURE(rc);
662}
663
664static int partRead(RTFILE File, PHOSTPARTITIONS pPart)
665{
666 uint8_t aBuffer[512];
667 int rc;
668
669 pPart->cPartitions = 0;
670 memset(pPart->aPartitions, '\0', sizeof(pPart->aPartitions));
671 rc = RTFileReadAt(File, 0, &aBuffer, sizeof(aBuffer), NULL);
672 if (RT_FAILURE(rc))
673 return rc;
674 if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa)
675 return VERR_INVALID_PARAMETER;
676
677 unsigned uExtended = (unsigned)-1;
678
679 for (unsigned i = 0; i < 4; i++)
680 {
681 uint8_t *p = &aBuffer[0x1be + i * 16];
682 if (p[4] == 0)
683 continue;
684 PHOSTPARTITION pCP = &pPart->aPartitions[pPart->cPartitions++];
685 pCP->uIndex = i + 1;
686 pCP->uType = p[4];
687 pCP->uStartCylinder = (uint32_t)p[3] + ((uint32_t)(p[2] & 0xc0) << 2);
688 pCP->uStartHead = p[1];
689 pCP->uStartSector = p[2] & 0x3f;
690 pCP->uEndCylinder = (uint32_t)p[7] + ((uint32_t)(p[6] & 0xc0) << 2);
691 pCP->uEndHead = p[5];
692 pCP->uEndSector = p[6] & 0x3f;
693 pCP->uStart = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
694 pCP->uSize = RT_MAKE_U32_FROM_U8(p[12], p[13], p[14], p[15]);
695 pCP->uPartDataStart = 0; /* will be filled out later properly. */
696 pCP->cPartDataSectors = 0;
697
698 if (PARTTYPE_IS_EXTENDED(p[4]))
699 {
700 if (uExtended == (unsigned)-1)
701 uExtended = (unsigned)(pCP - pPart->aPartitions);
702 else
703 {
704 RTMsgError("More than one extended partition");
705 return VERR_INVALID_PARAMETER;
706 }
707 }
708 }
709
710 if (uExtended != (unsigned)-1)
711 {
712 unsigned uIndex = 5;
713 uint64_t uStart = pPart->aPartitions[uExtended].uStart;
714 uint64_t uOffset = 0;
715 if (!uStart)
716 {
717 RTMsgError("Inconsistency for logical partition start");
718 return VERR_INVALID_PARAMETER;
719 }
720
721 do
722 {
723 rc = RTFileReadAt(File, (uStart + uOffset) * 512, &aBuffer, sizeof(aBuffer), NULL);
724 if (RT_FAILURE(rc))
725 return rc;
726
727 if (aBuffer[510] != 0x55 || aBuffer[511] != 0xaa)
728 {
729 RTMsgError("Logical partition without magic");
730 return VERR_INVALID_PARAMETER;
731 }
732 uint8_t *p = &aBuffer[0x1be];
733
734 if (p[4] == 0)
735 {
736 RTMsgError("Logical partition with type 0 encountered");
737 return VERR_INVALID_PARAMETER;
738 }
739
740 PHOSTPARTITION pCP = &pPart->aPartitions[pPart->cPartitions++];
741 pCP->uIndex = uIndex;
742 pCP->uType = p[4];
743 pCP->uStartCylinder = (uint32_t)p[3] + ((uint32_t)(p[2] & 0xc0) << 2);
744 pCP->uStartHead = p[1];
745 pCP->uStartSector = p[2] & 0x3f;
746 pCP->uEndCylinder = (uint32_t)p[7] + ((uint32_t)(p[6] & 0xc0) << 2);
747 pCP->uEndHead = p[5];
748 pCP->uEndSector = p[6] & 0x3f;
749 uint32_t uStartOffset = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
750 if (!uStartOffset)
751 {
752 RTMsgError("Invalid partition start offset");
753 return VERR_INVALID_PARAMETER;
754 }
755 pCP->uStart = uStart + uOffset + uStartOffset;
756 pCP->uSize = RT_MAKE_U32_FROM_U8(p[12], p[13], p[14], p[15]);
757 /* Fill out partitioning location info for EBR. */
758 pCP->uPartDataStart = uStart + uOffset;
759 pCP->cPartDataSectors = uStartOffset;
760 p += 16;
761 if (p[4] == 0)
762 uExtended = (unsigned)-1;
763 else if (PARTTYPE_IS_EXTENDED(p[4]))
764 {
765 uExtended = uIndex++;
766 uOffset = RT_MAKE_U32_FROM_U8(p[8], p[9], p[10], p[11]);
767 }
768 else
769 {
770 RTMsgError("Logical partition chain broken");
771 return VERR_INVALID_PARAMETER;
772 }
773 } while (uExtended != (unsigned)-1);
774 }
775
776 /* Sort partitions in ascending order of start sector, plus a trivial
777 * bit of consistency checking. */
778 for (unsigned i = 0; i < pPart->cPartitions-1; i++)
779 {
780 unsigned uMinIdx = i;
781 uint64_t uMinVal = pPart->aPartitions[i].uStart;
782 for (unsigned j = i + 1; j < pPart->cPartitions; j++)
783 {
784 if (pPart->aPartitions[j].uStart < uMinVal)
785 {
786 uMinIdx = j;
787 uMinVal = pPart->aPartitions[j].uStart;
788 }
789 else if (pPart->aPartitions[j].uStart == uMinVal)
790 {
791 RTMsgError("Two partitions start at the same place");
792 return VERR_INVALID_PARAMETER;
793 }
794 else if (pPart->aPartitions[j].uStart == 0)
795 {
796 RTMsgError("Partition starts at sector 0");
797 return VERR_INVALID_PARAMETER;
798 }
799 }
800 if (uMinIdx != i)
801 {
802 /* Swap entries at index i and uMinIdx. */
803 memcpy(&pPart->aPartitions[pPart->cPartitions],
804 &pPart->aPartitions[i], sizeof(HOSTPARTITION));
805 memcpy(&pPart->aPartitions[i],
806 &pPart->aPartitions[uMinIdx], sizeof(HOSTPARTITION));
807 memcpy(&pPart->aPartitions[uMinIdx],
808 &pPart->aPartitions[pPart->cPartitions], sizeof(HOSTPARTITION));
809 }
810 }
811
812 /* Fill out partitioning location info for MBR. */
813 pPart->aPartitions[0].uPartDataStart = 0;
814 pPart->aPartitions[0].cPartDataSectors = pPart->aPartitions[0].uStart;
815
816 /* Now do a some partition table consistency checking, to reject the most
817 * obvious garbage which can lead to trouble later. */
818 uint64_t uPrevEnd = 0;
819 for (unsigned i = 0; i < pPart->cPartitions-1; i++)
820 {
821 if (pPart->aPartitions[i].cPartDataSectors)
822 uPrevEnd = pPart->aPartitions[i].uPartDataStart + pPart->aPartitions[i].cPartDataSectors;
823 if (pPart->aPartitions[i].uStart < uPrevEnd)
824 {
825 RTMsgError("Overlapping partitions");
826 return VERR_INVALID_PARAMETER;
827 }
828 if (!PARTTYPE_IS_EXTENDED(pPart->aPartitions[i].uType))
829 uPrevEnd = pPart->aPartitions[i].uStart + pPart->aPartitions[i].uSize;
830 }
831
832 return VINF_SUCCESS;
833}
834
835static int CmdListPartitions(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
836{
837 Utf8Str rawdisk;
838
839 /* let's have a closer look at the arguments */
840 for (int i = 0; i < argc; i++)
841 {
842 if (strcmp(argv[i], "-rawdisk") == 0)
843 {
844 if (argc <= i + 1)
845 {
846 return errorArgument("Missing argument to '%s'", argv[i]);
847 }
848 i++;
849 rawdisk = argv[i];
850 }
851 else
852 {
853 return errorSyntax(USAGE_LISTPARTITIONS, "Invalid parameter '%s'", argv[i]);
854 }
855 }
856
857 if (rawdisk.isEmpty())
858 return errorSyntax(USAGE_LISTPARTITIONS, "Mandatory parameter -rawdisk missing");
859
860 RTFILE hRawFile;
861 int vrc = RTFileOpen(&hRawFile, rawdisk.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
862 if (RT_FAILURE(vrc))
863 {
864 RTMsgError("Cannot open the raw disk: %Rrc", vrc);
865 return vrc;
866 }
867
868 HOSTPARTITIONS partitions;
869 vrc = partRead(hRawFile, &partitions);
870 /* Don't bail out on errors, print the table and return the result code. */
871
872 RTPrintf("Number Type StartCHS EndCHS Size (MiB) Start (Sect)\n");
873 for (unsigned i = 0; i < partitions.cPartitions; i++)
874 {
875 /* Don't show the extended partition, otherwise users might think they
876 * can add it to the list of partitions for raw partition access. */
877 if (PARTTYPE_IS_EXTENDED(partitions.aPartitions[i].uType))
878 continue;
879
880 RTPrintf("%-7u %#04x %-4u/%-3u/%-2u %-4u/%-3u/%-2u %10llu %10llu\n",
881 partitions.aPartitions[i].uIndex,
882 partitions.aPartitions[i].uType,
883 partitions.aPartitions[i].uStartCylinder,
884 partitions.aPartitions[i].uStartHead,
885 partitions.aPartitions[i].uStartSector,
886 partitions.aPartitions[i].uEndCylinder,
887 partitions.aPartitions[i].uEndHead,
888 partitions.aPartitions[i].uEndSector,
889 partitions.aPartitions[i].uSize / 2048,
890 partitions.aPartitions[i].uStart);
891 }
892
893 return vrc;
894}
895
896static PVBOXHDDRAWPARTDESC appendPartDesc(uint32_t *pcPartDescs, PVBOXHDDRAWPARTDESC *ppPartDescs)
897{
898 (*pcPartDescs)++;
899 PVBOXHDDRAWPARTDESC p;
900 p = (PVBOXHDDRAWPARTDESC)RTMemRealloc(*ppPartDescs,
901 *pcPartDescs * sizeof(VBOXHDDRAWPARTDESC));
902 *ppPartDescs = p;
903 if (p)
904 {
905 p = p + *pcPartDescs - 1;
906 memset(p, '\0', sizeof(VBOXHDDRAWPARTDESC));
907 }
908
909 return p;
910}
911
912static int CmdCreateRawVMDK(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
913{
914 HRESULT rc = S_OK;
915 Utf8Str filename;
916 const char *pszMBRFilename = NULL;
917 Utf8Str rawdisk;
918 const char *pszPartitions = NULL;
919 bool fRelative = false;
920
921 uint64_t cbSize = 0;
922 PVBOXHDD pDisk = NULL;
923 VBOXHDDRAW RawDescriptor;
924 PVDINTERFACE pVDIfs = NULL;
925
926 /* let's have a closer look at the arguments */
927 for (int i = 0; i < argc; i++)
928 {
929 if (strcmp(argv[i], "-filename") == 0)
930 {
931 if (argc <= i + 1)
932 {
933 return errorArgument("Missing argument to '%s'", argv[i]);
934 }
935 i++;
936 filename = argv[i];
937 }
938 else if (strcmp(argv[i], "-mbr") == 0)
939 {
940 if (argc <= i + 1)
941 {
942 return errorArgument("Missing argument to '%s'", argv[i]);
943 }
944 i++;
945 pszMBRFilename = argv[i];
946 }
947 else if (strcmp(argv[i], "-rawdisk") == 0)
948 {
949 if (argc <= i + 1)
950 {
951 return errorArgument("Missing argument to '%s'", argv[i]);
952 }
953 i++;
954 rawdisk = argv[i];
955 }
956 else if (strcmp(argv[i], "-partitions") == 0)
957 {
958 if (argc <= i + 1)
959 {
960 return errorArgument("Missing argument to '%s'", argv[i]);
961 }
962 i++;
963 pszPartitions = argv[i];
964 }
965#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
966 else if (strcmp(argv[i], "-relative") == 0)
967 {
968 fRelative = true;
969 }
970#endif /* RT_OS_LINUX || RT_OS_FREEBSD */
971 else
972 return errorSyntax(USAGE_CREATERAWVMDK, "Invalid parameter '%s'", argv[i]);
973 }
974
975 if (filename.isEmpty())
976 return errorSyntax(USAGE_CREATERAWVMDK, "Mandatory parameter -filename missing");
977 if (rawdisk.isEmpty())
978 return errorSyntax(USAGE_CREATERAWVMDK, "Mandatory parameter -rawdisk missing");
979 if (!pszPartitions && pszMBRFilename)
980 return errorSyntax(USAGE_CREATERAWVMDK, "The parameter -mbr is only valid when the parameter -partitions is also present");
981
982#ifdef RT_OS_DARWIN
983 fRelative = true;
984#endif /* RT_OS_DARWIN */
985 RTFILE hRawFile;
986 int vrc = RTFileOpen(&hRawFile, rawdisk.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
987 if (RT_FAILURE(vrc))
988 {
989 RTMsgError("Cannot open the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
990 goto out;
991 }
992
993#ifdef RT_OS_WINDOWS
994 /* Windows NT has no IOCTL_DISK_GET_LENGTH_INFORMATION ioctl. This was
995 * added to Windows XP, so we have to use the available info from DriveGeo.
996 * Note that we cannot simply use IOCTL_DISK_GET_DRIVE_GEOMETRY as it
997 * yields a slightly different result than IOCTL_DISK_GET_LENGTH_INFO.
998 * We call IOCTL_DISK_GET_DRIVE_GEOMETRY first as we need to check the media
999 * type anyway, and if IOCTL_DISK_GET_LENGTH_INFORMATION is supported
1000 * we will later override cbSize.
1001 */
1002 DISK_GEOMETRY DriveGeo;
1003 DWORD cbDriveGeo;
1004 if (DeviceIoControl((HANDLE)RTFileToNative(hRawFile),
1005 IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
1006 &DriveGeo, sizeof(DriveGeo), &cbDriveGeo, NULL))
1007 {
1008 if ( DriveGeo.MediaType == FixedMedia
1009 || DriveGeo.MediaType == RemovableMedia)
1010 {
1011 cbSize = DriveGeo.Cylinders.QuadPart
1012 * DriveGeo.TracksPerCylinder
1013 * DriveGeo.SectorsPerTrack
1014 * DriveGeo.BytesPerSector;
1015 }
1016 else
1017 {
1018 RTMsgError("File '%s' is no fixed/removable medium device", rawdisk.c_str());
1019 vrc = VERR_INVALID_PARAMETER;
1020 goto out;
1021 }
1022
1023 GET_LENGTH_INFORMATION DiskLenInfo;
1024 DWORD junk;
1025 if (DeviceIoControl((HANDLE)RTFileToNative(hRawFile),
1026 IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
1027 &DiskLenInfo, sizeof(DiskLenInfo), &junk, (LPOVERLAPPED)NULL))
1028 {
1029 /* IOCTL_DISK_GET_LENGTH_INFO is supported -- override cbSize. */
1030 cbSize = DiskLenInfo.Length.QuadPart;
1031 }
1032 }
1033 else
1034 {
1035 vrc = RTErrConvertFromWin32(GetLastError());
1036 RTMsgError("Cannot get the geometry of the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
1037 goto out;
1038 }
1039#elif defined(RT_OS_LINUX)
1040 struct stat DevStat;
1041 if (!fstat(RTFileToNative(hRawFile), &DevStat) && S_ISBLK(DevStat.st_mode))
1042 {
1043#ifdef BLKGETSIZE64
1044 /* BLKGETSIZE64 is broken up to 2.4.17 and in many 2.5.x. In 2.6.0
1045 * it works without problems. */
1046 struct utsname utsname;
1047 if ( uname(&utsname) == 0
1048 && ( (strncmp(utsname.release, "2.5.", 4) == 0 && atoi(&utsname.release[4]) >= 18)
1049 || (strncmp(utsname.release, "2.", 2) == 0 && atoi(&utsname.release[2]) >= 6)))
1050 {
1051 uint64_t cbBlk;
1052 if (!ioctl(RTFileToNative(hRawFile), BLKGETSIZE64, &cbBlk))
1053 cbSize = cbBlk;
1054 }
1055#endif /* BLKGETSIZE64 */
1056 if (!cbSize)
1057 {
1058 long cBlocks;
1059 if (!ioctl(RTFileToNative(hRawFile), BLKGETSIZE, &cBlocks))
1060 cbSize = (uint64_t)cBlocks << 9;
1061 else
1062 {
1063 vrc = RTErrConvertFromErrno(errno);
1064 RTMsgError("Cannot get the size of the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
1065 goto out;
1066 }
1067 }
1068 }
1069 else
1070 {
1071 RTMsgError("File '%s' is no block device", rawdisk.c_str());
1072 vrc = VERR_INVALID_PARAMETER;
1073 goto out;
1074 }
1075#elif defined(RT_OS_DARWIN)
1076 struct stat DevStat;
1077 if (!fstat(RTFileToNative(hRawFile), &DevStat) && S_ISBLK(DevStat.st_mode))
1078 {
1079 uint64_t cBlocks;
1080 uint32_t cbBlock;
1081 if (!ioctl(RTFileToNative(hRawFile), DKIOCGETBLOCKCOUNT, &cBlocks))
1082 {
1083 if (!ioctl(RTFileToNative(hRawFile), DKIOCGETBLOCKSIZE, &cbBlock))
1084 cbSize = cBlocks * cbBlock;
1085 else
1086 {
1087 RTMsgError("Cannot get the block size for file '%s': %Rrc", rawdisk.c_str(), vrc);
1088 vrc = RTErrConvertFromErrno(errno);
1089 goto out;
1090 }
1091 }
1092 else
1093 {
1094 vrc = RTErrConvertFromErrno(errno);
1095 RTMsgError("Cannot get the block count for file '%s': %Rrc", rawdisk.c_str(), vrc);
1096 goto out;
1097 }
1098 }
1099 else
1100 {
1101 RTMsgError("File '%s' is no block device", rawdisk.c_str());
1102 vrc = VERR_INVALID_PARAMETER;
1103 goto out;
1104 }
1105#elif defined(RT_OS_SOLARIS)
1106 struct stat DevStat;
1107 if (!fstat(RTFileToNative(hRawFile), &DevStat) && ( S_ISBLK(DevStat.st_mode)
1108 || S_ISCHR(DevStat.st_mode)))
1109 {
1110 struct dk_minfo mediainfo;
1111 if (!ioctl(RTFileToNative(hRawFile), DKIOCGMEDIAINFO, &mediainfo))
1112 cbSize = mediainfo.dki_capacity * mediainfo.dki_lbsize;
1113 else
1114 {
1115 vrc = RTErrConvertFromErrno(errno);
1116 RTMsgError("Cannot get the size of the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
1117 goto out;
1118 }
1119 }
1120 else
1121 {
1122 RTMsgError("File '%s' is no block or char device", rawdisk.c_str());
1123 vrc = VERR_INVALID_PARAMETER;
1124 goto out;
1125 }
1126#elif defined(RT_OS_FREEBSD)
1127 struct stat DevStat;
1128 if (!fstat(RTFileToNative(hRawFile), &DevStat) && S_ISCHR(DevStat.st_mode))
1129 {
1130 off_t cbMedia = 0;
1131 if (!ioctl(RTFileToNative(hRawFile), DIOCGMEDIASIZE, &cbMedia))
1132 {
1133 cbSize = cbMedia;
1134 }
1135 else
1136 {
1137 vrc = RTErrConvertFromErrno(errno);
1138 RTMsgError("Cannot get the block count for file '%s': %Rrc", rawdisk.c_str(), vrc);
1139 goto out;
1140 }
1141 }
1142 else
1143 {
1144 RTMsgError("File '%s' is no character device", rawdisk.c_str());
1145 vrc = VERR_INVALID_PARAMETER;
1146 goto out;
1147 }
1148#else /* all unrecognized OSes */
1149 /* Hopefully this works on all other hosts. If it doesn't, it'll just fail
1150 * creating the VMDK, so no real harm done. */
1151 vrc = RTFileGetSize(hRawFile, &cbSize);
1152 if (RT_FAILURE(vrc))
1153 {
1154 RTMsgError("Cannot get the size of the raw disk '%s': %Rrc", rawdisk.c_str(), vrc);
1155 goto out;
1156 }
1157#endif
1158
1159 /* Check whether cbSize is actually sensible. */
1160 if (!cbSize || cbSize % 512)
1161 {
1162 RTMsgError("Detected size of raw disk '%s' is %s, an invalid value", rawdisk.c_str(), cbSize);
1163 vrc = VERR_INVALID_PARAMETER;
1164 goto out;
1165 }
1166
1167 RawDescriptor.szSignature[0] = 'R';
1168 RawDescriptor.szSignature[1] = 'A';
1169 RawDescriptor.szSignature[2] = 'W';
1170 RawDescriptor.szSignature[3] = '\0';
1171 if (!pszPartitions)
1172 {
1173 RawDescriptor.fRawDisk = true;
1174 RawDescriptor.pszRawDisk = rawdisk.c_str();
1175 }
1176 else
1177 {
1178 RawDescriptor.fRawDisk = false;
1179 RawDescriptor.pszRawDisk = NULL;
1180 RawDescriptor.cPartDescs = 0;
1181 RawDescriptor.pPartDescs = NULL;
1182
1183 uint32_t uPartitions = 0;
1184
1185 const char *p = pszPartitions;
1186 char *pszNext;
1187 uint32_t u32;
1188 while (*p != '\0')
1189 {
1190 vrc = RTStrToUInt32Ex(p, &pszNext, 0, &u32);
1191 if (RT_FAILURE(vrc))
1192 {
1193 RTMsgError("Incorrect value in partitions parameter");
1194 goto out;
1195 }
1196 uPartitions |= RT_BIT(u32);
1197 p = pszNext;
1198 if (*p == ',')
1199 p++;
1200 else if (*p != '\0')
1201 {
1202 RTMsgError("Incorrect separator in partitions parameter");
1203 vrc = VERR_INVALID_PARAMETER;
1204 goto out;
1205 }
1206 }
1207
1208 HOSTPARTITIONS partitions;
1209 vrc = partRead(hRawFile, &partitions);
1210 if (RT_FAILURE(vrc))
1211 {
1212 RTMsgError("Cannot read the partition information from '%s'", rawdisk.c_str());
1213 goto out;
1214 }
1215
1216 for (unsigned i = 0; i < partitions.cPartitions; i++)
1217 {
1218 if ( uPartitions & RT_BIT(partitions.aPartitions[i].uIndex)
1219 && PARTTYPE_IS_EXTENDED(partitions.aPartitions[i].uType))
1220 {
1221 /* Some ignorant user specified an extended partition.
1222 * Bad idea, as this would trigger an overlapping
1223 * partitions error later during VMDK creation. So warn
1224 * here and ignore what the user requested. */
1225 RTMsgWarning("It is not possible (and necessary) to explicitly give access to the "
1226 "extended partition %u. If required, enable access to all logical "
1227 "partitions inside this extended partition.",
1228 partitions.aPartitions[i].uIndex);
1229 uPartitions &= ~RT_BIT(partitions.aPartitions[i].uIndex);
1230 }
1231 }
1232
1233 for (unsigned i = 0; i < partitions.cPartitions; i++)
1234 {
1235 PVBOXHDDRAWPARTDESC pPartDesc = NULL;
1236
1237 /* first dump the MBR/EPT data area */
1238 if (partitions.aPartitions[i].cPartDataSectors)
1239 {
1240 pPartDesc = appendPartDesc(&RawDescriptor.cPartDescs,
1241 &RawDescriptor.pPartDescs);
1242 if (!pPartDesc)
1243 {
1244 RTMsgError("Out of memory allocating the partition list for '%s'", rawdisk.c_str());
1245 vrc = VERR_NO_MEMORY;
1246 goto out;
1247 }
1248
1249 /** @todo the clipping below isn't 100% accurate, as it should
1250 * actually clip to the track size. However, that's easier said
1251 * than done as figuring out the track size is heuristics. In
1252 * any case the clipping is adjusted later after sorting, to
1253 * prevent overlapping data areas on the resulting image. */
1254 pPartDesc->cbData = RT_MIN(partitions.aPartitions[i].cPartDataSectors, 63) * 512;
1255 pPartDesc->uStart = partitions.aPartitions[i].uPartDataStart * 512;
1256 Assert(pPartDesc->cbData - (size_t)pPartDesc->cbData == 0);
1257 void *pPartData = RTMemAlloc((size_t)pPartDesc->cbData);
1258 if (!pPartData)
1259 {
1260 RTMsgError("Out of memory allocating the partition descriptor for '%s'", rawdisk.c_str());
1261 vrc = VERR_NO_MEMORY;
1262 goto out;
1263 }
1264 vrc = RTFileReadAt(hRawFile, partitions.aPartitions[i].uPartDataStart * 512,
1265 pPartData, (size_t)pPartDesc->cbData, NULL);
1266 if (RT_FAILURE(vrc))
1267 {
1268 RTMsgError("Cannot read partition data from raw device '%s': %Rrc", rawdisk.c_str(), vrc);
1269 goto out;
1270 }
1271 /* Splice in the replacement MBR code if specified. */
1272 if ( partitions.aPartitions[i].uPartDataStart == 0
1273 && pszMBRFilename)
1274 {
1275 RTFILE MBRFile;
1276 vrc = RTFileOpen(&MBRFile, pszMBRFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1277 if (RT_FAILURE(vrc))
1278 {
1279 RTMsgError("Cannot open replacement MBR file '%s' specified with -mbr: %Rrc", pszMBRFilename, vrc);
1280 goto out;
1281 }
1282 vrc = RTFileReadAt(MBRFile, 0, pPartData, 0x1be, NULL);
1283 RTFileClose(MBRFile);
1284 if (RT_FAILURE(vrc))
1285 {
1286 RTMsgError("Cannot read replacement MBR file '%s': %Rrc", pszMBRFilename, vrc);
1287 goto out;
1288 }
1289 }
1290 pPartDesc->pvPartitionData = pPartData;
1291 }
1292
1293 if (PARTTYPE_IS_EXTENDED(partitions.aPartitions[i].uType))
1294 {
1295 /* Suppress exporting the actual extended partition. Only
1296 * logical partitions should be processed. However completely
1297 * ignoring it leads to leaving out the EBR data. */
1298 continue;
1299 }
1300
1301 /* set up values for non-relative device names */
1302 const char *pszRawName = rawdisk.c_str();
1303 uint64_t uStartOffset = partitions.aPartitions[i].uStart * 512;
1304
1305 pPartDesc = appendPartDesc(&RawDescriptor.cPartDescs,
1306 &RawDescriptor.pPartDescs);
1307 if (!pPartDesc)
1308 {
1309 RTMsgError("Out of memory allocating the partition list for '%s'", rawdisk.c_str());
1310 vrc = VERR_NO_MEMORY;
1311 goto out;
1312 }
1313
1314 if (uPartitions & RT_BIT(partitions.aPartitions[i].uIndex))
1315 {
1316 if (fRelative)
1317 {
1318#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
1319 /* Refer to the correct partition and use offset 0. */
1320 char *psz;
1321 RTStrAPrintf(&psz,
1322#if defined(RT_OS_LINUX)
1323 "%s%u",
1324#elif defined(RT_OS_FREEBSD)
1325 "%ss%u",
1326#endif
1327 rawdisk.c_str(),
1328 partitions.aPartitions[i].uIndex);
1329 if (!psz)
1330 {
1331 vrc = VERR_NO_STR_MEMORY;
1332 RTMsgError("Cannot create reference to individual partition %u, rc=%Rrc",
1333 partitions.aPartitions[i].uIndex, vrc);
1334 goto out;
1335 }
1336 pszRawName = psz;
1337 uStartOffset = 0;
1338#elif defined(RT_OS_DARWIN)
1339 /* Refer to the correct partition and use offset 0. */
1340 char *psz;
1341 RTStrAPrintf(&psz, "%ss%u", rawdisk.c_str(),
1342 partitions.aPartitions[i].uIndex);
1343 if (!psz)
1344 {
1345 vrc = VERR_NO_STR_MEMORY;
1346 RTMsgError("Cannot create reference to individual partition %u, rc=%Rrc",
1347 partitions.aPartitions[i].uIndex, vrc);
1348 goto out;
1349 }
1350 pszRawName = psz;
1351 uStartOffset = 0;
1352#else
1353 /** @todo not implemented for other hosts. Treat just like
1354 * not specified (this code is actually never reached). */
1355#endif
1356 }
1357
1358 pPartDesc->pszRawDevice = pszRawName;
1359 pPartDesc->uStartOffset = uStartOffset;
1360 }
1361 else
1362 {
1363 pPartDesc->pszRawDevice = NULL;
1364 pPartDesc->uStartOffset = 0;
1365 }
1366
1367 pPartDesc->uStart = partitions.aPartitions[i].uStart * 512;
1368 pPartDesc->cbData = partitions.aPartitions[i].uSize * 512;
1369 }
1370
1371 /* Sort data areas in ascending order of start. */
1372 for (unsigned i = 0; i < RawDescriptor.cPartDescs-1; i++)
1373 {
1374 unsigned uMinIdx = i;
1375 uint64_t uMinVal = RawDescriptor.pPartDescs[i].uStart;
1376 for (unsigned j = i + 1; j < RawDescriptor.cPartDescs; j++)
1377 {
1378 if (RawDescriptor.pPartDescs[j].uStart < uMinVal)
1379 {
1380 uMinIdx = j;
1381 uMinVal = RawDescriptor.pPartDescs[j].uStart;
1382 }
1383 }
1384 if (uMinIdx != i)
1385 {
1386 /* Swap entries at index i and uMinIdx. */
1387 VBOXHDDRAWPARTDESC tmp;
1388 memcpy(&tmp, &RawDescriptor.pPartDescs[i], sizeof(tmp));
1389 memcpy(&RawDescriptor.pPartDescs[i], &RawDescriptor.pPartDescs[uMinIdx], sizeof(tmp));
1390 memcpy(&RawDescriptor.pPartDescs[uMinIdx], &tmp, sizeof(tmp));
1391 }
1392 }
1393
1394 /* Have a second go at MBR/EPT area clipping. Now that the data areas
1395 * are sorted this is much easier to get 100% right. */
1396 for (unsigned i = 0; i < RawDescriptor.cPartDescs-1; i++)
1397 {
1398 if (RawDescriptor.pPartDescs[i].pvPartitionData)
1399 {
1400 RawDescriptor.pPartDescs[i].cbData = RT_MIN(RawDescriptor.pPartDescs[i+1].uStart - RawDescriptor.pPartDescs[i].uStart, RawDescriptor.pPartDescs[i].cbData);
1401 if (!RawDescriptor.pPartDescs[i].cbData)
1402 {
1403 RTMsgError("MBR/EPT overlaps with data area");
1404 vrc = VERR_INVALID_PARAMETER;
1405 goto out;
1406 }
1407 }
1408 }
1409 }
1410
1411 RTFileClose(hRawFile);
1412
1413#ifdef DEBUG_klaus
1414 RTPrintf("# start length startoffset partdataptr device\n");
1415 for (unsigned i = 0; i < RawDescriptor.cPartDescs; i++)
1416 {
1417 RTPrintf("%2u %14RU64 %14RU64 %14RU64 %#18p %s\n", i,
1418 RawDescriptor.pPartDescs[i].uStart,
1419 RawDescriptor.pPartDescs[i].cbData,
1420 RawDescriptor.pPartDescs[i].uStartOffset,
1421 RawDescriptor.pPartDescs[i].pvPartitionData,
1422 RawDescriptor.pPartDescs[i].pszRawDevice);
1423 }
1424#endif
1425
1426 VDINTERFACEERROR vdInterfaceError;
1427 vdInterfaceError.pfnError = handleVDError;
1428 vdInterfaceError.pfnMessage = handleVDMessage;
1429
1430 rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1431 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1432 AssertRC(vrc);
1433
1434 vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk); /* Raw VMDK's are harddisk only. */
1435 if (RT_FAILURE(vrc))
1436 {
1437 RTMsgError("Cannot create the virtual disk container: %Rrc", vrc);
1438 goto out;
1439 }
1440
1441 Assert(RT_MIN(cbSize / 512 / 16 / 63, 16383) -
1442 (unsigned int)RT_MIN(cbSize / 512 / 16 / 63, 16383) == 0);
1443 VDGEOMETRY PCHS, LCHS;
1444 PCHS.cCylinders = (unsigned int)RT_MIN(cbSize / 512 / 16 / 63, 16383);
1445 PCHS.cHeads = 16;
1446 PCHS.cSectors = 63;
1447 LCHS.cCylinders = 0;
1448 LCHS.cHeads = 0;
1449 LCHS.cSectors = 0;
1450 vrc = VDCreateBase(pDisk, "VMDK", filename.c_str(), cbSize,
1451 VD_IMAGE_FLAGS_FIXED | VD_VMDK_IMAGE_FLAGS_RAWDISK,
1452 (char *)&RawDescriptor, &PCHS, &LCHS, NULL,
1453 VD_OPEN_FLAGS_NORMAL, NULL, NULL);
1454 if (RT_FAILURE(vrc))
1455 {
1456 RTMsgError("Cannot create the raw disk VMDK: %Rrc", vrc);
1457 goto out;
1458 }
1459 RTPrintf("RAW host disk access VMDK file %s created successfully.\n", filename.c_str());
1460
1461 VDCloseAll(pDisk);
1462
1463 /* Clean up allocated memory etc. */
1464 if (pszPartitions)
1465 {
1466 for (unsigned i = 0; i < RawDescriptor.cPartDescs; i++)
1467 {
1468 /* Free memory allocated for relative device name. */
1469 if (fRelative && RawDescriptor.pPartDescs[i].pszRawDevice)
1470 RTStrFree((char *)(void *)RawDescriptor.pPartDescs[i].pszRawDevice);
1471 if (RawDescriptor.pPartDescs[i].pvPartitionData)
1472 RTMemFree((void *)RawDescriptor.pPartDescs[i].pvPartitionData);
1473 }
1474 if (RawDescriptor.pPartDescs)
1475 RTMemFree(RawDescriptor.pPartDescs);
1476 }
1477
1478 return SUCCEEDED(rc) ? 0 : 1;
1479
1480out:
1481 RTMsgError("The raw disk vmdk file was not created");
1482 return RT_SUCCESS(vrc) ? 0 : 1;
1483}
1484
1485static int CmdRenameVMDK(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1486{
1487 Utf8Str src;
1488 Utf8Str dst;
1489 /* Parse the arguments. */
1490 for (int i = 0; i < argc; i++)
1491 {
1492 if (strcmp(argv[i], "-from") == 0)
1493 {
1494 if (argc <= i + 1)
1495 {
1496 return errorArgument("Missing argument to '%s'", argv[i]);
1497 }
1498 i++;
1499 src = argv[i];
1500 }
1501 else if (strcmp(argv[i], "-to") == 0)
1502 {
1503 if (argc <= i + 1)
1504 {
1505 return errorArgument("Missing argument to '%s'", argv[i]);
1506 }
1507 i++;
1508 dst = argv[i];
1509 }
1510 else
1511 {
1512 return errorSyntax(USAGE_RENAMEVMDK, "Invalid parameter '%s'", argv[i]);
1513 }
1514 }
1515
1516 if (src.isEmpty())
1517 return errorSyntax(USAGE_RENAMEVMDK, "Mandatory parameter -from missing");
1518 if (dst.isEmpty())
1519 return errorSyntax(USAGE_RENAMEVMDK, "Mandatory parameter -to missing");
1520
1521 PVBOXHDD pDisk = NULL;
1522
1523 PVDINTERFACE pVDIfs = NULL;
1524 VDINTERFACEERROR vdInterfaceError;
1525 vdInterfaceError.pfnError = handleVDError;
1526 vdInterfaceError.pfnMessage = handleVDMessage;
1527
1528 int vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1529 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1530 AssertRC(vrc);
1531
1532 vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1533 if (RT_FAILURE(vrc))
1534 {
1535 RTMsgError("Cannot create the virtual disk container: %Rrc", vrc);
1536 return vrc;
1537 }
1538 else
1539 {
1540 vrc = VDOpen(pDisk, "VMDK", src.c_str(), VD_OPEN_FLAGS_NORMAL, NULL);
1541 if (RT_FAILURE(vrc))
1542 {
1543 RTMsgError("Cannot create the source image: %Rrc", vrc);
1544 }
1545 else
1546 {
1547 vrc = VDCopy(pDisk, 0, pDisk, "VMDK", dst.c_str(), true, 0,
1548 VD_IMAGE_FLAGS_NONE, NULL, VD_OPEN_FLAGS_NORMAL,
1549 NULL, NULL, NULL);
1550 if (RT_FAILURE(vrc))
1551 {
1552 RTMsgError("Cannot rename the image: %Rrc", vrc);
1553 }
1554 }
1555 }
1556 VDCloseAll(pDisk);
1557 return vrc;
1558}
1559
1560static int CmdConvertToRaw(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1561{
1562 Utf8Str srcformat;
1563 Utf8Str src;
1564 Utf8Str dst;
1565 bool fWriteToStdOut = false;
1566
1567 /* Parse the arguments. */
1568 for (int i = 0; i < argc; i++)
1569 {
1570 if (strcmp(argv[i], "-format") == 0)
1571 {
1572 if (argc <= i + 1)
1573 {
1574 return errorArgument("Missing argument to '%s'", argv[i]);
1575 }
1576 i++;
1577 srcformat = argv[i];
1578 }
1579 else if (src.isEmpty())
1580 {
1581 src = argv[i];
1582 }
1583 else if (dst.isEmpty())
1584 {
1585 dst = argv[i];
1586#ifdef ENABLE_CONVERT_RAW_TO_STDOUT
1587 if (!strcmp(argv[i], "stdout"))
1588 fWriteToStdOut = true;
1589#endif /* ENABLE_CONVERT_RAW_TO_STDOUT */
1590 }
1591 else
1592 {
1593 return errorSyntax(USAGE_CONVERTTORAW, "Invalid parameter '%s'", argv[i]);
1594 }
1595 }
1596
1597 if (src.isEmpty())
1598 return errorSyntax(USAGE_CONVERTTORAW, "Mandatory filename parameter missing");
1599 if (dst.isEmpty())
1600 return errorSyntax(USAGE_CONVERTTORAW, "Mandatory outputfile parameter missing");
1601
1602 PVBOXHDD pDisk = NULL;
1603
1604 PVDINTERFACE pVDIfs = NULL;
1605 VDINTERFACEERROR vdInterfaceError;
1606 vdInterfaceError.pfnError = handleVDError;
1607 vdInterfaceError.pfnMessage = handleVDMessage;
1608
1609 int vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1610 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1611 AssertRC(vrc);
1612
1613 /** @todo: Support convert to raw for floppy and DVD images too. */
1614 vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1615 if (RT_FAILURE(vrc))
1616 {
1617 RTMsgError("Cannot create the virtual disk container: %Rrc", vrc);
1618 return 1;
1619 }
1620
1621 /* Open raw output file. */
1622 RTFILE outFile;
1623 vrc = VINF_SUCCESS;
1624 if (fWriteToStdOut)
1625 vrc = RTFileFromNative(&outFile, 1);
1626 else
1627 vrc = RTFileOpen(&outFile, dst.c_str(), RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
1628 if (RT_FAILURE(vrc))
1629 {
1630 VDCloseAll(pDisk);
1631 RTMsgError("Cannot create destination file \"%s\": %Rrc", dst.c_str(), vrc);
1632 return 1;
1633 }
1634
1635 if (srcformat.isEmpty())
1636 {
1637 char *pszFormat = NULL;
1638 VDTYPE enmType = VDTYPE_INVALID;
1639 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
1640 src.c_str(), &pszFormat, &enmType);
1641 if (RT_FAILURE(vrc) || enmType != VDTYPE_HDD)
1642 {
1643 VDCloseAll(pDisk);
1644 if (!fWriteToStdOut)
1645 {
1646 RTFileClose(outFile);
1647 RTFileDelete(dst.c_str());
1648 }
1649 if (RT_FAILURE(vrc))
1650 RTMsgError("No file format specified and autodetect failed - please specify format: %Rrc", vrc);
1651 else
1652 RTMsgError("Only converting harddisk images is supported");
1653 return 1;
1654 }
1655 srcformat = pszFormat;
1656 RTStrFree(pszFormat);
1657 }
1658 vrc = VDOpen(pDisk, srcformat.c_str(), src.c_str(), VD_OPEN_FLAGS_READONLY, NULL);
1659 if (RT_FAILURE(vrc))
1660 {
1661 VDCloseAll(pDisk);
1662 if (!fWriteToStdOut)
1663 {
1664 RTFileClose(outFile);
1665 RTFileDelete(dst.c_str());
1666 }
1667 RTMsgError("Cannot open the source image: %Rrc", vrc);
1668 return 1;
1669 }
1670
1671 uint64_t cbSize = VDGetSize(pDisk, VD_LAST_IMAGE);
1672 uint64_t offFile = 0;
1673#define RAW_BUFFER_SIZE _128K
1674 size_t cbBuf = RAW_BUFFER_SIZE;
1675 void *pvBuf = RTMemAlloc(cbBuf);
1676 if (pvBuf)
1677 {
1678 RTStrmPrintf(g_pStdErr, "Converting image \"%s\" with size %RU64 bytes (%RU64MB) to raw...\n", src.c_str(), cbSize, (cbSize + _1M - 1) / _1M);
1679 while (offFile < cbSize)
1680 {
1681 size_t cb = (size_t)RT_MIN(cbSize - offFile, cbBuf);
1682 vrc = VDRead(pDisk, offFile, pvBuf, cb);
1683 if (RT_FAILURE(vrc))
1684 break;
1685 vrc = RTFileWrite(outFile, pvBuf, cb, NULL);
1686 if (RT_FAILURE(vrc))
1687 break;
1688 offFile += cb;
1689 }
1690 if (RT_FAILURE(vrc))
1691 {
1692 VDCloseAll(pDisk);
1693 if (!fWriteToStdOut)
1694 {
1695 RTFileClose(outFile);
1696 RTFileDelete(dst.c_str());
1697 }
1698 RTMsgError("Cannot copy image data: %Rrc", vrc);
1699 return 1;
1700 }
1701 }
1702 else
1703 {
1704 vrc = VERR_NO_MEMORY;
1705 VDCloseAll(pDisk);
1706 if (!fWriteToStdOut)
1707 {
1708 RTFileClose(outFile);
1709 RTFileDelete(dst.c_str());
1710 }
1711 RTMsgError("Out of memory allocating read buffer");
1712 return 1;
1713 }
1714
1715 if (!fWriteToStdOut)
1716 RTFileClose(outFile);
1717 VDCloseAll(pDisk);
1718 return 0;
1719}
1720
1721static int CmdConvertHardDisk(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1722{
1723 Utf8Str srcformat;
1724 Utf8Str dstformat;
1725 Utf8Str src;
1726 Utf8Str dst;
1727 int vrc;
1728 PVBOXHDD pSrcDisk = NULL;
1729 PVBOXHDD pDstDisk = NULL;
1730 VDTYPE enmSrcType = VDTYPE_INVALID;
1731
1732 /* Parse the arguments. */
1733 for (int i = 0; i < argc; i++)
1734 {
1735 if (strcmp(argv[i], "-srcformat") == 0)
1736 {
1737 if (argc <= i + 1)
1738 {
1739 return errorArgument("Missing argument to '%s'", argv[i]);
1740 }
1741 i++;
1742 srcformat = argv[i];
1743 }
1744 else if (strcmp(argv[i], "-dstformat") == 0)
1745 {
1746 if (argc <= i + 1)
1747 {
1748 return errorArgument("Missing argument to '%s'", argv[i]);
1749 }
1750 i++;
1751 dstformat = argv[i];
1752 }
1753 else if (src.isEmpty())
1754 {
1755 src = argv[i];
1756 }
1757 else if (dst.isEmpty())
1758 {
1759 dst = argv[i];
1760 }
1761 else
1762 {
1763 return errorSyntax(USAGE_CONVERTHD, "Invalid parameter '%s'", argv[i]);
1764 }
1765 }
1766
1767 if (src.isEmpty())
1768 return errorSyntax(USAGE_CONVERTHD, "Mandatory input image parameter missing");
1769 if (dst.isEmpty())
1770 return errorSyntax(USAGE_CONVERTHD, "Mandatory output image parameter missing");
1771
1772
1773 PVDINTERFACE pVDIfs = NULL;
1774 VDINTERFACEERROR vdInterfaceError;
1775 vdInterfaceError.pfnError = handleVDError;
1776 vdInterfaceError.pfnMessage = handleVDMessage;
1777
1778 vrc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1779 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1780 AssertRC(vrc);
1781
1782 do
1783 {
1784 /* Try to determine input image format */
1785 if (srcformat.isEmpty())
1786 {
1787 char *pszFormat = NULL;
1788 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
1789 src.c_str(), &pszFormat, &enmSrcType);
1790 if (RT_FAILURE(vrc))
1791 {
1792 RTMsgError("No file format specified and autodetect failed - please specify format: %Rrc", vrc);
1793 break;
1794 }
1795 srcformat = pszFormat;
1796 RTStrFree(pszFormat);
1797 }
1798
1799 vrc = VDCreate(pVDIfs, enmSrcType, &pSrcDisk);
1800 if (RT_FAILURE(vrc))
1801 {
1802 RTMsgError("Cannot create the source virtual disk container: %Rrc", vrc);
1803 break;
1804 }
1805
1806 /* Open the input image */
1807 vrc = VDOpen(pSrcDisk, srcformat.c_str(), src.c_str(), VD_OPEN_FLAGS_READONLY, NULL);
1808 if (RT_FAILURE(vrc))
1809 {
1810 RTMsgError("Cannot open the source image: %Rrc", vrc);
1811 break;
1812 }
1813
1814 /* Output format defaults to VDI */
1815 if (dstformat.isEmpty())
1816 dstformat = "VDI";
1817
1818 vrc = VDCreate(pVDIfs, enmSrcType, &pDstDisk);
1819 if (RT_FAILURE(vrc))
1820 {
1821 RTMsgError("Cannot create the destination virtual disk container: %Rrc", vrc);
1822 break;
1823 }
1824
1825 uint64_t cbSize = VDGetSize(pSrcDisk, VD_LAST_IMAGE);
1826 RTStrmPrintf(g_pStdErr, "Converting image \"%s\" with size %RU64 bytes (%RU64MB)...\n", src.c_str(), cbSize, (cbSize + _1M - 1) / _1M);
1827
1828 /* Create the output image */
1829 vrc = VDCopy(pSrcDisk, VD_LAST_IMAGE, pDstDisk, dstformat.c_str(),
1830 dst.c_str(), false, 0, VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED,
1831 NULL, VD_OPEN_FLAGS_NORMAL, NULL, NULL, NULL);
1832 if (RT_FAILURE(vrc))
1833 {
1834 RTMsgError("Cannot copy the image: %Rrc", vrc);
1835 break;
1836 }
1837 }
1838 while (0);
1839 if (pDstDisk)
1840 VDCloseAll(pDstDisk);
1841 if (pSrcDisk)
1842 VDCloseAll(pSrcDisk);
1843
1844 return RT_SUCCESS(vrc) ? 0 : 1;
1845}
1846
1847/**
1848 * Unloads the necessary driver.
1849 *
1850 * @returns VBox status code
1851 */
1852int CmdModUninstall(void)
1853{
1854 int rc;
1855
1856 rc = SUPR3Uninstall();
1857 if (RT_SUCCESS(rc))
1858 return 0;
1859 if (rc == VERR_NOT_IMPLEMENTED)
1860 return 0;
1861 return E_FAIL;
1862}
1863
1864/**
1865 * Loads the necessary driver.
1866 *
1867 * @returns VBox status code
1868 */
1869int CmdModInstall(void)
1870{
1871 int rc;
1872
1873 rc = SUPR3Install();
1874 if (RT_SUCCESS(rc))
1875 return 0;
1876 if (rc == VERR_NOT_IMPLEMENTED)
1877 return 0;
1878 return E_FAIL;
1879}
1880
1881int CmdDebugLog(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1882{
1883 /*
1884 * The first parameter is the name or UUID of a VM with a direct session
1885 * that we wish to open.
1886 */
1887 if (argc < 1)
1888 return errorSyntax(USAGE_DEBUGLOG, "Missing VM name/UUID");
1889
1890 ComPtr<IMachine> ptrMachine;
1891 HRESULT rc;
1892 CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
1893 ptrMachine.asOutParam()), 1);
1894
1895 CHECK_ERROR_RET(ptrMachine, LockMachine(aSession, LockType_Shared), 1);
1896
1897 /*
1898 * Get the debugger interface.
1899 */
1900 ComPtr<IConsole> ptrConsole;
1901 CHECK_ERROR_RET(aSession, COMGETTER(Console)(ptrConsole.asOutParam()), 1);
1902
1903 ComPtr<IMachineDebugger> ptrDebugger;
1904 CHECK_ERROR_RET(ptrConsole, COMGETTER(Debugger)(ptrDebugger.asOutParam()), 1);
1905
1906 /*
1907 * Parse the command.
1908 */
1909 bool fEnablePresent = false;
1910 bool fEnable = false;
1911 bool fFlagsPresent = false;
1912 RTCString strFlags;
1913 bool fGroupsPresent = false;
1914 RTCString strGroups;
1915 bool fDestsPresent = false;
1916 RTCString strDests;
1917
1918 static const RTGETOPTDEF s_aOptions[] =
1919 {
1920 { "--disable", 'E', RTGETOPT_REQ_NOTHING },
1921 { "--enable", 'e', RTGETOPT_REQ_NOTHING },
1922 { "--flags", 'f', RTGETOPT_REQ_STRING },
1923 { "--groups", 'g', RTGETOPT_REQ_STRING },
1924 { "--destinations", 'd', RTGETOPT_REQ_STRING }
1925 };
1926
1927 int ch;
1928 RTGETOPTUNION ValueUnion;
1929 RTGETOPTSTATE GetState;
1930 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
1931 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1932 {
1933 switch (ch)
1934 {
1935 case 'e':
1936 fEnablePresent = true;
1937 fEnable = true;
1938 break;
1939
1940 case 'E':
1941 fEnablePresent = true;
1942 fEnable = false;
1943 break;
1944
1945 case 'f':
1946 fFlagsPresent = true;
1947 if (*ValueUnion.psz)
1948 {
1949 if (strFlags.isNotEmpty())
1950 strFlags.append(' ');
1951 strFlags.append(ValueUnion.psz);
1952 }
1953 break;
1954
1955 case 'g':
1956 fGroupsPresent = true;
1957 if (*ValueUnion.psz)
1958 {
1959 if (strGroups.isNotEmpty())
1960 strGroups.append(' ');
1961 strGroups.append(ValueUnion.psz);
1962 }
1963 break;
1964
1965 case 'd':
1966 fDestsPresent = true;
1967 if (*ValueUnion.psz)
1968 {
1969 if (strDests.isNotEmpty())
1970 strDests.append(' ');
1971 strDests.append(ValueUnion.psz);
1972 }
1973 break;
1974
1975 default:
1976 return errorGetOpt(USAGE_DEBUGLOG , ch, &ValueUnion);
1977 }
1978 }
1979
1980 /*
1981 * Do the job.
1982 */
1983 if (fEnablePresent && !fEnable)
1984 CHECK_ERROR_RET(ptrDebugger, COMSETTER(LogEnabled)(FALSE), 1);
1985
1986 /** @todo flags, groups destination. */
1987 if (fFlagsPresent || fGroupsPresent || fDestsPresent)
1988 RTMsgWarning("One or more of the requested features are not implemented! Feel free to do this.");
1989
1990 if (fEnablePresent && fEnable)
1991 CHECK_ERROR_RET(ptrDebugger, COMSETTER(LogEnabled)(TRUE), 1);
1992 return 0;
1993}
1994
1995/**
1996 * Generate a SHA-256 password hash
1997 */
1998int CmdGeneratePasswordHash(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1999{
2000 /* one parameter, the password to hash */
2001 if (argc != 1)
2002 return errorSyntax(USAGE_PASSWORDHASH, "password to hash required");
2003
2004 uint8_t abDigest[RTSHA256_HASH_SIZE];
2005 RTSha256(argv[0], strlen(argv[0]), abDigest);
2006 char pszDigest[RTSHA256_DIGEST_LEN + 1];
2007 RTSha256ToString(abDigest, pszDigest, sizeof(pszDigest));
2008 RTPrintf("Password hash: %s\n", pszDigest);
2009
2010 return 0;
2011}
2012
2013/**
2014 * Print internal guest statistics or
2015 * set internal guest statistics update interval if specified
2016 */
2017int CmdGuestStats(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
2018{
2019 /* one parameter, guest name */
2020 if (argc < 1)
2021 return errorSyntax(USAGE_GUESTSTATS, "Missing VM name/UUID");
2022
2023 /*
2024 * Parse the command.
2025 */
2026 ULONG aUpdateInterval = 0;
2027
2028 static const RTGETOPTDEF s_aOptions[] =
2029 {
2030 { "--interval", 'i', RTGETOPT_REQ_UINT32 }
2031 };
2032
2033 int ch;
2034 RTGETOPTUNION ValueUnion;
2035 RTGETOPTSTATE GetState;
2036 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
2037 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
2038 {
2039 switch (ch)
2040 {
2041 case 'i':
2042 aUpdateInterval = ValueUnion.u32;
2043 break;
2044
2045 default:
2046 return errorGetOpt(USAGE_GUESTSTATS , ch, &ValueUnion);
2047 }
2048 }
2049
2050 if (argc > 1 && aUpdateInterval == 0)
2051 return errorSyntax(USAGE_GUESTSTATS, "Invalid update interval specified");
2052
2053 RTPrintf("argc=%d interval=%u\n", argc, aUpdateInterval);
2054
2055 ComPtr<IMachine> ptrMachine;
2056 HRESULT rc;
2057 CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
2058 ptrMachine.asOutParam()), 1);
2059
2060 CHECK_ERROR_RET(ptrMachine, LockMachine(aSession, LockType_Shared), 1);
2061
2062 /*
2063 * Get the guest interface.
2064 */
2065 ComPtr<IConsole> ptrConsole;
2066 CHECK_ERROR_RET(aSession, COMGETTER(Console)(ptrConsole.asOutParam()), 1);
2067
2068 ComPtr<IGuest> ptrGuest;
2069 CHECK_ERROR_RET(ptrConsole, COMGETTER(Guest)(ptrGuest.asOutParam()), 1);
2070
2071 if (aUpdateInterval)
2072 CHECK_ERROR_RET(ptrGuest, COMSETTER(StatisticsUpdateInterval)(aUpdateInterval), 1);
2073 else
2074 {
2075 ULONG mCpuUser, mCpuKernel, mCpuIdle;
2076 ULONG mMemTotal, mMemFree, mMemBalloon, mMemShared, mMemCache, mPageTotal;
2077 ULONG ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal, ulMemSharedTotal;
2078
2079 CHECK_ERROR_RET(ptrGuest, InternalGetStatistics(&mCpuUser, &mCpuKernel, &mCpuIdle,
2080 &mMemTotal, &mMemFree, &mMemBalloon, &mMemShared, &mMemCache,
2081 &mPageTotal, &ulMemAllocTotal, &ulMemFreeTotal, &ulMemBalloonTotal, &ulMemSharedTotal), 1);
2082 RTPrintf("mCpuUser=%u mCpuKernel=%u mCpuIdle=%u\n"
2083 "mMemTotal=%u mMemFree=%u mMemBalloon=%u mMemShared=%u mMemCache=%u\n"
2084 "mPageTotal=%u ulMemAllocTotal=%u ulMemFreeTotal=%u ulMemBalloonTotal=%u ulMemSharedTotal=%u\n",
2085 mCpuUser, mCpuKernel, mCpuIdle,
2086 mMemTotal, mMemFree, mMemBalloon, mMemShared, mMemCache,
2087 mPageTotal, ulMemAllocTotal, ulMemFreeTotal, ulMemBalloonTotal, ulMemSharedTotal);
2088
2089 }
2090
2091 return 0;
2092}
2093
2094
2095/**
2096 * Wrapper for handling internal commands
2097 */
2098int handleInternalCommands(HandlerArg *a)
2099{
2100 g_fInternalMode = true;
2101
2102 /* at least a command is required */
2103 if (a->argc < 1)
2104 return errorSyntax(USAGE_ALL, "Command missing");
2105
2106 /*
2107 * The 'string switch' on command name.
2108 */
2109 const char *pszCmd = a->argv[0];
2110 if (!strcmp(pszCmd, "loadsyms"))
2111 return CmdLoadSyms(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2112 //if (!strcmp(pszCmd, "unloadsyms"))
2113 // return CmdUnloadSyms(argc - 1 , &a->argv[1]);
2114 if (!strcmp(pszCmd, "sethduuid") || !strcmp(pszCmd, "sethdparentuuid"))
2115 return CmdSetHDUUID(a->argc, &a->argv[0], a->virtualBox, a->session);
2116 if (!strcmp(pszCmd, "dumphdinfo"))
2117 return CmdDumpHDInfo(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2118 if (!strcmp(pszCmd, "listpartitions"))
2119 return CmdListPartitions(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2120 if (!strcmp(pszCmd, "createrawvmdk"))
2121 return CmdCreateRawVMDK(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2122 if (!strcmp(pszCmd, "renamevmdk"))
2123 return CmdRenameVMDK(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2124 if (!strcmp(pszCmd, "converttoraw"))
2125 return CmdConvertToRaw(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2126 if (!strcmp(pszCmd, "converthd"))
2127 return CmdConvertHardDisk(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2128 if (!strcmp(pszCmd, "modinstall"))
2129 return CmdModInstall();
2130 if (!strcmp(pszCmd, "moduninstall"))
2131 return CmdModUninstall();
2132 if (!strcmp(pszCmd, "debuglog"))
2133 return CmdDebugLog(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2134 if (!strcmp(pszCmd, "passwordhash"))
2135 return CmdGeneratePasswordHash(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2136 if (!strcmp(pszCmd, "gueststats"))
2137 return CmdGuestStats(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2138
2139 /* default: */
2140 return errorSyntax(USAGE_ALL, "Invalid command '%s'", a->argv[0]);
2141}
2142
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