VirtualBox

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

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

FE/VBoxManage: Remove the now unused VBoxManageHelp build target and the VBOX_ONLY_DOCS #ifdef's in the code, ​bugref:9186

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

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