VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp@ 81784

Last change on this file since 81784 was 80585, checked in by vboxsync, 5 years ago

Runtime: Some renaming to stay consistent (*Get* always returns what is asked for while *Query* returns a status code and where to store the value on success is given as a pointer)

  • RTVfsFileGetSize -> RTVfsFileQuerySize
  • RTFileQuerySize -> RTFileQuerySizeByPath
  • RTFileGetSize -> RTFileQuerySize
  • RTFileGetSizeMaxEx -> RTFileQuerySizeMaxEx
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 92.2 KB
Line 
1/* $Id: VBoxManageDisk.cpp 80585 2019-09-04 14:05:50Z vboxsync $ */
2/** @file
3 * VBoxManage - The disk/medium related commands.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#ifndef VBOX_ONLY_DOCS
19
20
21/*********************************************************************************************************************************
22* Header Files *
23*********************************************************************************************************************************/
24#include <VBox/com/com.h>
25#include <VBox/com/array.h>
26#include <VBox/com/ErrorInfo.h>
27#include <VBox/com/errorprint.h>
28#include <VBox/com/VirtualBox.h>
29
30#include <iprt/asm.h>
31#include <iprt/file.h>
32#include <iprt/path.h>
33#include <iprt/param.h>
34#include <iprt/stream.h>
35#include <iprt/string.h>
36#include <iprt/ctype.h>
37#include <iprt/getopt.h>
38#include <VBox/log.h>
39#include <VBox/vd.h>
40
41#include "VBoxManage.h"
42using namespace com;
43
44/** Medium category. */
45typedef enum MEDIUMCATEGORY
46{
47 MEDIUMCATEGORY_NONE = 0,
48 MEDIUMCATEGORY_DISK,
49 MEDIUMCATEGORY_DVD,
50 MEDIUMCATEGORY_FLOPPY
51} MEDIUMCATEGORY;
52
53
54
55// funcs
56///////////////////////////////////////////////////////////////////////////////
57
58
59static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va)
60{
61 RT_NOREF(pvUser);
62 RTMsgErrorV(pszFormat, va);
63 RTMsgError("Error code %Rrc at %s(%u) in function %s", rc, RT_SRC_POS_ARGS);
64}
65
66static int parseMediumVariant(const char *psz, MediumVariant_T *pMediumVariant)
67{
68 int rc = VINF_SUCCESS;
69 unsigned uMediumVariant = (unsigned)(*pMediumVariant);
70 while (psz && *psz && RT_SUCCESS(rc))
71 {
72 size_t len;
73 const char *pszComma = strchr(psz, ',');
74 if (pszComma)
75 len = pszComma - psz;
76 else
77 len = strlen(psz);
78 if (len > 0)
79 {
80 // Parsing is intentionally inconsistent: "standard" resets the
81 // variant, whereas the other flags are cumulative.
82 if (!RTStrNICmp(psz, "standard", len))
83 uMediumVariant = MediumVariant_Standard;
84 else if ( !RTStrNICmp(psz, "fixed", len)
85 || !RTStrNICmp(psz, "static", len))
86 uMediumVariant |= MediumVariant_Fixed;
87 else if (!RTStrNICmp(psz, "Diff", len))
88 uMediumVariant |= MediumVariant_Diff;
89 else if (!RTStrNICmp(psz, "split2g", len))
90 uMediumVariant |= MediumVariant_VmdkSplit2G;
91 else if ( !RTStrNICmp(psz, "stream", len)
92 || !RTStrNICmp(psz, "streamoptimized", len))
93 uMediumVariant |= MediumVariant_VmdkStreamOptimized;
94 else if (!RTStrNICmp(psz, "esx", len))
95 uMediumVariant |= MediumVariant_VmdkESX;
96 else if (!RTStrNICmp(psz, "formatted", len))
97 uMediumVariant |= MediumVariant_Formatted;
98 else
99 rc = VERR_PARSE_ERROR;
100 }
101 if (pszComma)
102 psz += len + 1;
103 else
104 psz += len;
105 }
106
107 if (RT_SUCCESS(rc))
108 *pMediumVariant = (MediumVariant_T)uMediumVariant;
109 return rc;
110}
111
112int parseMediumType(const char *psz, MediumType_T *penmMediumType)
113{
114 int rc = VINF_SUCCESS;
115 MediumType_T enmMediumType = MediumType_Normal;
116 if (!RTStrICmp(psz, "normal"))
117 enmMediumType = MediumType_Normal;
118 else if (!RTStrICmp(psz, "immutable"))
119 enmMediumType = MediumType_Immutable;
120 else if (!RTStrICmp(psz, "writethrough"))
121 enmMediumType = MediumType_Writethrough;
122 else if (!RTStrICmp(psz, "shareable"))
123 enmMediumType = MediumType_Shareable;
124 else if (!RTStrICmp(psz, "readonly"))
125 enmMediumType = MediumType_Readonly;
126 else if (!RTStrICmp(psz, "multiattach"))
127 enmMediumType = MediumType_MultiAttach;
128 else
129 rc = VERR_PARSE_ERROR;
130
131 if (RT_SUCCESS(rc))
132 *penmMediumType = enmMediumType;
133 return rc;
134}
135
136/** @todo move this into getopt, as getting bool values is generic */
137int parseBool(const char *psz, bool *pb)
138{
139 int rc = VINF_SUCCESS;
140 if ( !RTStrICmp(psz, "on")
141 || !RTStrICmp(psz, "yes")
142 || !RTStrICmp(psz, "true")
143 || !RTStrICmp(psz, "1")
144 || !RTStrICmp(psz, "enable")
145 || !RTStrICmp(psz, "enabled"))
146 {
147 *pb = true;
148 }
149 else if ( !RTStrICmp(psz, "off")
150 || !RTStrICmp(psz, "no")
151 || !RTStrICmp(psz, "false")
152 || !RTStrICmp(psz, "0")
153 || !RTStrICmp(psz, "disable")
154 || !RTStrICmp(psz, "disabled"))
155 {
156 *pb = false;
157 }
158 else
159 rc = VERR_PARSE_ERROR;
160
161 return rc;
162}
163
164HRESULT openMedium(HandlerArg *a, const char *pszFilenameOrUuid,
165 DeviceType_T enmDevType, AccessMode_T enmAccessMode,
166 ComPtr<IMedium> &pMedium, bool fForceNewUuidOnOpen,
167 bool fSilent)
168{
169 HRESULT rc;
170 Guid id(pszFilenameOrUuid);
171 char szFilenameAbs[RTPATH_MAX] = "";
172
173 /* If it is no UUID, convert the filename to an absolute one. */
174 if (!id.isValid())
175 {
176 int irc = RTPathAbs(pszFilenameOrUuid, szFilenameAbs, sizeof(szFilenameAbs));
177 if (RT_FAILURE(irc))
178 {
179 if (!fSilent)
180 RTMsgError("Cannot convert filename \"%s\" to absolute path", pszFilenameOrUuid);
181 return E_FAIL;
182 }
183 pszFilenameOrUuid = szFilenameAbs;
184 }
185
186 if (!fSilent)
187 CHECK_ERROR(a->virtualBox, OpenMedium(Bstr(pszFilenameOrUuid).raw(),
188 enmDevType,
189 enmAccessMode,
190 fForceNewUuidOnOpen,
191 pMedium.asOutParam()));
192 else
193 rc = a->virtualBox->OpenMedium(Bstr(pszFilenameOrUuid).raw(),
194 enmDevType,
195 enmAccessMode,
196 fForceNewUuidOnOpen,
197 pMedium.asOutParam());
198
199 return rc;
200}
201
202static HRESULT createMedium(HandlerArg *a, const char *pszFormat,
203 const char *pszFilename, DeviceType_T enmDevType,
204 AccessMode_T enmAccessMode, ComPtr<IMedium> &pMedium)
205{
206 HRESULT rc;
207 char szFilenameAbs[RTPATH_MAX] = "";
208
209 /** @todo laziness shortcut. should really check the MediumFormatCapabilities */
210 if (RTStrICmp(pszFormat, "iSCSI"))
211 {
212 int irc = RTPathAbs(pszFilename, szFilenameAbs, sizeof(szFilenameAbs));
213 if (RT_FAILURE(irc))
214 {
215 RTMsgError("Cannot convert filename \"%s\" to absolute path", pszFilename);
216 return E_FAIL;
217 }
218 pszFilename = szFilenameAbs;
219 }
220
221 CHECK_ERROR(a->virtualBox, CreateMedium(Bstr(pszFormat).raw(),
222 Bstr(pszFilename).raw(),
223 enmAccessMode,
224 enmDevType,
225 pMedium.asOutParam()));
226 return rc;
227}
228
229static const RTGETOPTDEF g_aCreateMediumOptions[] =
230{
231 { "disk", 'H', RTGETOPT_REQ_NOTHING },
232 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
233 { "floppy", 'L', RTGETOPT_REQ_NOTHING },
234 { "--filename", 'f', RTGETOPT_REQ_STRING },
235 { "-filename", 'f', RTGETOPT_REQ_STRING }, // deprecated
236 { "--diffparent", 'd', RTGETOPT_REQ_STRING },
237 { "--size", 's', RTGETOPT_REQ_UINT64 },
238 { "-size", 's', RTGETOPT_REQ_UINT64 }, // deprecated
239 { "--sizebyte", 'S', RTGETOPT_REQ_UINT64 },
240 { "--format", 'o', RTGETOPT_REQ_STRING },
241 { "-format", 'o', RTGETOPT_REQ_STRING }, // deprecated
242 { "--static", 'F', RTGETOPT_REQ_NOTHING },
243 { "-static", 'F', RTGETOPT_REQ_NOTHING }, // deprecated
244 { "--variant", 'm', RTGETOPT_REQ_STRING },
245 { "-variant", 'm', RTGETOPT_REQ_STRING }, // deprecated
246 { "--property", 'p', RTGETOPT_REQ_STRING }
247};
248
249RTEXITCODE handleCreateMedium(HandlerArg *a)
250{
251 HRESULT rc;
252 int vrc;
253 const char *filename = NULL;
254 const char *diffparent = NULL;
255 uint64_t size = 0;
256 typedef struct MEDIUMPROPERTY_LIST
257 {
258 struct MEDIUMPROPERTY_LIST *next;
259 const char *key;
260 const char *value;
261 } MEDIUMPROPERTY, *PMEDIUMPROPERTY;
262 PMEDIUMPROPERTY pMediumProps = NULL;
263 enum
264 {
265 CMD_NONE,
266 CMD_DISK,
267 CMD_DVD,
268 CMD_FLOPPY
269 } cmd = CMD_NONE;
270 const char *format = NULL;
271 bool fBase = true;
272 MediumVariant_T enmMediumVariant = MediumVariant_Standard;
273
274 int c;
275 RTGETOPTUNION ValueUnion;
276 RTGETOPTSTATE GetState;
277 // start at 0 because main() has hacked both the argc and argv given to us
278 RTGetOptInit(&GetState, a->argc, a->argv, g_aCreateMediumOptions, RT_ELEMENTS(g_aCreateMediumOptions),
279 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
280 while ((c = RTGetOpt(&GetState, &ValueUnion)))
281 {
282 switch (c)
283 {
284 case 'H': // disk
285 if (cmd != CMD_NONE)
286 return errorSyntax(USAGE_CREATEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
287 cmd = CMD_DISK;
288 break;
289
290 case 'D': // DVD
291 if (cmd != CMD_NONE)
292 return errorSyntax(USAGE_CREATEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
293 cmd = CMD_DVD;
294 break;
295
296 case 'L': // floppy
297 if (cmd != CMD_NONE)
298 return errorSyntax(USAGE_CREATEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
299 cmd = CMD_FLOPPY;
300 break;
301
302 case 'f': // --filename
303 filename = ValueUnion.psz;
304 break;
305
306 case 'd': // --diffparent
307 diffparent = ValueUnion.psz;
308 fBase = false;
309 break;
310
311 case 's': // --size
312 size = ValueUnion.u64 * _1M;
313 break;
314
315 case 'S': // --sizebyte
316 size = ValueUnion.u64;
317 break;
318
319 case 'o': // --format
320 format = ValueUnion.psz;
321 break;
322
323 case 'p': // --property
324 {
325 /* allocate property kvp, parse, and append to end of singly linked list */
326# define PROP_MAXLEN 256
327 PMEDIUMPROPERTY pNewProp = (PMEDIUMPROPERTY)RTMemAlloc(sizeof(MEDIUMPROPERTY));
328 if (!pNewProp)
329 return errorArgument("Can't allocate memory for property '%s'", ValueUnion.psz);
330 size_t cchKvp = RTStrNLen(ValueUnion.psz, PROP_MAXLEN);
331 char *pszEqual = (char *)memchr(ValueUnion.psz, '=', cchKvp);
332 if (pszEqual)
333 {
334 *pszEqual = '\0'; /* Warning! Modifies argument string. */
335 pNewProp->next = NULL;
336 pNewProp->key = (char *)ValueUnion.psz;
337 pNewProp->value = pszEqual + 1;
338 }
339 if (pMediumProps)
340 {
341 PMEDIUMPROPERTY pProp;
342 for (pProp = pMediumProps; pProp->next; pProp = pProp->next)
343 continue;
344 pProp->next = pNewProp;
345 }
346 else
347 pMediumProps = pNewProp;
348 break;
349 }
350
351 case 'F': // --static ("fixed"/"flat")
352 {
353 unsigned uMediumVariant = (unsigned)enmMediumVariant;
354 uMediumVariant |= MediumVariant_Fixed;
355 enmMediumVariant = (MediumVariant_T)uMediumVariant;
356 break;
357 }
358
359 case 'm': // --variant
360 vrc = parseMediumVariant(ValueUnion.psz, &enmMediumVariant);
361 if (RT_FAILURE(vrc))
362 return errorArgument("Invalid medium variant '%s'", ValueUnion.psz);
363 break;
364
365 case VINF_GETOPT_NOT_OPTION:
366 return errorSyntax(USAGE_CREATEMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
367
368 default:
369 if (c > 0)
370 {
371 if (RT_C_IS_PRINT(c))
372 return errorSyntax(USAGE_CREATEMEDIUM, "Invalid option -%c", c);
373 else
374 return errorSyntax(USAGE_CREATEMEDIUM, "Invalid option case %i", c);
375 }
376 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
377 return errorSyntax(USAGE_CREATEMEDIUM, "unknown option: %s\n", ValueUnion.psz);
378 else if (ValueUnion.pDef)
379 return errorSyntax(USAGE_CREATEMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
380 else
381 return errorSyntax(USAGE_CREATEMEDIUM, "error: %Rrs", c);
382 }
383 }
384
385 /* check the outcome */
386 if (cmd == CMD_NONE)
387 cmd = CMD_DISK;
388 ComPtr<IMedium> pParentMedium;
389 if (fBase)
390 {
391 if ( !filename
392 || !*filename
393 || size == 0)
394 return errorSyntax(USAGE_CREATEMEDIUM, "Parameters --filename and --size are required");
395 if (!format || !*format)
396 {
397 if (cmd == CMD_DISK)
398 format = "VDI";
399 else if (cmd == CMD_DVD || cmd == CMD_FLOPPY)
400 {
401 format = "RAW";
402 unsigned uMediumVariant = (unsigned)enmMediumVariant;
403 uMediumVariant |= MediumVariant_Fixed;
404 enmMediumVariant = (MediumVariant_T)uMediumVariant;
405 }
406 }
407 }
408 else
409 {
410 if ( !filename
411 || !*filename)
412 return errorSyntax(USAGE_CREATEMEDIUM, "Parameters --filename is required");
413 size = 0;
414 if (cmd != CMD_DISK)
415 return errorSyntax(USAGE_CREATEMEDIUM, "Creating a differencing medium is only supported for hard disks");
416 enmMediumVariant = MediumVariant_Diff;
417 if (!format || !*format)
418 {
419 const char *pszExt = RTPathSuffix(filename);
420 /* Skip over . if there is an extension. */
421 if (pszExt)
422 pszExt++;
423 if (!pszExt || !*pszExt)
424 format = "VDI";
425 else
426 format = pszExt;
427 }
428 rc = openMedium(a, diffparent, DeviceType_HardDisk,
429 AccessMode_ReadWrite, pParentMedium,
430 false /* fForceNewUuidOnOpen */, false /* fSilent */);
431 if (FAILED(rc))
432 return RTEXITCODE_FAILURE;
433 if (pParentMedium.isNull())
434 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid parent hard disk reference, avoiding crash");
435 MediumState_T state;
436 CHECK_ERROR(pParentMedium, COMGETTER(State)(&state));
437 if (FAILED(rc))
438 return RTEXITCODE_FAILURE;
439 if (state == MediumState_Inaccessible)
440 {
441 CHECK_ERROR(pParentMedium, RefreshState(&state));
442 if (FAILED(rc))
443 return RTEXITCODE_FAILURE;
444 }
445 }
446 /* check for filename extension */
447 /** @todo use IMediumFormat to cover all extensions generically */
448 Utf8Str strName(filename);
449 if (!RTPathHasSuffix(strName.c_str()))
450 {
451 Utf8Str strFormat(format);
452 if (cmd == CMD_DISK)
453 {
454 if (strFormat.compare("vmdk", RTCString::CaseInsensitive) == 0)
455 strName.append(".vmdk");
456 else if (strFormat.compare("vhd", RTCString::CaseInsensitive) == 0)
457 strName.append(".vhd");
458 else
459 strName.append(".vdi");
460 }
461 else if (cmd == CMD_DVD)
462 strName.append(".iso");
463 else if (cmd == CMD_FLOPPY)
464 strName.append(".img");
465 filename = strName.c_str();
466 }
467
468 ComPtr<IMedium> pMedium;
469 if (cmd == CMD_DISK)
470 rc = createMedium(a, format, filename, DeviceType_HardDisk,
471 AccessMode_ReadWrite, pMedium);
472 else if (cmd == CMD_DVD)
473 rc = createMedium(a, format, filename, DeviceType_DVD,
474 AccessMode_ReadOnly, pMedium);
475 else if (cmd == CMD_FLOPPY)
476 rc = createMedium(a, format, filename, DeviceType_Floppy,
477 AccessMode_ReadWrite, pMedium);
478 else
479 rc = E_INVALIDARG; /* cannot happen but make gcc happy */
480
481
482 if (SUCCEEDED(rc) && pMedium)
483 {
484 if (pMediumProps)
485 for (PMEDIUMPROPERTY pProp = pMediumProps; pProp;)
486 {
487 CHECK_ERROR(pMedium, SetProperty(Bstr(pProp->key).raw(), Bstr(pProp->value).raw()));
488 PMEDIUMPROPERTY next = pProp->next;
489 RTMemFree(pProp);
490 pProp = next;
491 }
492
493 ComPtr<IProgress> pProgress;
494 com::SafeArray<MediumVariant_T> l_variants(sizeof(MediumVariant_T)*8);
495
496 for (ULONG i = 0; i < l_variants.size(); ++i)
497 {
498 ULONG temp = enmMediumVariant;
499 temp &= 1<<i;
500 l_variants [i] = (MediumVariant_T)temp;
501 }
502
503 if (fBase)
504 CHECK_ERROR(pMedium, CreateBaseStorage(size, ComSafeArrayAsInParam(l_variants), pProgress.asOutParam()));
505 else
506 CHECK_ERROR(pParentMedium, CreateDiffStorage(pMedium, ComSafeArrayAsInParam(l_variants), pProgress.asOutParam()));
507 if (SUCCEEDED(rc) && pProgress)
508 {
509 rc = showProgress(pProgress);
510 CHECK_PROGRESS_ERROR(pProgress, ("Failed to create medium"));
511 }
512 }
513
514 if (SUCCEEDED(rc) && pMedium)
515 {
516 Bstr uuid;
517 CHECK_ERROR(pMedium, COMGETTER(Id)(uuid.asOutParam()));
518 RTPrintf("Medium created. UUID: %s\n", Utf8Str(uuid).c_str());
519
520 //CHECK_ERROR(pMedium, Close());
521 }
522 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
523}
524
525static const RTGETOPTDEF g_aModifyMediumOptions[] =
526{
527 { "disk", 'H', RTGETOPT_REQ_NOTHING },
528 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
529 { "floppy", 'L', RTGETOPT_REQ_NOTHING },
530 { "--type", 't', RTGETOPT_REQ_STRING },
531 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
532 { "settype", 't', RTGETOPT_REQ_STRING }, // deprecated
533 { "--autoreset", 'z', RTGETOPT_REQ_STRING },
534 { "-autoreset", 'z', RTGETOPT_REQ_STRING }, // deprecated
535 { "autoreset", 'z', RTGETOPT_REQ_STRING }, // deprecated
536 { "--property", 'p', RTGETOPT_REQ_STRING },
537 { "--compact", 'c', RTGETOPT_REQ_NOTHING },
538 { "-compact", 'c', RTGETOPT_REQ_NOTHING }, // deprecated
539 { "compact", 'c', RTGETOPT_REQ_NOTHING }, // deprecated
540 { "--resize", 'r', RTGETOPT_REQ_UINT64 },
541 { "--resizebyte", 'R', RTGETOPT_REQ_UINT64 },
542 { "--move", 'm', RTGETOPT_REQ_STRING },
543 { "--setlocation", 'l', RTGETOPT_REQ_STRING },
544 { "--description", 'd', RTGETOPT_REQ_STRING }
545};
546
547RTEXITCODE handleModifyMedium(HandlerArg *a)
548{
549 HRESULT rc;
550 int vrc;
551 enum {
552 CMD_NONE,
553 CMD_DISK,
554 CMD_DVD,
555 CMD_FLOPPY
556 } cmd = CMD_NONE;
557 ComPtr<IMedium> pMedium;
558 MediumType_T enmMediumType = MediumType_Normal; /* Shut up MSC */
559 bool AutoReset = false;
560 SafeArray<BSTR> mediumPropNames;
561 SafeArray<BSTR> mediumPropValues;
562 bool fModifyMediumType = false;
563 bool fModifyAutoReset = false;
564 bool fModifyProperties = false;
565 bool fModifyCompact = false;
566 bool fModifyResize = false;
567 bool fModifyResizeMB = false;
568 bool fMoveMedium = false;
569 bool fModifyDescription = false;
570 bool fSetNewLocation = false;
571 uint64_t cbResize = 0;
572 const char *pszFilenameOrUuid = NULL;
573 char *pszNewLocation = NULL;
574
575 int c;
576 RTGETOPTUNION ValueUnion;
577 RTGETOPTSTATE GetState;
578 // start at 0 because main() has hacked both the argc and argv given to us
579 RTGetOptInit(&GetState, a->argc, a->argv, g_aModifyMediumOptions, RT_ELEMENTS(g_aModifyMediumOptions),
580 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
581 while ((c = RTGetOpt(&GetState, &ValueUnion)))
582 {
583 switch (c)
584 {
585 case 'H': // disk
586 if (cmd != CMD_NONE)
587 return errorSyntax(USAGE_MODIFYMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
588 cmd = CMD_DISK;
589 break;
590
591 case 'D': // DVD
592 if (cmd != CMD_NONE)
593 return errorSyntax(USAGE_MODIFYMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
594 cmd = CMD_DVD;
595 break;
596
597 case 'L': // floppy
598 if (cmd != CMD_NONE)
599 return errorSyntax(USAGE_MODIFYMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
600 cmd = CMD_FLOPPY;
601 break;
602
603 case 't': // --type
604 vrc = parseMediumType(ValueUnion.psz, &enmMediumType);
605 if (RT_FAILURE(vrc))
606 return errorArgument("Invalid medium type '%s'", ValueUnion.psz);
607 fModifyMediumType = true;
608 break;
609
610 case 'z': // --autoreset
611 vrc = parseBool(ValueUnion.psz, &AutoReset);
612 if (RT_FAILURE(vrc))
613 return errorArgument("Invalid autoreset parameter '%s'", ValueUnion.psz);
614 fModifyAutoReset = true;
615 break;
616
617 case 'p': // --property
618 {
619 /* Parse 'name=value' */
620 char *pszProperty = RTStrDup(ValueUnion.psz);
621 if (pszProperty)
622 {
623 char *pDelimiter = strchr(pszProperty, '=');
624 if (pDelimiter)
625 {
626 *pDelimiter = '\0';
627
628 Bstr bstrName(pszProperty);
629 Bstr bstrValue(&pDelimiter[1]);
630 bstrName.detachTo(mediumPropNames.appendedRaw());
631 bstrValue.detachTo(mediumPropValues.appendedRaw());
632 fModifyProperties = true;
633 }
634 else
635 {
636 errorArgument("Invalid --property argument '%s'", ValueUnion.psz);
637 rc = E_FAIL;
638 }
639 RTStrFree(pszProperty);
640 }
641 else
642 {
643 RTStrmPrintf(g_pStdErr, "Error: Failed to allocate memory for medium property '%s'\n", ValueUnion.psz);
644 rc = E_FAIL;
645 }
646 break;
647 }
648
649 case 'c': // --compact
650 fModifyCompact = true;
651 break;
652
653 case 'r': // --resize
654 cbResize = ValueUnion.u64 * _1M;
655 fModifyResize = true;
656 fModifyResizeMB = true; // do sanity check!
657 break;
658
659 case 'R': // --resizebyte
660 cbResize = ValueUnion.u64;
661 fModifyResize = true;
662 break;
663
664 case 'm': // --move
665 /* Get a new location */
666 pszNewLocation = RTPathAbsDup(ValueUnion.psz);
667 fMoveMedium = true;
668 break;
669
670 case 'l': // --setlocation
671 /* Get a new location */
672 pszNewLocation = RTPathAbsDup(ValueUnion.psz);
673 fSetNewLocation = true;
674 break;
675
676 case 'd': // --description
677 /* Get a new description */
678 pszNewLocation = RTStrDup(ValueUnion.psz);
679 fModifyDescription = true;
680 break;
681
682 case VINF_GETOPT_NOT_OPTION:
683 if (!pszFilenameOrUuid)
684 pszFilenameOrUuid = ValueUnion.psz;
685 else
686 return errorSyntax(USAGE_MODIFYMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
687 break;
688
689 default:
690 if (c > 0)
691 {
692 if (RT_C_IS_PRINT(c))
693 return errorSyntax(USAGE_MODIFYMEDIUM, "Invalid option -%c", c);
694 else
695 return errorSyntax(USAGE_MODIFYMEDIUM, "Invalid option case %i", c);
696 }
697 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
698 return errorSyntax(USAGE_MODIFYMEDIUM, "unknown option: %s\n", ValueUnion.psz);
699 else if (ValueUnion.pDef)
700 return errorSyntax(USAGE_MODIFYMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
701 else
702 return errorSyntax(USAGE_MODIFYMEDIUM, "error: %Rrs", c);
703 }
704 }
705
706 if (cmd == CMD_NONE)
707 cmd = CMD_DISK;
708
709 if (!pszFilenameOrUuid)
710 return errorSyntax(USAGE_MODIFYMEDIUM, "Medium name or UUID required");
711
712 if (!fModifyMediumType
713 && !fModifyAutoReset
714 && !fModifyProperties
715 && !fModifyCompact
716 && !fModifyResize
717 && !fMoveMedium
718 && !fSetNewLocation
719 && !fModifyDescription
720 )
721 return errorSyntax(USAGE_MODIFYMEDIUM, "No operation specified");
722
723 /* Always open the medium if necessary, there is no other way. */
724 if (cmd == CMD_DISK)
725 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
726 AccessMode_ReadWrite, pMedium,
727 false /* fForceNewUuidOnOpen */, false /* fSilent */);
728 else if (cmd == CMD_DVD)
729 rc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD,
730 AccessMode_ReadOnly, pMedium,
731 false /* fForceNewUuidOnOpen */, false /* fSilent */);
732 else if (cmd == CMD_FLOPPY)
733 rc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy,
734 AccessMode_ReadWrite, pMedium,
735 false /* fForceNewUuidOnOpen */, false /* fSilent */);
736 else
737 rc = E_INVALIDARG; /* cannot happen but make gcc happy */
738 if (FAILED(rc))
739 return RTEXITCODE_FAILURE;
740 if (pMedium.isNull())
741 {
742 RTMsgError("Invalid medium reference, avoiding crash");
743 return RTEXITCODE_FAILURE;
744 }
745
746 if ( fModifyResize
747 && fModifyResizeMB)
748 {
749 // Sanity check
750 //
751 // In general users should know what they do but in this case users have no
752 // alternative to VBoxManage. If happens that one wants to resize the disk
753 // and uses --resize and does not consider that this parameter expects the
754 // new medium size in MB not Byte. If the operation is started and then
755 // aborted by the user, the result is most likely a medium which doesn't
756 // work anymore.
757 MediumState_T state;
758 pMedium->RefreshState(&state);
759 LONG64 logicalSize;
760 pMedium->COMGETTER(LogicalSize)(&logicalSize);
761 if (cbResize > (uint64_t)logicalSize * 1000)
762 {
763 RTMsgError("Error: Attempt to resize the medium from %RU64.%RU64 MB to %RU64.%RU64 MB. Use --resizebyte if this is intended!\n",
764 logicalSize / _1M, (logicalSize % _1M) / (_1M / 10), cbResize / _1M, (cbResize % _1M) / (_1M / 10));
765 return RTEXITCODE_FAILURE;
766 }
767 }
768
769 if (fModifyMediumType)
770 {
771 MediumType_T enmCurrMediumType;
772 CHECK_ERROR(pMedium, COMGETTER(Type)(&enmCurrMediumType));
773
774 if (enmCurrMediumType != enmMediumType)
775 CHECK_ERROR(pMedium, COMSETTER(Type)(enmMediumType));
776 }
777
778 if (fModifyAutoReset)
779 {
780 CHECK_ERROR(pMedium, COMSETTER(AutoReset)(AutoReset));
781 }
782
783 if (fModifyProperties)
784 {
785 CHECK_ERROR(pMedium, SetProperties(ComSafeArrayAsInParam(mediumPropNames), ComSafeArrayAsInParam(mediumPropValues)));
786 }
787
788 if (fModifyCompact)
789 {
790 ComPtr<IProgress> pProgress;
791 CHECK_ERROR(pMedium, Compact(pProgress.asOutParam()));
792 if (SUCCEEDED(rc))
793 rc = showProgress(pProgress);
794 if (FAILED(rc))
795 {
796 if (rc == E_NOTIMPL)
797 RTMsgError("Compact medium operation is not implemented!");
798 else if (rc == VBOX_E_NOT_SUPPORTED)
799 RTMsgError("Compact medium operation for this format is not implemented yet!");
800 else if (!pProgress.isNull())
801 CHECK_PROGRESS_ERROR(pProgress, ("Failed to compact medium"));
802 else
803 RTMsgError("Failed to compact medium!");
804 }
805 }
806
807 if (fModifyResize)
808 {
809 ComPtr<IProgress> pProgress;
810 CHECK_ERROR(pMedium, Resize(cbResize, pProgress.asOutParam()));
811 if (SUCCEEDED(rc))
812 rc = showProgress(pProgress);
813 if (FAILED(rc))
814 {
815 if (!pProgress.isNull())
816 CHECK_PROGRESS_ERROR(pProgress, ("Failed to resize medium"));
817 else if (rc == E_NOTIMPL)
818 RTMsgError("Resize medium operation is not implemented!");
819 else if (rc == VBOX_E_NOT_SUPPORTED)
820 RTMsgError("Resize medium operation for this format is not implemented yet!");
821 else
822 RTMsgError("Failed to resize medium!");
823 }
824 }
825
826 if (fMoveMedium)
827 {
828 do
829 {
830 ComPtr<IProgress> pProgress;
831 Utf8Str strLocation(pszNewLocation);
832 RTStrFree(pszNewLocation);
833 CHECK_ERROR(pMedium, MoveTo(Bstr(strLocation).raw(), pProgress.asOutParam()));
834
835 if (SUCCEEDED(rc) && !pProgress.isNull())
836 {
837 rc = showProgress(pProgress);
838 CHECK_PROGRESS_ERROR(pProgress, ("Failed to move medium"));
839 }
840
841 Bstr uuid;
842 CHECK_ERROR_BREAK(pMedium, COMGETTER(Id)(uuid.asOutParam()));
843
844 RTPrintf("Move medium with UUID %s finished\n", Utf8Str(uuid).c_str());
845 }
846 while (0);
847 }
848
849 if (fSetNewLocation)
850 {
851 Utf8Str strLocation(pszNewLocation);
852 RTStrFree(pszNewLocation);
853 CHECK_ERROR(pMedium, COMSETTER(Location)(Bstr(strLocation).raw()));
854
855 Bstr uuid;
856 CHECK_ERROR(pMedium, COMGETTER(Id)(uuid.asOutParam()));
857 RTPrintf("Set new location of medium with UUID %s finished\n", Utf8Str(uuid).c_str());
858 }
859
860 if (fModifyDescription)
861 {
862 CHECK_ERROR(pMedium, COMSETTER(Description)(Bstr(pszNewLocation).raw()));
863
864 RTPrintf("Medium description has been changed.\n");
865 }
866
867 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
868}
869
870static const RTGETOPTDEF g_aCloneMediumOptions[] =
871{
872 { "disk", 'd', RTGETOPT_REQ_NOTHING },
873 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
874 { "floppy", 'f', RTGETOPT_REQ_NOTHING },
875 { "--format", 'o', RTGETOPT_REQ_STRING },
876 { "-format", 'o', RTGETOPT_REQ_STRING },
877 { "--static", 'F', RTGETOPT_REQ_NOTHING },
878 { "-static", 'F', RTGETOPT_REQ_NOTHING },
879 { "--existing", 'E', RTGETOPT_REQ_NOTHING },
880 { "--variant", 'm', RTGETOPT_REQ_STRING },
881 { "-variant", 'm', RTGETOPT_REQ_STRING },
882};
883
884RTEXITCODE handleCloneMedium(HandlerArg *a)
885{
886 HRESULT rc;
887 int vrc;
888 enum {
889 CMD_NONE,
890 CMD_DISK,
891 CMD_DVD,
892 CMD_FLOPPY
893 } cmd = CMD_NONE;
894 const char *pszSrc = NULL;
895 const char *pszDst = NULL;
896 Bstr format;
897 MediumVariant_T enmMediumVariant = MediumVariant_Standard;
898 bool fExisting = false;
899
900 int c;
901 RTGETOPTUNION ValueUnion;
902 RTGETOPTSTATE GetState;
903 // start at 0 because main() has hacked both the argc and argv given to us
904 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloneMediumOptions, RT_ELEMENTS(g_aCloneMediumOptions),
905 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
906 while ((c = RTGetOpt(&GetState, &ValueUnion)))
907 {
908 switch (c)
909 {
910 case 'd': // disk
911 if (cmd != CMD_NONE)
912 return errorSyntax(USAGE_CLONEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
913 cmd = CMD_DISK;
914 break;
915
916 case 'D': // DVD
917 if (cmd != CMD_NONE)
918 return errorSyntax(USAGE_CLONEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
919 cmd = CMD_DVD;
920 break;
921
922 case 'f': // floppy
923 if (cmd != CMD_NONE)
924 return errorSyntax(USAGE_CLONEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
925 cmd = CMD_FLOPPY;
926 break;
927
928 case 'o': // --format
929 format = ValueUnion.psz;
930 break;
931
932 case 'F': // --static
933 {
934 unsigned uMediumVariant = (unsigned)enmMediumVariant;
935 uMediumVariant |= MediumVariant_Fixed;
936 enmMediumVariant = (MediumVariant_T)uMediumVariant;
937 break;
938 }
939
940 case 'E': // --existing
941 fExisting = true;
942 break;
943
944 case 'm': // --variant
945 vrc = parseMediumVariant(ValueUnion.psz, &enmMediumVariant);
946 if (RT_FAILURE(vrc))
947 return errorArgument("Invalid medium variant '%s'", ValueUnion.psz);
948 break;
949
950 case VINF_GETOPT_NOT_OPTION:
951 if (!pszSrc)
952 pszSrc = ValueUnion.psz;
953 else if (!pszDst)
954 pszDst = ValueUnion.psz;
955 else
956 return errorSyntax(USAGE_CLONEMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
957 break;
958
959 default:
960 if (c > 0)
961 {
962 if (RT_C_IS_GRAPH(c))
963 return errorSyntax(USAGE_CLONEMEDIUM, "unhandled option: -%c", c);
964 else
965 return errorSyntax(USAGE_CLONEMEDIUM, "unhandled option: %i", c);
966 }
967 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
968 return errorSyntax(USAGE_CLONEMEDIUM, "unknown option: %s", ValueUnion.psz);
969 else if (ValueUnion.pDef)
970 return errorSyntax(USAGE_CLONEMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
971 else
972 return errorSyntax(USAGE_CLONEMEDIUM, "error: %Rrs", c);
973 }
974 }
975
976 if (cmd == CMD_NONE)
977 cmd = CMD_DISK;
978 if (!pszSrc)
979 return errorSyntax(USAGE_CLONEMEDIUM, "Mandatory UUID or input file parameter missing");
980 if (!pszDst)
981 return errorSyntax(USAGE_CLONEMEDIUM, "Mandatory output file parameter missing");
982 if (fExisting && (!format.isEmpty() || enmMediumVariant != MediumVariant_Standard))
983 return errorSyntax(USAGE_CLONEMEDIUM, "Specified options which cannot be used with --existing");
984
985 ComPtr<IMedium> pSrcMedium;
986 ComPtr<IMedium> pDstMedium;
987
988 if (cmd == CMD_DISK)
989 rc = openMedium(a, pszSrc, DeviceType_HardDisk, AccessMode_ReadOnly, pSrcMedium,
990 false /* fForceNewUuidOnOpen */, false /* fSilent */);
991 else if (cmd == CMD_DVD)
992 rc = openMedium(a, pszSrc, DeviceType_DVD, AccessMode_ReadOnly, pSrcMedium,
993 false /* fForceNewUuidOnOpen */, false /* fSilent */);
994 else if (cmd == CMD_FLOPPY)
995 rc = openMedium(a, pszSrc, DeviceType_Floppy, AccessMode_ReadOnly, pSrcMedium,
996 false /* fForceNewUuidOnOpen */, false /* fSilent */);
997 else
998 rc = E_INVALIDARG; /* cannot happen but make gcc happy */
999 if (FAILED(rc))
1000 return RTEXITCODE_FAILURE;
1001
1002 do
1003 {
1004 /* open/create destination medium */
1005 if (fExisting)
1006 {
1007 if (cmd == CMD_DISK)
1008 rc = openMedium(a, pszDst, DeviceType_HardDisk, AccessMode_ReadWrite, pDstMedium,
1009 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1010 else if (cmd == CMD_DVD)
1011 rc = openMedium(a, pszDst, DeviceType_DVD, AccessMode_ReadOnly, pDstMedium,
1012 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1013 else if (cmd == CMD_FLOPPY)
1014 rc = openMedium(a, pszDst, DeviceType_Floppy, AccessMode_ReadWrite, pDstMedium,
1015 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1016 if (FAILED(rc))
1017 break;
1018
1019 /* Perform accessibility check now. */
1020 MediumState_T state;
1021 CHECK_ERROR_BREAK(pDstMedium, RefreshState(&state));
1022 CHECK_ERROR_BREAK(pDstMedium, COMGETTER(Format)(format.asOutParam()));
1023 }
1024 else
1025 {
1026 /*
1027 * In case the format is unspecified check that the source medium supports
1028 * image creation and use the same format for the destination image.
1029 * Use the default image format if it is not supported.
1030 */
1031 if (format.isEmpty())
1032 {
1033 ComPtr<IMediumFormat> pMediumFmt;
1034 com::SafeArray<MediumFormatCapabilities_T> l_caps;
1035 CHECK_ERROR_BREAK(pSrcMedium, COMGETTER(MediumFormat)(pMediumFmt.asOutParam()));
1036 CHECK_ERROR_BREAK(pMediumFmt, COMGETTER(Capabilities)(ComSafeArrayAsOutParam(l_caps)));
1037 ULONG caps=0;
1038 for (size_t i = 0; i < l_caps.size(); i++)
1039 caps |= l_caps[i];
1040 if (caps & ( MediumFormatCapabilities_CreateDynamic
1041 | MediumFormatCapabilities_CreateFixed))
1042 CHECK_ERROR_BREAK(pMediumFmt, COMGETTER(Id)(format.asOutParam()));
1043 }
1044 Utf8Str strFormat(format);
1045 if (cmd == CMD_DISK)
1046 rc = createMedium(a, strFormat.c_str(), pszDst, DeviceType_HardDisk,
1047 AccessMode_ReadWrite, pDstMedium);
1048 else if (cmd == CMD_DVD)
1049 rc = createMedium(a, strFormat.c_str(), pszDst, DeviceType_DVD,
1050 AccessMode_ReadOnly, pDstMedium);
1051 else if (cmd == CMD_FLOPPY)
1052 rc = createMedium(a, strFormat.c_str(), pszDst, DeviceType_Floppy,
1053 AccessMode_ReadWrite, pDstMedium);
1054 if (FAILED(rc))
1055 break;
1056 }
1057
1058 ComPtr<IProgress> pProgress;
1059 com::SafeArray<MediumVariant_T> l_variants(sizeof(MediumVariant_T)*8);
1060
1061 for (ULONG i = 0; i < l_variants.size(); ++i)
1062 {
1063 ULONG temp = enmMediumVariant;
1064 temp &= 1<<i;
1065 l_variants [i] = (MediumVariant_T)temp;
1066 }
1067
1068 CHECK_ERROR_BREAK(pSrcMedium, CloneTo(pDstMedium, ComSafeArrayAsInParam(l_variants), NULL, pProgress.asOutParam()));
1069
1070 rc = showProgress(pProgress);
1071 CHECK_PROGRESS_ERROR_BREAK(pProgress, ("Failed to clone medium"));
1072
1073 Bstr uuid;
1074 CHECK_ERROR_BREAK(pDstMedium, COMGETTER(Id)(uuid.asOutParam()));
1075
1076 RTPrintf("Clone medium created in format '%ls'. UUID: %s\n",
1077 format.raw(), Utf8Str(uuid).c_str());
1078 }
1079 while (0);
1080
1081 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1082}
1083
1084static const RTGETOPTDEF g_aConvertFromRawHardDiskOptions[] =
1085{
1086 { "--format", 'o', RTGETOPT_REQ_STRING },
1087 { "-format", 'o', RTGETOPT_REQ_STRING },
1088 { "--static", 'F', RTGETOPT_REQ_NOTHING },
1089 { "-static", 'F', RTGETOPT_REQ_NOTHING },
1090 { "--variant", 'm', RTGETOPT_REQ_STRING },
1091 { "-variant", 'm', RTGETOPT_REQ_STRING },
1092 { "--uuid", 'u', RTGETOPT_REQ_STRING },
1093};
1094
1095RTEXITCODE handleConvertFromRaw(HandlerArg *a)
1096{
1097 int rc = VINF_SUCCESS;
1098 bool fReadFromStdIn = false;
1099 const char *format = "VDI";
1100 const char *srcfilename = NULL;
1101 const char *dstfilename = NULL;
1102 const char *filesize = NULL;
1103 unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
1104 void *pvBuf = NULL;
1105 RTUUID uuid;
1106 PCRTUUID pUuid = NULL;
1107
1108 int c;
1109 RTGETOPTUNION ValueUnion;
1110 RTGETOPTSTATE GetState;
1111 // start at 0 because main() has hacked both the argc and argv given to us
1112 RTGetOptInit(&GetState, a->argc, a->argv, g_aConvertFromRawHardDiskOptions, RT_ELEMENTS(g_aConvertFromRawHardDiskOptions),
1113 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1114 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1115 {
1116 switch (c)
1117 {
1118 case 'u': // --uuid
1119 if (RT_FAILURE(RTUuidFromStr(&uuid, ValueUnion.psz)))
1120 return errorSyntax(USAGE_CONVERTFROMRAW, "Invalid UUID '%s'", ValueUnion.psz);
1121 pUuid = &uuid;
1122 break;
1123 case 'o': // --format
1124 format = ValueUnion.psz;
1125 break;
1126
1127 case 'm': // --variant
1128 {
1129 MediumVariant_T enmMediumVariant = MediumVariant_Standard;
1130 rc = parseMediumVariant(ValueUnion.psz, &enmMediumVariant);
1131 if (RT_FAILURE(rc))
1132 return errorArgument("Invalid medium variant '%s'", ValueUnion.psz);
1133 /// @todo cleaner solution than assuming 1:1 mapping?
1134 uImageFlags = (unsigned)enmMediumVariant;
1135 break;
1136 }
1137 case VINF_GETOPT_NOT_OPTION:
1138 if (!srcfilename)
1139 {
1140 srcfilename = ValueUnion.psz;
1141 fReadFromStdIn = !strcmp(srcfilename, "stdin");
1142 }
1143 else if (!dstfilename)
1144 dstfilename = ValueUnion.psz;
1145 else if (fReadFromStdIn && !filesize)
1146 filesize = ValueUnion.psz;
1147 else
1148 return errorSyntax(USAGE_CONVERTFROMRAW, "Invalid parameter '%s'", ValueUnion.psz);
1149 break;
1150
1151 default:
1152 return errorGetOpt(USAGE_CONVERTFROMRAW, c, &ValueUnion);
1153 }
1154 }
1155
1156 if (!srcfilename || !dstfilename || (fReadFromStdIn && !filesize))
1157 return errorSyntax(USAGE_CONVERTFROMRAW, "Incorrect number of parameters");
1158 RTStrmPrintf(g_pStdErr, "Converting from raw image file=\"%s\" to file=\"%s\"...\n",
1159 srcfilename, dstfilename);
1160
1161 PVDISK pDisk = NULL;
1162
1163 PVDINTERFACE pVDIfs = NULL;
1164 VDINTERFACEERROR vdInterfaceError;
1165 vdInterfaceError.pfnError = handleVDError;
1166 vdInterfaceError.pfnMessage = NULL;
1167
1168 rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1169 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1170 AssertRC(rc);
1171
1172 /* open raw image file. */
1173 RTFILE File;
1174 if (fReadFromStdIn)
1175 rc = RTFileFromNative(&File, RTFILE_NATIVE_STDIN);
1176 else
1177 rc = RTFileOpen(&File, srcfilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1178 if (RT_FAILURE(rc))
1179 {
1180 RTMsgError("Cannot open file \"%s\": %Rrc", srcfilename, rc);
1181 goto out;
1182 }
1183
1184 uint64_t cbFile;
1185 /* get image size. */
1186 if (fReadFromStdIn)
1187 cbFile = RTStrToUInt64(filesize);
1188 else
1189 rc = RTFileQuerySize(File, &cbFile);
1190 if (RT_FAILURE(rc))
1191 {
1192 RTMsgError("Cannot get image size for file \"%s\": %Rrc", srcfilename, rc);
1193 goto out;
1194 }
1195
1196 RTStrmPrintf(g_pStdErr, "Creating %s image with size %RU64 bytes (%RU64MB)...\n",
1197 (uImageFlags & VD_IMAGE_FLAGS_FIXED) ? "fixed" : "dynamic", cbFile, (cbFile + _1M - 1) / _1M);
1198 char pszComment[256];
1199 RTStrPrintf(pszComment, sizeof(pszComment), "Converted image from %s", srcfilename);
1200 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1201 if (RT_FAILURE(rc))
1202 {
1203 RTMsgError("Cannot create the virtual disk container: %Rrc", rc);
1204 goto out;
1205 }
1206
1207 Assert(RT_MIN(cbFile / 512 / 16 / 63, 16383) -
1208 (unsigned int)RT_MIN(cbFile / 512 / 16 / 63, 16383) == 0);
1209 VDGEOMETRY PCHS, LCHS;
1210 PCHS.cCylinders = (unsigned int)RT_MIN(cbFile / 512 / 16 / 63, 16383);
1211 PCHS.cHeads = 16;
1212 PCHS.cSectors = 63;
1213 LCHS.cCylinders = 0;
1214 LCHS.cHeads = 0;
1215 LCHS.cSectors = 0;
1216 rc = VDCreateBase(pDisk, format, dstfilename, cbFile,
1217 uImageFlags, pszComment, &PCHS, &LCHS, pUuid,
1218 VD_OPEN_FLAGS_NORMAL, NULL, NULL);
1219 if (RT_FAILURE(rc))
1220 {
1221 RTMsgError("Cannot create the disk image \"%s\": %Rrc", dstfilename, rc);
1222 goto out;
1223 }
1224
1225 size_t cbBuffer;
1226 cbBuffer = _1M;
1227 pvBuf = RTMemAlloc(cbBuffer);
1228 if (!pvBuf)
1229 {
1230 rc = VERR_NO_MEMORY;
1231 RTMsgError("Out of memory allocating buffers for image \"%s\": %Rrc", dstfilename, rc);
1232 goto out;
1233 }
1234
1235 uint64_t offFile;
1236 offFile = 0;
1237 while (offFile < cbFile)
1238 {
1239 size_t cbRead;
1240 size_t cbToRead;
1241 cbRead = 0;
1242 cbToRead = cbFile - offFile >= (uint64_t)cbBuffer ?
1243 cbBuffer : (size_t)(cbFile - offFile);
1244 rc = RTFileRead(File, pvBuf, cbToRead, &cbRead);
1245 if (RT_FAILURE(rc) || !cbRead)
1246 break;
1247 rc = VDWrite(pDisk, offFile, pvBuf, cbRead);
1248 if (RT_FAILURE(rc))
1249 {
1250 RTMsgError("Failed to write to disk image \"%s\": %Rrc", dstfilename, rc);
1251 goto out;
1252 }
1253 offFile += cbRead;
1254 }
1255
1256out:
1257 if (pvBuf)
1258 RTMemFree(pvBuf);
1259 if (pDisk)
1260 VDClose(pDisk, RT_FAILURE(rc));
1261 if (File != NIL_RTFILE)
1262 RTFileClose(File);
1263
1264 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1265}
1266
1267HRESULT showMediumInfo(const ComPtr<IVirtualBox> &pVirtualBox,
1268 const ComPtr<IMedium> &pMedium,
1269 const char *pszParentUUID,
1270 bool fOptLong)
1271{
1272 HRESULT rc = S_OK;
1273 do
1274 {
1275 Bstr uuid;
1276 pMedium->COMGETTER(Id)(uuid.asOutParam());
1277 RTPrintf("UUID: %ls\n", uuid.raw());
1278 if (pszParentUUID)
1279 RTPrintf("Parent UUID: %s\n", pszParentUUID);
1280
1281 /* check for accessibility */
1282 MediumState_T enmState;
1283 CHECK_ERROR_BREAK(pMedium, RefreshState(&enmState));
1284 const char *pszState = "unknown";
1285 switch (enmState)
1286 {
1287 case MediumState_NotCreated:
1288 pszState = "not created";
1289 break;
1290 case MediumState_Created:
1291 pszState = "created";
1292 break;
1293 case MediumState_LockedRead:
1294 pszState = "locked read";
1295 break;
1296 case MediumState_LockedWrite:
1297 pszState = "locked write";
1298 break;
1299 case MediumState_Inaccessible:
1300 pszState = "inaccessible";
1301 break;
1302 case MediumState_Creating:
1303 pszState = "creating";
1304 break;
1305 case MediumState_Deleting:
1306 pszState = "deleting";
1307 break;
1308#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
1309 case MediumState_32BitHack: break; /* Shut up compiler warnings. */
1310#endif
1311 }
1312 RTPrintf("State: %s\n", pszState);
1313
1314 if (fOptLong && enmState == MediumState_Inaccessible)
1315 {
1316 Bstr err;
1317 CHECK_ERROR_BREAK(pMedium, COMGETTER(LastAccessError)(err.asOutParam()));
1318 RTPrintf("Access Error: %ls\n", err.raw());
1319 }
1320
1321 if (fOptLong)
1322 {
1323 Bstr description;
1324 pMedium->COMGETTER(Description)(description.asOutParam());
1325 if (!description.isEmpty())
1326 RTPrintf("Description: %ls\n", description.raw());
1327 }
1328
1329 MediumType_T type;
1330 pMedium->COMGETTER(Type)(&type);
1331 const char *typeStr = "unknown";
1332 switch (type)
1333 {
1334 case MediumType_Normal:
1335 if (pszParentUUID && Guid(pszParentUUID).isValid())
1336 typeStr = "normal (differencing)";
1337 else
1338 typeStr = "normal (base)";
1339 break;
1340 case MediumType_Immutable:
1341 typeStr = "immutable";
1342 break;
1343 case MediumType_Writethrough:
1344 typeStr = "writethrough";
1345 break;
1346 case MediumType_Shareable:
1347 typeStr = "shareable";
1348 break;
1349 case MediumType_Readonly:
1350 typeStr = "readonly";
1351 break;
1352 case MediumType_MultiAttach:
1353 typeStr = "multiattach";
1354 break;
1355#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
1356 case MediumType_32BitHack: break; /* Shut up compiler warnings. */
1357#endif
1358 }
1359 RTPrintf("Type: %s\n", typeStr);
1360
1361 /* print out information specific for differencing media */
1362 if (fOptLong && pszParentUUID && Guid(pszParentUUID).isValid())
1363 {
1364 BOOL autoReset = FALSE;
1365 pMedium->COMGETTER(AutoReset)(&autoReset);
1366 RTPrintf("Auto-Reset: %s\n", autoReset ? "on" : "off");
1367 }
1368
1369 Bstr loc;
1370 pMedium->COMGETTER(Location)(loc.asOutParam());
1371 RTPrintf("Location: %ls\n", loc.raw());
1372
1373 Bstr format;
1374 pMedium->COMGETTER(Format)(format.asOutParam());
1375 RTPrintf("Storage format: %ls\n", format.raw());
1376
1377 if (fOptLong)
1378 {
1379 com::SafeArray<MediumVariant_T> safeArray_variant;
1380
1381 pMedium->COMGETTER(Variant)(ComSafeArrayAsOutParam(safeArray_variant));
1382 ULONG variant=0;
1383 for (size_t i = 0; i < safeArray_variant.size(); i++)
1384 variant |= safeArray_variant[i];
1385
1386 const char *variantStr = "unknown";
1387 switch (variant & ~(MediumVariant_Fixed | MediumVariant_Diff))
1388 {
1389 case MediumVariant_VmdkSplit2G:
1390 variantStr = "split2G";
1391 break;
1392 case MediumVariant_VmdkStreamOptimized:
1393 variantStr = "streamOptimized";
1394 break;
1395 case MediumVariant_VmdkESX:
1396 variantStr = "ESX";
1397 break;
1398 case MediumVariant_Standard:
1399 variantStr = "default";
1400 break;
1401 }
1402 const char *variantTypeStr = "dynamic";
1403 if (variant & MediumVariant_Fixed)
1404 variantTypeStr = "fixed";
1405 else if (variant & MediumVariant_Diff)
1406 variantTypeStr = "differencing";
1407 RTPrintf("Format variant: %s %s\n", variantTypeStr, variantStr);
1408 }
1409
1410 LONG64 logicalSize;
1411 pMedium->COMGETTER(LogicalSize)(&logicalSize);
1412 RTPrintf("Capacity: %lld MBytes\n", logicalSize >> 20);
1413 if (fOptLong)
1414 {
1415 LONG64 actualSize;
1416 pMedium->COMGETTER(Size)(&actualSize);
1417 RTPrintf("Size on disk: %lld MBytes\n", actualSize >> 20);
1418 }
1419
1420 Bstr strCipher;
1421 Bstr strPasswordId;
1422 HRESULT rc2 = pMedium->GetEncryptionSettings(strCipher.asOutParam(), strPasswordId.asOutParam());
1423 if (SUCCEEDED(rc2))
1424 {
1425 RTPrintf("Encryption: enabled\n");
1426 if (fOptLong)
1427 {
1428 RTPrintf("Cipher: %ls\n", strCipher.raw());
1429 RTPrintf("Password ID: %ls\n", strPasswordId.raw());
1430 }
1431 }
1432 else
1433 RTPrintf("Encryption: disabled\n");
1434
1435 if (fOptLong)
1436 {
1437 com::SafeArray<BSTR> names;
1438 com::SafeArray<BSTR> values;
1439 pMedium->GetProperties(Bstr().raw(), ComSafeArrayAsOutParam(names), ComSafeArrayAsOutParam(values));
1440 size_t cNames = names.size();
1441 size_t cValues = values.size();
1442 bool fFirst = true;
1443 for (size_t i = 0; i < cNames; i++)
1444 {
1445 Bstr value;
1446 if (i < cValues)
1447 value = values[i];
1448 RTPrintf("%s%ls=%ls\n",
1449 fFirst ? "Property: " : " ",
1450 names[i], value.raw());
1451 fFirst = false;
1452 }
1453 }
1454
1455 if (fOptLong)
1456 {
1457 bool fFirst = true;
1458 com::SafeArray<BSTR> machineIds;
1459 pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(machineIds));
1460 for (size_t i = 0; i < machineIds.size(); i++)
1461 {
1462 ComPtr<IMachine> pMachine;
1463 CHECK_ERROR(pVirtualBox, FindMachine(machineIds[i], pMachine.asOutParam()));
1464 if (pMachine)
1465 {
1466 Bstr name;
1467 pMachine->COMGETTER(Name)(name.asOutParam());
1468 pMachine->COMGETTER(Id)(uuid.asOutParam());
1469 RTPrintf("%s%ls (UUID: %ls)",
1470 fFirst ? "In use by VMs: " : " ",
1471 name.raw(), machineIds[i]);
1472 fFirst = false;
1473 com::SafeArray<BSTR> snapshotIds;
1474 pMedium->GetSnapshotIds(machineIds[i],
1475 ComSafeArrayAsOutParam(snapshotIds));
1476 for (size_t j = 0; j < snapshotIds.size(); j++)
1477 {
1478 ComPtr<ISnapshot> pSnapshot;
1479 pMachine->FindSnapshot(snapshotIds[j], pSnapshot.asOutParam());
1480 if (pSnapshot)
1481 {
1482 Bstr snapshotName;
1483 pSnapshot->COMGETTER(Name)(snapshotName.asOutParam());
1484 RTPrintf(" [%ls (UUID: %ls)]", snapshotName.raw(), snapshotIds[j]);
1485 }
1486 }
1487 RTPrintf("\n");
1488 }
1489 }
1490 }
1491
1492 if (fOptLong)
1493 {
1494 com::SafeIfaceArray<IMedium> children;
1495 pMedium->COMGETTER(Children)(ComSafeArrayAsOutParam(children));
1496 bool fFirst = true;
1497 for (size_t i = 0; i < children.size(); i++)
1498 {
1499 ComPtr<IMedium> pChild(children[i]);
1500 if (pChild)
1501 {
1502 Bstr childUUID;
1503 pChild->COMGETTER(Id)(childUUID.asOutParam());
1504 RTPrintf("%s%ls\n",
1505 fFirst ? "Child UUIDs: " : " ",
1506 childUUID.raw());
1507 fFirst = false;
1508 }
1509 }
1510 }
1511 }
1512 while (0);
1513
1514 return rc;
1515}
1516
1517static const RTGETOPTDEF g_aShowMediumInfoOptions[] =
1518{
1519 { "disk", 'd', RTGETOPT_REQ_NOTHING },
1520 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
1521 { "floppy", 'f', RTGETOPT_REQ_NOTHING },
1522};
1523
1524RTEXITCODE handleShowMediumInfo(HandlerArg *a)
1525{
1526 enum {
1527 CMD_NONE,
1528 CMD_DISK,
1529 CMD_DVD,
1530 CMD_FLOPPY
1531 } cmd = CMD_NONE;
1532 const char *pszFilenameOrUuid = NULL;
1533
1534 int c;
1535 RTGETOPTUNION ValueUnion;
1536 RTGETOPTSTATE GetState;
1537 // start at 0 because main() has hacked both the argc and argv given to us
1538 RTGetOptInit(&GetState, a->argc, a->argv, g_aShowMediumInfoOptions, RT_ELEMENTS(g_aShowMediumInfoOptions),
1539 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1540 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1541 {
1542 switch (c)
1543 {
1544 case 'd': // disk
1545 if (cmd != CMD_NONE)
1546 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Only one command can be specified: '%s'", ValueUnion.psz);
1547 cmd = CMD_DISK;
1548 break;
1549
1550 case 'D': // DVD
1551 if (cmd != CMD_NONE)
1552 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Only one command can be specified: '%s'", ValueUnion.psz);
1553 cmd = CMD_DVD;
1554 break;
1555
1556 case 'f': // floppy
1557 if (cmd != CMD_NONE)
1558 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Only one command can be specified: '%s'", ValueUnion.psz);
1559 cmd = CMD_FLOPPY;
1560 break;
1561
1562 case VINF_GETOPT_NOT_OPTION:
1563 if (!pszFilenameOrUuid)
1564 pszFilenameOrUuid = ValueUnion.psz;
1565 else
1566 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Invalid parameter '%s'", ValueUnion.psz);
1567 break;
1568
1569 default:
1570 if (c > 0)
1571 {
1572 if (RT_C_IS_PRINT(c))
1573 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Invalid option -%c", c);
1574 else
1575 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Invalid option case %i", c);
1576 }
1577 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1578 return errorSyntax(USAGE_SHOWMEDIUMINFO, "unknown option: %s\n", ValueUnion.psz);
1579 else if (ValueUnion.pDef)
1580 return errorSyntax(USAGE_SHOWMEDIUMINFO, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1581 else
1582 return errorSyntax(USAGE_SHOWMEDIUMINFO, "error: %Rrs", c);
1583 }
1584 }
1585
1586 if (cmd == CMD_NONE)
1587 cmd = CMD_DISK;
1588
1589 /* check for required options */
1590 if (!pszFilenameOrUuid)
1591 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Medium name or UUID required");
1592
1593 HRESULT rc = S_OK; /* Prevents warning. */
1594
1595 ComPtr<IMedium> pMedium;
1596 if (cmd == CMD_DISK)
1597 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
1598 AccessMode_ReadOnly, pMedium,
1599 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1600 else if (cmd == CMD_DVD)
1601 rc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD,
1602 AccessMode_ReadOnly, pMedium,
1603 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1604 else if (cmd == CMD_FLOPPY)
1605 rc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy,
1606 AccessMode_ReadOnly, pMedium,
1607 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1608 if (FAILED(rc))
1609 return RTEXITCODE_FAILURE;
1610
1611 Utf8Str strParentUUID("base");
1612 ComPtr<IMedium> pParent;
1613 pMedium->COMGETTER(Parent)(pParent.asOutParam());
1614 if (!pParent.isNull())
1615 {
1616 Bstr bstrParentUUID;
1617 pParent->COMGETTER(Id)(bstrParentUUID.asOutParam());
1618 strParentUUID = bstrParentUUID;
1619 }
1620
1621 rc = showMediumInfo(a->virtualBox, pMedium, strParentUUID.c_str(), true);
1622
1623 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1624}
1625
1626static const RTGETOPTDEF g_aCloseMediumOptions[] =
1627{
1628 { "disk", 'd', RTGETOPT_REQ_NOTHING },
1629 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
1630 { "floppy", 'f', RTGETOPT_REQ_NOTHING },
1631 { "--delete", 'r', RTGETOPT_REQ_NOTHING },
1632};
1633
1634RTEXITCODE handleCloseMedium(HandlerArg *a)
1635{
1636 HRESULT rc = S_OK;
1637 enum {
1638 CMD_NONE,
1639 CMD_DISK,
1640 CMD_DVD,
1641 CMD_FLOPPY
1642 } cmd = CMD_NONE;
1643 const char *pszFilenameOrUuid = NULL;
1644 bool fDelete = false;
1645
1646 int c;
1647 RTGETOPTUNION ValueUnion;
1648 RTGETOPTSTATE GetState;
1649 // start at 0 because main() has hacked both the argc and argv given to us
1650 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloseMediumOptions, RT_ELEMENTS(g_aCloseMediumOptions),
1651 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1652 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1653 {
1654 switch (c)
1655 {
1656 case 'd': // disk
1657 if (cmd != CMD_NONE)
1658 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1659 cmd = CMD_DISK;
1660 break;
1661
1662 case 'D': // DVD
1663 if (cmd != CMD_NONE)
1664 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1665 cmd = CMD_DVD;
1666 break;
1667
1668 case 'f': // floppy
1669 if (cmd != CMD_NONE)
1670 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1671 cmd = CMD_FLOPPY;
1672 break;
1673
1674 case 'r': // --delete
1675 fDelete = true;
1676 break;
1677
1678 case VINF_GETOPT_NOT_OPTION:
1679 if (!pszFilenameOrUuid)
1680 pszFilenameOrUuid = ValueUnion.psz;
1681 else
1682 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
1683 break;
1684
1685 default:
1686 if (c > 0)
1687 {
1688 if (RT_C_IS_PRINT(c))
1689 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option -%c", c);
1690 else
1691 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option case %i", c);
1692 }
1693 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1694 return errorSyntax(USAGE_CLOSEMEDIUM, "unknown option: %s\n", ValueUnion.psz);
1695 else if (ValueUnion.pDef)
1696 return errorSyntax(USAGE_CLOSEMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1697 else
1698 return errorSyntax(USAGE_CLOSEMEDIUM, "error: %Rrs", c);
1699 }
1700 }
1701
1702 /* check for required options */
1703 if (cmd == CMD_NONE)
1704 cmd = CMD_DISK;
1705 if (!pszFilenameOrUuid)
1706 return errorSyntax(USAGE_CLOSEMEDIUM, "Medium name or UUID required");
1707
1708 ComPtr<IMedium> pMedium;
1709 if (cmd == CMD_DISK)
1710 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
1711 AccessMode_ReadWrite, pMedium,
1712 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1713 else if (cmd == CMD_DVD)
1714 rc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD,
1715 AccessMode_ReadOnly, pMedium,
1716 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1717 else if (cmd == CMD_FLOPPY)
1718 rc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy,
1719 AccessMode_ReadWrite, pMedium,
1720 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1721
1722 if (SUCCEEDED(rc) && pMedium)
1723 {
1724 if (fDelete)
1725 {
1726 ComPtr<IProgress> pProgress;
1727 CHECK_ERROR(pMedium, DeleteStorage(pProgress.asOutParam()));
1728 if (SUCCEEDED(rc))
1729 {
1730 rc = showProgress(pProgress);
1731 CHECK_PROGRESS_ERROR(pProgress, ("Failed to delete medium"));
1732 }
1733 else
1734 RTMsgError("Failed to delete medium. Error code %Rrc", rc);
1735 }
1736 CHECK_ERROR(pMedium, Close());
1737 }
1738
1739 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1740}
1741
1742RTEXITCODE handleMediumProperty(HandlerArg *a)
1743{
1744 HRESULT rc = S_OK;
1745 const char *pszCmd = NULL;
1746 enum {
1747 CMD_NONE,
1748 CMD_DISK,
1749 CMD_DVD,
1750 CMD_FLOPPY
1751 } cmd = CMD_NONE;
1752 const char *pszAction = NULL;
1753 const char *pszFilenameOrUuid = NULL;
1754 const char *pszProperty = NULL;
1755 ComPtr<IMedium> pMedium;
1756
1757 pszCmd = (a->argc > 0) ? a->argv[0] : "";
1758 if ( !RTStrICmp(pszCmd, "disk")
1759 || !RTStrICmp(pszCmd, "dvd")
1760 || !RTStrICmp(pszCmd, "floppy"))
1761 {
1762 if (!RTStrICmp(pszCmd, "disk"))
1763 cmd = CMD_DISK;
1764 else if (!RTStrICmp(pszCmd, "dvd"))
1765 cmd = CMD_DVD;
1766 else if (!RTStrICmp(pszCmd, "floppy"))
1767 cmd = CMD_FLOPPY;
1768 else
1769 {
1770 AssertMsgFailed(("unexpected parameter %s\n", pszCmd));
1771 cmd = CMD_DISK;
1772 }
1773 a->argv++;
1774 a->argc--;
1775 }
1776 else
1777 {
1778 pszCmd = NULL;
1779 cmd = CMD_DISK;
1780 }
1781
1782 if (a->argc == 0)
1783 return errorSyntax(USAGE_MEDIUMPROPERTY, "Missing action");
1784
1785 pszAction = a->argv[0];
1786 if ( RTStrICmp(pszAction, "set")
1787 && RTStrICmp(pszAction, "get")
1788 && RTStrICmp(pszAction, "delete"))
1789 return errorSyntax(USAGE_MEDIUMPROPERTY, "Invalid action given: %s", pszAction);
1790
1791 if ( ( !RTStrICmp(pszAction, "set")
1792 && a->argc != 4)
1793 || ( RTStrICmp(pszAction, "set")
1794 && a->argc != 3))
1795 return errorSyntax(USAGE_MEDIUMPROPERTY, "Invalid number of arguments given for action: %s", pszAction);
1796
1797 pszFilenameOrUuid = a->argv[1];
1798 pszProperty = a->argv[2];
1799
1800 if (cmd == CMD_DISK)
1801 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
1802 AccessMode_ReadWrite, pMedium,
1803 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1804 else if (cmd == CMD_DVD)
1805 rc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD,
1806 AccessMode_ReadOnly, pMedium,
1807 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1808 else if (cmd == CMD_FLOPPY)
1809 rc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy,
1810 AccessMode_ReadWrite, pMedium,
1811 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1812 if (SUCCEEDED(rc) && !pMedium.isNull())
1813 {
1814 if (!RTStrICmp(pszAction, "set"))
1815 {
1816 const char *pszValue = a->argv[3];
1817 CHECK_ERROR(pMedium, SetProperty(Bstr(pszProperty).raw(), Bstr(pszValue).raw()));
1818 }
1819 else if (!RTStrICmp(pszAction, "get"))
1820 {
1821 Bstr strVal;
1822 CHECK_ERROR(pMedium, GetProperty(Bstr(pszProperty).raw(), strVal.asOutParam()));
1823 if (SUCCEEDED(rc))
1824 RTPrintf("%s=%ls\n", pszProperty, strVal.raw());
1825 }
1826 else if (!RTStrICmp(pszAction, "delete"))
1827 {
1828 CHECK_ERROR(pMedium, SetProperty(Bstr(pszProperty).raw(), Bstr().raw()));
1829 /** @todo */
1830 }
1831 }
1832
1833 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1834}
1835
1836static const RTGETOPTDEF g_aEncryptMediumOptions[] =
1837{
1838 { "--newpassword", 'n', RTGETOPT_REQ_STRING },
1839 { "--oldpassword", 'o', RTGETOPT_REQ_STRING },
1840 { "--cipher", 'c', RTGETOPT_REQ_STRING },
1841 { "--newpasswordid", 'i', RTGETOPT_REQ_STRING }
1842};
1843
1844RTEXITCODE handleEncryptMedium(HandlerArg *a)
1845{
1846 HRESULT rc;
1847 ComPtr<IMedium> hardDisk;
1848 const char *pszPasswordNew = NULL;
1849 const char *pszPasswordOld = NULL;
1850 const char *pszCipher = NULL;
1851 const char *pszFilenameOrUuid = NULL;
1852 const char *pszNewPasswordId = NULL;
1853 Utf8Str strPasswordNew;
1854 Utf8Str strPasswordOld;
1855
1856 int c;
1857 RTGETOPTUNION ValueUnion;
1858 RTGETOPTSTATE GetState;
1859 // start at 0 because main() has hacked both the argc and argv given to us
1860 RTGetOptInit(&GetState, a->argc, a->argv, g_aEncryptMediumOptions, RT_ELEMENTS(g_aEncryptMediumOptions),
1861 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1862 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1863 {
1864 switch (c)
1865 {
1866 case 'n': // --newpassword
1867 pszPasswordNew = ValueUnion.psz;
1868 break;
1869
1870 case 'o': // --oldpassword
1871 pszPasswordOld = ValueUnion.psz;
1872 break;
1873
1874 case 'c': // --cipher
1875 pszCipher = ValueUnion.psz;
1876 break;
1877
1878 case 'i': // --newpasswordid
1879 pszNewPasswordId = ValueUnion.psz;
1880 break;
1881
1882 case VINF_GETOPT_NOT_OPTION:
1883 if (!pszFilenameOrUuid)
1884 pszFilenameOrUuid = ValueUnion.psz;
1885 else
1886 return errorSyntax(USAGE_ENCRYPTMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
1887 break;
1888
1889 default:
1890 if (c > 0)
1891 {
1892 if (RT_C_IS_PRINT(c))
1893 return errorSyntax(USAGE_ENCRYPTMEDIUM, "Invalid option -%c", c);
1894 else
1895 return errorSyntax(USAGE_ENCRYPTMEDIUM, "Invalid option case %i", c);
1896 }
1897 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1898 return errorSyntax(USAGE_ENCRYPTMEDIUM, "unknown option: %s\n", ValueUnion.psz);
1899 else if (ValueUnion.pDef)
1900 return errorSyntax(USAGE_ENCRYPTMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1901 else
1902 return errorSyntax(USAGE_ENCRYPTMEDIUM, "error: %Rrs", c);
1903 }
1904 }
1905
1906 if (!pszFilenameOrUuid)
1907 return errorSyntax(USAGE_ENCRYPTMEDIUM, "Disk name or UUID required");
1908
1909 if (!pszPasswordNew && !pszPasswordOld)
1910 return errorSyntax(USAGE_ENCRYPTMEDIUM, "No password specified");
1911
1912 if ( (pszPasswordNew && !pszNewPasswordId)
1913 || (!pszPasswordNew && pszNewPasswordId))
1914 return errorSyntax(USAGE_ENCRYPTMEDIUM, "A new password must always have a valid identifier set at the same time");
1915
1916 if (pszPasswordNew)
1917 {
1918 if (!RTStrCmp(pszPasswordNew, "-"))
1919 {
1920 /* Get password from console. */
1921 RTEXITCODE rcExit = readPasswordFromConsole(&strPasswordNew, "Enter new password:");
1922 if (rcExit == RTEXITCODE_FAILURE)
1923 return rcExit;
1924 }
1925 else
1926 {
1927 RTEXITCODE rcExit = readPasswordFile(pszPasswordNew, &strPasswordNew);
1928 if (rcExit == RTEXITCODE_FAILURE)
1929 {
1930 RTMsgError("Failed to read new password from file");
1931 return rcExit;
1932 }
1933 }
1934 }
1935
1936 if (pszPasswordOld)
1937 {
1938 if (!RTStrCmp(pszPasswordOld, "-"))
1939 {
1940 /* Get password from console. */
1941 RTEXITCODE rcExit = readPasswordFromConsole(&strPasswordOld, "Enter old password:");
1942 if (rcExit == RTEXITCODE_FAILURE)
1943 return rcExit;
1944 }
1945 else
1946 {
1947 RTEXITCODE rcExit = readPasswordFile(pszPasswordOld, &strPasswordOld);
1948 if (rcExit == RTEXITCODE_FAILURE)
1949 {
1950 RTMsgError("Failed to read old password from file");
1951 return rcExit;
1952 }
1953 }
1954 }
1955
1956 /* Always open the medium if necessary, there is no other way. */
1957 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
1958 AccessMode_ReadWrite, hardDisk,
1959 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1960 if (FAILED(rc))
1961 return RTEXITCODE_FAILURE;
1962 if (hardDisk.isNull())
1963 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid hard disk reference, avoiding crash");
1964
1965 ComPtr<IProgress> progress;
1966 CHECK_ERROR(hardDisk, ChangeEncryption(Bstr(strPasswordOld).raw(), Bstr(pszCipher).raw(),
1967 Bstr(strPasswordNew).raw(), Bstr(pszNewPasswordId).raw(),
1968 progress.asOutParam()));
1969 if (SUCCEEDED(rc))
1970 rc = showProgress(progress);
1971 if (FAILED(rc))
1972 {
1973 if (rc == E_NOTIMPL)
1974 RTMsgError("Encrypt hard disk operation is not implemented!");
1975 else if (rc == VBOX_E_NOT_SUPPORTED)
1976 RTMsgError("Encrypt hard disk operation for this cipher is not implemented yet!");
1977 else if (!progress.isNull())
1978 CHECK_PROGRESS_ERROR(progress, ("Failed to encrypt hard disk"));
1979 else
1980 RTMsgError("Failed to encrypt hard disk!");
1981 }
1982
1983 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1984}
1985
1986RTEXITCODE handleCheckMediumPassword(HandlerArg *a)
1987{
1988 HRESULT rc;
1989 ComPtr<IMedium> hardDisk;
1990 const char *pszFilenameOrUuid = NULL;
1991 Utf8Str strPassword;
1992
1993 if (a->argc != 2)
1994 return errorSyntax(USAGE_MEDIUMENCCHKPWD, "Invalid number of arguments: %d", a->argc);
1995
1996 pszFilenameOrUuid = a->argv[0];
1997
1998 if (!RTStrCmp(a->argv[1], "-"))
1999 {
2000 /* Get password from console. */
2001 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter password:");
2002 if (rcExit == RTEXITCODE_FAILURE)
2003 return rcExit;
2004 }
2005 else
2006 {
2007 RTEXITCODE rcExit = readPasswordFile(a->argv[1], &strPassword);
2008 if (rcExit == RTEXITCODE_FAILURE)
2009 {
2010 RTMsgError("Failed to read password from file");
2011 return rcExit;
2012 }
2013 }
2014
2015 /* Always open the medium if necessary, there is no other way. */
2016 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
2017 AccessMode_ReadWrite, hardDisk,
2018 false /* fForceNewUuidOnOpen */, false /* fSilent */);
2019 if (FAILED(rc))
2020 return RTEXITCODE_FAILURE;
2021 if (hardDisk.isNull())
2022 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid hard disk reference, avoiding crash");
2023
2024 CHECK_ERROR(hardDisk, CheckEncryptionPassword(Bstr(strPassword).raw()));
2025 if (SUCCEEDED(rc))
2026 RTPrintf("The given password is correct\n");
2027 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2028}
2029
2030
2031/*********************************************************************************************************************************
2032* The mediumio command *
2033*********************************************************************************************************************************/
2034
2035/**
2036 * Common MediumIO options.
2037 */
2038typedef struct MEDIUMIOCOMMONOPT
2039{
2040 const char *pszFilenameOrUuid;
2041 DeviceType_T enmDeviceType;
2042 const char *pszPasswordFile;
2043} MEDIUMIOCOMMONOPT;
2044typedef MEDIUMIOCOMMONOPT *PMEDIUMIOCOMMONOPT;
2045typedef MEDIUMIOCOMMONOPT const *PCMEDIUMIOCOMMONOPT;
2046
2047/* For RTGETOPTDEF array initializer. */
2048#define MEDIUMIOCOMMONOPT_DEFS() \
2049 { "--disk", 'd', RTGETOPT_REQ_STRING }, \
2050 { "--harddisk", 'd', RTGETOPT_REQ_STRING }, \
2051 { "disk", 'd', RTGETOPT_REQ_STRING }, \
2052 { "harddisk", 'd', RTGETOPT_REQ_STRING }, \
2053 { "--dvd", 'D', RTGETOPT_REQ_STRING }, \
2054 { "--iso", 'D', RTGETOPT_REQ_STRING }, \
2055 { "dvd", 'D', RTGETOPT_REQ_STRING }, \
2056 { "iso", 'D', RTGETOPT_REQ_STRING }, \
2057 { "--floppy", 'f', RTGETOPT_REQ_STRING }, \
2058 { "floppy", 'f', RTGETOPT_REQ_STRING }, \
2059 { "--password-file", 'P', RTGETOPT_REQ_STRING }
2060
2061/* For option switch. */
2062#define MEDIUMIOCOMMONOPT_CASES(a_pCommonOpts) \
2063 case 'd': \
2064 (a_pCommonOpts)->enmDeviceType = DeviceType_HardDisk; \
2065 (a_pCommonOpts)->pszFilenameOrUuid = ValueUnion.psz; \
2066 break; \
2067 case 'D': \
2068 (a_pCommonOpts)->enmDeviceType = DeviceType_DVD; \
2069 (a_pCommonOpts)->pszFilenameOrUuid = ValueUnion.psz; \
2070 break; \
2071 case 'f': \
2072 (a_pCommonOpts)->enmDeviceType = DeviceType_Floppy; \
2073 (a_pCommonOpts)->pszFilenameOrUuid = ValueUnion.psz; \
2074 break; \
2075 case 'P': \
2076 (a_pCommonOpts)->pszPasswordFile = ValueUnion.psz; \
2077 break
2078
2079
2080/**
2081 * Worker for mediumio operations that returns a IMediumIO for the specified
2082 * medium.
2083 *
2084 * @returns Exit code.
2085 * @param pHandler The handler state structure (for IVirtualBox).
2086 * @param pCommonOpts Common mediumio options.
2087 * @param fWritable Whether to open writable (true) or read only
2088 * (false).
2089 * @param rPtrMediumIO Where to return the IMediumIO pointer.
2090 * @param pcbMedium Where to return the meidum size. Optional.
2091 */
2092static RTEXITCODE mediumIOOpenMediumForIO(HandlerArg *pHandler, PCMEDIUMIOCOMMONOPT pCommonOpts, bool fWritable,
2093 ComPtr<IMediumIO> &rPtrMediumIO, uint64_t *pcbMedium = NULL)
2094{
2095 /* Clear returns. */
2096 if (pcbMedium)
2097 *pcbMedium = 0;
2098 rPtrMediumIO.setNull();
2099
2100 /*
2101 * Make sure a medium was specified already.
2102 */
2103 if (pCommonOpts->enmDeviceType == DeviceType_Null)
2104 return errorSyntax("No medium specified!");
2105
2106 /*
2107 * Read the password.
2108 */
2109 Bstr bstrPassword;
2110 if (pCommonOpts->pszPasswordFile)
2111 {
2112 Utf8Str strPassword;
2113 RTEXITCODE rcExit;
2114 if (pCommonOpts->pszPasswordFile[0] == '-' && pCommonOpts->pszPasswordFile[1] == '\0')
2115 rcExit = readPasswordFromConsole(&strPassword, "Enter encryption password:");
2116 else
2117 rcExit = readPasswordFile(pCommonOpts->pszPasswordFile, &strPassword);
2118 if (rcExit != RTEXITCODE_SUCCESS)
2119 return rcExit;
2120 bstrPassword = strPassword;
2121 strPassword.assign(strPassword.length(), '*');
2122 }
2123
2124 /*
2125 * Open the medium and then get I/O access to it.
2126 */
2127 ComPtr<IMedium> ptrMedium;
2128 HRESULT hrc = openMedium(pHandler, pCommonOpts->pszFilenameOrUuid, pCommonOpts->enmDeviceType,
2129 fWritable ? AccessMode_ReadWrite : AccessMode_ReadOnly,
2130 ptrMedium, false /* fForceNewUuidOnOpen */, false /* fSilent */);
2131 if (SUCCEEDED(hrc))
2132 {
2133 CHECK_ERROR2I_STMT(ptrMedium, OpenForIO(fWritable, bstrPassword.raw(), rPtrMediumIO.asOutParam()), hrc = hrcCheck);
2134
2135 /*
2136 * If the size is requested get it after we've opened it.
2137 */
2138 if (pcbMedium && SUCCEEDED(hrc))
2139 {
2140 LONG64 cbLogical = 0;
2141 CHECK_ERROR2I_STMT(ptrMedium, COMGETTER(LogicalSize)(&cbLogical), hrc = hrcCheck);
2142 *pcbMedium = cbLogical;
2143 if (!SUCCEEDED(hrc))
2144 rPtrMediumIO.setNull();
2145 }
2146 }
2147
2148 if (bstrPassword.isNotEmpty())
2149 memset(bstrPassword.mutableRaw(), '*', bstrPassword.length() * sizeof(RTUTF16));
2150 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2151}
2152
2153
2154/**
2155 * mediumio formatfat
2156 */
2157static RTEXITCODE handleMediumIOFormatFat(HandlerArg *a, int iFirst, PMEDIUMIOCOMMONOPT pCommonOpts)
2158{
2159 /*
2160 * Parse the options.
2161 */
2162 bool fQuick = false;
2163 static const RTGETOPTDEF s_aOptions[] =
2164 {
2165 MEDIUMIOCOMMONOPT_DEFS(),
2166 { "--quick", 'q', RTGETOPT_REQ_NOTHING },
2167 };
2168
2169 RTGETOPTSTATE GetState;
2170 int rc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2171 AssertRC(rc);
2172 RTGETOPTUNION ValueUnion;
2173 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2174 {
2175 switch (rc)
2176 {
2177 MEDIUMIOCOMMONOPT_CASES(pCommonOpts);
2178
2179 case 'q':
2180 fQuick = true;
2181 break;
2182
2183 default:
2184 return errorGetOpt(rc, &ValueUnion);
2185 }
2186 }
2187
2188 /*
2189 * Open the medium for I/O and format it.
2190 */
2191 ComPtr<IMediumIO> ptrMediumIO;
2192 RTEXITCODE rcExit = mediumIOOpenMediumForIO(a, pCommonOpts, true /*fWritable*/, ptrMediumIO);
2193 if (rcExit != RTEXITCODE_SUCCESS)
2194 return rcExit;
2195 CHECK_ERROR2I_RET(ptrMediumIO, FormatFAT(fQuick), RTEXITCODE_FAILURE);
2196 return RTEXITCODE_SUCCESS;
2197}
2198
2199/**
2200 * mediumio cat
2201 */
2202static RTEXITCODE handleMediumIOCat(HandlerArg *a, int iFirst, PMEDIUMIOCOMMONOPT pCommonOpts)
2203{
2204 /*
2205 * Parse the options.
2206 */
2207 static const RTGETOPTDEF s_aOptions[] =
2208 {
2209 MEDIUMIOCOMMONOPT_DEFS(),
2210 { "--hex", 'H', RTGETOPT_REQ_NOTHING },
2211 { "--offset", 'o', RTGETOPT_REQ_UINT64 },
2212 { "--output", 'O', RTGETOPT_REQ_STRING },
2213 { "--size", 's', RTGETOPT_REQ_UINT64 },
2214 };
2215 bool fHex = false;
2216 uint64_t off = 0;
2217 const char *pszOutput = NULL;
2218 uint64_t cb = UINT64_MAX;
2219
2220 RTGETOPTSTATE GetState;
2221 int rc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2222 AssertRC(rc);
2223 RTGETOPTUNION ValueUnion;
2224 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2225 {
2226 switch (rc)
2227 {
2228 MEDIUMIOCOMMONOPT_CASES(pCommonOpts);
2229
2230 case 'H':
2231 fHex = true;
2232 break;
2233
2234 case 'o':
2235 off = ValueUnion.u64;
2236 break;
2237
2238 case 'O':
2239 pszOutput = ValueUnion.psz;
2240 break;
2241
2242 case 's':
2243 cb = ValueUnion.u64;
2244 break;
2245
2246 default:
2247 return errorGetOpt(rc, &ValueUnion);
2248 }
2249 }
2250
2251 /*
2252 * Open the medium for I/O.
2253 */
2254 ComPtr<IMediumIO> ptrMediumIO;
2255 uint64_t cbMedium;
2256 RTEXITCODE rcExit = mediumIOOpenMediumForIO(a, pCommonOpts, false /*fWritable*/, ptrMediumIO, &cbMedium);
2257 if (rcExit == RTEXITCODE_SUCCESS)
2258 {
2259 /*
2260 * Do we have an output file or do we write to stdout?
2261 */
2262 PRTSTREAM pOut = NULL;
2263 if (pszOutput && (pszOutput[0] != '-' || pszOutput[1] != '\0'))
2264 {
2265 int vrc = RTStrmOpen(pszOutput, fHex ? "wt" : "wb", &pOut);
2266 if (RT_FAILURE(vrc))
2267 rcExit = RTMsgErrorExitFailure("Error opening '%s' for writing: %Rrc", pszOutput, vrc);
2268 }
2269 else
2270 {
2271 pOut = g_pStdOut;
2272 if (!fHex)
2273 RTStrmSetMode(pOut, true, -1);
2274 }
2275
2276 if (rcExit == RTEXITCODE_SUCCESS)
2277 {
2278 /*
2279 * Adjust 'cb' now that we've got the medium size.
2280 */
2281 if (off >= cbMedium)
2282 {
2283 RTMsgWarning("Specified offset (%#RX64) is beyond the end of the medium (%#RX64)", off, cbMedium);
2284 cb = 0;
2285 }
2286 else if ( cb > cbMedium
2287 || cb + off > cbMedium)
2288 cb = cbMedium - off;
2289
2290 /*
2291 * Hex dump preps. (The duplication detection is making ASSUMPTIONS about
2292 * all the reads being a multiple of cchWidth, except for the final one.)
2293 */
2294 char abHexBuf[16] = { 0 };
2295 size_t cbHexBuf = 0;
2296 unsigned const cchWidth = RT_ELEMENTS(abHexBuf);
2297 uint64_t const offEndDupCheck = cb - cchWidth;
2298 uint64_t cDuplicates = 0;
2299
2300 /*
2301 * Do the reading.
2302 */
2303 while (cb > 0)
2304 {
2305 char szLine[32 + cchWidth * 4 + 32];
2306
2307 /* Do the reading. */
2308 uint32_t const cbToRead = (uint32_t)RT_MIN(cb, _128K);
2309 SafeArray<BYTE> SafeArrayBuf;
2310 HRESULT hrc = ptrMediumIO->Read(off, cbToRead, ComSafeArrayAsOutParam(SafeArrayBuf));
2311 if (FAILED(hrc))
2312 {
2313 RTStrPrintf(szLine, sizeof(szLine), "Read(%zu bytes at %#RX64)", cbToRead, off);
2314 com::GlueHandleComError(ptrMediumIO, szLine, hrc, __FILE__, __LINE__);
2315 break;
2316 }
2317
2318 /* Output the data. */
2319 size_t const cbReturned = SafeArrayBuf.size();
2320 if (cbReturned)
2321 {
2322 BYTE const *pbBuf = SafeArrayBuf.raw();
2323 int vrc = VINF_SUCCESS;
2324 if (!fHex)
2325 vrc = RTStrmWrite(pOut, pbBuf, cbReturned);
2326 else
2327 {
2328 /* hexdump -C */
2329 uint64_t offHex = off;
2330 uint64_t const offHexEnd = off + cbReturned;
2331 while (offHex < offHexEnd)
2332 {
2333 if ( offHex >= offEndDupCheck
2334 || cbHexBuf == 0
2335 || memcmp(pbBuf, abHexBuf, cchWidth) != 0
2336 || ( cDuplicates == 0
2337 && ( offHex + cchWidth >= offEndDupCheck
2338 || memcmp(pbBuf + cchWidth, pbBuf, cchWidth) != 0)) )
2339 {
2340 if (cDuplicates > 0)
2341 {
2342 RTStrmPrintf(pOut, "********** <ditto x %RU64>\n", cDuplicates);
2343 cDuplicates = 0;
2344 }
2345
2346 size_t cch = RTStrPrintf(szLine, sizeof(szLine), "%012RX64:", offHex);
2347 unsigned i;
2348 for (i = 0; i < cchWidth && offHex + i < offHexEnd; i++)
2349 {
2350 static const char s_szHexDigits[17] = "0123456789abcdef";
2351 szLine[cch++] = (i & 7) || i == 0 ? ' ' : '-';
2352 uint8_t const u8 = pbBuf[i];
2353 szLine[cch++] = s_szHexDigits[u8 >> 4];
2354 szLine[cch++] = s_szHexDigits[u8 & 0xf];
2355 }
2356 while (i++ < cchWidth)
2357 {
2358 szLine[cch++] = ' ';
2359 szLine[cch++] = ' ';
2360 szLine[cch++] = ' ';
2361 }
2362 szLine[cch++] = ' ';
2363
2364 for (i = 0; i < cchWidth && offHex + i < offHexEnd; i++)
2365 {
2366 uint8_t const u8 = pbBuf[i];
2367 szLine[cch++] = u8 < 127 && u8 >= 32 ? u8 : '.';
2368 }
2369 szLine[cch++] = '\n';
2370 szLine[cch] = '\0';
2371
2372 vrc = RTStrmWrite(pOut, szLine, cch);
2373 if (RT_FAILURE(vrc))
2374 break;
2375
2376
2377 /* copy bytes over to the duplication detection buffer. */
2378 cbHexBuf = (size_t)RT_MIN(cchWidth, offHexEnd - offHex);
2379 memcpy(abHexBuf, pbBuf, cbHexBuf);
2380 }
2381 else
2382 cDuplicates++;
2383
2384 /* Advance to next line. */
2385 pbBuf += cchWidth;
2386 offHex += cchWidth;
2387 }
2388 }
2389 if (RT_FAILURE(vrc))
2390 {
2391 rcExit = RTMsgErrorExitFailure("Error writing to '%s': %Rrc", pszOutput, vrc);
2392 break;
2393 }
2394 }
2395
2396 /* Advance. */
2397 if (cbReturned != cbToRead)
2398 {
2399 rcExit = RTMsgErrorExitFailure("Expected read() at offset %RU64 (%#RX64) to return %#zx bytes, only got %#zx!\n",
2400 off, off, cbReturned, cbToRead);
2401 break;
2402 }
2403 off += cbReturned;
2404 cb -= cbReturned;
2405 }
2406
2407 /*
2408 * Close output.
2409 */
2410 if (pOut != g_pStdOut)
2411 {
2412 int vrc = RTStrmClose(pOut);
2413 if (RT_FAILURE(vrc))
2414 rcExit = RTMsgErrorExitFailure("Error closing '%s': %Rrc", pszOutput, vrc);
2415 }
2416 else if (!fHex)
2417 RTStrmSetMode(pOut, false, -1);
2418 }
2419 }
2420 return rcExit;
2421}
2422
2423/**
2424 * mediumio stream
2425 */
2426static RTEXITCODE handleMediumIOStream(HandlerArg *a, int iFirst, PMEDIUMIOCOMMONOPT pCommonOpts)
2427{
2428 /*
2429 * Parse the options.
2430 */
2431 static const RTGETOPTDEF s_aOptions[] =
2432 {
2433 MEDIUMIOCOMMONOPT_DEFS(),
2434 { "--output", 'O', RTGETOPT_REQ_STRING },
2435 { "--format", 'F', RTGETOPT_REQ_STRING },
2436 { "--variant", 'v', RTGETOPT_REQ_STRING }
2437 };
2438 const char *pszOutput = NULL;
2439 MediumVariant_T enmMediumVariant = MediumVariant_Standard;
2440 Bstr strFormat;
2441
2442 RTGETOPTSTATE GetState;
2443 int rc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2444 AssertRC(rc);
2445 RTGETOPTUNION ValueUnion;
2446 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2447 {
2448 switch (rc)
2449 {
2450 MEDIUMIOCOMMONOPT_CASES(pCommonOpts);
2451
2452 case 'O':
2453 pszOutput = ValueUnion.psz;
2454 break;
2455 case 'F':
2456 strFormat = ValueUnion.psz;
2457 break;
2458 case 'v': // --variant
2459 {
2460 int vrc = parseMediumVariant(ValueUnion.psz, &enmMediumVariant);
2461 if (RT_FAILURE(vrc))
2462 return errorArgument("Invalid medium variant '%s'", ValueUnion.psz);
2463 break;
2464 }
2465
2466 default:
2467 return errorGetOpt(rc, &ValueUnion);
2468 }
2469 }
2470
2471 /*
2472 * Open the medium for I/O.
2473 */
2474 ComPtr<IMediumIO> ptrMediumIO;
2475 uint64_t cbMedium;
2476 RTEXITCODE rcExit = mediumIOOpenMediumForIO(a, pCommonOpts, false /*fWritable*/, ptrMediumIO, &cbMedium);
2477 if (rcExit == RTEXITCODE_SUCCESS)
2478 {
2479 /*
2480 * Do we have an output file or do we write to stdout?
2481 */
2482 PRTSTREAM pOut = NULL;
2483 if (pszOutput && (pszOutput[0] != '-' || pszOutput[1] != '\0'))
2484 {
2485 int vrc = RTStrmOpen(pszOutput, "wb", &pOut);
2486 if (RT_FAILURE(vrc))
2487 rcExit = RTMsgErrorExitFailure("Error opening '%s' for writing: %Rrc", pszOutput, vrc);
2488 }
2489 else
2490 {
2491 pOut = g_pStdOut;
2492 RTStrmSetMode(pOut, true, -1);
2493 }
2494
2495 if (rcExit == RTEXITCODE_SUCCESS)
2496 {
2497 ComPtr<IDataStream> ptrDataStream;
2498 ComPtr<IProgress> ptrProgress;
2499
2500 com::SafeArray<MediumVariant_T> l_variants(sizeof(MediumVariant_T)*8);
2501
2502 for (ULONG i = 0; i < l_variants.size(); ++i)
2503 {
2504 ULONG temp = enmMediumVariant;
2505 temp &= 1<<i;
2506 l_variants [i] = (MediumVariant_T)temp;
2507 }
2508
2509 HRESULT hrc = ptrMediumIO->ConvertToStream(strFormat.raw(), ComSafeArrayAsInParam(l_variants), 10 * _1M, ptrDataStream.asOutParam(), ptrProgress.asOutParam());
2510 if (hrc == S_OK)
2511 {
2512 /* Read until we reached the end of the stream. */
2513 for (;;)
2514 {
2515 SafeArray<BYTE> SafeArrayBuf;
2516
2517 hrc = ptrDataStream->Read(_64K, 0 /*Infinite wait*/, ComSafeArrayAsOutParam(SafeArrayBuf));
2518 if ( FAILED(hrc)
2519 || SafeArrayBuf.size() == 0)
2520 break;
2521
2522 /* Output the data. */
2523 size_t const cbReturned = SafeArrayBuf.size();
2524 if (cbReturned)
2525 {
2526 BYTE const *pbBuf = SafeArrayBuf.raw();
2527 int vrc = VINF_SUCCESS;
2528 vrc = RTStrmWrite(pOut, pbBuf, cbReturned);
2529 if (RT_FAILURE(vrc))
2530 {
2531 rcExit = RTMsgErrorExitFailure("Error writing to '%s': %Rrc", pszOutput, vrc);
2532 break;
2533 }
2534 }
2535
2536 /** @todo Check progress. */
2537 }
2538 }
2539 else
2540 {
2541 com::GlueHandleComError(ptrMediumIO, "ConvertToStream()", hrc, __FILE__, __LINE__);
2542 rcExit = RTEXITCODE_FAILURE;
2543 }
2544
2545 /*
2546 * Close output.
2547 */
2548 if (pOut != g_pStdOut)
2549 {
2550 int vrc = RTStrmClose(pOut);
2551 if (RT_FAILURE(vrc))
2552 rcExit = RTMsgErrorExitFailure("Error closing '%s': %Rrc", pszOutput, vrc);
2553 }
2554 else
2555 RTStrmSetMode(pOut, false, -1);
2556 }
2557 }
2558 return rcExit;
2559}
2560
2561
2562RTEXITCODE handleMediumIO(HandlerArg *a)
2563{
2564 /*
2565 * Parse image-option and sub-command.
2566 */
2567 static const RTGETOPTDEF s_aOptions[] =
2568 {
2569 MEDIUMIOCOMMONOPT_DEFS(),
2570 /* sub-commands */
2571 { "formatfat", 1000, RTGETOPT_REQ_NOTHING },
2572 { "cat", 1001, RTGETOPT_REQ_NOTHING },
2573 { "stream", 1002, RTGETOPT_REQ_NOTHING },
2574 };
2575 MEDIUMIOCOMMONOPT CommonOpts = { NULL, DeviceType_Null, NULL };
2576
2577 RTGETOPTSTATE GetState;
2578 int rc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
2579 AssertRC(rc);
2580 RTGETOPTUNION ValueUnion;
2581 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2582 {
2583 switch (rc)
2584 {
2585 MEDIUMIOCOMMONOPT_CASES(&CommonOpts);
2586
2587 /* Sub-commands: */
2588 case 1000:
2589 setCurrentSubcommand(HELP_SCOPE_MEDIUMIO_FORMATFAT);
2590 return handleMediumIOFormatFat(a, GetState.iNext, &CommonOpts);
2591 case 1001:
2592 setCurrentSubcommand(HELP_SCOPE_MEDIUMIO_CAT);
2593 return handleMediumIOCat(a, GetState.iNext, &CommonOpts);
2594 case 1002:
2595 setCurrentSubcommand(HELP_SCOPE_MEDIUMIO_STREAM);
2596 return handleMediumIOStream(a, GetState.iNext, &CommonOpts);
2597
2598 case VINF_GETOPT_NOT_OPTION:
2599 return errorUnknownSubcommand(ValueUnion.psz);
2600
2601 default:
2602 return errorGetOpt(rc, &ValueUnion);
2603 }
2604 }
2605 return errorNoSubcommand();
2606}
2607
2608#endif /* !VBOX_ONLY_DOCS */
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