VirtualBox

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

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

Frontends/VBoxManage: makes no sense to register a raw disk/partition VMDK immediately with the new registry logic

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 68.3 KB
Line 
1/* $Id: VBoxInternalManage.cpp 35238 2010-12-20 11:18:40Z 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"
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 host the parameter -relative causes a VMDK file to be created\n"
189 " which refers to individual partitions instead to the entire disk.\n"
190 " The necessary partition numbers can be queried with\n"
191 " VBoxManage internalcommands listpartitions\n"
192 "\n"
193 : "",
194 (u64Cmd & USAGE_RENAMEVMDK)
195 ? " renamevmdk -from <filename> -to <filename>\n"
196 " Renames an existing VMDK image, including the base file and all its extents.\n"
197 "\n"
198 : "",
199 (u64Cmd & USAGE_CONVERTTORAW)
200 ? " converttoraw [-format <fileformat>] <filename> <outputfile>"
201#ifdef ENABLE_CONVERT_RAW_TO_STDOUT
202 "|stdout"
203#endif /* ENABLE_CONVERT_RAW_TO_STDOUT */
204 "\n"
205 " Convert image to raw, writing to file"
206#ifdef ENABLE_CONVERT_RAW_TO_STDOUT
207 " or stdout"
208#endif /* ENABLE_CONVERT_RAW_TO_STDOUT */
209 ".\n"
210 "\n"
211 : "",
212 (u64Cmd & USAGE_CONVERTHD)
213 ? " converthd [-srcformat VDI|VMDK|VHD|RAW]\n"
214 " [-dstformat VDI|VMDK|VHD|RAW]\n"
215 " <inputfile> <outputfile>\n"
216 " converts hard disk images between formats\n"
217 "\n"
218 : "",
219#ifdef RT_OS_WINDOWS
220 (u64Cmd & USAGE_MODINSTALL)
221 ? " modinstall\n"
222 " Installs the necessary driver for the host OS\n"
223 "\n"
224 : "",
225 (u64Cmd & USAGE_MODUNINSTALL)
226 ? " moduninstall\n"
227 " Deinstalls the driver\n"
228 "\n"
229 : "",
230#else
231 "",
232 "",
233#endif
234 (u64Cmd & USAGE_DEBUGLOG)
235 ? " debuglog <vmname>|<uuid> [--enable|--disable] [--flags todo]\n"
236 " [--groups todo] [--destinations todo]\n"
237 " Controls debug logging.\n"
238 "\n"
239 : "",
240 (u64Cmd & USAGE_PASSWORDHASH)
241 ? " passwordhash <passsword>\n"
242 " Generates a password hash.\n"
243 "\n"
244 :
245 ""
246 );
247}
248
249/** @todo this is no longer necessary, we can enumerate extra data */
250/**
251 * Finds a new unique key name.
252 *
253 * I don't think this is 100% race condition proof, but we assumes
254 * the user is not trying to push this point.
255 *
256 * @returns Result from the insert.
257 * @param pMachine The Machine object.
258 * @param pszKeyBase The base key.
259 * @param rKey Reference to the string object in which we will return the key.
260 */
261static HRESULT NewUniqueKey(ComPtr<IMachine> pMachine, const char *pszKeyBase, Utf8Str &rKey)
262{
263 Bstr KeyBase(pszKeyBase);
264 Bstr Keys;
265 HRESULT hrc = pMachine->GetExtraData(KeyBase.raw(), Keys.asOutParam());
266 if (FAILED(hrc))
267 return hrc;
268
269 /* if there are no keys, it's simple. */
270 if (Keys.isEmpty())
271 {
272 rKey = "1";
273 return pMachine->SetExtraData(KeyBase.raw(), Bstr(rKey).raw());
274 }
275
276 /* find a unique number - brute force rulez. */
277 Utf8Str KeysUtf8(Keys);
278 const char *pszKeys = RTStrStripL(KeysUtf8.c_str());
279 for (unsigned i = 1; i < 1000000; i++)
280 {
281 char szKey[32];
282 size_t cchKey = RTStrPrintf(szKey, sizeof(szKey), "%#x", i);
283 const char *psz = strstr(pszKeys, szKey);
284 while (psz)
285 {
286 if ( ( psz == pszKeys
287 || psz[-1] == ' ')
288 && ( psz[cchKey] == ' '
289 || !psz[cchKey])
290 )
291 break;
292 psz = strstr(psz + cchKey, szKey);
293 }
294 if (!psz)
295 {
296 rKey = szKey;
297 Utf8StrFmt NewKeysUtf8("%s %s", pszKeys, szKey);
298 return pMachine->SetExtraData(KeyBase.raw(),
299 Bstr(NewKeysUtf8).raw());
300 }
301 }
302 RTMsgError("Cannot find unique key for '%s'!", pszKeyBase);
303 return E_FAIL;
304}
305
306
307#if 0
308/**
309 * Remove a key.
310 *
311 * I don't think this isn't 100% race condition proof, but we assumes
312 * the user is not trying to push this point.
313 *
314 * @returns Result from the insert.
315 * @param pMachine The machine object.
316 * @param pszKeyBase The base key.
317 * @param pszKey The key to remove.
318 */
319static HRESULT RemoveKey(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey)
320{
321 Bstr Keys;
322 HRESULT hrc = pMachine->GetExtraData(Bstr(pszKeyBase), Keys.asOutParam());
323 if (FAILED(hrc))
324 return hrc;
325
326 /* if there are no keys, it's simple. */
327 if (Keys.isEmpty())
328 return S_OK;
329
330 char *pszKeys;
331 int rc = RTUtf16ToUtf8(Keys.raw(), &pszKeys);
332 if (RT_SUCCESS(rc))
333 {
334 /* locate it */
335 size_t cchKey = strlen(pszKey);
336 char *psz = strstr(pszKeys, pszKey);
337 while (psz)
338 {
339 if ( ( psz == pszKeys
340 || psz[-1] == ' ')
341 && ( psz[cchKey] == ' '
342 || !psz[cchKey])
343 )
344 break;
345 psz = strstr(psz + cchKey, pszKey);
346 }
347 if (psz)
348 {
349 /* remove it */
350 char *pszNext = RTStrStripL(psz + cchKey);
351 if (*pszNext)
352 memmove(psz, pszNext, strlen(pszNext) + 1);
353 else
354 *psz = '\0';
355 psz = RTStrStrip(pszKeys);
356
357 /* update */
358 hrc = pMachine->SetExtraData(Bstr(pszKeyBase), Bstr(psz));
359 }
360
361 RTStrFree(pszKeys);
362 return hrc;
363 }
364 else
365 RTMsgError("Failed to delete key '%s' from '%s', string conversion error %Rrc!",
366 pszKey, pszKeyBase, rc);
367
368 return E_FAIL;
369}
370#endif
371
372
373/**
374 * Sets a key value, does necessary error bitching.
375 *
376 * @returns COM status code.
377 * @param pMachine The Machine object.
378 * @param pszKeyBase The key base.
379 * @param pszKey The key.
380 * @param pszAttribute The attribute name.
381 * @param pszValue The string value.
382 */
383static HRESULT SetString(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey, const char *pszAttribute, const char *pszValue)
384{
385 HRESULT hrc = pMachine->SetExtraData(BstrFmt("%s/%s/%s", pszKeyBase,
386 pszKey, pszAttribute).raw(),
387 Bstr(pszValue).raw());
388 if (FAILED(hrc))
389 RTMsgError("Failed to set '%s/%s/%s' to '%s'! hrc=%#x",
390 pszKeyBase, pszKey, pszAttribute, pszValue, hrc);
391 return hrc;
392}
393
394
395/**
396 * Sets a key value, does necessary error bitching.
397 *
398 * @returns COM status code.
399 * @param pMachine The Machine object.
400 * @param pszKeyBase The key base.
401 * @param pszKey The key.
402 * @param pszAttribute The attribute name.
403 * @param u64Value The value.
404 */
405static HRESULT SetUInt64(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey, const char *pszAttribute, uint64_t u64Value)
406{
407 char szValue[64];
408 RTStrPrintf(szValue, sizeof(szValue), "%#RX64", u64Value);
409 return SetString(pMachine, pszKeyBase, pszKey, pszAttribute, szValue);
410}
411
412
413/**
414 * Sets a key value, does necessary error bitching.
415 *
416 * @returns COM status code.
417 * @param pMachine The Machine object.
418 * @param pszKeyBase The key base.
419 * @param pszKey The key.
420 * @param pszAttribute The attribute name.
421 * @param i64Value The value.
422 */
423static HRESULT SetInt64(ComPtr<IMachine> pMachine, const char *pszKeyBase, const char *pszKey, const char *pszAttribute, int64_t i64Value)
424{
425 char szValue[64];
426 RTStrPrintf(szValue, sizeof(szValue), "%RI64", i64Value);
427 return SetString(pMachine, pszKeyBase, pszKey, pszAttribute, szValue);
428}
429
430
431/**
432 * Identical to the 'loadsyms' command.
433 */
434static int CmdLoadSyms(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
435{
436 HRESULT rc;
437
438 /*
439 * Get the VM
440 */
441 ComPtr<IMachine> machine;
442 CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
443 machine.asOutParam()), 1);
444
445 /*
446 * Parse the command.
447 */
448 const char *pszFilename;
449 int64_t offDelta = 0;
450 const char *pszModule = NULL;
451 uint64_t ModuleAddress = ~0;
452 uint64_t ModuleSize = 0;
453
454 /* filename */
455 if (argc < 2)
456 return errorArgument("Missing the filename argument!\n");
457 pszFilename = argv[1];
458
459 /* offDelta */
460 if (argc >= 3)
461 {
462 int irc = RTStrToInt64Ex(argv[2], NULL, 0, &offDelta);
463 if (RT_FAILURE(irc))
464 return errorArgument(argv[0], "Failed to read delta '%s', rc=%Rrc\n", argv[2], rc);
465 }
466
467 /* pszModule */
468 if (argc >= 4)
469 pszModule = argv[3];
470
471 /* ModuleAddress */
472 if (argc >= 5)
473 {
474 int irc = RTStrToUInt64Ex(argv[4], NULL, 0, &ModuleAddress);
475 if (RT_FAILURE(irc))
476 return errorArgument(argv[0], "Failed to read module address '%s', rc=%Rrc\n", argv[4], rc);
477 }
478
479 /* ModuleSize */
480 if (argc >= 6)
481 {
482 int irc = RTStrToUInt64Ex(argv[5], NULL, 0, &ModuleSize);
483 if (RT_FAILURE(irc))
484 return errorArgument(argv[0], "Failed to read module size '%s', rc=%Rrc\n", argv[5], rc);
485 }
486
487 /*
488 * Add extra data.
489 */
490 Utf8Str KeyStr;
491 HRESULT hrc = NewUniqueKey(machine, "VBoxInternal/DBGF/loadsyms", KeyStr);
492 if (SUCCEEDED(hrc))
493 hrc = SetString(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "Filename", pszFilename);
494 if (SUCCEEDED(hrc) && argc >= 3)
495 hrc = SetInt64(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "Delta", offDelta);
496 if (SUCCEEDED(hrc) && argc >= 4)
497 hrc = SetString(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "Module", pszModule);
498 if (SUCCEEDED(hrc) && argc >= 5)
499 hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "ModuleAddress", ModuleAddress);
500 if (SUCCEEDED(hrc) && argc >= 6)
501 hrc = SetUInt64(machine, "VBoxInternal/DBGF/loadsyms", KeyStr.c_str(), "ModuleSize", ModuleSize);
502
503 return FAILED(hrc);
504}
505
506
507static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va)
508{
509 RTMsgErrorV(pszFormat, va);
510 RTMsgError("Error code %Rrc at %s(%u) in function %s", rc, RT_SRC_POS_ARGS);
511}
512
513static int handleVDMessage(void *pvUser, const char *pszFormat, va_list va)
514{
515 NOREF(pvUser);
516 return RTPrintfV(pszFormat, va);
517}
518
519static int CmdSetHDUUID(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
520{
521 Guid uuid;
522 RTUUID rtuuid;
523 enum eUuidType {
524 HDUUID,
525 HDPARENTUUID
526 } uuidType;
527
528 if (!strcmp(argv[0], "sethduuid"))
529 {
530 uuidType = HDUUID;
531 if (argc != 3 && argc != 2)
532 return errorSyntax(USAGE_SETHDUUID, "Not enough parameters");
533 /* if specified, take UUID, otherwise generate a new one */
534 if (argc == 3)
535 {
536 if (RT_FAILURE(RTUuidFromStr(&rtuuid, argv[2])))
537 return errorSyntax(USAGE_SETHDUUID, "Invalid UUID parameter");
538 uuid = argv[2];
539 } else
540 uuid.create();
541 }
542 else if (!strcmp(argv[0], "sethdparentuuid"))
543 {
544 uuidType = HDPARENTUUID;
545 if (argc != 3)
546 return errorSyntax(USAGE_SETHDPARENTUUID, "Not enough parameters");
547 if (RT_FAILURE(RTUuidFromStr(&rtuuid, argv[2])))
548 return errorSyntax(USAGE_SETHDPARENTUUID, "Invalid UUID parameter");
549 uuid = argv[2];
550 }
551 else
552 return errorSyntax(USAGE_SETHDUUID, "Invalid invocation");
553
554 /* just try it */
555 char *pszFormat = NULL;
556 VDTYPE enmType = VDTYPE_INVALID;
557 int rc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
558 argv[1], &pszFormat, &enmType);
559 if (RT_FAILURE(rc))
560 {
561 RTMsgError("Format autodetect failed: %Rrc", rc);
562 return 1;
563 }
564
565 PVBOXHDD pDisk = NULL;
566
567 PVDINTERFACE pVDIfs = NULL;
568 VDINTERFACE vdInterfaceError;
569 VDINTERFACEERROR vdInterfaceErrorCallbacks;
570 vdInterfaceErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
571 vdInterfaceErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
572 vdInterfaceErrorCallbacks.pfnError = handleVDError;
573 vdInterfaceErrorCallbacks.pfnMessage = handleVDMessage;
574
575 rc = VDInterfaceAdd(&vdInterfaceError, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
576 &vdInterfaceErrorCallbacks, NULL, &pVDIfs);
577 AssertRC(rc);
578
579 rc = VDCreate(pVDIfs, enmType, &pDisk);
580 if (RT_FAILURE(rc))
581 {
582 RTMsgError("Cannot create the virtual disk container: %Rrc", rc);
583 return 1;
584 }
585
586 /* Open the image */
587 rc = VDOpen(pDisk, pszFormat, argv[1], VD_OPEN_FLAGS_NORMAL, NULL);
588 if (RT_FAILURE(rc))
589 {
590 RTMsgError("Cannot open the image: %Rrc", rc);
591 return 1;
592 }
593
594 if (uuidType == HDUUID)
595 rc = VDSetUuid(pDisk, VD_LAST_IMAGE, uuid.raw());
596 else
597 rc = VDSetParentUuid(pDisk, VD_LAST_IMAGE, uuid.raw());
598 if (RT_FAILURE(rc))
599 RTMsgError("Cannot set a new UUID: %Rrc", rc);
600 else
601 RTPrintf("UUID changed to: %s\n", uuid.toString().c_str());
602
603 VDCloseAll(pDisk);
604
605 return RT_FAILURE(rc);
606}
607
608
609static int CmdDumpHDInfo(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
610{
611 /* we need exactly one parameter: the image file */
612 if (argc != 1)
613 {
614 return errorSyntax(USAGE_DUMPHDINFO, "Not enough parameters");
615 }
616
617 /* just try it */
618 char *pszFormat = NULL;
619 VDTYPE enmType = VDTYPE_INVALID;
620 int rc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
621 argv[0], &pszFormat, &enmType);
622 if (RT_FAILURE(rc))
623 {
624 RTMsgError("Format autodetect failed: %Rrc", rc);
625 return 1;
626 }
627
628 PVBOXHDD pDisk = NULL;
629
630 PVDINTERFACE pVDIfs = NULL;
631 VDINTERFACE vdInterfaceError;
632 VDINTERFACEERROR vdInterfaceErrorCallbacks;
633 vdInterfaceErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
634 vdInterfaceErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
635 vdInterfaceErrorCallbacks.pfnError = handleVDError;
636 vdInterfaceErrorCallbacks.pfnMessage = handleVDMessage;
637
638 rc = VDInterfaceAdd(&vdInterfaceError, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
639 &vdInterfaceErrorCallbacks, NULL, &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 RawFile;
861 int vrc = RTFileOpen(&RawFile, 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(RawFile, &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#ifdef RT_OS_LINUX
966 else if (strcmp(argv[i], "-relative") == 0)
967 {
968 fRelative = true;
969 }
970#endif /* RT_OS_LINUX */
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 RawFile;
986 int vrc = RTFileOpen(&RawFile, 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)RawFile,
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)RawFile,
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(RawFile, &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(RawFile, BLKGETSIZE64, &cbBlk))
1053 cbSize = cbBlk;
1054 }
1055#endif /* BLKGETSIZE64 */
1056 if (!cbSize)
1057 {
1058 long cBlocks;
1059 if (!ioctl(RawFile, 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(RawFile, &DevStat) && S_ISBLK(DevStat.st_mode))
1078 {
1079 uint64_t cBlocks;
1080 uint32_t cbBlock;
1081 if (!ioctl(RawFile, DKIOCGETBLOCKCOUNT, &cBlocks))
1082 {
1083 if (!ioctl(RawFile, 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(RawFile, &DevStat) && ( S_ISBLK(DevStat.st_mode)
1108 || S_ISCHR(DevStat.st_mode)))
1109 {
1110 struct dk_minfo mediainfo;
1111 if (!ioctl(RawFile, 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(RawFile, &DevStat) && S_ISCHR(DevStat.st_mode))
1129 {
1130 off_t cbMedia = 0;
1131 if (!ioctl(RawFile, 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(RawFile, &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(RawFile, &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(RawFile, 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#ifdef RT_OS_LINUX
1319 /* Refer to the correct partition and use offset 0. */
1320 char *psz;
1321 RTStrAPrintf(&psz, "%s%u", rawdisk.c_str(),
1322 partitions.aPartitions[i].uIndex);
1323 if (!psz)
1324 {
1325 vrc = VERR_NO_STR_MEMORY;
1326 RTMsgError("Cannot create reference to individual partition %u, rc=%Rrc",
1327 partitions.aPartitions[i].uIndex, vrc);
1328 goto out;
1329 }
1330 pszRawName = psz;
1331 uStartOffset = 0;
1332#elif defined(RT_OS_DARWIN)
1333 /* Refer to the correct partition and use offset 0. */
1334 char *psz;
1335 RTStrAPrintf(&psz, "%ss%u", rawdisk.c_str(),
1336 partitions.aPartitions[i].uIndex);
1337 if (!psz)
1338 {
1339 vrc = VERR_NO_STR_MEMORY;
1340 RTMsgError("Cannot create reference to individual partition %u, rc=%Rrc",
1341 partitions.aPartitions[i].uIndex, vrc);
1342 goto out;
1343 }
1344 pszRawName = psz;
1345 uStartOffset = 0;
1346#else
1347 /** @todo not implemented for other hosts. Treat just like
1348 * not specified (this code is actually never reached). */
1349#endif
1350 }
1351
1352 pPartDesc->pszRawDevice = pszRawName;
1353 pPartDesc->uStartOffset = uStartOffset;
1354 }
1355 else
1356 {
1357 pPartDesc->pszRawDevice = NULL;
1358 pPartDesc->uStartOffset = 0;
1359 }
1360
1361 pPartDesc->uStart = partitions.aPartitions[i].uStart * 512;
1362 pPartDesc->cbData = partitions.aPartitions[i].uSize * 512;
1363 }
1364
1365 /* Sort data areas in ascending order of start. */
1366 for (unsigned i = 0; i < RawDescriptor.cPartDescs-1; i++)
1367 {
1368 unsigned uMinIdx = i;
1369 uint64_t uMinVal = RawDescriptor.pPartDescs[i].uStart;
1370 for (unsigned j = i + 1; j < RawDescriptor.cPartDescs; j++)
1371 {
1372 if (RawDescriptor.pPartDescs[j].uStart < uMinVal)
1373 {
1374 uMinIdx = j;
1375 uMinVal = RawDescriptor.pPartDescs[j].uStart;
1376 }
1377 }
1378 if (uMinIdx != i)
1379 {
1380 /* Swap entries at index i and uMinIdx. */
1381 VBOXHDDRAWPARTDESC tmp;
1382 memcpy(&tmp, &RawDescriptor.pPartDescs[i], sizeof(tmp));
1383 memcpy(&RawDescriptor.pPartDescs[i], &RawDescriptor.pPartDescs[uMinIdx], sizeof(tmp));
1384 memcpy(&RawDescriptor.pPartDescs[uMinIdx], &tmp, sizeof(tmp));
1385 }
1386 }
1387
1388 /* Have a second go at MBR/EPT area clipping. Now that the data areas
1389 * are sorted this is much easier to get 100% right. */
1390 for (unsigned i = 0; i < RawDescriptor.cPartDescs-1; i++)
1391 {
1392 if (RawDescriptor.pPartDescs[i].pvPartitionData)
1393 {
1394 RawDescriptor.pPartDescs[i].cbData = RT_MIN(RawDescriptor.pPartDescs[i+1].uStart - RawDescriptor.pPartDescs[i].uStart, RawDescriptor.pPartDescs[i].cbData);
1395 if (!RawDescriptor.pPartDescs[i].cbData)
1396 {
1397 RTMsgError("MBR/EPT overlaps with data area");
1398 vrc = VERR_INVALID_PARAMETER;
1399 goto out;
1400 }
1401 }
1402 }
1403 }
1404
1405 RTFileClose(RawFile);
1406
1407#ifdef DEBUG_klaus
1408 RTPrintf("# start length startoffset partdataptr device\n");
1409 for (unsigned i = 0; i < RawDescriptor.cPartDescs; i++)
1410 {
1411 RTPrintf("%2u %14RU64 %14RU64 %14RU64 %#18p %s\n", i,
1412 RawDescriptor.pPartDescs[i].uStart,
1413 RawDescriptor.pPartDescs[i].cbData,
1414 RawDescriptor.pPartDescs[i].uStartOffset,
1415 RawDescriptor.pPartDescs[i].pvPartitionData,
1416 RawDescriptor.pPartDescs[i].pszRawDevice);
1417 }
1418#endif
1419
1420 VDINTERFACE vdInterfaceError;
1421 VDINTERFACEERROR vdInterfaceErrorCallbacks;
1422 vdInterfaceErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
1423 vdInterfaceErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
1424 vdInterfaceErrorCallbacks.pfnError = handleVDError;
1425 vdInterfaceErrorCallbacks.pfnMessage = handleVDMessage;
1426
1427 vrc = VDInterfaceAdd(&vdInterfaceError, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1428 &vdInterfaceErrorCallbacks, NULL, &pVDIfs);
1429 AssertRC(vrc);
1430
1431 vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk); /* Raw VMDK's are harddisk only. */
1432 if (RT_FAILURE(vrc))
1433 {
1434 RTMsgError("Cannot create the virtual disk container: %Rrc", vrc);
1435 goto out;
1436 }
1437
1438 Assert(RT_MIN(cbSize / 512 / 16 / 63, 16383) -
1439 (unsigned int)RT_MIN(cbSize / 512 / 16 / 63, 16383) == 0);
1440 VDGEOMETRY PCHS, LCHS;
1441 PCHS.cCylinders = (unsigned int)RT_MIN(cbSize / 512 / 16 / 63, 16383);
1442 PCHS.cHeads = 16;
1443 PCHS.cSectors = 63;
1444 LCHS.cCylinders = 0;
1445 LCHS.cHeads = 0;
1446 LCHS.cSectors = 0;
1447 vrc = VDCreateBase(pDisk, "VMDK", filename.c_str(), cbSize,
1448 VD_IMAGE_FLAGS_FIXED | VD_VMDK_IMAGE_FLAGS_RAWDISK,
1449 (char *)&RawDescriptor, &PCHS, &LCHS, NULL,
1450 VD_OPEN_FLAGS_NORMAL, NULL, NULL);
1451 if (RT_FAILURE(vrc))
1452 {
1453 RTMsgError("Cannot create the raw disk VMDK: %Rrc", vrc);
1454 goto out;
1455 }
1456 RTPrintf("RAW host disk access VMDK file %s created successfully.\n", filename.c_str());
1457
1458 VDCloseAll(pDisk);
1459
1460 /* Clean up allocated memory etc. */
1461 if (pszPartitions)
1462 {
1463 for (unsigned i = 0; i < RawDescriptor.cPartDescs; i++)
1464 {
1465 /* Free memory allocated for relative device name. */
1466 if (fRelative && RawDescriptor.pPartDescs[i].pszRawDevice)
1467 RTStrFree((char *)(void *)RawDescriptor.pPartDescs[i].pszRawDevice);
1468 if (RawDescriptor.pPartDescs[i].pvPartitionData)
1469 RTMemFree((void *)RawDescriptor.pPartDescs[i].pvPartitionData);
1470 }
1471 if (RawDescriptor.pPartDescs)
1472 RTMemFree(RawDescriptor.pPartDescs);
1473 }
1474
1475 return SUCCEEDED(rc) ? 0 : 1;
1476
1477out:
1478 RTMsgError("The raw disk vmdk file was not created");
1479 return RT_SUCCESS(vrc) ? 0 : 1;
1480}
1481
1482static int CmdRenameVMDK(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1483{
1484 Utf8Str src;
1485 Utf8Str dst;
1486 /* Parse the arguments. */
1487 for (int i = 0; i < argc; i++)
1488 {
1489 if (strcmp(argv[i], "-from") == 0)
1490 {
1491 if (argc <= i + 1)
1492 {
1493 return errorArgument("Missing argument to '%s'", argv[i]);
1494 }
1495 i++;
1496 src = argv[i];
1497 }
1498 else if (strcmp(argv[i], "-to") == 0)
1499 {
1500 if (argc <= i + 1)
1501 {
1502 return errorArgument("Missing argument to '%s'", argv[i]);
1503 }
1504 i++;
1505 dst = argv[i];
1506 }
1507 else
1508 {
1509 return errorSyntax(USAGE_RENAMEVMDK, "Invalid parameter '%s'", argv[i]);
1510 }
1511 }
1512
1513 if (src.isEmpty())
1514 return errorSyntax(USAGE_RENAMEVMDK, "Mandatory parameter -from missing");
1515 if (dst.isEmpty())
1516 return errorSyntax(USAGE_RENAMEVMDK, "Mandatory parameter -to missing");
1517
1518 PVBOXHDD pDisk = NULL;
1519
1520 PVDINTERFACE pVDIfs = NULL;
1521 VDINTERFACE vdInterfaceError;
1522 VDINTERFACEERROR vdInterfaceErrorCallbacks;
1523 vdInterfaceErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
1524 vdInterfaceErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
1525 vdInterfaceErrorCallbacks.pfnError = handleVDError;
1526 vdInterfaceErrorCallbacks.pfnMessage = handleVDMessage;
1527
1528 int vrc = VDInterfaceAdd(&vdInterfaceError, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1529 &vdInterfaceErrorCallbacks, NULL, &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 VDINTERFACE vdInterfaceError;
1606 VDINTERFACEERROR vdInterfaceErrorCallbacks;
1607 vdInterfaceErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
1608 vdInterfaceErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
1609 vdInterfaceErrorCallbacks.pfnError = handleVDError;
1610 vdInterfaceErrorCallbacks.pfnMessage = handleVDMessage;
1611
1612 int vrc = VDInterfaceAdd(&vdInterfaceError, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1613 &vdInterfaceErrorCallbacks, NULL, &pVDIfs);
1614 AssertRC(vrc);
1615
1616 /** @todo: Support convert to raw for floppy and DVD images too. */
1617 vrc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1618 if (RT_FAILURE(vrc))
1619 {
1620 RTMsgError("Cannot create the virtual disk container: %Rrc", vrc);
1621 return 1;
1622 }
1623
1624 /* Open raw output file. */
1625 RTFILE outFile;
1626 vrc = VINF_SUCCESS;
1627 if (fWriteToStdOut)
1628 outFile = 1;
1629 else
1630 vrc = RTFileOpen(&outFile, dst.c_str(), RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_ALL);
1631 if (RT_FAILURE(vrc))
1632 {
1633 VDCloseAll(pDisk);
1634 RTMsgError("Cannot create destination file \"%s\": %Rrc", dst.c_str(), vrc);
1635 return 1;
1636 }
1637
1638 if (srcformat.isEmpty())
1639 {
1640 char *pszFormat = NULL;
1641 VDTYPE enmType = VDTYPE_INVALID;
1642 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
1643 src.c_str(), &pszFormat, &enmType);
1644 if (RT_FAILURE(vrc) || enmType != VDTYPE_HDD)
1645 {
1646 VDCloseAll(pDisk);
1647 if (!fWriteToStdOut)
1648 {
1649 RTFileClose(outFile);
1650 RTFileDelete(dst.c_str());
1651 }
1652 if (RT_FAILURE(vrc))
1653 RTMsgError("No file format specified and autodetect failed - please specify format: %Rrc", vrc);
1654 else
1655 RTMsgError("Only converting harddisk images is supported");
1656 return 1;
1657 }
1658 srcformat = pszFormat;
1659 RTStrFree(pszFormat);
1660 }
1661 vrc = VDOpen(pDisk, srcformat.c_str(), src.c_str(), VD_OPEN_FLAGS_READONLY, NULL);
1662 if (RT_FAILURE(vrc))
1663 {
1664 VDCloseAll(pDisk);
1665 if (!fWriteToStdOut)
1666 {
1667 RTFileClose(outFile);
1668 RTFileDelete(dst.c_str());
1669 }
1670 RTMsgError("Cannot open the source image: %Rrc", vrc);
1671 return 1;
1672 }
1673
1674 uint64_t cbSize = VDGetSize(pDisk, VD_LAST_IMAGE);
1675 uint64_t offFile = 0;
1676#define RAW_BUFFER_SIZE _128K
1677 size_t cbBuf = RAW_BUFFER_SIZE;
1678 void *pvBuf = RTMemAlloc(cbBuf);
1679 if (pvBuf)
1680 {
1681 RTStrmPrintf(g_pStdErr, "Converting image \"%s\" with size %RU64 bytes (%RU64MB) to raw...\n", src.c_str(), cbSize, (cbSize + _1M - 1) / _1M);
1682 while (offFile < cbSize)
1683 {
1684 size_t cb = (size_t)RT_MIN(cbSize - offFile, cbBuf);
1685 vrc = VDRead(pDisk, offFile, pvBuf, cb);
1686 if (RT_FAILURE(vrc))
1687 break;
1688 vrc = RTFileWrite(outFile, pvBuf, cb, NULL);
1689 if (RT_FAILURE(vrc))
1690 break;
1691 offFile += cb;
1692 }
1693 if (RT_FAILURE(vrc))
1694 {
1695 VDCloseAll(pDisk);
1696 if (!fWriteToStdOut)
1697 {
1698 RTFileClose(outFile);
1699 RTFileDelete(dst.c_str());
1700 }
1701 RTMsgError("Cannot copy image data: %Rrc", vrc);
1702 return 1;
1703 }
1704 }
1705 else
1706 {
1707 vrc = VERR_NO_MEMORY;
1708 VDCloseAll(pDisk);
1709 if (!fWriteToStdOut)
1710 {
1711 RTFileClose(outFile);
1712 RTFileDelete(dst.c_str());
1713 }
1714 RTMsgError("Out of memory allocating read buffer");
1715 return 1;
1716 }
1717
1718 if (!fWriteToStdOut)
1719 RTFileClose(outFile);
1720 VDCloseAll(pDisk);
1721 return 0;
1722}
1723
1724static int CmdConvertHardDisk(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1725{
1726 Utf8Str srcformat;
1727 Utf8Str dstformat;
1728 Utf8Str src;
1729 Utf8Str dst;
1730 int vrc;
1731 PVBOXHDD pSrcDisk = NULL;
1732 PVBOXHDD pDstDisk = NULL;
1733 VDTYPE enmSrcType = VDTYPE_INVALID;
1734
1735 /* Parse the arguments. */
1736 for (int i = 0; i < argc; i++)
1737 {
1738 if (strcmp(argv[i], "-srcformat") == 0)
1739 {
1740 if (argc <= i + 1)
1741 {
1742 return errorArgument("Missing argument to '%s'", argv[i]);
1743 }
1744 i++;
1745 srcformat = argv[i];
1746 }
1747 else if (strcmp(argv[i], "-dstformat") == 0)
1748 {
1749 if (argc <= i + 1)
1750 {
1751 return errorArgument("Missing argument to '%s'", argv[i]);
1752 }
1753 i++;
1754 dstformat = argv[i];
1755 }
1756 else if (src.isEmpty())
1757 {
1758 src = argv[i];
1759 }
1760 else if (dst.isEmpty())
1761 {
1762 dst = argv[i];
1763 }
1764 else
1765 {
1766 return errorSyntax(USAGE_CONVERTHD, "Invalid parameter '%s'", argv[i]);
1767 }
1768 }
1769
1770 if (src.isEmpty())
1771 return errorSyntax(USAGE_CONVERTHD, "Mandatory input image parameter missing");
1772 if (dst.isEmpty())
1773 return errorSyntax(USAGE_CONVERTHD, "Mandatory output image parameter missing");
1774
1775
1776 PVDINTERFACE pVDIfs = NULL;
1777 VDINTERFACE vdInterfaceError;
1778 VDINTERFACEERROR vdInterfaceErrorCallbacks;
1779 vdInterfaceErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
1780 vdInterfaceErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
1781 vdInterfaceErrorCallbacks.pfnError = handleVDError;
1782 vdInterfaceErrorCallbacks.pfnMessage = handleVDMessage;
1783
1784 vrc = VDInterfaceAdd(&vdInterfaceError, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1785 &vdInterfaceErrorCallbacks, NULL, &pVDIfs);
1786 AssertRC(vrc);
1787
1788 do
1789 {
1790 /* Try to determine input image format */
1791 if (srcformat.isEmpty())
1792 {
1793 char *pszFormat = NULL;
1794 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
1795 src.c_str(), &pszFormat, &enmSrcType);
1796 if (RT_FAILURE(vrc))
1797 {
1798 RTMsgError("No file format specified and autodetect failed - please specify format: %Rrc", vrc);
1799 break;
1800 }
1801 srcformat = pszFormat;
1802 RTStrFree(pszFormat);
1803 }
1804
1805 vrc = VDCreate(pVDIfs, enmSrcType, &pSrcDisk);
1806 if (RT_FAILURE(vrc))
1807 {
1808 RTMsgError("Cannot create the source virtual disk container: %Rrc", vrc);
1809 break;
1810 }
1811
1812 /* Open the input image */
1813 vrc = VDOpen(pSrcDisk, srcformat.c_str(), src.c_str(), VD_OPEN_FLAGS_READONLY, NULL);
1814 if (RT_FAILURE(vrc))
1815 {
1816 RTMsgError("Cannot open the source image: %Rrc", vrc);
1817 break;
1818 }
1819
1820 /* Output format defaults to VDI */
1821 if (dstformat.isEmpty())
1822 dstformat = "VDI";
1823
1824 vrc = VDCreate(pVDIfs, enmSrcType, &pDstDisk);
1825 if (RT_FAILURE(vrc))
1826 {
1827 RTMsgError("Cannot create the destination virtual disk container: %Rrc", vrc);
1828 break;
1829 }
1830
1831 uint64_t cbSize = VDGetSize(pSrcDisk, VD_LAST_IMAGE);
1832 RTStrmPrintf(g_pStdErr, "Converting image \"%s\" with size %RU64 bytes (%RU64MB)...\n", src.c_str(), cbSize, (cbSize + _1M - 1) / _1M);
1833
1834 /* Create the output image */
1835 vrc = VDCopy(pSrcDisk, VD_LAST_IMAGE, pDstDisk, dstformat.c_str(),
1836 dst.c_str(), false, 0, VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED,
1837 NULL, VD_OPEN_FLAGS_NORMAL, NULL, NULL, NULL);
1838 if (RT_FAILURE(vrc))
1839 {
1840 RTMsgError("Cannot copy the image: %Rrc", vrc);
1841 break;
1842 }
1843 }
1844 while (0);
1845 if (pDstDisk)
1846 VDCloseAll(pDstDisk);
1847 if (pSrcDisk)
1848 VDCloseAll(pSrcDisk);
1849
1850 return RT_SUCCESS(vrc) ? 0 : 1;
1851}
1852
1853/**
1854 * Unloads the necessary driver.
1855 *
1856 * @returns VBox status code
1857 */
1858int CmdModUninstall(void)
1859{
1860 int rc;
1861
1862 rc = SUPR3Uninstall();
1863 if (RT_SUCCESS(rc))
1864 return 0;
1865 if (rc == VERR_NOT_IMPLEMENTED)
1866 return 0;
1867 return E_FAIL;
1868}
1869
1870/**
1871 * Loads the necessary driver.
1872 *
1873 * @returns VBox status code
1874 */
1875int CmdModInstall(void)
1876{
1877 int rc;
1878
1879 rc = SUPR3Install();
1880 if (RT_SUCCESS(rc))
1881 return 0;
1882 if (rc == VERR_NOT_IMPLEMENTED)
1883 return 0;
1884 return E_FAIL;
1885}
1886
1887int CmdDebugLog(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
1888{
1889 /*
1890 * The first parameter is the name or UUID of a VM with a direct session
1891 * that we wish to open.
1892 */
1893 if (argc < 1)
1894 return errorSyntax(USAGE_DEBUGLOG, "Missing VM name/UUID");
1895
1896 ComPtr<IMachine> ptrMachine;
1897 HRESULT rc;
1898 CHECK_ERROR_RET(aVirtualBox, FindMachine(Bstr(argv[0]).raw(),
1899 ptrMachine.asOutParam()), 1);
1900
1901 CHECK_ERROR_RET(ptrMachine, LockMachine(aSession, LockType_Shared), 1);
1902
1903 /*
1904 * Get the debugger interface.
1905 */
1906 ComPtr<IConsole> ptrConsole;
1907 CHECK_ERROR_RET(aSession, COMGETTER(Console)(ptrConsole.asOutParam()), 1);
1908
1909 ComPtr<IMachineDebugger> ptrDebugger;
1910 CHECK_ERROR_RET(ptrConsole, COMGETTER(Debugger)(ptrDebugger.asOutParam()), 1);
1911
1912 /*
1913 * Parse the command.
1914 */
1915 bool fEnablePresent = false;
1916 bool fEnable = false;
1917 bool fFlagsPresent = false;
1918 iprt::MiniString strFlags;
1919 bool fGroupsPresent = false;
1920 iprt::MiniString strGroups;
1921 bool fDestsPresent = false;
1922 iprt::MiniString strDests;
1923
1924 static const RTGETOPTDEF s_aOptions[] =
1925 {
1926 { "--disable", 'E', RTGETOPT_REQ_NOTHING },
1927 { "--enable", 'e', RTGETOPT_REQ_NOTHING },
1928 { "--flags", 'f', RTGETOPT_REQ_STRING },
1929 { "--groups", 'g', RTGETOPT_REQ_STRING },
1930 { "--destinations", 'd', RTGETOPT_REQ_STRING }
1931 };
1932
1933 int ch;
1934 RTGETOPTUNION ValueUnion;
1935 RTGETOPTSTATE GetState;
1936 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
1937 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1938 {
1939 switch (ch)
1940 {
1941 case 'e':
1942 fEnablePresent = true;
1943 fEnable = true;
1944 break;
1945
1946 case 'E':
1947 fEnablePresent = true;
1948 fEnable = false;
1949 break;
1950
1951 case 'f':
1952 fFlagsPresent = true;
1953 if (*ValueUnion.psz)
1954 {
1955 if (strFlags.isNotEmpty())
1956 strFlags.append(' ');
1957 strFlags.append(ValueUnion.psz);
1958 }
1959 break;
1960
1961 case 'g':
1962 fGroupsPresent = true;
1963 if (*ValueUnion.psz)
1964 {
1965 if (strGroups.isNotEmpty())
1966 strGroups.append(' ');
1967 strGroups.append(ValueUnion.psz);
1968 }
1969 break;
1970
1971 case 'd':
1972 fDestsPresent = true;
1973 if (*ValueUnion.psz)
1974 {
1975 if (strDests.isNotEmpty())
1976 strDests.append(' ');
1977 strDests.append(ValueUnion.psz);
1978 }
1979 break;
1980
1981 default:
1982 return errorGetOpt(USAGE_DEBUGLOG , ch, &ValueUnion);
1983 }
1984 }
1985
1986 /*
1987 * Do the job.
1988 */
1989 if (fEnablePresent && !fEnable)
1990 CHECK_ERROR_RET(ptrDebugger, COMSETTER(LogEnabled)(FALSE), 1);
1991
1992 /** @todo flags, groups destination. */
1993 if (fFlagsPresent || fGroupsPresent || fDestsPresent)
1994 RTMsgWarning("One or more of the requested features are not implemented! Feel free to do this.");
1995
1996 if (fEnablePresent && fEnable)
1997 CHECK_ERROR_RET(ptrDebugger, COMSETTER(LogEnabled)(TRUE), 1);
1998 return 0;
1999}
2000
2001/**
2002 * Generate a SHA-256 password hash
2003 */
2004int CmdGeneratePasswordHash(int argc, char **argv, ComPtr<IVirtualBox> aVirtualBox, ComPtr<ISession> aSession)
2005{
2006 /* one parameter, the password to hash */
2007 if (argc != 1)
2008 return errorSyntax(USAGE_PASSWORDHASH, "password to hash required");
2009
2010 uint8_t abDigest[RTSHA256_HASH_SIZE];
2011 RTSha256(argv[0], strlen(argv[0]), abDigest);
2012 char pszDigest[RTSHA256_DIGEST_LEN + 1];
2013 RTSha256ToString(abDigest, pszDigest, sizeof(pszDigest));
2014 RTPrintf("Password hash: %s\n", pszDigest);
2015
2016 return 0;
2017}
2018
2019/**
2020 * Wrapper for handling internal commands
2021 */
2022int handleInternalCommands(HandlerArg *a)
2023{
2024 g_fInternalMode = true;
2025
2026 /* at least a command is required */
2027 if (a->argc < 1)
2028 return errorSyntax(USAGE_ALL, "Command missing");
2029
2030 /*
2031 * The 'string switch' on command name.
2032 */
2033 const char *pszCmd = a->argv[0];
2034 if (!strcmp(pszCmd, "loadsyms"))
2035 return CmdLoadSyms(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2036 //if (!strcmp(pszCmd, "unloadsyms"))
2037 // return CmdUnloadSyms(argc - 1 , &a->argv[1]);
2038 if (!strcmp(pszCmd, "sethduuid") || !strcmp(pszCmd, "sethdparentuuid"))
2039 return CmdSetHDUUID(a->argc, &a->argv[0], a->virtualBox, a->session);
2040 if (!strcmp(pszCmd, "dumphdinfo"))
2041 return CmdDumpHDInfo(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2042 if (!strcmp(pszCmd, "listpartitions"))
2043 return CmdListPartitions(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2044 if (!strcmp(pszCmd, "createrawvmdk"))
2045 return CmdCreateRawVMDK(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2046 if (!strcmp(pszCmd, "renamevmdk"))
2047 return CmdRenameVMDK(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2048 if (!strcmp(pszCmd, "converttoraw"))
2049 return CmdConvertToRaw(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2050 if (!strcmp(pszCmd, "converthd"))
2051 return CmdConvertHardDisk(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2052 if (!strcmp(pszCmd, "modinstall"))
2053 return CmdModInstall();
2054 if (!strcmp(pszCmd, "moduninstall"))
2055 return CmdModUninstall();
2056 if (!strcmp(pszCmd, "debuglog"))
2057 return CmdDebugLog(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2058 if (!strcmp(pszCmd, "passwordhash"))
2059 return CmdGeneratePasswordHash(a->argc - 1, &a->argv[1], a->virtualBox, a->session);
2060
2061 /* default: */
2062 return errorSyntax(USAGE_ALL, "Invalid command '%s'", a->argv[0]);
2063}
2064
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use