VirtualBox

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

Last change on this file since 98101 was 97935, checked in by vboxsync, 2 years ago

Main: Added resizeAndCloneTo(). Modified cloneTo(). bugref:10090

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