VirtualBox

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

Last change on this file since 52320 was 51889, checked in by vboxsync, 10 years ago

FE/VBoxManage: Interface for setting, getting and deleting medium properties

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.3 KB
Line 
1/* $Id: VBoxManageDisk.cpp 51889 2014-07-06 19:40:14Z vboxsync $ */
2/** @file
3 * VBoxManage - The disk related commands.
4 */
5
6/*
7 * Copyright (C) 2006-2013 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* Header Files *
22*******************************************************************************/
23#include <VBox/com/com.h>
24#include <VBox/com/array.h>
25#include <VBox/com/ErrorInfo.h>
26#include <VBox/com/errorprint.h>
27#include <VBox/com/VirtualBox.h>
28
29#include <iprt/asm.h>
30#include <iprt/file.h>
31#include <iprt/path.h>
32#include <iprt/param.h>
33#include <iprt/stream.h>
34#include <iprt/string.h>
35#include <iprt/ctype.h>
36#include <iprt/getopt.h>
37#include <VBox/log.h>
38#include <VBox/vd.h>
39
40#include "VBoxManage.h"
41using namespace com;
42
43
44// funcs
45///////////////////////////////////////////////////////////////////////////////
46
47
48static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va)
49{
50 RTMsgErrorV(pszFormat, va);
51 RTMsgError("Error code %Rrc at %s(%u) in function %s", rc, RT_SRC_POS_ARGS);
52}
53
54
55static int parseDiskVariant(const char *psz, MediumVariant_T *pDiskVariant)
56{
57 int rc = VINF_SUCCESS;
58 unsigned DiskVariant = (unsigned)(*pDiskVariant);
59 while (psz && *psz && RT_SUCCESS(rc))
60 {
61 size_t len;
62 const char *pszComma = strchr(psz, ',');
63 if (pszComma)
64 len = pszComma - psz;
65 else
66 len = strlen(psz);
67 if (len > 0)
68 {
69 // Parsing is intentionally inconsistent: "standard" resets the
70 // variant, whereas the other flags are cumulative.
71 if (!RTStrNICmp(psz, "standard", len))
72 DiskVariant = MediumVariant_Standard;
73 else if ( !RTStrNICmp(psz, "fixed", len)
74 || !RTStrNICmp(psz, "static", len))
75 DiskVariant |= MediumVariant_Fixed;
76 else if (!RTStrNICmp(psz, "Diff", len))
77 DiskVariant |= MediumVariant_Diff;
78 else if (!RTStrNICmp(psz, "split2g", len))
79 DiskVariant |= MediumVariant_VmdkSplit2G;
80 else if ( !RTStrNICmp(psz, "stream", len)
81 || !RTStrNICmp(psz, "streamoptimized", len))
82 DiskVariant |= MediumVariant_VmdkStreamOptimized;
83 else if (!RTStrNICmp(psz, "esx", len))
84 DiskVariant |= MediumVariant_VmdkESX;
85 else
86 rc = VERR_PARSE_ERROR;
87 }
88 if (pszComma)
89 psz += len + 1;
90 else
91 psz += len;
92 }
93
94 if (RT_SUCCESS(rc))
95 *pDiskVariant = (MediumVariant_T)DiskVariant;
96 return rc;
97}
98
99int parseDiskType(const char *psz, MediumType_T *pDiskType)
100{
101 int rc = VINF_SUCCESS;
102 MediumType_T DiskType = MediumType_Normal;
103 if (!RTStrICmp(psz, "normal"))
104 DiskType = MediumType_Normal;
105 else if (!RTStrICmp(psz, "immutable"))
106 DiskType = MediumType_Immutable;
107 else if (!RTStrICmp(psz, "writethrough"))
108 DiskType = MediumType_Writethrough;
109 else if (!RTStrICmp(psz, "shareable"))
110 DiskType = MediumType_Shareable;
111 else if (!RTStrICmp(psz, "readonly"))
112 DiskType = MediumType_Readonly;
113 else if (!RTStrICmp(psz, "multiattach"))
114 DiskType = MediumType_MultiAttach;
115 else
116 rc = VERR_PARSE_ERROR;
117
118 if (RT_SUCCESS(rc))
119 *pDiskType = DiskType;
120 return rc;
121}
122
123/** @todo move this into getopt, as getting bool values is generic */
124int parseBool(const char *psz, bool *pb)
125{
126 int rc = VINF_SUCCESS;
127 if ( !RTStrICmp(psz, "on")
128 || !RTStrICmp(psz, "yes")
129 || !RTStrICmp(psz, "true")
130 || !RTStrICmp(psz, "1")
131 || !RTStrICmp(psz, "enable")
132 || !RTStrICmp(psz, "enabled"))
133 {
134 *pb = true;
135 }
136 else if ( !RTStrICmp(psz, "off")
137 || !RTStrICmp(psz, "no")
138 || !RTStrICmp(psz, "false")
139 || !RTStrICmp(psz, "0")
140 || !RTStrICmp(psz, "disable")
141 || !RTStrICmp(psz, "disabled"))
142 {
143 *pb = false;
144 }
145 else
146 rc = VERR_PARSE_ERROR;
147
148 return rc;
149}
150
151HRESULT openMedium(HandlerArg *a, const char *pszFilenameOrUuid,
152 DeviceType_T enmDevType, AccessMode_T enmAccessMode,
153 ComPtr<IMedium> &pMedium, bool fForceNewUuidOnOpen,
154 bool fSilent)
155{
156 HRESULT rc;
157 Guid id(pszFilenameOrUuid);
158 char szFilenameAbs[RTPATH_MAX] = "";
159
160 /* If it is no UUID, convert the filename to an absolute one. */
161 if (!id.isValid())
162 {
163 int irc = RTPathAbs(pszFilenameOrUuid, szFilenameAbs, sizeof(szFilenameAbs));
164 if (RT_FAILURE(irc))
165 {
166 if (!fSilent)
167 RTMsgError("Cannot convert filename \"%s\" to absolute path", pszFilenameOrUuid);
168 return E_FAIL;
169 }
170 pszFilenameOrUuid = szFilenameAbs;
171 }
172
173 if (!fSilent)
174 CHECK_ERROR(a->virtualBox, OpenMedium(Bstr(pszFilenameOrUuid).raw(),
175 enmDevType,
176 enmAccessMode,
177 fForceNewUuidOnOpen,
178 pMedium.asOutParam()));
179 else
180 rc = a->virtualBox->OpenMedium(Bstr(pszFilenameOrUuid).raw(),
181 enmDevType,
182 enmAccessMode,
183 fForceNewUuidOnOpen,
184 pMedium.asOutParam());
185
186 return rc;
187}
188
189static HRESULT createHardDisk(HandlerArg *a, const char *pszFormat,
190 const char *pszFilename, ComPtr<IMedium> &pMedium)
191{
192 HRESULT rc;
193 char szFilenameAbs[RTPATH_MAX] = "";
194
195 /** @todo laziness shortcut. should really check the MediumFormatCapabilities */
196 if (RTStrICmp(pszFormat, "iSCSI"))
197 {
198 int irc = RTPathAbs(pszFilename, szFilenameAbs, sizeof(szFilenameAbs));
199 if (RT_FAILURE(irc))
200 {
201 RTMsgError("Cannot convert filename \"%s\" to absolute path", pszFilename);
202 return E_FAIL;
203 }
204 pszFilename = szFilenameAbs;
205 }
206
207 CHECK_ERROR(a->virtualBox, CreateHardDisk(Bstr(pszFormat).raw(),
208 Bstr(pszFilename).raw(),
209 pMedium.asOutParam()));
210 return rc;
211}
212
213static const RTGETOPTDEF g_aCreateHardDiskOptions[] =
214{
215 { "--filename", 'f', RTGETOPT_REQ_STRING },
216 { "-filename", 'f', RTGETOPT_REQ_STRING }, // deprecated
217 { "--diffparent", 'd', RTGETOPT_REQ_STRING },
218 { "--size", 's', RTGETOPT_REQ_UINT64 },
219 { "-size", 's', RTGETOPT_REQ_UINT64 }, // deprecated
220 { "--sizebyte", 'S', RTGETOPT_REQ_UINT64 },
221 { "--format", 'o', RTGETOPT_REQ_STRING },
222 { "-format", 'o', RTGETOPT_REQ_STRING }, // deprecated
223 { "--static", 'F', RTGETOPT_REQ_NOTHING },
224 { "-static", 'F', RTGETOPT_REQ_NOTHING }, // deprecated
225 { "--variant", 'm', RTGETOPT_REQ_STRING },
226 { "-variant", 'm', RTGETOPT_REQ_STRING }, // deprecated
227};
228
229int handleCreateHardDisk(HandlerArg *a)
230{
231 HRESULT rc;
232 int vrc;
233 const char *filename = NULL;
234 const char *diffparent = NULL;
235 uint64_t size = 0;
236 const char *format = NULL;
237 bool fBase = true;
238 MediumVariant_T DiskVariant = MediumVariant_Standard;
239
240 int c;
241 RTGETOPTUNION ValueUnion;
242 RTGETOPTSTATE GetState;
243 // start at 0 because main() has hacked both the argc and argv given to us
244 RTGetOptInit(&GetState, a->argc, a->argv, g_aCreateHardDiskOptions, RT_ELEMENTS(g_aCreateHardDiskOptions),
245 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
246 while ((c = RTGetOpt(&GetState, &ValueUnion)))
247 {
248 switch (c)
249 {
250 case 'f': // --filename
251 filename = ValueUnion.psz;
252 break;
253
254 case 'd': // --diffparent
255 diffparent = ValueUnion.psz;
256 fBase = false;
257 break;
258
259 case 's': // --size
260 size = ValueUnion.u64 * _1M;
261 break;
262
263 case 'S': // --sizebyte
264 size = ValueUnion.u64;
265 break;
266
267 case 'o': // --format
268 format = ValueUnion.psz;
269 break;
270
271 case 'F': // --static ("fixed"/"flat")
272 {
273 unsigned uDiskVariant = (unsigned)DiskVariant;
274 uDiskVariant |= MediumVariant_Fixed;
275 DiskVariant = (MediumVariant_T)uDiskVariant;
276 break;
277 }
278
279 case 'm': // --variant
280 vrc = parseDiskVariant(ValueUnion.psz, &DiskVariant);
281 if (RT_FAILURE(vrc))
282 return errorArgument("Invalid hard disk variant '%s'", ValueUnion.psz);
283 break;
284
285 case VINF_GETOPT_NOT_OPTION:
286 return errorSyntax(USAGE_CREATEHD, "Invalid parameter '%s'", ValueUnion.psz);
287
288 default:
289 if (c > 0)
290 {
291 if (RT_C_IS_PRINT(c))
292 return errorSyntax(USAGE_CREATEHD, "Invalid option -%c", c);
293 else
294 return errorSyntax(USAGE_CREATEHD, "Invalid option case %i", c);
295 }
296 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
297 return errorSyntax(USAGE_CREATEHD, "unknown option: %s\n", ValueUnion.psz);
298 else if (ValueUnion.pDef)
299 return errorSyntax(USAGE_CREATEHD, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
300 else
301 return errorSyntax(USAGE_CREATEHD, "error: %Rrs", c);
302 }
303 }
304
305 /* check the outcome */
306 ComPtr<IMedium> parentHardDisk;
307 if (fBase)
308 {
309 if ( !filename
310 || !*filename
311 || size == 0)
312 return errorSyntax(USAGE_CREATEHD, "Parameters --filename and --size are required");
313 if (!format || !*format)
314 format = "VDI";
315 }
316 else
317 {
318 if ( !filename
319 || !*filename)
320 return errorSyntax(USAGE_CREATEHD, "Parameters --filename is required");
321 size = 0;
322 DiskVariant = MediumVariant_Diff;
323 if (!format || !*format)
324 {
325 const char *pszExt = RTPathSuffix(filename);
326 /* Skip over . if there is an extension. */
327 if (pszExt)
328 pszExt++;
329 if (!pszExt || !*pszExt)
330 format = "VDI";
331 else
332 format = pszExt;
333 }
334 rc = openMedium(a, diffparent, DeviceType_HardDisk,
335 AccessMode_ReadWrite, parentHardDisk,
336 false /* fForceNewUuidOnOpen */, false /* fSilent */);
337 if (FAILED(rc))
338 return 1;
339 if (parentHardDisk.isNull())
340 {
341 RTMsgError("Invalid parent hard disk reference, avoiding crash");
342 return 1;
343 }
344 MediumState_T state;
345 CHECK_ERROR(parentHardDisk, COMGETTER(State)(&state));
346 if (FAILED(rc))
347 return 1;
348 if (state == MediumState_Inaccessible)
349 {
350 CHECK_ERROR(parentHardDisk, RefreshState(&state));
351 if (FAILED(rc))
352 return 1;
353 }
354 }
355 /* check for filename extension */
356 /** @todo use IMediumFormat to cover all extensions generically */
357 Utf8Str strName(filename);
358 if (!RTPathHasSuffix(strName.c_str()))
359 {
360 Utf8Str strFormat(format);
361 if (strFormat.compare("vmdk", RTCString::CaseInsensitive) == 0)
362 strName.append(".vmdk");
363 else if (strFormat.compare("vhd", RTCString::CaseInsensitive) == 0)
364 strName.append(".vhd");
365 else
366 strName.append(".vdi");
367 filename = strName.c_str();
368 }
369
370 ComPtr<IMedium> hardDisk;
371 rc = createHardDisk(a, format, filename, hardDisk);
372 if (SUCCEEDED(rc) && hardDisk)
373 {
374 ComPtr<IProgress> progress;
375 com::SafeArray<MediumVariant_T> l_variants(sizeof(MediumVariant_T)*8);
376
377 for (ULONG i = 0; i < l_variants.size(); ++i)
378 {
379 ULONG temp = DiskVariant;
380 temp &= 1<<i;
381 l_variants [i] = (MediumVariant_T)temp;
382 }
383
384 if (fBase)
385 CHECK_ERROR(hardDisk, CreateBaseStorage(size, ComSafeArrayAsInParam(l_variants), progress.asOutParam()));
386 else
387 CHECK_ERROR(parentHardDisk, CreateDiffStorage(hardDisk, ComSafeArrayAsInParam(l_variants), progress.asOutParam()));
388 if (SUCCEEDED(rc) && progress)
389 {
390 rc = showProgress(progress);
391 CHECK_PROGRESS_ERROR(progress, ("Failed to create hard disk"));
392 if (SUCCEEDED(rc))
393 {
394 Bstr uuid;
395 CHECK_ERROR(hardDisk, COMGETTER(Id)(uuid.asOutParam()));
396 RTPrintf("Disk image created. UUID: %s\n", Utf8Str(uuid).c_str());
397 }
398 }
399
400 CHECK_ERROR(hardDisk, Close());
401 }
402 return SUCCEEDED(rc) ? 0 : 1;
403}
404
405static const RTGETOPTDEF g_aModifyHardDiskOptions[] =
406{
407 { "--type", 't', RTGETOPT_REQ_STRING },
408 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
409 { "settype", 't', RTGETOPT_REQ_STRING }, // deprecated
410 { "--autoreset", 'z', RTGETOPT_REQ_STRING },
411 { "-autoreset", 'z', RTGETOPT_REQ_STRING }, // deprecated
412 { "autoreset", 'z', RTGETOPT_REQ_STRING }, // deprecated
413 { "--property", 'p', RTGETOPT_REQ_STRING },
414 { "--compact", 'c', RTGETOPT_REQ_NOTHING },
415 { "-compact", 'c', RTGETOPT_REQ_NOTHING }, // deprecated
416 { "compact", 'c', RTGETOPT_REQ_NOTHING }, // deprecated
417 { "--resize", 'r', RTGETOPT_REQ_UINT64 },
418 { "--resizebyte", 'R', RTGETOPT_REQ_UINT64 }
419};
420
421int handleModifyHardDisk(HandlerArg *a)
422{
423 HRESULT rc;
424 int vrc;
425 ComPtr<IMedium> hardDisk;
426 MediumType_T DiskType;
427 bool AutoReset = false;
428 SafeArray<BSTR> mediumPropNames;
429 SafeArray<BSTR> mediumPropValues;
430 bool fModifyDiskType = false;
431 bool fModifyAutoReset = false;
432 bool fModifyProperties = false;
433 bool fModifyCompact = false;
434 bool fModifyResize = false;
435 uint64_t cbResize = 0;
436 const char *FilenameOrUuid = NULL;
437
438 int c;
439 RTGETOPTUNION ValueUnion;
440 RTGETOPTSTATE GetState;
441 // start at 0 because main() has hacked both the argc and argv given to us
442 RTGetOptInit(&GetState, a->argc, a->argv, g_aModifyHardDiskOptions, RT_ELEMENTS(g_aModifyHardDiskOptions),
443 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
444 while ((c = RTGetOpt(&GetState, &ValueUnion)))
445 {
446 switch (c)
447 {
448 case 't': // --type
449 vrc = parseDiskType(ValueUnion.psz, &DiskType);
450 if (RT_FAILURE(vrc))
451 return errorArgument("Invalid hard disk type '%s'", ValueUnion.psz);
452 fModifyDiskType = true;
453 break;
454
455 case 'z': // --autoreset
456 vrc = parseBool(ValueUnion.psz, &AutoReset);
457 if (RT_FAILURE(vrc))
458 return errorArgument("Invalid autoreset parameter '%s'", ValueUnion.psz);
459 fModifyAutoReset = true;
460 break;
461
462 case 'p': // --property
463 {
464 /* Parse 'name=value' */
465 char *pszProperty = RTStrDup(ValueUnion.psz);
466 if (pszProperty)
467 {
468 char *pDelimiter = strchr(pszProperty, '=');
469 if (pDelimiter)
470 {
471 *pDelimiter = '\0';
472
473 Bstr bstrName(pszProperty);
474 Bstr bstrValue(&pDelimiter[1]);
475 bstrName.detachTo(mediumPropNames.appendedRaw());
476 bstrValue.detachTo(mediumPropValues.appendedRaw());
477 fModifyProperties = true;
478 }
479 else
480 {
481 errorArgument("Invalid --property argument '%s'", ValueUnion.psz);
482 rc = E_FAIL;
483 }
484 RTStrFree(pszProperty);
485 }
486 else
487 {
488 RTStrmPrintf(g_pStdErr, "Error: Failed to allocate memory for medium property '%s'\n", ValueUnion.psz);
489 rc = E_FAIL;
490 }
491 break;
492 }
493
494 case 'c': // --compact
495 fModifyCompact = true;
496 break;
497
498 case 'r': // --resize
499 cbResize = ValueUnion.u64 * _1M;
500 fModifyResize = true;
501 break;
502
503 case 'R': // --resizebyte
504 cbResize = ValueUnion.u64;
505 fModifyResize = true;
506 break;
507
508 case VINF_GETOPT_NOT_OPTION:
509 if (!FilenameOrUuid)
510 FilenameOrUuid = ValueUnion.psz;
511 else
512 return errorSyntax(USAGE_MODIFYHD, "Invalid parameter '%s'", ValueUnion.psz);
513 break;
514
515 default:
516 if (c > 0)
517 {
518 if (RT_C_IS_PRINT(c))
519 return errorSyntax(USAGE_MODIFYHD, "Invalid option -%c", c);
520 else
521 return errorSyntax(USAGE_MODIFYHD, "Invalid option case %i", c);
522 }
523 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
524 return errorSyntax(USAGE_MODIFYHD, "unknown option: %s\n", ValueUnion.psz);
525 else if (ValueUnion.pDef)
526 return errorSyntax(USAGE_MODIFYHD, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
527 else
528 return errorSyntax(USAGE_MODIFYHD, "error: %Rrs", c);
529 }
530 }
531
532 if (!FilenameOrUuid)
533 return errorSyntax(USAGE_MODIFYHD, "Disk name or UUID required");
534
535 if (!fModifyDiskType && !fModifyAutoReset && !fModifyProperties && !fModifyCompact && !fModifyResize)
536 return errorSyntax(USAGE_MODIFYHD, "No operation specified");
537
538 /* Always open the medium if necessary, there is no other way. */
539 rc = openMedium(a, FilenameOrUuid, DeviceType_HardDisk,
540 AccessMode_ReadWrite, hardDisk,
541 false /* fForceNewUuidOnOpen */, false /* fSilent */);
542 if (FAILED(rc))
543 return 1;
544 if (hardDisk.isNull())
545 {
546 RTMsgError("Invalid hard disk reference, avoiding crash");
547 return 1;
548 }
549
550 if (fModifyDiskType)
551 {
552 MediumType_T hddType;
553 CHECK_ERROR(hardDisk, COMGETTER(Type)(&hddType));
554
555 if (hddType != DiskType)
556 CHECK_ERROR(hardDisk, COMSETTER(Type)(DiskType));
557 }
558
559 if (fModifyAutoReset)
560 {
561 CHECK_ERROR(hardDisk, COMSETTER(AutoReset)(AutoReset));
562 }
563
564 if (fModifyProperties)
565 {
566 CHECK_ERROR(hardDisk, SetProperties(ComSafeArrayAsInParam(mediumPropNames), ComSafeArrayAsInParam(mediumPropValues)));
567 }
568
569 if (fModifyCompact)
570 {
571 ComPtr<IProgress> progress;
572 CHECK_ERROR(hardDisk, Compact(progress.asOutParam()));
573 if (SUCCEEDED(rc))
574 rc = showProgress(progress);
575 if (FAILED(rc))
576 {
577 if (rc == E_NOTIMPL)
578 RTMsgError("Compact hard disk operation is not implemented!");
579 else if (rc == VBOX_E_NOT_SUPPORTED)
580 RTMsgError("Compact hard disk operation for this format is not implemented yet!");
581 else if (!progress.isNull())
582 CHECK_PROGRESS_ERROR(progress, ("Failed to compact hard disk"));
583 else
584 RTMsgError("Failed to compact hard disk!");
585 }
586 }
587
588 if (fModifyResize)
589 {
590 ComPtr<IProgress> progress;
591 CHECK_ERROR(hardDisk, Resize(cbResize, progress.asOutParam()));
592 if (SUCCEEDED(rc))
593 rc = showProgress(progress);
594 if (FAILED(rc))
595 {
596 if (rc == E_NOTIMPL)
597 RTMsgError("Resize hard disk operation is not implemented!");
598 else if (rc == VBOX_E_NOT_SUPPORTED)
599 RTMsgError("Resize hard disk operation for this format is not implemented yet!");
600 else
601 CHECK_PROGRESS_ERROR(progress, ("Failed to resize hard disk"));
602 }
603 }
604
605 return SUCCEEDED(rc) ? 0 : 1;
606}
607
608static const RTGETOPTDEF g_aCloneHardDiskOptions[] =
609{
610 { "--format", 'o', RTGETOPT_REQ_STRING },
611 { "-format", 'o', RTGETOPT_REQ_STRING },
612 { "--static", 'F', RTGETOPT_REQ_NOTHING },
613 { "-static", 'F', RTGETOPT_REQ_NOTHING },
614 { "--existing", 'E', RTGETOPT_REQ_NOTHING },
615 { "--variant", 'm', RTGETOPT_REQ_STRING },
616 { "-variant", 'm', RTGETOPT_REQ_STRING },
617};
618
619int handleCloneHardDisk(HandlerArg *a)
620{
621 HRESULT rc;
622 int vrc;
623 const char *pszSrc = NULL;
624 const char *pszDst = NULL;
625 Bstr format;
626 MediumVariant_T DiskVariant = MediumVariant_Standard;
627 bool fExisting = false;
628
629 int c;
630 RTGETOPTUNION ValueUnion;
631 RTGETOPTSTATE GetState;
632 // start at 0 because main() has hacked both the argc and argv given to us
633 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloneHardDiskOptions, RT_ELEMENTS(g_aCloneHardDiskOptions),
634 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
635 while ((c = RTGetOpt(&GetState, &ValueUnion)))
636 {
637 switch (c)
638 {
639 case 'o': // --format
640 format = ValueUnion.psz;
641 break;
642
643 case 'F': // --static
644 {
645 unsigned uDiskVariant = (unsigned)DiskVariant;
646 uDiskVariant |= MediumVariant_Fixed;
647 DiskVariant = (MediumVariant_T)uDiskVariant;
648 break;
649 }
650
651 case 'E': // --existing
652 fExisting = true;
653 break;
654
655 case 'm': // --variant
656 vrc = parseDiskVariant(ValueUnion.psz, &DiskVariant);
657 if (RT_FAILURE(vrc))
658 return errorArgument("Invalid hard disk variant '%s'", ValueUnion.psz);
659 break;
660
661 case VINF_GETOPT_NOT_OPTION:
662 if (!pszSrc)
663 pszSrc = ValueUnion.psz;
664 else if (!pszDst)
665 pszDst = ValueUnion.psz;
666 else
667 return errorSyntax(USAGE_CLONEHD, "Invalid parameter '%s'", ValueUnion.psz);
668 break;
669
670 default:
671 if (c > 0)
672 {
673 if (RT_C_IS_GRAPH(c))
674 return errorSyntax(USAGE_CLONEHD, "unhandled option: -%c", c);
675 else
676 return errorSyntax(USAGE_CLONEHD, "unhandled option: %i", c);
677 }
678 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
679 return errorSyntax(USAGE_CLONEHD, "unknown option: %s", ValueUnion.psz);
680 else if (ValueUnion.pDef)
681 return errorSyntax(USAGE_CLONEHD, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
682 else
683 return errorSyntax(USAGE_CLONEHD, "error: %Rrs", c);
684 }
685 }
686
687 if (!pszSrc)
688 return errorSyntax(USAGE_CLONEHD, "Mandatory UUID or input file parameter missing");
689 if (!pszDst)
690 return errorSyntax(USAGE_CLONEHD, "Mandatory output file parameter missing");
691 if (fExisting && (!format.isEmpty() || DiskVariant != MediumType_Normal))
692 return errorSyntax(USAGE_CLONEHD, "Specified options which cannot be used with --existing");
693
694 ComPtr<IMedium> srcDisk;
695 ComPtr<IMedium> dstDisk;
696
697 rc = openMedium(a, pszSrc, DeviceType_HardDisk, AccessMode_ReadOnly,
698 srcDisk, false /* fForceNewUuidOnOpen */,
699 false /* fSilent */);
700 if (FAILED(rc))
701 return 1;
702
703 do
704 {
705 /* open/create destination hard disk */
706 if (fExisting)
707 {
708 rc = openMedium(a, pszDst, DeviceType_HardDisk,
709 AccessMode_ReadWrite, dstDisk,
710 false /* fForceNewUuidOnOpen */,
711 false /* fSilent */);
712 if (FAILED(rc))
713 break;
714
715 /* Perform accessibility check now. */
716 MediumState_T state;
717 CHECK_ERROR_BREAK(dstDisk, RefreshState(&state));
718 CHECK_ERROR_BREAK(dstDisk, COMGETTER(Format)(format.asOutParam()));
719 }
720 else
721 {
722 /* use the format of the source hard disk if unspecified */
723 if (format.isEmpty())
724 CHECK_ERROR_BREAK(srcDisk, COMGETTER(Format)(format.asOutParam()));
725 rc = createHardDisk(a, Utf8Str(format).c_str(), pszDst, dstDisk);
726 if (FAILED(rc))
727 break;
728 }
729
730 ComPtr<IProgress> progress;
731 com::SafeArray<MediumVariant_T> l_variants(sizeof(MediumVariant_T)*8);
732
733 for (ULONG i = 0; i < l_variants.size(); ++i)
734 {
735 ULONG temp = DiskVariant;
736 temp &= 1<<i;
737 l_variants [i] = (MediumVariant_T)temp;
738 }
739
740 CHECK_ERROR_BREAK(srcDisk, CloneTo(dstDisk, ComSafeArrayAsInParam(l_variants), NULL, progress.asOutParam()));
741
742 rc = showProgress(progress);
743 CHECK_PROGRESS_ERROR_BREAK(progress, ("Failed to clone hard disk"));
744
745 Bstr uuid;
746 CHECK_ERROR_BREAK(dstDisk, COMGETTER(Id)(uuid.asOutParam()));
747
748 RTPrintf("Clone hard disk created in format '%ls'. UUID: %s\n",
749 format.raw(), Utf8Str(uuid).c_str());
750 }
751 while (0);
752
753 return SUCCEEDED(rc) ? 0 : 1;
754}
755
756static const RTGETOPTDEF g_aConvertFromRawHardDiskOptions[] =
757{
758 { "--format", 'o', RTGETOPT_REQ_STRING },
759 { "-format", 'o', RTGETOPT_REQ_STRING },
760 { "--static", 'F', RTGETOPT_REQ_NOTHING },
761 { "-static", 'F', RTGETOPT_REQ_NOTHING },
762 { "--variant", 'm', RTGETOPT_REQ_STRING },
763 { "-variant", 'm', RTGETOPT_REQ_STRING },
764 { "--uuid", 'u', RTGETOPT_REQ_STRING },
765};
766
767RTEXITCODE handleConvertFromRaw(int argc, char *argv[])
768{
769 int rc = VINF_SUCCESS;
770 bool fReadFromStdIn = false;
771 const char *format = "VDI";
772 const char *srcfilename = NULL;
773 const char *dstfilename = NULL;
774 const char *filesize = NULL;
775 unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
776 void *pvBuf = NULL;
777 RTUUID uuid;
778 PCRTUUID pUuid = NULL;
779
780 int c;
781 RTGETOPTUNION ValueUnion;
782 RTGETOPTSTATE GetState;
783 // start at 0 because main() has hacked both the argc and argv given to us
784 RTGetOptInit(&GetState, argc, argv, g_aConvertFromRawHardDiskOptions, RT_ELEMENTS(g_aConvertFromRawHardDiskOptions),
785 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
786 while ((c = RTGetOpt(&GetState, &ValueUnion)))
787 {
788 switch (c)
789 {
790 case 'u': // --uuid
791 if (RT_FAILURE(RTUuidFromStr(&uuid, ValueUnion.psz)))
792 return errorSyntax(USAGE_CONVERTFROMRAW, "Invalid UUID '%s'", ValueUnion.psz);
793 pUuid = &uuid;
794 break;
795 case 'o': // --format
796 format = ValueUnion.psz;
797 break;
798
799 case 'm': // --variant
800 {
801 MediumVariant_T DiskVariant = MediumVariant_Standard;
802 rc = parseDiskVariant(ValueUnion.psz, &DiskVariant);
803 if (RT_FAILURE(rc))
804 return errorArgument("Invalid hard disk variant '%s'", ValueUnion.psz);
805 /// @todo cleaner solution than assuming 1:1 mapping?
806 uImageFlags = (unsigned)DiskVariant;
807 break;
808 }
809 case VINF_GETOPT_NOT_OPTION:
810 if (!srcfilename)
811 {
812 srcfilename = ValueUnion.psz;
813 fReadFromStdIn = !strcmp(srcfilename, "stdin");
814 }
815 else if (!dstfilename)
816 dstfilename = ValueUnion.psz;
817 else if (fReadFromStdIn && !filesize)
818 filesize = ValueUnion.psz;
819 else
820 return errorSyntax(USAGE_CONVERTFROMRAW, "Invalid parameter '%s'", ValueUnion.psz);
821 break;
822
823 default:
824 return errorGetOpt(USAGE_CONVERTFROMRAW, c, &ValueUnion);
825 }
826 }
827
828 if (!srcfilename || !dstfilename || (fReadFromStdIn && !filesize))
829 return errorSyntax(USAGE_CONVERTFROMRAW, "Incorrect number of parameters");
830 RTStrmPrintf(g_pStdErr, "Converting from raw image file=\"%s\" to file=\"%s\"...\n",
831 srcfilename, dstfilename);
832
833 PVBOXHDD pDisk = NULL;
834
835 PVDINTERFACE pVDIfs = NULL;
836 VDINTERFACEERROR vdInterfaceError;
837 vdInterfaceError.pfnError = handleVDError;
838 vdInterfaceError.pfnMessage = NULL;
839
840 rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
841 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
842 AssertRC(rc);
843
844 /* open raw image file. */
845 RTFILE File;
846 if (fReadFromStdIn)
847 rc = RTFileFromNative(&File, RTFILE_NATIVE_STDIN);
848 else
849 rc = RTFileOpen(&File, srcfilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
850 if (RT_FAILURE(rc))
851 {
852 RTMsgError("Cannot open file \"%s\": %Rrc", srcfilename, rc);
853 goto out;
854 }
855
856 uint64_t cbFile;
857 /* get image size. */
858 if (fReadFromStdIn)
859 cbFile = RTStrToUInt64(filesize);
860 else
861 rc = RTFileGetSize(File, &cbFile);
862 if (RT_FAILURE(rc))
863 {
864 RTMsgError("Cannot get image size for file \"%s\": %Rrc", srcfilename, rc);
865 goto out;
866 }
867
868 RTStrmPrintf(g_pStdErr, "Creating %s image with size %RU64 bytes (%RU64MB)...\n",
869 (uImageFlags & VD_IMAGE_FLAGS_FIXED) ? "fixed" : "dynamic", cbFile, (cbFile + _1M - 1) / _1M);
870 char pszComment[256];
871 RTStrPrintf(pszComment, sizeof(pszComment), "Converted image from %s", srcfilename);
872 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
873 if (RT_FAILURE(rc))
874 {
875 RTMsgError("Cannot create the virtual disk container: %Rrc", rc);
876 goto out;
877 }
878
879 Assert(RT_MIN(cbFile / 512 / 16 / 63, 16383) -
880 (unsigned int)RT_MIN(cbFile / 512 / 16 / 63, 16383) == 0);
881 VDGEOMETRY PCHS, LCHS;
882 PCHS.cCylinders = (unsigned int)RT_MIN(cbFile / 512 / 16 / 63, 16383);
883 PCHS.cHeads = 16;
884 PCHS.cSectors = 63;
885 LCHS.cCylinders = 0;
886 LCHS.cHeads = 0;
887 LCHS.cSectors = 0;
888 rc = VDCreateBase(pDisk, format, dstfilename, cbFile,
889 uImageFlags, pszComment, &PCHS, &LCHS, pUuid,
890 VD_OPEN_FLAGS_NORMAL, NULL, NULL);
891 if (RT_FAILURE(rc))
892 {
893 RTMsgError("Cannot create the disk image \"%s\": %Rrc", dstfilename, rc);
894 goto out;
895 }
896
897 size_t cbBuffer;
898 cbBuffer = _1M;
899 pvBuf = RTMemAlloc(cbBuffer);
900 if (!pvBuf)
901 {
902 rc = VERR_NO_MEMORY;
903 RTMsgError("Out of memory allocating buffers for image \"%s\": %Rrc", dstfilename, rc);
904 goto out;
905 }
906
907 uint64_t offFile;
908 offFile = 0;
909 while (offFile < cbFile)
910 {
911 size_t cbRead;
912 size_t cbToRead;
913 cbRead = 0;
914 cbToRead = cbFile - offFile >= (uint64_t)cbBuffer ?
915 cbBuffer : (size_t)(cbFile - offFile);
916 rc = RTFileRead(File, pvBuf, cbToRead, &cbRead);
917 if (RT_FAILURE(rc) || !cbRead)
918 break;
919 rc = VDWrite(pDisk, offFile, pvBuf, cbRead);
920 if (RT_FAILURE(rc))
921 {
922 RTMsgError("Failed to write to disk image \"%s\": %Rrc", dstfilename, rc);
923 goto out;
924 }
925 offFile += cbRead;
926 }
927
928out:
929 if (pvBuf)
930 RTMemFree(pvBuf);
931 if (pDisk)
932 VDClose(pDisk, RT_FAILURE(rc));
933 if (File != NIL_RTFILE)
934 RTFileClose(File);
935
936 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
937}
938
939HRESULT showMediumInfo(const ComPtr<IVirtualBox> &pVirtualBox,
940 const ComPtr<IMedium> &pMedium,
941 const char *pszParentUUID,
942 bool fOptLong)
943{
944 HRESULT rc = S_OK;
945 do
946 {
947 Bstr uuid;
948 pMedium->COMGETTER(Id)(uuid.asOutParam());
949 RTPrintf("UUID: %ls\n", uuid.raw());
950 if (pszParentUUID)
951 RTPrintf("Parent UUID: %s\n", pszParentUUID);
952
953 /* check for accessibility */
954 MediumState_T enmState;
955 CHECK_ERROR_BREAK(pMedium, RefreshState(&enmState));
956 pMedium->RefreshState(&enmState);
957 const char *pszState = "unknown";
958 switch (enmState)
959 {
960 case MediumState_NotCreated:
961 pszState = "not created";
962 break;
963 case MediumState_Created:
964 pszState = "created";
965 break;
966 case MediumState_LockedRead:
967 pszState = "locked read";
968 break;
969 case MediumState_LockedWrite:
970 pszState = "locked write";
971 break;
972 case MediumState_Inaccessible:
973 pszState = "inaccessible";
974 break;
975 case MediumState_Creating:
976 pszState = "creating";
977 break;
978 case MediumState_Deleting:
979 pszState = "deleting";
980 break;
981 }
982 RTPrintf("State: %s\n", pszState);
983
984 if (fOptLong && enmState == MediumState_Inaccessible)
985 {
986 Bstr err;
987 CHECK_ERROR_BREAK(pMedium, COMGETTER(LastAccessError)(err.asOutParam()));
988 RTPrintf("Access Error: %ls\n", err.raw());
989 }
990
991 if (fOptLong)
992 {
993 Bstr description;
994 pMedium->COMGETTER(Description)(description.asOutParam());
995 if (!description.isEmpty())
996 RTPrintf("Description: %ls\n", description.raw());
997 }
998
999 MediumType_T type;
1000 pMedium->COMGETTER(Type)(&type);
1001 const char *typeStr = "unknown";
1002 switch (type)
1003 {
1004 case MediumType_Normal:
1005 if (pszParentUUID && Guid(pszParentUUID).isValid())
1006 typeStr = "normal (differencing)";
1007 else
1008 typeStr = "normal (base)";
1009 break;
1010 case MediumType_Immutable:
1011 typeStr = "immutable";
1012 break;
1013 case MediumType_Writethrough:
1014 typeStr = "writethrough";
1015 break;
1016 case MediumType_Shareable:
1017 typeStr = "shareable";
1018 break;
1019 case MediumType_Readonly:
1020 typeStr = "readonly";
1021 break;
1022 case MediumType_MultiAttach:
1023 typeStr = "multiattach";
1024 break;
1025 }
1026 RTPrintf("Type: %s\n", typeStr);
1027
1028 /* print out information specific for differencing hard disks */
1029 if (fOptLong && pszParentUUID && Guid(pszParentUUID).isValid())
1030 {
1031 BOOL autoReset = FALSE;
1032 pMedium->COMGETTER(AutoReset)(&autoReset);
1033 RTPrintf("Auto-Reset: %s\n", autoReset ? "on" : "off");
1034 }
1035
1036 Bstr loc;
1037 pMedium->COMGETTER(Location)(loc.asOutParam());
1038 RTPrintf("Location: %ls\n", loc.raw());
1039
1040 Bstr format;
1041 pMedium->COMGETTER(Format)(format.asOutParam());
1042 RTPrintf("Storage format: %ls\n", format.raw());
1043
1044 if (fOptLong)
1045 {
1046 com::SafeArray<MediumVariant_T> safeArray_variant;
1047
1048 pMedium->COMGETTER(Variant)(ComSafeArrayAsOutParam(safeArray_variant));
1049 ULONG variant=0;
1050 for (size_t i = 0; i < safeArray_variant.size(); i++)
1051 variant |= safeArray_variant[i];
1052
1053 const char *variantStr = "unknown";
1054 switch (variant & ~(MediumVariant_Fixed | MediumVariant_Diff))
1055 {
1056 case MediumVariant_VmdkSplit2G:
1057 variantStr = "split2G";
1058 break;
1059 case MediumVariant_VmdkStreamOptimized:
1060 variantStr = "streamOptimized";
1061 break;
1062 case MediumVariant_VmdkESX:
1063 variantStr = "ESX";
1064 break;
1065 case MediumVariant_Standard:
1066 variantStr = "default";
1067 break;
1068 }
1069 const char *variantTypeStr = "dynamic";
1070 if (variant & MediumVariant_Fixed)
1071 variantTypeStr = "fixed";
1072 else if (variant & MediumVariant_Diff)
1073 variantTypeStr = "differencing";
1074 RTPrintf("Format variant: %s %s\n", variantTypeStr, variantStr);
1075 }
1076
1077 LONG64 logicalSize;
1078 pMedium->COMGETTER(LogicalSize)(&logicalSize);
1079 RTPrintf("Capacity: %lld MBytes\n", logicalSize >> 20);
1080 if (fOptLong)
1081 {
1082 LONG64 actualSize;
1083 pMedium->COMGETTER(Size)(&actualSize);
1084 RTPrintf("Size on disk: %lld MBytes\n", actualSize >> 20);
1085 }
1086
1087 if (fOptLong)
1088 {
1089 com::SafeArray<BSTR> names;
1090 com::SafeArray<BSTR> values;
1091 pMedium->GetProperties(Bstr().raw(), ComSafeArrayAsOutParam(names), ComSafeArrayAsOutParam(values));
1092 size_t cNames = names.size();
1093 size_t cValues = values.size();
1094 bool fFirst = true;
1095 for (size_t i = 0; i < cNames; i++)
1096 {
1097 Bstr value;
1098 if (i < cValues)
1099 value = values[i];
1100 RTPrintf("%s%ls=%ls\n",
1101 fFirst ? "Property: " : " ",
1102 names[i], value.raw());
1103 }
1104 }
1105
1106 if (fOptLong)
1107 {
1108 bool fFirst = true;
1109 com::SafeArray<BSTR> machineIds;
1110 pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(machineIds));
1111 for (size_t i = 0; i < machineIds.size(); i++)
1112 {
1113 ComPtr<IMachine> machine;
1114 CHECK_ERROR(pVirtualBox, FindMachine(machineIds[i], machine.asOutParam()));
1115 if (machine)
1116 {
1117 Bstr name;
1118 machine->COMGETTER(Name)(name.asOutParam());
1119 machine->COMGETTER(Id)(uuid.asOutParam());
1120 RTPrintf("%s%ls (UUID: %ls)",
1121 fFirst ? "In use by VMs: " : " ",
1122 name.raw(), machineIds[i]);
1123 fFirst = false;
1124 com::SafeArray<BSTR> snapshotIds;
1125 pMedium->GetSnapshotIds(machineIds[i],
1126 ComSafeArrayAsOutParam(snapshotIds));
1127 for (size_t j = 0; j < snapshotIds.size(); j++)
1128 {
1129 ComPtr<ISnapshot> snapshot;
1130 machine->FindSnapshot(snapshotIds[j], snapshot.asOutParam());
1131 if (snapshot)
1132 {
1133 Bstr snapshotName;
1134 snapshot->COMGETTER(Name)(snapshotName.asOutParam());
1135 RTPrintf(" [%ls (UUID: %ls)]", snapshotName.raw(), snapshotIds[j]);
1136 }
1137 }
1138 RTPrintf("\n");
1139 }
1140 }
1141 }
1142
1143 if (fOptLong)
1144 {
1145 com::SafeIfaceArray<IMedium> children;
1146 pMedium->COMGETTER(Children)(ComSafeArrayAsOutParam(children));
1147 bool fFirst = true;
1148 for (size_t i = 0; i < children.size(); i++)
1149 {
1150 ComPtr<IMedium> pChild(children[i]);
1151 if (pChild)
1152 {
1153 Bstr childUUID;
1154 pChild->COMGETTER(Id)(childUUID.asOutParam());
1155 RTPrintf("%s%ls\n",
1156 fFirst ? "Child UUIDs: " : " ",
1157 childUUID.raw());
1158 fFirst = false;
1159 }
1160 }
1161 }
1162 }
1163 while (0);
1164
1165 return rc;
1166}
1167
1168static const RTGETOPTDEF g_aShowHardDiskInfoOptions[] =
1169{
1170 { "--dummy", 256, RTGETOPT_REQ_NOTHING }, // placeholder for C++
1171};
1172
1173int handleShowHardDiskInfo(HandlerArg *a)
1174{
1175 HRESULT rc;
1176 const char *FilenameOrUuid = NULL;
1177
1178 int c;
1179 RTGETOPTUNION ValueUnion;
1180 RTGETOPTSTATE GetState;
1181 // start at 0 because main() has hacked both the argc and argv given to us
1182 RTGetOptInit(&GetState, a->argc, a->argv, g_aShowHardDiskInfoOptions, RT_ELEMENTS(g_aShowHardDiskInfoOptions),
1183 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1184 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1185 {
1186 switch (c)
1187 {
1188 case VINF_GETOPT_NOT_OPTION:
1189 if (!FilenameOrUuid)
1190 FilenameOrUuid = ValueUnion.psz;
1191 else
1192 return errorSyntax(USAGE_SHOWHDINFO, "Invalid parameter '%s'", ValueUnion.psz);
1193 break;
1194
1195 default:
1196 if (c > 0)
1197 {
1198 if (RT_C_IS_PRINT(c))
1199 return errorSyntax(USAGE_SHOWHDINFO, "Invalid option -%c", c);
1200 else
1201 return errorSyntax(USAGE_SHOWHDINFO, "Invalid option case %i", c);
1202 }
1203 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1204 return errorSyntax(USAGE_SHOWHDINFO, "unknown option: %s\n", ValueUnion.psz);
1205 else if (ValueUnion.pDef)
1206 return errorSyntax(USAGE_SHOWHDINFO, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1207 else
1208 return errorSyntax(USAGE_SHOWHDINFO, "error: %Rrs", c);
1209 }
1210 }
1211
1212 /* check for required options */
1213 if (!FilenameOrUuid)
1214 return errorSyntax(USAGE_SHOWHDINFO, "Disk name or UUID required");
1215
1216 ComPtr<IMedium> hardDisk;
1217 rc = openMedium(a, FilenameOrUuid, DeviceType_HardDisk,
1218 AccessMode_ReadOnly, hardDisk,
1219 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1220 if (FAILED(rc))
1221 return 1;
1222
1223 Utf8Str strParentUUID("base");
1224 ComPtr<IMedium> parent;
1225 hardDisk->COMGETTER(Parent)(parent.asOutParam());
1226 if (!parent.isNull())
1227 {
1228 Bstr bstrParentUUID;
1229 parent->COMGETTER(Id)(bstrParentUUID.asOutParam());
1230 strParentUUID = bstrParentUUID;
1231 }
1232
1233 rc = showMediumInfo(a->virtualBox, hardDisk, strParentUUID.c_str(), true);
1234
1235 return SUCCEEDED(rc) ? 0 : 1;
1236}
1237
1238static const RTGETOPTDEF g_aCloseMediumOptions[] =
1239{
1240 { "disk", 'd', RTGETOPT_REQ_NOTHING },
1241 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
1242 { "floppy", 'f', RTGETOPT_REQ_NOTHING },
1243 { "--delete", 'r', RTGETOPT_REQ_NOTHING },
1244};
1245
1246int handleCloseMedium(HandlerArg *a)
1247{
1248 HRESULT rc = S_OK;
1249 enum {
1250 CMD_NONE,
1251 CMD_DISK,
1252 CMD_DVD,
1253 CMD_FLOPPY
1254 } cmd = CMD_NONE;
1255 const char *FilenameOrUuid = NULL;
1256 bool fDelete = false;
1257
1258 int c;
1259 RTGETOPTUNION ValueUnion;
1260 RTGETOPTSTATE GetState;
1261 // start at 0 because main() has hacked both the argc and argv given to us
1262 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloseMediumOptions, RT_ELEMENTS(g_aCloseMediumOptions),
1263 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1264 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1265 {
1266 switch (c)
1267 {
1268 case 'd': // disk
1269 if (cmd != CMD_NONE)
1270 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1271 cmd = CMD_DISK;
1272 break;
1273
1274 case 'D': // DVD
1275 if (cmd != CMD_NONE)
1276 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1277 cmd = CMD_DVD;
1278 break;
1279
1280 case 'f': // floppy
1281 if (cmd != CMD_NONE)
1282 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1283 cmd = CMD_FLOPPY;
1284 break;
1285
1286 case 'r': // --delete
1287 fDelete = true;
1288 break;
1289
1290 case VINF_GETOPT_NOT_OPTION:
1291 if (!FilenameOrUuid)
1292 FilenameOrUuid = ValueUnion.psz;
1293 else
1294 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
1295 break;
1296
1297 default:
1298 if (c > 0)
1299 {
1300 if (RT_C_IS_PRINT(c))
1301 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option -%c", c);
1302 else
1303 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option case %i", c);
1304 }
1305 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1306 return errorSyntax(USAGE_CLOSEMEDIUM, "unknown option: %s\n", ValueUnion.psz);
1307 else if (ValueUnion.pDef)
1308 return errorSyntax(USAGE_CLOSEMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1309 else
1310 return errorSyntax(USAGE_CLOSEMEDIUM, "error: %Rrs", c);
1311 }
1312 }
1313
1314 /* check for required options */
1315 if (cmd == CMD_NONE)
1316 return errorSyntax(USAGE_CLOSEMEDIUM, "Command variant disk/dvd/floppy required");
1317 if (!FilenameOrUuid)
1318 return errorSyntax(USAGE_CLOSEMEDIUM, "Disk name or UUID required");
1319
1320 ComPtr<IMedium> medium;
1321
1322 if (cmd == CMD_DISK)
1323 rc = openMedium(a, FilenameOrUuid, DeviceType_HardDisk,
1324 AccessMode_ReadWrite, medium,
1325 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1326 else if (cmd == CMD_DVD)
1327 rc = openMedium(a, FilenameOrUuid, DeviceType_DVD,
1328 AccessMode_ReadOnly, medium,
1329 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1330 else if (cmd == CMD_FLOPPY)
1331 rc = openMedium(a, FilenameOrUuid, DeviceType_Floppy,
1332 AccessMode_ReadWrite, medium,
1333 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1334
1335 if (SUCCEEDED(rc) && medium)
1336 {
1337 if (fDelete)
1338 {
1339 ComPtr<IProgress> progress;
1340 CHECK_ERROR(medium, DeleteStorage(progress.asOutParam()));
1341 if (SUCCEEDED(rc))
1342 {
1343 rc = showProgress(progress);
1344 CHECK_PROGRESS_ERROR(progress, ("Failed to delete medium"));
1345 }
1346 else
1347 RTMsgError("Failed to delete medium. Error code %Rrc", rc);
1348 }
1349 CHECK_ERROR(medium, Close());
1350 }
1351
1352 return SUCCEEDED(rc) ? 0 : 1;
1353}
1354
1355int handleMediumProperty(HandlerArg *a)
1356{
1357 HRESULT rc = S_OK;
1358 const char *pszAction = NULL;
1359 const char *pszFilenameOrUuid = NULL;
1360 const char *pszProperty = NULL;
1361 ComPtr<IMedium> pMedium;
1362
1363 if (a->argc == 0)
1364 return errorSyntax(USAGE_HDPROPERTY, "Missing action");
1365
1366 pszAction = a->argv[0];
1367 if ( RTStrICmp(pszAction, "set")
1368 && RTStrICmp(pszAction, "get")
1369 && RTStrICmp(pszAction, "delete"))
1370 return errorSyntax(USAGE_HDPROPERTY, "Invalid action given: %s", pszAction);
1371
1372 if ( ( !RTStrICmp(pszAction, "set")
1373 && a->argc != 4)
1374 || ( RTStrICmp(pszAction, "set")
1375 && a->argc != 3))
1376 return errorSyntax(USAGE_HDPROPERTY, "Invalid number of arguments given for action: %s", pszAction);
1377
1378 pszFilenameOrUuid = a->argv[1];
1379 pszProperty = a->argv[2];
1380
1381 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
1382 AccessMode_ReadWrite, pMedium,
1383 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1384 if (SUCCEEDED(rc) && !pMedium.isNull())
1385 {
1386 if (!RTStrICmp(pszAction, "set"))
1387 {
1388 const char *pszValue = a->argv[3];
1389 CHECK_ERROR(pMedium, SetProperty(Bstr(pszProperty).raw(), Bstr(pszValue).raw()));
1390 }
1391 else if (!RTStrICmp(pszAction, "get"))
1392 {
1393 Bstr strVal;
1394 CHECK_ERROR(pMedium, GetProperty(Bstr(pszProperty).raw(), strVal.asOutParam()));
1395 if (SUCCEEDED(rc))
1396 RTPrintf("%s=%ls\n", pszProperty, strVal.raw());
1397 }
1398 else if (!RTStrICmp(pszAction, "delete"))
1399 {
1400 /** @todo */
1401 }
1402 }
1403
1404 return SUCCEEDED(rc) ? 0 : 1;
1405}
1406
1407#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