VirtualBox

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

最後變更 在這個檔案從77622是 77622,由 vboxsync 提交於 6 年 前

Bug fix/ syntax cleanup of AllocationBlockSize / createmedium property creation code

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

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