VirtualBox

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

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

Frontends + Main: Adjust to the new rules wrt. to rc -> hrc,vrc usage. This also fixes quite a few bugs wrt shadow variables, wrong return values and output error translations / exit codes. Also see @todos added. ​​bugref:10223

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