VirtualBox

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

Last change on this file since 94177 was 94177, checked in by vboxsync, 3 years ago

doc/manual,FE/VBoxManage: Convert closemedium command to refentry documentation, bugref:9186

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

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