VirtualBox

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

Last change on this file since 86663 was 85936, checked in by vboxsync, 4 years ago

VBoxManage/createmedium: gcc 4.x build fix. Use RTMemDup instead of RTMemAlloc+memcpy. bugref:9224

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

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