VirtualBox

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

Last change on this file was 99204, checked in by vboxsync, 13 months ago

VBoxManage: nits

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

© 2023 Oracle
ContactPrivacy policyTerms of Use