VirtualBox

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

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

scm copyright and license note update

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