VirtualBox

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

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

Added AllocationBlockSize property to VDI backend, and ability to set properties with VBoxManage --property key=value

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

© 2023 Oracle
ContactPrivacy policyTerms of Use