VirtualBox

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

Last change on this file since 107757 was 107757, checked in by vboxsync, 3 weeks ago

src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp: Fixed warnings found by Parfait (unused assignment). jiraref:VBP-1424

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 99.6 KB
Line 
1/* $Id: VBoxManageDisk.cpp 107757 2025-01-14 13:27:34Z vboxsync $ */
2/** @file
3 * VBoxManage - The disk/medium related commands.
4 */
5
6/*
7 * Copyright (C) 2006-2024 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 int vrc;
670 enum {
671 CMD_NONE,
672 CMD_DISK,
673 CMD_DVD,
674 CMD_FLOPPY
675 } cmd = CMD_NONE;
676 ComPtr<IMedium> pMedium;
677 MediumType_T enmMediumType = MediumType_Normal; /* Shut up MSC */
678 bool AutoReset = false;
679 SafeArray<BSTR> mediumPropNames;
680 SafeArray<BSTR> mediumPropValues;
681 bool fModifyMediumType = false;
682 bool fModifyAutoReset = false;
683 bool fModifyProperties = false;
684 bool fModifyCompact = false;
685 bool fModifyResize = false;
686 bool fModifyResizeMB = false;
687 bool fMoveMedium = false;
688 bool fModifyDescription = false;
689 bool fSetNewLocation = false;
690 uint64_t cbResize = 0;
691 const char *pszFilenameOrUuid = NULL;
692 char *pszNewLocation = NULL;
693
694 int c;
695 RTGETOPTUNION ValueUnion;
696 RTGETOPTSTATE GetState;
697 // start at 0 because main() has hacked both the argc and argv given to us
698 RTGetOptInit(&GetState, a->argc, a->argv, g_aModifyMediumOptions, RT_ELEMENTS(g_aModifyMediumOptions),
699 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
700 while ((c = RTGetOpt(&GetState, &ValueUnion)))
701 {
702 switch (c)
703 {
704 case 'H': // disk
705 if (cmd != CMD_NONE)
706 return errorSyntax(Disk::tr("Only one command can be specified: '%s'"), ValueUnion.psz);
707 cmd = CMD_DISK;
708 break;
709
710 case 'D': // DVD
711 if (cmd != CMD_NONE)
712 return errorSyntax(Disk::tr("Only one command can be specified: '%s'"), ValueUnion.psz);
713 cmd = CMD_DVD;
714 break;
715
716 case 'L': // floppy
717 if (cmd != CMD_NONE)
718 return errorSyntax(Disk::tr("Only one command can be specified: '%s'"), ValueUnion.psz);
719 cmd = CMD_FLOPPY;
720 break;
721
722 case 't': // --type
723 vrc = parseMediumType(ValueUnion.psz, &enmMediumType);
724 if (RT_FAILURE(vrc))
725 return errorArgument(Disk::tr("Invalid medium type '%s'"), ValueUnion.psz);
726 fModifyMediumType = true;
727 break;
728
729 case 'z': // --autoreset
730 vrc = parseBool(ValueUnion.psz, &AutoReset);
731 if (RT_FAILURE(vrc))
732 return errorArgument(Disk::tr("Invalid autoreset parameter '%s'"), ValueUnion.psz);
733 fModifyAutoReset = true;
734 break;
735
736 case 'p': // --property
737 {
738 /* Parse 'name=value' */
739 char *pszProperty = RTStrDup(ValueUnion.psz);
740 if (pszProperty)
741 {
742 char *pszDelimiter = strchr(pszProperty, '=');
743 if (pszDelimiter)
744 {
745 *pszDelimiter = '\0';
746
747 Bstr bstrName(pszProperty);
748 Bstr bstrValue(&pszDelimiter[1]);
749 bstrName.detachTo(mediumPropNames.appendedRaw());
750 bstrValue.detachTo(mediumPropValues.appendedRaw());
751 fModifyProperties = true;
752 }
753
754 RTStrFree(pszProperty);
755
756 if (!pszDelimiter)
757 return errorArgument(Disk::tr("Invalid --property argument '%s'"), ValueUnion.psz);
758 }
759 else
760 {
761 RTStrmPrintf(g_pStdErr, Disk::tr("Error: Failed to allocate memory for medium property '%s'\n"),
762 ValueUnion.psz);
763 return RTEXITCODE_FAILURE;
764 }
765 break;
766 }
767
768 case 'c': // --compact
769 fModifyCompact = true;
770 break;
771
772 case 'r': // --resize
773 cbResize = ValueUnion.u64 * _1M;
774 fModifyResize = true;
775 fModifyResizeMB = true; // do sanity check!
776 break;
777
778 case 'R': // --resizebyte
779 cbResize = ValueUnion.u64;
780 fModifyResize = true;
781 break;
782
783 case 'm': // --move
784 /* Get a new location */
785 pszNewLocation = RTPathAbsDup(ValueUnion.psz);
786 fMoveMedium = true;
787 break;
788
789 case 'l': // --setlocation
790 /* Get a new location */
791 pszNewLocation = RTPathAbsDup(ValueUnion.psz);
792 fSetNewLocation = true;
793 break;
794
795 case 'd': // --description
796 /* Get a new description */
797 pszNewLocation = RTStrDup(ValueUnion.psz);
798 fModifyDescription = true;
799 break;
800
801 case VINF_GETOPT_NOT_OPTION:
802 if (!pszFilenameOrUuid)
803 pszFilenameOrUuid = ValueUnion.psz;
804 else
805 return errorSyntax(Disk::tr("Invalid parameter '%s'"), ValueUnion.psz);
806 break;
807
808 default:
809 if (c > 0)
810 {
811 if (RT_C_IS_PRINT(c))
812 return errorSyntax(Disk::tr("Invalid option -%c"), c);
813 else
814 return errorSyntax(Disk::tr("Invalid option case %i"), c);
815 }
816 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
817 return errorSyntax(Disk::tr("unknown option: %s\n"), ValueUnion.psz);
818 else if (ValueUnion.pDef)
819 return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c);
820 else
821 return errorSyntax(Disk::tr("error: %Rrs"), c);
822 }
823 }
824
825 if (cmd == CMD_NONE)
826 cmd = CMD_DISK;
827
828 if (!pszFilenameOrUuid)
829 return errorSyntax(Disk::tr("Medium name or UUID required"));
830
831 if (!fModifyMediumType
832 && !fModifyAutoReset
833 && !fModifyProperties
834 && !fModifyCompact
835 && !fModifyResize
836 && !fMoveMedium
837 && !fSetNewLocation
838 && !fModifyDescription
839 )
840 return errorSyntax(Disk::tr("No operation specified"));
841
842 HRESULT hrc;
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 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 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 enum {
1776 CMD_NONE,
1777 CMD_DISK,
1778 CMD_DVD,
1779 CMD_FLOPPY
1780 } cmd = CMD_NONE;
1781 const char *pszFilenameOrUuid = NULL;
1782 bool fDelete = false;
1783
1784 int c;
1785 RTGETOPTUNION ValueUnion;
1786 RTGETOPTSTATE GetState;
1787 // start at 0 because main() has hacked both the argc and argv given to us
1788 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloseMediumOptions, RT_ELEMENTS(g_aCloseMediumOptions),
1789 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1790 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1791 {
1792 switch (c)
1793 {
1794 case 'd': // disk
1795 if (cmd != CMD_NONE)
1796 return errorSyntax(Disk::tr("Only one command can be specified: '%s'"), ValueUnion.psz);
1797 cmd = CMD_DISK;
1798 break;
1799
1800 case 'D': // DVD
1801 if (cmd != CMD_NONE)
1802 return errorSyntax(Disk::tr("Only one command can be specified: '%s'"), ValueUnion.psz);
1803 cmd = CMD_DVD;
1804 break;
1805
1806 case 'f': // floppy
1807 if (cmd != CMD_NONE)
1808 return errorSyntax(Disk::tr("Only one command can be specified: '%s'"), ValueUnion.psz);
1809 cmd = CMD_FLOPPY;
1810 break;
1811
1812 case 'r': // --delete
1813 fDelete = true;
1814 break;
1815
1816 case VINF_GETOPT_NOT_OPTION:
1817 if (!pszFilenameOrUuid)
1818 pszFilenameOrUuid = ValueUnion.psz;
1819 else
1820 return errorSyntax(Disk::tr("Invalid parameter '%s'"), ValueUnion.psz);
1821 break;
1822
1823 default:
1824 if (c > 0)
1825 {
1826 if (RT_C_IS_PRINT(c))
1827 return errorSyntax(Disk::tr("Invalid option -%c"), c);
1828 else
1829 return errorSyntax(Disk::tr("Invalid option case %i"), c);
1830 }
1831 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1832 return errorSyntax(Disk::tr("unknown option: %s\n"), ValueUnion.psz);
1833 else if (ValueUnion.pDef)
1834 return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c);
1835 else
1836 return errorSyntax(Disk::tr("error: %Rrs"), c);
1837 }
1838 }
1839
1840 /* check for required options */
1841 if (cmd == CMD_NONE)
1842 cmd = CMD_DISK;
1843
1844 if (!pszFilenameOrUuid)
1845 return errorSyntax(Disk::tr("Medium name or UUID required"));
1846
1847 HRESULT hrc = S_OK;
1848
1849 ComPtr<IMedium> pMedium;
1850 if (cmd == CMD_DISK)
1851 hrc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
1852 AccessMode_ReadWrite, pMedium,
1853 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1854 else if (cmd == CMD_DVD)
1855 hrc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD,
1856 AccessMode_ReadOnly, pMedium,
1857 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1858 else if (cmd == CMD_FLOPPY)
1859 hrc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy,
1860 AccessMode_ReadWrite, pMedium,
1861 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1862
1863 if (SUCCEEDED(hrc) && pMedium)
1864 {
1865 if (fDelete)
1866 {
1867 ComPtr<IProgress> pProgress;
1868 CHECK_ERROR(pMedium, DeleteStorage(pProgress.asOutParam()));
1869 if (SUCCEEDED(hrc))
1870 {
1871 showProgress(pProgress);
1872 CHECK_PROGRESS_ERROR(pProgress, (Disk::tr("Failed to delete medium")));
1873 }
1874 else
1875 RTMsgError(Disk::tr("Failed to delete medium. Error code %Rhrc"), hrc);
1876 }
1877 CHECK_ERROR(pMedium, Close());
1878 }
1879
1880 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1881}
1882
1883RTEXITCODE handleMediumProperty(HandlerArg *a)
1884{
1885 HRESULT hrc = S_OK;
1886 const char *pszCmd = NULL;
1887 enum {
1888 CMD_NONE,
1889 CMD_DISK,
1890 CMD_DVD,
1891 CMD_FLOPPY
1892 } cmd = CMD_NONE;
1893 const char *pszAction = NULL;
1894 const char *pszFilenameOrUuid = NULL;
1895 const char *pszProperty = NULL;
1896 ComPtr<IMedium> pMedium;
1897
1898 pszCmd = (a->argc > 0) ? a->argv[0] : "";
1899 if ( !RTStrICmp(pszCmd, "disk")
1900 || !RTStrICmp(pszCmd, "dvd")
1901 || !RTStrICmp(pszCmd, "floppy"))
1902 {
1903 if (!RTStrICmp(pszCmd, "disk"))
1904 cmd = CMD_DISK;
1905 else if (!RTStrICmp(pszCmd, "dvd"))
1906 cmd = CMD_DVD;
1907 else if (!RTStrICmp(pszCmd, "floppy"))
1908 cmd = CMD_FLOPPY;
1909 else
1910 {
1911 AssertMsgFailed((Disk::tr("unexpected parameter %s\n"), pszCmd));
1912 cmd = CMD_DISK;
1913 }
1914 a->argv++;
1915 a->argc--;
1916 }
1917 else
1918 {
1919 pszCmd = NULL;
1920 cmd = CMD_DISK;
1921 }
1922
1923 if (a->argc == 0)
1924 return errorSyntax(Disk::tr("Missing action"));
1925
1926 pszAction = a->argv[0];
1927 if ( RTStrICmp(pszAction, "set")
1928 && RTStrICmp(pszAction, "get")
1929 && RTStrICmp(pszAction, "delete"))
1930 return errorSyntax(Disk::tr("Invalid action given: %s"), pszAction);
1931
1932 if ( ( !RTStrICmp(pszAction, "set")
1933 && a->argc != 4)
1934 || ( RTStrICmp(pszAction, "set")
1935 && a->argc != 3))
1936 return errorSyntax(Disk::tr("Invalid number of arguments given for action: %s"), pszAction);
1937
1938 pszFilenameOrUuid = a->argv[1];
1939 pszProperty = a->argv[2];
1940
1941 if (cmd == CMD_DISK)
1942 hrc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
1943 AccessMode_ReadWrite, pMedium,
1944 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1945 else if (cmd == CMD_DVD)
1946 hrc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD,
1947 AccessMode_ReadOnly, pMedium,
1948 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1949 else if (cmd == CMD_FLOPPY)
1950 hrc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy,
1951 AccessMode_ReadWrite, pMedium,
1952 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1953 if (SUCCEEDED(hrc) && !pMedium.isNull())
1954 {
1955 if (!RTStrICmp(pszAction, "set"))
1956 {
1957 const char *pszValue = a->argv[3];
1958 CHECK_ERROR(pMedium, SetProperty(Bstr(pszProperty).raw(), Bstr(pszValue).raw()));
1959 }
1960 else if (!RTStrICmp(pszAction, "get"))
1961 {
1962 /*
1963 * Trigger a call to Medium::i_queryInfo()->VDOpen()->pfnOpen() to
1964 * open the virtual device and populate its properties for
1965 * Medium::getProperty() to retrieve.
1966 */
1967 MediumState_T state;
1968 CHECK_ERROR(pMedium, RefreshState(&state));
1969
1970 Bstr strVal;
1971 CHECK_ERROR(pMedium, GetProperty(Bstr(pszProperty).raw(), strVal.asOutParam()));
1972 if (SUCCEEDED(hrc))
1973 RTPrintf("%s=%ls\n", pszProperty, strVal.raw());
1974 }
1975 else if (!RTStrICmp(pszAction, "delete"))
1976 {
1977 CHECK_ERROR(pMedium, SetProperty(Bstr(pszProperty).raw(), Bstr().raw()));
1978 /** @todo */
1979 }
1980 }
1981
1982 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1983}
1984
1985static const RTGETOPTDEF g_aEncryptMediumOptions[] =
1986{
1987 { "--newpassword", 'n', RTGETOPT_REQ_STRING },
1988 { "--oldpassword", 'o', RTGETOPT_REQ_STRING },
1989 { "--cipher", 'c', RTGETOPT_REQ_STRING },
1990 { "--newpasswordid", 'i', RTGETOPT_REQ_STRING }
1991};
1992
1993RTEXITCODE handleEncryptMedium(HandlerArg *a)
1994{
1995 HRESULT hrc;
1996 ComPtr<IMedium> hardDisk;
1997 const char *pszPasswordNew = NULL;
1998 const char *pszPasswordOld = NULL;
1999 const char *pszCipher = NULL;
2000 const char *pszFilenameOrUuid = NULL;
2001 const char *pszNewPasswordId = NULL;
2002 Utf8Str strPasswordNew;
2003 Utf8Str strPasswordOld;
2004
2005 int c;
2006 RTGETOPTUNION ValueUnion;
2007 RTGETOPTSTATE GetState;
2008 // start at 0 because main() has hacked both the argc and argv given to us
2009 RTGetOptInit(&GetState, a->argc, a->argv, g_aEncryptMediumOptions, RT_ELEMENTS(g_aEncryptMediumOptions),
2010 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
2011 while ((c = RTGetOpt(&GetState, &ValueUnion)))
2012 {
2013 switch (c)
2014 {
2015 case 'n': // --newpassword
2016 pszPasswordNew = ValueUnion.psz;
2017 break;
2018
2019 case 'o': // --oldpassword
2020 pszPasswordOld = ValueUnion.psz;
2021 break;
2022
2023 case 'c': // --cipher
2024 pszCipher = ValueUnion.psz;
2025 break;
2026
2027 case 'i': // --newpasswordid
2028 pszNewPasswordId = ValueUnion.psz;
2029 break;
2030
2031 case VINF_GETOPT_NOT_OPTION:
2032 if (!pszFilenameOrUuid)
2033 pszFilenameOrUuid = ValueUnion.psz;
2034 else
2035 return errorSyntax(Disk::tr("Invalid parameter '%s'"), ValueUnion.psz);
2036 break;
2037
2038 default:
2039 if (c > 0)
2040 {
2041 if (RT_C_IS_PRINT(c))
2042 return errorSyntax(Disk::tr("Invalid option -%c"), c);
2043 else
2044 return errorSyntax(Disk::tr("Invalid option case %i"), c);
2045 }
2046 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
2047 return errorSyntax(Disk::tr("unknown option: %s\n"), ValueUnion.psz);
2048 else if (ValueUnion.pDef)
2049 return errorSyntax("%s: %Rrs", ValueUnion.pDef->pszLong, c);
2050 else
2051 return errorSyntax(Disk::tr("error: %Rrs"), c);
2052 }
2053 }
2054
2055 if (!pszFilenameOrUuid)
2056 return errorSyntax(Disk::tr("Disk name or UUID required"));
2057
2058 if (!pszPasswordNew && !pszPasswordOld)
2059 return errorSyntax(Disk::tr("No password specified"));
2060
2061 if ( (pszPasswordNew && !pszNewPasswordId)
2062 || (!pszPasswordNew && pszNewPasswordId))
2063 return errorSyntax(Disk::tr("A new password must always have a valid identifier set at the same time"));
2064
2065 if (pszPasswordNew)
2066 {
2067 if (!RTStrCmp(pszPasswordNew, "-"))
2068 {
2069 /* Get password from console. */
2070 RTEXITCODE rcExit = readPasswordFromConsole(&strPasswordNew, Disk::tr("Enter new password:"));
2071 if (rcExit == RTEXITCODE_FAILURE)
2072 return rcExit;
2073 }
2074 else
2075 {
2076 RTEXITCODE rcExit = readPasswordFile(pszPasswordNew, &strPasswordNew);
2077 if (rcExit == RTEXITCODE_FAILURE)
2078 {
2079 RTMsgError(Disk::tr("Failed to read new password from file"));
2080 return rcExit;
2081 }
2082 }
2083 }
2084
2085 if (pszPasswordOld)
2086 {
2087 if (!RTStrCmp(pszPasswordOld, "-"))
2088 {
2089 /* Get password from console. */
2090 RTEXITCODE rcExit = readPasswordFromConsole(&strPasswordOld, Disk::tr("Enter old password:"));
2091 if (rcExit == RTEXITCODE_FAILURE)
2092 return rcExit;
2093 }
2094 else
2095 {
2096 RTEXITCODE rcExit = readPasswordFile(pszPasswordOld, &strPasswordOld);
2097 if (rcExit == RTEXITCODE_FAILURE)
2098 {
2099 RTMsgError(Disk::tr("Failed to read old password from file"));
2100 return rcExit;
2101 }
2102 }
2103 }
2104
2105 /* Always open the medium if necessary, there is no other way. */
2106 hrc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
2107 AccessMode_ReadWrite, hardDisk,
2108 false /* fForceNewUuidOnOpen */, false /* fSilent */);
2109 if (FAILED(hrc))
2110 return RTEXITCODE_FAILURE;
2111 if (hardDisk.isNull())
2112 return RTMsgErrorExit(RTEXITCODE_FAILURE, Disk::tr("Invalid hard disk reference, avoiding crash"));
2113
2114 ComPtr<IProgress> progress;
2115 CHECK_ERROR(hardDisk, ChangeEncryption(Bstr(strPasswordOld).raw(), Bstr(pszCipher).raw(),
2116 Bstr(strPasswordNew).raw(), Bstr(pszNewPasswordId).raw(),
2117 progress.asOutParam()));
2118 if (SUCCEEDED(hrc))
2119 hrc = showProgress(progress);
2120 if (FAILED(hrc))
2121 {
2122 if (hrc == E_NOTIMPL)
2123 RTMsgError(Disk::tr("Encrypt hard disk operation is not implemented!"));
2124 else if (hrc == VBOX_E_NOT_SUPPORTED)
2125 RTMsgError(Disk::tr("Encrypt hard disk operation for this cipher is not implemented yet!"));
2126 else if (!progress.isNull())
2127 CHECK_PROGRESS_ERROR(progress, (Disk::tr("Failed to encrypt hard disk")));
2128 else
2129 RTMsgError(Disk::tr("Failed to encrypt hard disk!"));
2130 }
2131
2132 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2133}
2134
2135RTEXITCODE handleCheckMediumPassword(HandlerArg *a)
2136{
2137 HRESULT hrc;
2138 ComPtr<IMedium> hardDisk;
2139 const char *pszFilenameOrUuid = NULL;
2140 Utf8Str strPassword;
2141
2142 if (a->argc != 2)
2143 return errorSyntax(Disk::tr("Invalid number of arguments: %d"), a->argc);
2144
2145 pszFilenameOrUuid = a->argv[0];
2146
2147 if (!RTStrCmp(a->argv[1], "-"))
2148 {
2149 /* Get password from console. */
2150 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, Disk::tr("Enter password:"));
2151 if (rcExit == RTEXITCODE_FAILURE)
2152 return rcExit;
2153 }
2154 else
2155 {
2156 RTEXITCODE rcExit = readPasswordFile(a->argv[1], &strPassword);
2157 if (rcExit == RTEXITCODE_FAILURE)
2158 {
2159 RTMsgError(Disk::tr("Failed to read password from file"));
2160 return rcExit;
2161 }
2162 }
2163
2164 /* Always open the medium if necessary, there is no other way. */
2165 hrc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
2166 AccessMode_ReadWrite, hardDisk,
2167 false /* fForceNewUuidOnOpen */, false /* fSilent */);
2168 if (FAILED(hrc))
2169 return RTEXITCODE_FAILURE;
2170 if (hardDisk.isNull())
2171 return RTMsgErrorExit(RTEXITCODE_FAILURE, Disk::tr("Invalid hard disk reference, avoiding crash"));
2172
2173 CHECK_ERROR(hardDisk, CheckEncryptionPassword(Bstr(strPassword).raw()));
2174 if (SUCCEEDED(hrc))
2175 RTPrintf(Disk::tr("The given password is correct\n"));
2176 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2177}
2178
2179
2180/*********************************************************************************************************************************
2181* The mediumio command *
2182*********************************************************************************************************************************/
2183
2184/**
2185 * Common MediumIO options.
2186 */
2187typedef struct MEDIUMIOCOMMONOPT
2188{
2189 const char *pszFilenameOrUuid;
2190 DeviceType_T enmDeviceType;
2191 const char *pszPasswordFile;
2192} MEDIUMIOCOMMONOPT;
2193typedef MEDIUMIOCOMMONOPT *PMEDIUMIOCOMMONOPT;
2194typedef MEDIUMIOCOMMONOPT const *PCMEDIUMIOCOMMONOPT;
2195
2196/* For RTGETOPTDEF array initializer. */
2197#define MEDIUMIOCOMMONOPT_DEFS() \
2198 { "--disk", 'd', RTGETOPT_REQ_STRING }, \
2199 { "--harddisk", 'd', RTGETOPT_REQ_STRING }, \
2200 { "disk", 'd', RTGETOPT_REQ_STRING }, \
2201 { "harddisk", 'd', RTGETOPT_REQ_STRING }, \
2202 { "--dvd", 'D', RTGETOPT_REQ_STRING }, \
2203 { "--iso", 'D', RTGETOPT_REQ_STRING }, \
2204 { "dvd", 'D', RTGETOPT_REQ_STRING }, \
2205 { "iso", 'D', RTGETOPT_REQ_STRING }, \
2206 { "--floppy", 'f', RTGETOPT_REQ_STRING }, \
2207 { "floppy", 'f', RTGETOPT_REQ_STRING }, \
2208 { "--password-file", 'P', RTGETOPT_REQ_STRING }
2209
2210/* For option switch. */
2211#define MEDIUMIOCOMMONOPT_CASES(a_pCommonOpts) \
2212 case 'd': \
2213 (a_pCommonOpts)->enmDeviceType = DeviceType_HardDisk; \
2214 (a_pCommonOpts)->pszFilenameOrUuid = ValueUnion.psz; \
2215 break; \
2216 case 'D': \
2217 (a_pCommonOpts)->enmDeviceType = DeviceType_DVD; \
2218 (a_pCommonOpts)->pszFilenameOrUuid = ValueUnion.psz; \
2219 break; \
2220 case 'f': \
2221 (a_pCommonOpts)->enmDeviceType = DeviceType_Floppy; \
2222 (a_pCommonOpts)->pszFilenameOrUuid = ValueUnion.psz; \
2223 break; \
2224 case 'P': \
2225 (a_pCommonOpts)->pszPasswordFile = ValueUnion.psz; \
2226 break
2227
2228
2229/**
2230 * Worker for mediumio operations that returns a IMediumIO for the specified
2231 * medium.
2232 *
2233 * @returns Exit code.
2234 * @param pHandler The handler state structure (for IVirtualBox).
2235 * @param pCommonOpts Common mediumio options.
2236 * @param fWritable Whether to open writable (true) or read only
2237 * (false).
2238 * @param rPtrMediumIO Where to return the IMediumIO pointer.
2239 * @param pcbMedium Where to return the meidum size. Optional.
2240 */
2241static RTEXITCODE mediumIOOpenMediumForIO(HandlerArg *pHandler, PCMEDIUMIOCOMMONOPT pCommonOpts, bool fWritable,
2242 ComPtr<IMediumIO> &rPtrMediumIO, uint64_t *pcbMedium = NULL)
2243{
2244 /* Clear returns. */
2245 if (pcbMedium)
2246 *pcbMedium = 0;
2247 rPtrMediumIO.setNull();
2248
2249 /*
2250 * Make sure a medium was specified already.
2251 */
2252 if (pCommonOpts->enmDeviceType == DeviceType_Null)
2253 return errorSyntax(Disk::tr("No medium specified!"));
2254
2255 /*
2256 * Read the password.
2257 */
2258 Bstr bstrPassword;
2259 if (pCommonOpts->pszPasswordFile)
2260 {
2261 Utf8Str strPassword;
2262 RTEXITCODE rcExit;
2263 if (pCommonOpts->pszPasswordFile[0] == '-' && pCommonOpts->pszPasswordFile[1] == '\0')
2264 rcExit = readPasswordFromConsole(&strPassword, Disk::tr("Enter encryption password:"));
2265 else
2266 rcExit = readPasswordFile(pCommonOpts->pszPasswordFile, &strPassword);
2267 if (rcExit != RTEXITCODE_SUCCESS)
2268 return rcExit;
2269 bstrPassword = strPassword;
2270 strPassword.assign(strPassword.length(), '*');
2271 }
2272
2273 /*
2274 * Open the medium and then get I/O access to it.
2275 */
2276 ComPtr<IMedium> ptrMedium;
2277 HRESULT hrc = openMedium(pHandler, pCommonOpts->pszFilenameOrUuid, pCommonOpts->enmDeviceType,
2278 fWritable ? AccessMode_ReadWrite : AccessMode_ReadOnly,
2279 ptrMedium, false /* fForceNewUuidOnOpen */, false /* fSilent */);
2280 if (SUCCEEDED(hrc))
2281 {
2282 CHECK_ERROR2I_STMT(ptrMedium, OpenForIO(fWritable, bstrPassword.raw(), rPtrMediumIO.asOutParam()), hrc = hrcCheck);
2283
2284 /*
2285 * If the size is requested get it after we've opened it.
2286 */
2287 if (pcbMedium && SUCCEEDED(hrc))
2288 {
2289 LONG64 cbLogical = 0;
2290 CHECK_ERROR2I_STMT(ptrMedium, COMGETTER(LogicalSize)(&cbLogical), hrc = hrcCheck);
2291 *pcbMedium = cbLogical;
2292 if (!SUCCEEDED(hrc))
2293 rPtrMediumIO.setNull();
2294 }
2295 }
2296
2297 if (bstrPassword.isNotEmpty())
2298 memset(bstrPassword.mutableRaw(), '*', bstrPassword.length() * sizeof(RTUTF16));
2299 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2300}
2301
2302
2303/**
2304 * mediumio formatfat
2305 */
2306static RTEXITCODE handleMediumIOFormatFat(HandlerArg *a, int iFirst, PMEDIUMIOCOMMONOPT pCommonOpts)
2307{
2308 /*
2309 * Parse the options.
2310 */
2311 bool fQuick = false;
2312 static const RTGETOPTDEF s_aOptions[] =
2313 {
2314 MEDIUMIOCOMMONOPT_DEFS(),
2315 { "--quick", 'q', RTGETOPT_REQ_NOTHING },
2316 };
2317
2318 RTGETOPTSTATE GetState;
2319 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2320 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2321 RTGETOPTUNION ValueUnion;
2322 while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2323 {
2324 switch (vrc)
2325 {
2326 MEDIUMIOCOMMONOPT_CASES(pCommonOpts);
2327
2328 case 'q':
2329 fQuick = true;
2330 break;
2331
2332 default:
2333 return errorGetOpt(vrc, &ValueUnion);
2334 }
2335 }
2336
2337 /*
2338 * Open the medium for I/O and format it.
2339 */
2340 ComPtr<IMediumIO> ptrMediumIO;
2341 RTEXITCODE rcExit = mediumIOOpenMediumForIO(a, pCommonOpts, true /*fWritable*/, ptrMediumIO);
2342 if (rcExit != RTEXITCODE_SUCCESS)
2343 return rcExit;
2344 CHECK_ERROR2I_RET(ptrMediumIO, FormatFAT(fQuick), RTEXITCODE_FAILURE);
2345 return RTEXITCODE_SUCCESS;
2346}
2347
2348/**
2349 * mediumio cat
2350 */
2351static RTEXITCODE handleMediumIOCat(HandlerArg *a, int iFirst, PMEDIUMIOCOMMONOPT pCommonOpts)
2352{
2353 /*
2354 * Parse the options.
2355 */
2356 static const RTGETOPTDEF s_aOptions[] =
2357 {
2358 MEDIUMIOCOMMONOPT_DEFS(),
2359 { "--hex", 'H', RTGETOPT_REQ_NOTHING },
2360 { "--offset", 'o', RTGETOPT_REQ_UINT64 },
2361 { "--output", 'O', RTGETOPT_REQ_STRING },
2362 { "--size", 's', RTGETOPT_REQ_UINT64 },
2363 };
2364 bool fHex = false;
2365 uint64_t off = 0;
2366 const char *pszOutput = NULL;
2367 uint64_t cb = UINT64_MAX;
2368
2369 RTGETOPTSTATE GetState;
2370 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2371 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2372 RTGETOPTUNION ValueUnion;
2373 while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2374 {
2375 switch (vrc)
2376 {
2377 MEDIUMIOCOMMONOPT_CASES(pCommonOpts);
2378
2379 case 'H':
2380 fHex = true;
2381 break;
2382
2383 case 'o':
2384 off = ValueUnion.u64;
2385 break;
2386
2387 case 'O':
2388 pszOutput = ValueUnion.psz;
2389 break;
2390
2391 case 's':
2392 cb = ValueUnion.u64;
2393 break;
2394
2395 default:
2396 return errorGetOpt(vrc, &ValueUnion);
2397 }
2398 }
2399
2400 /*
2401 * Open the medium for I/O.
2402 */
2403 ComPtr<IMediumIO> ptrMediumIO;
2404 uint64_t cbMedium;
2405 RTEXITCODE rcExit = mediumIOOpenMediumForIO(a, pCommonOpts, false /*fWritable*/, ptrMediumIO, &cbMedium);
2406 if (rcExit == RTEXITCODE_SUCCESS)
2407 {
2408 /*
2409 * Do we have an output file or do we write to stdout?
2410 */
2411 PRTSTREAM pOut = NULL;
2412 if (pszOutput && (pszOutput[0] != '-' || pszOutput[1] != '\0'))
2413 {
2414 vrc = RTStrmOpen(pszOutput, fHex ? "wt" : "wb", &pOut);
2415 if (RT_FAILURE(vrc))
2416 rcExit = RTMsgErrorExitFailure(Disk::tr("Error opening '%s' for writing: %Rrc"), pszOutput, vrc);
2417 }
2418 else
2419 {
2420 pOut = g_pStdOut;
2421 if (!fHex)
2422 RTStrmSetMode(pOut, true, -1);
2423 }
2424
2425 if (rcExit == RTEXITCODE_SUCCESS)
2426 {
2427 /*
2428 * Adjust 'cb' now that we've got the medium size.
2429 */
2430 if (off >= cbMedium)
2431 {
2432 RTMsgWarning(Disk::tr("Specified offset (%#RX64) is beyond the end of the medium (%#RX64)"), off, cbMedium);
2433 cb = 0;
2434 }
2435 else if ( cb > cbMedium
2436 || cb + off > cbMedium)
2437 cb = cbMedium - off;
2438
2439 /*
2440 * Hex dump preps. (The duplication detection is making ASSUMPTIONS about
2441 * all the reads being a multiple of cchWidth, except for the final one.)
2442 */
2443 char abHexBuf[16] = { 0 };
2444 size_t cbHexBuf = 0;
2445 unsigned const cchWidth = RT_ELEMENTS(abHexBuf);
2446 uint64_t const offEndDupCheck = cb - cchWidth;
2447 uint64_t cDuplicates = 0;
2448
2449 /*
2450 * Do the reading.
2451 */
2452 while (cb > 0)
2453 {
2454 char szLine[32 + cchWidth * 4 + 32];
2455
2456 /* Do the reading. */
2457 uint32_t const cbToRead = (uint32_t)RT_MIN(cb, _128K);
2458 SafeArray<BYTE> SafeArrayBuf;
2459 HRESULT hrc = ptrMediumIO->Read(off, cbToRead, ComSafeArrayAsOutParam(SafeArrayBuf));
2460 if (FAILED(hrc))
2461 {
2462 RTStrPrintf(szLine, sizeof(szLine), Disk::tr("Read(%zu bytes at %#RX64)", "", cbToRead), cbToRead, off);
2463 com::GlueHandleComError(ptrMediumIO, szLine, hrc, __FILE__, __LINE__);
2464 break;
2465 }
2466
2467 /* Output the data. */
2468 size_t const cbReturned = SafeArrayBuf.size();
2469 if (cbReturned)
2470 {
2471 BYTE const *pbBuf = SafeArrayBuf.raw();
2472 if (!fHex)
2473 vrc = RTStrmWrite(pOut, pbBuf, cbReturned);
2474 else
2475 {
2476 /* hexdump -C */
2477 vrc = VINF_SUCCESS;
2478 uint64_t offHex = off;
2479 uint64_t const offHexEnd = off + cbReturned;
2480 while (offHex < offHexEnd)
2481 {
2482 if ( offHex >= offEndDupCheck
2483 || cbHexBuf == 0
2484 || memcmp(pbBuf, abHexBuf, cchWidth) != 0
2485 || ( cDuplicates == 0
2486 && ( offHex + cchWidth >= offEndDupCheck
2487 || memcmp(pbBuf + cchWidth, pbBuf, cchWidth) != 0)) )
2488 {
2489 if (cDuplicates > 0)
2490 {
2491 RTStrmPrintf(pOut, Disk::tr("********** <ditto x %RU64>\n"), cDuplicates);
2492 cDuplicates = 0;
2493 }
2494
2495 size_t cch = RTStrPrintf(szLine, sizeof(szLine), "%012RX64:", offHex);
2496 unsigned i;
2497 for (i = 0; i < cchWidth && offHex + i < offHexEnd; i++)
2498 {
2499 static const char s_szHexDigits[17] = "0123456789abcdef";
2500 szLine[cch++] = (i & 7) || i == 0 ? ' ' : '-';
2501 uint8_t const u8 = pbBuf[i];
2502 szLine[cch++] = s_szHexDigits[u8 >> 4];
2503 szLine[cch++] = s_szHexDigits[u8 & 0xf];
2504 }
2505 while (i++ < cchWidth)
2506 {
2507 szLine[cch++] = ' ';
2508 szLine[cch++] = ' ';
2509 szLine[cch++] = ' ';
2510 }
2511 szLine[cch++] = ' ';
2512
2513 for (i = 0; i < cchWidth && offHex + i < offHexEnd; i++)
2514 {
2515 uint8_t const u8 = pbBuf[i];
2516 szLine[cch++] = u8 < 127 && u8 >= 32 ? u8 : '.';
2517 }
2518 szLine[cch++] = '\n';
2519 szLine[cch] = '\0';
2520
2521 vrc = RTStrmWrite(pOut, szLine, cch);
2522 if (RT_FAILURE(vrc))
2523 break;
2524
2525
2526 /* copy bytes over to the duplication detection buffer. */
2527 cbHexBuf = (size_t)RT_MIN(cchWidth, offHexEnd - offHex);
2528 memcpy(abHexBuf, pbBuf, cbHexBuf);
2529 }
2530 else
2531 cDuplicates++;
2532
2533 /* Advance to next line. */
2534 pbBuf += cchWidth;
2535 offHex += cchWidth;
2536 }
2537 }
2538 if (RT_FAILURE(vrc))
2539 {
2540 rcExit = RTMsgErrorExitFailure(Disk::tr("Error writing to '%s': %Rrc"), pszOutput, vrc);
2541 break;
2542 }
2543 }
2544
2545 /* Advance. */
2546 if (cbReturned != cbToRead)
2547 {
2548 rcExit = RTMsgErrorExitFailure(Disk::tr("Expected read() at offset %RU64 (%#RX64) to return %#zx bytes, only got %#zx!\n",
2549 "", cbReturned),
2550 off, off, cbReturned, cbToRead);
2551 break;
2552 }
2553 off += cbReturned;
2554 cb -= cbReturned;
2555 }
2556
2557 /*
2558 * Close output.
2559 */
2560 if (pOut != g_pStdOut)
2561 {
2562 vrc = RTStrmClose(pOut);
2563 if (RT_FAILURE(vrc))
2564 rcExit = RTMsgErrorExitFailure(Disk::tr("Error closing '%s': %Rrc"), pszOutput, vrc);
2565 }
2566 else if (!fHex)
2567 RTStrmSetMode(pOut, false, -1);
2568 }
2569 }
2570 return rcExit;
2571}
2572
2573/**
2574 * mediumio stream
2575 */
2576static RTEXITCODE handleMediumIOStream(HandlerArg *a, int iFirst, PMEDIUMIOCOMMONOPT pCommonOpts)
2577{
2578 /*
2579 * Parse the options.
2580 */
2581 static const RTGETOPTDEF s_aOptions[] =
2582 {
2583 MEDIUMIOCOMMONOPT_DEFS(),
2584 { "--output", 'O', RTGETOPT_REQ_STRING },
2585 { "--format", 'F', RTGETOPT_REQ_STRING },
2586 { "--variant", 'v', RTGETOPT_REQ_STRING }
2587 };
2588 const char *pszOutput = NULL;
2589 MediumVariant_T enmMediumVariant = MediumVariant_Standard;
2590 Bstr strFormat;
2591
2592 RTGETOPTSTATE GetState;
2593 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2594 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2595 RTGETOPTUNION ValueUnion;
2596 while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2597 {
2598 switch (vrc)
2599 {
2600 MEDIUMIOCOMMONOPT_CASES(pCommonOpts);
2601
2602 case 'O':
2603 pszOutput = ValueUnion.psz;
2604 break;
2605 case 'F':
2606 strFormat = ValueUnion.psz;
2607 break;
2608 case 'v': // --variant
2609 {
2610 vrc = parseMediumVariant(ValueUnion.psz, &enmMediumVariant);
2611 if (RT_FAILURE(vrc))
2612 return errorArgument(Disk::tr("Invalid medium variant '%s'"), ValueUnion.psz);
2613 break;
2614 }
2615
2616 default:
2617 return errorGetOpt(vrc, &ValueUnion);
2618 }
2619 }
2620
2621 /*
2622 * Open the medium for I/O.
2623 */
2624 ComPtr<IMediumIO> ptrMediumIO;
2625 uint64_t cbMedium;
2626 RTEXITCODE rcExit = mediumIOOpenMediumForIO(a, pCommonOpts, false /*fWritable*/, ptrMediumIO, &cbMedium);
2627 if (rcExit == RTEXITCODE_SUCCESS)
2628 {
2629 /*
2630 * Do we have an output file or do we write to stdout?
2631 */
2632 PRTSTREAM pOut = NULL;
2633 if (pszOutput && (pszOutput[0] != '-' || pszOutput[1] != '\0'))
2634 {
2635 vrc = RTStrmOpen(pszOutput, "wb", &pOut);
2636 if (RT_FAILURE(vrc))
2637 rcExit = RTMsgErrorExitFailure(Disk::tr("Error opening '%s' for writing: %Rrc"), pszOutput, vrc);
2638 }
2639 else
2640 {
2641 pOut = g_pStdOut;
2642 RTStrmSetMode(pOut, true, -1);
2643 }
2644
2645 if (rcExit == RTEXITCODE_SUCCESS)
2646 {
2647 ComPtr<IDataStream> ptrDataStream;
2648 ComPtr<IProgress> ptrProgress;
2649
2650 com::SafeArray<MediumVariant_T> l_variants(sizeof(MediumVariant_T)*8);
2651
2652 for (ULONG i = 0; i < l_variants.size(); ++i)
2653 {
2654 ULONG temp = enmMediumVariant;
2655 temp &= 1<<i;
2656 l_variants [i] = (MediumVariant_T)temp;
2657 }
2658
2659 HRESULT hrc = ptrMediumIO->ConvertToStream(strFormat.raw(), ComSafeArrayAsInParam(l_variants), 10 * _1M, ptrDataStream.asOutParam(), ptrProgress.asOutParam());
2660 if (hrc == S_OK)
2661 {
2662 /* Read until we reached the end of the stream. */
2663 for (;;)
2664 {
2665 SafeArray<BYTE> SafeArrayBuf;
2666
2667 hrc = ptrDataStream->Read(_64K, 0 /*Infinite wait*/, ComSafeArrayAsOutParam(SafeArrayBuf));
2668 if ( FAILED(hrc)
2669 || SafeArrayBuf.size() == 0)
2670 break;
2671
2672 /* Output the data. */
2673 size_t const cbReturned = SafeArrayBuf.size();
2674 if (cbReturned)
2675 {
2676 BYTE const *pbBuf = SafeArrayBuf.raw();
2677 vrc = RTStrmWrite(pOut, pbBuf, cbReturned);
2678 if (RT_FAILURE(vrc))
2679 {
2680 rcExit = RTMsgErrorExitFailure(Disk::tr("Error writing to '%s': %Rrc"), pszOutput, vrc);
2681 break;
2682 }
2683 }
2684
2685 /** @todo Check progress. */
2686 }
2687 }
2688 else
2689 {
2690 com::GlueHandleComError(ptrMediumIO, "ConvertToStream()", hrc, __FILE__, __LINE__);
2691 rcExit = RTEXITCODE_FAILURE;
2692 }
2693
2694 /*
2695 * Close output.
2696 */
2697 if (pOut != g_pStdOut)
2698 {
2699 vrc = RTStrmClose(pOut);
2700 if (RT_FAILURE(vrc))
2701 rcExit = RTMsgErrorExitFailure(Disk::tr("Error closing '%s': %Rrc"), pszOutput, vrc);
2702 }
2703 else
2704 RTStrmSetMode(pOut, false, -1);
2705 }
2706 }
2707 return rcExit;
2708}
2709
2710
2711RTEXITCODE handleMediumIO(HandlerArg *a)
2712{
2713 /*
2714 * Parse image-option and sub-command.
2715 */
2716 static const RTGETOPTDEF s_aOptions[] =
2717 {
2718 MEDIUMIOCOMMONOPT_DEFS(),
2719 /* sub-commands */
2720 { "formatfat", 1000, RTGETOPT_REQ_NOTHING },
2721 { "cat", 1001, RTGETOPT_REQ_NOTHING },
2722 { "stream", 1002, RTGETOPT_REQ_NOTHING },
2723 };
2724 MEDIUMIOCOMMONOPT CommonOpts = { NULL, DeviceType_Null, NULL };
2725
2726 RTGETOPTSTATE GetState;
2727 int vrc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
2728 AssertRCReturn(vrc, RTEXITCODE_FAILURE);
2729 RTGETOPTUNION ValueUnion;
2730 while ((vrc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2731 {
2732 switch (vrc)
2733 {
2734 MEDIUMIOCOMMONOPT_CASES(&CommonOpts);
2735
2736 /* Sub-commands: */
2737 case 1000:
2738 setCurrentSubcommand(HELP_SCOPE_MEDIUMIO_FORMATFAT);
2739 return handleMediumIOFormatFat(a, GetState.iNext, &CommonOpts);
2740 case 1001:
2741 setCurrentSubcommand(HELP_SCOPE_MEDIUMIO_CAT);
2742 return handleMediumIOCat(a, GetState.iNext, &CommonOpts);
2743 case 1002:
2744 setCurrentSubcommand(HELP_SCOPE_MEDIUMIO_STREAM);
2745 return handleMediumIOStream(a, GetState.iNext, &CommonOpts);
2746
2747 case VINF_GETOPT_NOT_OPTION:
2748 return errorUnknownSubcommand(ValueUnion.psz);
2749
2750 default:
2751 return errorGetOpt(vrc, &ValueUnion);
2752 }
2753 }
2754 return errorNoSubcommand();
2755}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette