VirtualBox

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

最後變更 在這個檔案從35036是 34888,由 vboxsync 提交於 14 年 前

Frontends/VBoxManage: Clean up "modifyhd", and unbreak some of the options. Make the resize option take MBs (previously was documented this way and expected bytes), and add a resizebyte option for special purposes

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 43.2 KB
 
1/* $Id: VBoxManageDisk.cpp 34888 2010-12-09 14:17:58Z vboxsync $ */
2/** @file
3 * VBoxManage - The disk related commands.
4 */
5
6/*
7 * Copyright (C) 2006-2010 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* 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 RTMsgError(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
112 rc = VERR_PARSE_ERROR;
113
114 if (RT_SUCCESS(rc))
115 *pDiskType = DiskType;
116 return rc;
117}
118
119/** @todo move this into getopt, as getting bool values is generic */
120static int parseBool(const char *psz, bool *pb)
121{
122 int rc = VINF_SUCCESS;
123 if ( !RTStrICmp(psz, "on")
124 || !RTStrICmp(psz, "yes")
125 || !RTStrICmp(psz, "true")
126 || !RTStrICmp(psz, "1")
127 || !RTStrICmp(psz, "enable")
128 || !RTStrICmp(psz, "enabled"))
129 {
130 *pb = true;
131 }
132 else if ( !RTStrICmp(psz, "off")
133 || !RTStrICmp(psz, "no")
134 || !RTStrICmp(psz, "false")
135 || !RTStrICmp(psz, "0")
136 || !RTStrICmp(psz, "disable")
137 || !RTStrICmp(psz, "disabled"))
138 {
139 *pb = false;
140 }
141 else
142 rc = VERR_PARSE_ERROR;
143
144 return rc;
145}
146
147static const RTGETOPTDEF g_aCreateHardDiskOptions[] =
148{
149 { "--filename", 'f', RTGETOPT_REQ_STRING },
150 { "-filename", 'f', RTGETOPT_REQ_STRING }, // deprecated
151 { "--size", 's', RTGETOPT_REQ_UINT64 },
152 { "-size", 's', RTGETOPT_REQ_UINT64 }, // deprecated
153 { "--sizebyte", 'S', RTGETOPT_REQ_UINT64 },
154 { "--format", 'o', RTGETOPT_REQ_STRING },
155 { "-format", 'o', RTGETOPT_REQ_STRING }, // deprecated
156 { "--static", 'F', RTGETOPT_REQ_NOTHING },
157 { "-static", 'F', RTGETOPT_REQ_NOTHING }, // deprecated
158 { "--variant", 'm', RTGETOPT_REQ_STRING },
159 { "-variant", 'm', RTGETOPT_REQ_STRING }, // deprecated
160};
161
162int handleCreateHardDisk(HandlerArg *a)
163{
164 HRESULT rc;
165 int vrc;
166 Bstr filename;
167 uint64_t size = 0;
168 Bstr format = "VDI";
169 MediumVariant_T DiskVariant = MediumVariant_Standard;
170
171 int c;
172 RTGETOPTUNION ValueUnion;
173 RTGETOPTSTATE GetState;
174 // start at 0 because main() has hacked both the argc and argv given to us
175 RTGetOptInit(&GetState, a->argc, a->argv, g_aCreateHardDiskOptions, RT_ELEMENTS(g_aCreateHardDiskOptions),
176 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
177 while ((c = RTGetOpt(&GetState, &ValueUnion)))
178 {
179 switch (c)
180 {
181 case 'f': // --filename
182 filename = ValueUnion.psz;
183 break;
184
185 case 's': // --size
186 size = ValueUnion.u64 * _1M;
187 break;
188
189 case 'S': // --sizebyte
190 size = ValueUnion.u64;
191 break;
192
193 case 'o': // --format
194 format = ValueUnion.psz;
195 break;
196
197 case 'F': // --static ("fixed"/"flat")
198 {
199 unsigned uDiskVariant = (unsigned)DiskVariant;
200 uDiskVariant |= MediumVariant_Fixed;
201 DiskVariant = (MediumVariant_T)uDiskVariant;
202 break;
203 }
204
205 case 'm': // --variant
206 vrc = parseDiskVariant(ValueUnion.psz, &DiskVariant);
207 if (RT_FAILURE(vrc))
208 return errorArgument("Invalid hard disk variant '%s'", ValueUnion.psz);
209 break;
210
211 case VINF_GETOPT_NOT_OPTION:
212 return errorSyntax(USAGE_CREATEHD, "Invalid parameter '%s'", ValueUnion.psz);
213
214 default:
215 if (c > 0)
216 {
217 if (RT_C_IS_PRINT(c))
218 return errorSyntax(USAGE_CREATEHD, "Invalid option -%c", c);
219 else
220 return errorSyntax(USAGE_CREATEHD, "Invalid option case %i", c);
221 }
222 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
223 return errorSyntax(USAGE_CREATEHD, "unknown option: %s\n", ValueUnion.psz);
224 else if (ValueUnion.pDef)
225 return errorSyntax(USAGE_CREATEHD, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
226 else
227 return errorSyntax(USAGE_CREATEHD, "error: %Rrs", c);
228 }
229 }
230
231 /* check the outcome */
232 if ( filename.isEmpty()
233 || size == 0)
234 return errorSyntax(USAGE_CREATEHD, "Parameters --filename and --size are required");
235
236 /* check for filename extension */
237 Utf8Str strName(filename);
238 if (!RTPathHaveExt(strName.c_str()))
239 {
240 Utf8Str strFormat(format);
241 if (strFormat.compare("vmdk", iprt::MiniString::CaseInsensitive) == 0)
242 strName.append(".vmdk");
243 else if (strFormat.compare("vhd", iprt::MiniString::CaseInsensitive) == 0)
244 strName.append(".vhd");
245 else
246 strName.append(".vdi");
247 filename = Bstr(strName);
248 }
249
250 ComPtr<IMedium> hardDisk;
251 CHECK_ERROR(a->virtualBox, CreateHardDisk(format.raw(), filename.raw(),
252 hardDisk.asOutParam()));
253 if (SUCCEEDED(rc) && hardDisk)
254 {
255 ComPtr<IProgress> progress;
256 CHECK_ERROR(hardDisk, CreateBaseStorage(size, DiskVariant, progress.asOutParam()));
257 if (SUCCEEDED(rc) && progress)
258 {
259 rc = showProgress(progress);
260 if (FAILED(rc))
261 {
262 com::ProgressErrorInfo info(progress);
263 if (info.isBasicAvailable())
264 RTMsgError("Failed to create hard disk. Error message: %lS", info.getText().raw());
265 else
266 RTMsgError("Failed to create hard disk. No error message available!");
267 }
268 else
269 {
270 Bstr uuid;
271 CHECK_ERROR(hardDisk, COMGETTER(Id)(uuid.asOutParam()));
272 RTPrintf("Disk image created. UUID: %s\n", Utf8Str(uuid).c_str());
273 }
274 }
275 CHECK_ERROR(hardDisk, Close());
276 }
277 return SUCCEEDED(rc) ? 0 : 1;
278}
279
280static const RTGETOPTDEF g_aModifyHardDiskOptions[] =
281{
282 { "--type", 't', RTGETOPT_REQ_STRING },
283 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
284 { "settype", 't', RTGETOPT_REQ_STRING }, // deprecated
285 { "--autoreset", 'z', RTGETOPT_REQ_STRING },
286 { "-autoreset", 'z', RTGETOPT_REQ_STRING }, // deprecated
287 { "autoreset", 'z', RTGETOPT_REQ_STRING }, // deprecated
288 { "--compact", 'c', RTGETOPT_REQ_NOTHING },
289 { "-compact", 'c', RTGETOPT_REQ_NOTHING }, // deprecated
290 { "compact", 'c', RTGETOPT_REQ_NOTHING }, // deprecated
291 { "--resize", 'r', RTGETOPT_REQ_UINT64 },
292 { "--resizebyte", 'R', RTGETOPT_REQ_UINT64 }
293};
294
295int handleModifyHardDisk(HandlerArg *a)
296{
297 HRESULT rc;
298 int vrc;
299 ComPtr<IMedium> hardDisk;
300 MediumType_T DiskType;
301 bool AutoReset = false;
302 bool fModifyDiskType = false, fModifyAutoReset = false, fModifyCompact = false;
303 bool fModifyResize = false;
304 uint64_t cbResize = 0;
305 const char *FilenameOrUuid = NULL;
306 bool unknown = false;
307
308 int c;
309 RTGETOPTUNION ValueUnion;
310 RTGETOPTSTATE GetState;
311 // start at 0 because main() has hacked both the argc and argv given to us
312 RTGetOptInit(&GetState, a->argc, a->argv, g_aModifyHardDiskOptions, RT_ELEMENTS(g_aModifyHardDiskOptions),
313 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
314 while ((c = RTGetOpt(&GetState, &ValueUnion)))
315 {
316 switch (c)
317 {
318 case 't': // --type
319 vrc = parseDiskType(ValueUnion.psz, &DiskType);
320 if (RT_FAILURE(vrc))
321 return errorArgument("Invalid hard disk type '%s'", ValueUnion.psz);
322 fModifyDiskType = true;
323 break;
324
325 case 'z': // --autoreset
326 vrc = parseBool(ValueUnion.psz, &AutoReset);
327 if (RT_FAILURE(vrc))
328 return errorArgument("Invalid autoreset parameter '%s'", ValueUnion.psz);
329 fModifyAutoReset = true;
330 break;
331
332 case 'c': // --compact
333 fModifyCompact = true;
334 break;
335
336 case 'r': // --resize
337 cbResize = ValueUnion.u64 * _1M;
338 fModifyResize = true;
339 break;
340
341 case 'R': // --resizebyte
342 cbResize = ValueUnion.u64;
343 fModifyResize = true;
344 break;
345
346 case VINF_GETOPT_NOT_OPTION:
347 if (!FilenameOrUuid)
348 FilenameOrUuid = ValueUnion.psz;
349 else
350 return errorSyntax(USAGE_CREATEHD, "Invalid parameter '%s'", ValueUnion.psz);
351 break;
352
353 default:
354 if (c > 0)
355 {
356 if (RT_C_IS_PRINT(c))
357 return errorSyntax(USAGE_MODIFYHD, "Invalid option -%c", c);
358 else
359 return errorSyntax(USAGE_MODIFYHD, "Invalid option case %i", c);
360 }
361 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
362 return errorSyntax(USAGE_MODIFYHD, "unknown option: %s\n", ValueUnion.psz);
363 else if (ValueUnion.pDef)
364 return errorSyntax(USAGE_MODIFYHD, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
365 else
366 return errorSyntax(USAGE_MODIFYHD, "error: %Rrs", c);
367 }
368 }
369
370 if (!FilenameOrUuid)
371 return errorSyntax(USAGE_MODIFYHD, "Disk name or UUID required");
372
373 if (!fModifyDiskType && !fModifyAutoReset && !fModifyCompact && !fModifyResize)
374 return errorSyntax(USAGE_MODIFYHD, "No operation specified");
375
376 /* Depending on the operation the medium must be in the registry or
377 * may be opened on demand. */
378 if (fModifyDiskType || fModifyAutoReset)
379 {
380 CHECK_ERROR(a->virtualBox, FindMedium(Bstr(FilenameOrUuid).raw(),
381 DeviceType_HardDisk,
382 hardDisk.asOutParam()));
383 }
384 else
385 {
386 rc = a->virtualBox->FindMedium(Bstr(FilenameOrUuid).raw(),
387 DeviceType_HardDisk,
388 hardDisk.asOutParam());
389 /* the hard disk image might not be registered */
390 if (!hardDisk)
391 {
392 unknown = true;
393 rc = a->virtualBox->OpenMedium(Bstr(FilenameOrUuid).raw(),
394 DeviceType_HardDisk,
395 AccessMode_ReadWrite,
396 hardDisk.asOutParam());
397 if (rc == VBOX_E_FILE_ERROR)
398 {
399 char szFilenameAbs[RTPATH_MAX] = "";
400 int irc = RTPathAbs(FilenameOrUuid, szFilenameAbs, sizeof(szFilenameAbs));
401 if (RT_FAILURE(irc))
402 {
403 RTMsgError("Cannot convert filename \"%s\" to absolute path", FilenameOrUuid);
404 return 1;
405 }
406 CHECK_ERROR(a->virtualBox, OpenMedium(Bstr(szFilenameAbs).raw(),
407 DeviceType_HardDisk,
408 AccessMode_ReadWrite,
409 hardDisk.asOutParam()));
410 }
411 }
412 }
413 if (FAILED(rc))
414 return 1;
415 if (hardDisk.isNull())
416 {
417 RTMsgError("Invalid hard disk reference, avoiding crash");
418 return 1;
419 }
420
421 if (fModifyDiskType)
422 {
423 MediumType_T hddType;
424 CHECK_ERROR(hardDisk, COMGETTER(Type)(&hddType));
425
426 if (hddType != DiskType)
427 CHECK_ERROR(hardDisk, COMSETTER(Type)(DiskType));
428 }
429
430 if (fModifyAutoReset)
431 {
432 CHECK_ERROR(hardDisk, COMSETTER(AutoReset)(AutoReset));
433 }
434
435 if (fModifyCompact)
436 {
437 ComPtr<IProgress> progress;
438 CHECK_ERROR(hardDisk, Compact(progress.asOutParam()));
439 if (SUCCEEDED(rc))
440 rc = showProgress(progress);
441 if (FAILED(rc))
442 {
443 if (rc == E_NOTIMPL)
444 RTMsgError("Compact hard disk operation is not implemented!");
445 else if (rc == VBOX_E_NOT_SUPPORTED)
446 RTMsgError("Compact hard disk operation for this format is not implemented yet!");
447 else
448 com::GluePrintRCMessage(rc);
449 }
450 }
451
452 if (fModifyResize)
453 {
454 ComPtr<IProgress> progress;
455 CHECK_ERROR(hardDisk, Resize(cbResize, progress.asOutParam()));
456 if (SUCCEEDED(rc))
457 rc = showProgress(progress);
458 if (FAILED(rc))
459 {
460 if (rc == E_NOTIMPL)
461 RTMsgError("Resize hard disk operation is not implemented!");
462 else if (rc == VBOX_E_NOT_SUPPORTED)
463 RTMsgError("Resize hard disk operation for this format is not implemented yet!");
464 else
465 com::GluePrintRCMessage(rc);
466 }
467 }
468
469 if (unknown)
470 hardDisk->Close();
471
472 return SUCCEEDED(rc) ? 0 : 1;
473}
474
475static const RTGETOPTDEF g_aCloneHardDiskOptions[] =
476{
477 { "--format", 'o', RTGETOPT_REQ_STRING },
478 { "-format", 'o', RTGETOPT_REQ_STRING },
479 { "--static", 'F', RTGETOPT_REQ_NOTHING },
480 { "-static", 'F', RTGETOPT_REQ_NOTHING },
481 { "--existing", 'E', RTGETOPT_REQ_NOTHING },
482 { "--variant", 'm', RTGETOPT_REQ_STRING },
483 { "-variant", 'm', RTGETOPT_REQ_STRING },
484 { "--type", 't', RTGETOPT_REQ_STRING },
485 { "-type", 't', RTGETOPT_REQ_STRING },
486 { "--remember", 'r', RTGETOPT_REQ_NOTHING },
487 { "-remember", 'r', RTGETOPT_REQ_NOTHING },
488 { "--register", 'r', RTGETOPT_REQ_NOTHING },
489 { "-register", 'r', RTGETOPT_REQ_NOTHING },
490};
491
492int handleCloneHardDisk(HandlerArg *a)
493{
494 HRESULT rc;
495 int vrc;
496 Bstr src, dst;
497 Bstr format;
498 MediumVariant_T DiskVariant = MediumVariant_Standard;
499 bool fExisting = false;
500 bool fRemember = false;
501 bool fSetDiskType = false;
502 MediumType_T DiskType = MediumType_Normal;
503
504 int c;
505 RTGETOPTUNION ValueUnion;
506 RTGETOPTSTATE GetState;
507 // start at 0 because main() has hacked both the argc and argv given to us
508 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloneHardDiskOptions, RT_ELEMENTS(g_aCloneHardDiskOptions),
509 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
510 while ((c = RTGetOpt(&GetState, &ValueUnion)))
511 {
512 switch (c)
513 {
514 case 'o': // --format
515 format = ValueUnion.psz;
516 break;
517
518 case 'F': // --static
519 {
520 unsigned uDiskVariant = (unsigned)DiskVariant;
521 uDiskVariant |= MediumVariant_Fixed;
522 DiskVariant = (MediumVariant_T)uDiskVariant;
523 break;
524 }
525
526 case 'E': // --existing
527 fExisting = true;
528 break;
529
530 case 'm': // --variant
531 vrc = parseDiskVariant(ValueUnion.psz, &DiskVariant);
532 if (RT_FAILURE(vrc))
533 return errorArgument("Invalid hard disk variant '%s'", ValueUnion.psz);
534 break;
535
536 case 'r': // --remember
537 fRemember = true;
538 break;
539
540 case 't': // --type
541 vrc = parseDiskType(ValueUnion.psz, &DiskType);
542 if (RT_FAILURE(vrc))
543 return errorArgument("Invalid hard disk type '%s'", ValueUnion.psz);
544 fSetDiskType = true;
545 break;
546
547 case VINF_GETOPT_NOT_OPTION:
548 if (src.isEmpty())
549 src = ValueUnion.psz;
550 else if (dst.isEmpty())
551 dst = ValueUnion.psz;
552 else
553 return errorSyntax(USAGE_CLONEHD, "Invalid parameter '%s'", ValueUnion.psz);
554 break;
555
556 default:
557 if (c > 0)
558 {
559 if (RT_C_IS_GRAPH(c))
560 return errorSyntax(USAGE_CLONEHD, "unhandled option: -%c", c);
561 else
562 return errorSyntax(USAGE_CLONEHD, "unhandled option: %i", c);
563 }
564 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
565 return errorSyntax(USAGE_CLONEHD, "unknown option: %s", ValueUnion.psz);
566 else if (ValueUnion.pDef)
567 return errorSyntax(USAGE_CLONEHD, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
568 else
569 return errorSyntax(USAGE_CLONEHD, "error: %Rrs", c);
570 }
571 }
572
573 if (src.isEmpty())
574 return errorSyntax(USAGE_CLONEHD, "Mandatory UUID or input file parameter missing");
575 if (dst.isEmpty())
576 return errorSyntax(USAGE_CLONEHD, "Mandatory output file parameter missing");
577 if (fExisting && (!format.isEmpty() || DiskVariant != MediumType_Normal))
578 return errorSyntax(USAGE_CLONEHD, "Specified options which cannot be used with --existing");
579
580 ComPtr<IMedium> srcDisk;
581 ComPtr<IMedium> dstDisk;
582 bool fSrcUnknown = false;
583 bool fDstUnknown = false;
584
585 rc = a->virtualBox->FindMedium(src.raw(), DeviceType_HardDisk,
586 srcDisk.asOutParam());
587 /* no? well, then it's an unknown image */
588 if (FAILED (rc))
589 {
590 rc = a->virtualBox->OpenMedium(src.raw(), DeviceType_HardDisk,
591 AccessMode_ReadWrite,
592 srcDisk.asOutParam());
593 if (rc == VBOX_E_FILE_ERROR)
594 {
595 char szFilenameAbs[RTPATH_MAX] = "";
596 int irc = RTPathAbs(Utf8Str(src).c_str(), szFilenameAbs, sizeof(szFilenameAbs));
597 if (RT_FAILURE(irc))
598 {
599 RTMsgError("Cannot convert filename \"%s\" to absolute path", Utf8Str(src).c_str());
600 return 1;
601 }
602 CHECK_ERROR(a->virtualBox, OpenMedium(Bstr(szFilenameAbs).raw(),
603 DeviceType_HardDisk,
604 AccessMode_ReadWrite,
605 srcDisk.asOutParam()));
606 }
607 else if (SUCCEEDED(rc))
608 fSrcUnknown = true;
609 else
610 {
611 com::GluePrintRCMessage(rc);
612 return 1;
613 }
614 }
615
616 do
617 {
618 /* open/create destination hard disk */
619 if (fExisting)
620 {
621 rc = a->virtualBox->FindMedium(dst.raw(), DeviceType_HardDisk,
622 dstDisk.asOutParam());
623 /* no? well, then it's an unknown image */
624 if (FAILED(rc))
625 {
626 rc = a->virtualBox->OpenMedium(dst.raw(), DeviceType_HardDisk,
627 AccessMode_ReadWrite,
628 dstDisk.asOutParam());
629 if (rc == VBOX_E_FILE_ERROR)
630 {
631 char szFilenameAbs[RTPATH_MAX] = "";
632 int irc = RTPathAbs(Utf8Str(dst).c_str(), szFilenameAbs, sizeof(szFilenameAbs));
633 if (RT_FAILURE(irc))
634 {
635 RTMsgError("Cannot convert filename \"%s\" to absolute path", Utf8Str(dst).c_str());
636 return 1;
637 }
638 CHECK_ERROR_BREAK(a->virtualBox, OpenMedium(Bstr(szFilenameAbs).raw(),
639 DeviceType_HardDisk,
640 AccessMode_ReadWrite,
641 dstDisk.asOutParam()));
642 }
643 else if (SUCCEEDED(rc))
644 fDstUnknown = true;
645 else
646 {
647 com::GluePrintRCMessage(rc);
648 break;
649 }
650 }
651 else
652 fRemember = true;
653 if (SUCCEEDED(rc))
654 {
655 /* Perform accessibility check now. */
656 MediumState_T state;
657 CHECK_ERROR_BREAK(dstDisk, RefreshState(&state));
658 CHECK_ERROR_BREAK(dstDisk, COMGETTER(Format)(format.asOutParam()));
659 }
660 }
661 else
662 {
663 /* use the format of the source hard disk if unspecified */
664 if (format.isEmpty())
665 CHECK_ERROR_BREAK(srcDisk, COMGETTER(Format)(format.asOutParam()));
666 CHECK_ERROR_BREAK(a->virtualBox, CreateHardDisk(format.raw(),
667 dst.raw(),
668 dstDisk.asOutParam()));
669 }
670
671 ComPtr<IProgress> progress;
672 CHECK_ERROR_BREAK(srcDisk, CloneTo(dstDisk, DiskVariant, NULL, progress.asOutParam()));
673
674 rc = showProgress(progress);
675 if (FAILED(rc))
676 {
677 com::ProgressErrorInfo info(progress);
678 if (info.isBasicAvailable())
679 RTMsgError("Failed to clone hard disk. Error message: %lS", info.getText().raw());
680 else
681 RTMsgError("Failed to clone hard disk. No error message available!");
682 break;
683 }
684
685 Bstr uuid;
686 CHECK_ERROR_BREAK(dstDisk, COMGETTER(Id)(uuid.asOutParam()));
687
688 RTPrintf("Clone hard disk created in format '%ls'. UUID: %s\n",
689 format.raw(), Utf8Str(uuid).c_str());
690 }
691 while (0);
692
693 if (!fRemember && !dstDisk.isNull())
694 {
695 /* forget the created clone */
696 dstDisk->Close();
697 }
698 else if (fSetDiskType)
699 {
700 CHECK_ERROR(dstDisk, COMSETTER(Type)(DiskType));
701 }
702
703 if (fSrcUnknown)
704 {
705 /* close the unknown hard disk to forget it again */
706 srcDisk->Close();
707 }
708
709 return SUCCEEDED(rc) ? 0 : 1;
710}
711
712static const RTGETOPTDEF g_aConvertFromRawHardDiskOptions[] =
713{
714 { "--format", 'o', RTGETOPT_REQ_STRING },
715 { "-format", 'o', RTGETOPT_REQ_STRING },
716 { "--static", 'F', RTGETOPT_REQ_NOTHING },
717 { "-static", 'F', RTGETOPT_REQ_NOTHING },
718 { "--variant", 'm', RTGETOPT_REQ_STRING },
719 { "-variant", 'm', RTGETOPT_REQ_STRING },
720};
721
722RTEXITCODE handleConvertFromRaw(int argc, char *argv[])
723{
724 int rc = VINF_SUCCESS;
725 bool fReadFromStdIn = false;
726 const char *format = "VDI";
727 const char *srcfilename = NULL;
728 const char *dstfilename = NULL;
729 const char *filesize = NULL;
730 unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
731 void *pvBuf = NULL;
732
733 int c;
734 RTGETOPTUNION ValueUnion;
735 RTGETOPTSTATE GetState;
736 // start at 0 because main() has hacked both the argc and argv given to us
737 RTGetOptInit(&GetState, argc, argv, g_aConvertFromRawHardDiskOptions, RT_ELEMENTS(g_aConvertFromRawHardDiskOptions),
738 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
739 while ((c = RTGetOpt(&GetState, &ValueUnion)))
740 {
741 switch (c)
742 {
743 case 'o': // --format
744 format = ValueUnion.psz;
745 break;
746
747 case 'm': // --variant
748 MediumVariant_T DiskVariant;
749 rc = parseDiskVariant(ValueUnion.psz, &DiskVariant);
750 if (RT_FAILURE(rc))
751 return errorArgument("Invalid hard disk variant '%s'", ValueUnion.psz);
752 /// @todo cleaner solution than assuming 1:1 mapping?
753 uImageFlags = (unsigned)DiskVariant;
754 break;
755
756 case VINF_GETOPT_NOT_OPTION:
757 if (!srcfilename)
758 {
759 srcfilename = ValueUnion.psz;
760// If you change the OS list here don't forget to update VBoxManageHelp.cpp.
761#ifndef RT_OS_WINDOWS
762 fReadFromStdIn = !strcmp(srcfilename, "stdin");
763#endif
764 }
765 else if (!dstfilename)
766 dstfilename = ValueUnion.psz;
767 else if (fReadFromStdIn && !filesize)
768 filesize = ValueUnion.psz;
769 else
770 return errorSyntax(USAGE_CONVERTFROMRAW, "Invalid parameter '%s'", ValueUnion.psz);
771 break;
772
773 default:
774 return errorGetOpt(USAGE_CONVERTFROMRAW, c, &ValueUnion);
775 }
776 }
777
778 if (!srcfilename || !dstfilename || (fReadFromStdIn && !filesize))
779 return errorSyntax(USAGE_CONVERTFROMRAW, "Incorrect number of parameters");
780 RTStrmPrintf(g_pStdErr, "Converting from raw image file=\"%s\" to file=\"%s\"...\n",
781 srcfilename, dstfilename);
782
783 PVBOXHDD pDisk = NULL;
784
785 PVDINTERFACE pVDIfs = NULL;
786 VDINTERFACE vdInterfaceError;
787 VDINTERFACEERROR vdInterfaceErrorCallbacks;
788 vdInterfaceErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
789 vdInterfaceErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
790 vdInterfaceErrorCallbacks.pfnError = handleVDError;
791 vdInterfaceErrorCallbacks.pfnMessage = NULL;
792
793 rc = VDInterfaceAdd(&vdInterfaceError, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
794 &vdInterfaceErrorCallbacks, NULL, &pVDIfs);
795 AssertRC(rc);
796
797 /* open raw image file. */
798 RTFILE File;
799 if (fReadFromStdIn)
800 File = 0;
801 else
802 rc = RTFileOpen(&File, srcfilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
803 if (RT_FAILURE(rc))
804 {
805 RTMsgError("Cannot open file \"%s\": %Rrc", srcfilename, rc);
806 goto out;
807 }
808
809 uint64_t cbFile;
810 /* get image size. */
811 if (fReadFromStdIn)
812 cbFile = RTStrToUInt64(filesize);
813 else
814 rc = RTFileGetSize(File, &cbFile);
815 if (RT_FAILURE(rc))
816 {
817 RTMsgError("Cannot get image size for file \"%s\": %Rrc", srcfilename, rc);
818 goto out;
819 }
820
821 RTStrmPrintf(g_pStdErr, "Creating %s image with size %RU64 bytes (%RU64MB)...\n",
822 (uImageFlags & VD_IMAGE_FLAGS_FIXED) ? "fixed" : "dynamic", cbFile, (cbFile + _1M - 1) / _1M);
823 char pszComment[256];
824 RTStrPrintf(pszComment, sizeof(pszComment), "Converted image from %s", srcfilename);
825 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
826 if (RT_FAILURE(rc))
827 {
828 RTMsgError("Cannot create the virtual disk container: %Rrc", rc);
829 goto out;
830 }
831
832 Assert(RT_MIN(cbFile / 512 / 16 / 63, 16383) -
833 (unsigned int)RT_MIN(cbFile / 512 / 16 / 63, 16383) == 0);
834 VDGEOMETRY PCHS, LCHS;
835 PCHS.cCylinders = (unsigned int)RT_MIN(cbFile / 512 / 16 / 63, 16383);
836 PCHS.cHeads = 16;
837 PCHS.cSectors = 63;
838 LCHS.cCylinders = 0;
839 LCHS.cHeads = 0;
840 LCHS.cSectors = 0;
841 rc = VDCreateBase(pDisk, format, dstfilename, cbFile,
842 uImageFlags, pszComment, &PCHS, &LCHS, NULL,
843 VD_OPEN_FLAGS_NORMAL, NULL, NULL);
844 if (RT_FAILURE(rc))
845 {
846 RTMsgError("Cannot create the disk image \"%s\": %Rrc", dstfilename, rc);
847 goto out;
848 }
849
850 size_t cbBuffer;
851 cbBuffer = _1M;
852 pvBuf = RTMemAlloc(cbBuffer);
853 if (!pvBuf)
854 {
855 rc = VERR_NO_MEMORY;
856 RTMsgError("Out of memory allocating buffers for image \"%s\": %Rrc", dstfilename, rc);
857 goto out;
858 }
859
860 uint64_t offFile;
861 offFile = 0;
862 while (offFile < cbFile)
863 {
864 size_t cbRead;
865 size_t cbToRead;
866 cbRead = 0;
867 cbToRead = cbFile - offFile >= (uint64_t)cbBuffer ?
868 cbBuffer : (size_t)(cbFile - offFile);
869 rc = RTFileRead(File, pvBuf, cbToRead, &cbRead);
870 if (RT_FAILURE(rc) || !cbRead)
871 break;
872 rc = VDWrite(pDisk, offFile, pvBuf, cbRead);
873 if (RT_FAILURE(rc))
874 {
875 RTMsgError("Failed to write to disk image \"%s\": %Rrc", dstfilename, rc);
876 goto out;
877 }
878 offFile += cbRead;
879 }
880
881out:
882 if (pvBuf)
883 RTMemFree(pvBuf);
884 if (pDisk)
885 VDClose(pDisk, RT_FAILURE(rc));
886 if (File != NIL_RTFILE)
887 RTFileClose(File);
888
889 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
890}
891
892static const RTGETOPTDEF g_aShowHardDiskInfoOptions[] =
893{
894 { "--dummy", 256, RTGETOPT_REQ_NOTHING }, // placeholder for C++
895};
896
897int handleShowHardDiskInfo(HandlerArg *a)
898{
899 HRESULT rc;
900 const char *FilenameOrUuid = NULL;
901
902 int c;
903 RTGETOPTUNION ValueUnion;
904 RTGETOPTSTATE GetState;
905 // start at 0 because main() has hacked both the argc and argv given to us
906 RTGetOptInit(&GetState, a->argc, a->argv, g_aShowHardDiskInfoOptions, RT_ELEMENTS(g_aShowHardDiskInfoOptions),
907 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
908 while ((c = RTGetOpt(&GetState, &ValueUnion)))
909 {
910 switch (c)
911 {
912 case VINF_GETOPT_NOT_OPTION:
913 if (!FilenameOrUuid)
914 FilenameOrUuid = ValueUnion.psz;
915 else
916 return errorSyntax(USAGE_SHOWHDINFO, "Invalid parameter '%s'", ValueUnion.psz);
917 break;
918
919 default:
920 if (c > 0)
921 {
922 if (RT_C_IS_PRINT(c))
923 return errorSyntax(USAGE_SHOWHDINFO, "Invalid option -%c", c);
924 else
925 return errorSyntax(USAGE_SHOWHDINFO, "Invalid option case %i", c);
926 }
927 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
928 return errorSyntax(USAGE_SHOWHDINFO, "unknown option: %s\n", ValueUnion.psz);
929 else if (ValueUnion.pDef)
930 return errorSyntax(USAGE_SHOWHDINFO, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
931 else
932 return errorSyntax(USAGE_SHOWHDINFO, "error: %Rrs", c);
933 }
934 }
935
936 /* check for required options */
937 if (!FilenameOrUuid)
938 return errorSyntax(USAGE_SHOWHDINFO, "Disk name or UUID required");
939
940 ComPtr<IMedium> hardDisk;
941 bool unknown = false;
942 /* first guess is that it's a UUID */
943 rc = a->virtualBox->FindMedium(Bstr(FilenameOrUuid).raw(),
944 DeviceType_HardDisk,
945 hardDisk.asOutParam());
946 /* no? well, then it's an unknown image */
947 if (FAILED(rc))
948 {
949 rc = a->virtualBox->OpenMedium(Bstr(FilenameOrUuid).raw(),
950 DeviceType_HardDisk,
951 AccessMode_ReadWrite,
952 hardDisk.asOutParam());
953 if (rc == VBOX_E_FILE_ERROR)
954 {
955 char szFilenameAbs[RTPATH_MAX] = "";
956 int vrc = RTPathAbs(FilenameOrUuid, szFilenameAbs, sizeof(szFilenameAbs));
957 if (RT_FAILURE(vrc))
958 {
959 RTMsgError("Cannot convert filename \"%s\" to absolute path", FilenameOrUuid);
960 return 1;
961 }
962 CHECK_ERROR(a->virtualBox, OpenMedium(Bstr(szFilenameAbs).raw(),
963 DeviceType_HardDisk,
964 AccessMode_ReadWrite,
965 hardDisk.asOutParam()));
966 }
967 else if (SUCCEEDED(rc))
968 unknown = true;
969 else
970 {
971 com::GluePrintRCMessage(rc);
972 return 1;
973 }
974 }
975
976 do
977 {
978 Bstr uuid;
979 hardDisk->COMGETTER(Id)(uuid.asOutParam());
980 RTPrintf("UUID: %s\n", Utf8Str(uuid).c_str());
981
982 /* check for accessibility */
983 /// @todo NEWMEDIA check accessibility of all parents
984 /// @todo NEWMEDIA print the full state value
985 MediumState_T state;
986 CHECK_ERROR_BREAK(hardDisk, RefreshState(&state));
987 RTPrintf("Accessible: %s\n", state != MediumState_Inaccessible ? "yes" : "no");
988
989 if (state == MediumState_Inaccessible)
990 {
991 Bstr err;
992 CHECK_ERROR_BREAK(hardDisk, COMGETTER(LastAccessError)(err.asOutParam()));
993 RTPrintf("Access Error: %lS\n", err.raw());
994 }
995
996 Bstr description;
997 hardDisk->COMGETTER(Description)(description.asOutParam());
998 if (!description.isEmpty())
999 {
1000 RTPrintf("Description: %lS\n", description.raw());
1001 }
1002
1003 LONG64 logicalSize;
1004 hardDisk->COMGETTER(LogicalSize)(&logicalSize);
1005 RTPrintf("Logical size: %lld MBytes\n", logicalSize);
1006 LONG64 actualSize;
1007 hardDisk->COMGETTER(Size)(&actualSize);
1008 RTPrintf("Current size on disk: %lld MBytes\n", actualSize >> 20);
1009
1010 ComPtr <IMedium> parent;
1011 hardDisk->COMGETTER(Parent)(parent.asOutParam());
1012
1013 MediumType_T type;
1014 hardDisk->COMGETTER(Type)(&type);
1015 const char *typeStr = "unknown";
1016 switch (type)
1017 {
1018 case MediumType_Normal:
1019 if (!parent.isNull())
1020 typeStr = "normal (differencing)";
1021 else
1022 typeStr = "normal (base)";
1023 break;
1024 case MediumType_Immutable:
1025 typeStr = "immutable";
1026 break;
1027 case MediumType_Writethrough:
1028 typeStr = "writethrough";
1029 break;
1030 case MediumType_Shareable:
1031 typeStr = "shareable";
1032 break;
1033 }
1034 RTPrintf("Type: %s\n", typeStr);
1035
1036 Bstr format;
1037 hardDisk->COMGETTER(Format)(format.asOutParam());
1038 RTPrintf("Storage format: %lS\n", format.raw());
1039 MediumVariant_T variant;
1040 hardDisk->COMGETTER(Variant)(&variant);
1041 const char *variantStr = "unknown";
1042 switch (variant & ~(MediumVariant_Fixed | MediumVariant_Diff))
1043 {
1044 case MediumVariant_VmdkSplit2G:
1045 variantStr = "split2G";
1046 break;
1047 case MediumVariant_VmdkStreamOptimized:
1048 variantStr = "streamOptimized";
1049 break;
1050 case MediumVariant_VmdkESX:
1051 variantStr = "ESX";
1052 break;
1053 case MediumVariant_Standard:
1054 variantStr = "default";
1055 break;
1056 }
1057 const char *variantTypeStr = "dynamic";
1058 if (variant & MediumVariant_Fixed)
1059 variantTypeStr = "fixed";
1060 else if (variant & MediumVariant_Diff)
1061 variantTypeStr = "differencing";
1062 RTPrintf("Format variant: %s %s\n", variantTypeStr, variantStr);
1063
1064 /// @todo also dump config parameters (iSCSI)
1065
1066 if (!unknown)
1067 {
1068 com::SafeArray<BSTR> machineIds;
1069 hardDisk->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(machineIds));
1070 for (size_t j = 0; j < machineIds.size(); ++ j)
1071 {
1072 ComPtr<IMachine> machine;
1073 CHECK_ERROR(a->virtualBox, FindMachine(machineIds[j], machine.asOutParam()));
1074 ASSERT(machine);
1075 Bstr name;
1076 machine->COMGETTER(Name)(name.asOutParam());
1077 machine->COMGETTER(Id)(uuid.asOutParam());
1078 RTPrintf("%s%lS (UUID: %lS)\n",
1079 j == 0 ? "In use by VMs: " : " ",
1080 name.raw(), machineIds[j]);
1081 }
1082 /// @todo NEWMEDIA check usage in snapshots too
1083 /// @todo NEWMEDIA also list children
1084 }
1085
1086 Bstr loc;
1087 hardDisk->COMGETTER(Location)(loc.asOutParam());
1088 RTPrintf("Location: %lS\n", loc.raw());
1089
1090 /* print out information specific for differencing hard disks */
1091 if (!parent.isNull())
1092 {
1093 BOOL autoReset = FALSE;
1094 hardDisk->COMGETTER(AutoReset)(&autoReset);
1095 RTPrintf("Auto-Reset: %s\n", autoReset ? "on" : "off");
1096 }
1097 }
1098 while (0);
1099
1100 if (unknown)
1101 {
1102 /* close the unknown hard disk to forget it again */
1103 hardDisk->Close();
1104 }
1105
1106 return SUCCEEDED(rc) ? 0 : 1;
1107}
1108
1109static const RTGETOPTDEF g_aCloseMediumOptions[] =
1110{
1111 { "disk", 'd', RTGETOPT_REQ_NOTHING },
1112 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
1113 { "floppy", 'f', RTGETOPT_REQ_NOTHING },
1114 { "--delete", 'r', RTGETOPT_REQ_NOTHING },
1115};
1116
1117int handleCloseMedium(HandlerArg *a)
1118{
1119 HRESULT rc = S_OK;
1120 enum {
1121 CMD_NONE,
1122 CMD_DISK,
1123 CMD_DVD,
1124 CMD_FLOPPY
1125 } cmd = CMD_NONE;
1126 const char *FilenameOrUuid = NULL;
1127 bool fDelete = false;
1128
1129 int c;
1130 RTGETOPTUNION ValueUnion;
1131 RTGETOPTSTATE GetState;
1132 // start at 0 because main() has hacked both the argc and argv given to us
1133 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloseMediumOptions, RT_ELEMENTS(g_aCloseMediumOptions),
1134 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1135 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1136 {
1137 switch (c)
1138 {
1139 case 'd': // disk
1140 if (cmd != CMD_NONE)
1141 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1142 cmd = CMD_DISK;
1143 break;
1144
1145 case 'D': // DVD
1146 if (cmd != CMD_NONE)
1147 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1148 cmd = CMD_DVD;
1149 break;
1150
1151 case 'f': // floppy
1152 if (cmd != CMD_NONE)
1153 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1154 cmd = CMD_FLOPPY;
1155 break;
1156
1157 case 'r': // --delete
1158 fDelete = true;
1159 break;
1160
1161 case VINF_GETOPT_NOT_OPTION:
1162 if (!FilenameOrUuid)
1163 FilenameOrUuid = ValueUnion.psz;
1164 else
1165 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
1166 break;
1167
1168 default:
1169 if (c > 0)
1170 {
1171 if (RT_C_IS_PRINT(c))
1172 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option -%c", c);
1173 else
1174 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option case %i", c);
1175 }
1176 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1177 return errorSyntax(USAGE_CLOSEMEDIUM, "unknown option: %s\n", ValueUnion.psz);
1178 else if (ValueUnion.pDef)
1179 return errorSyntax(USAGE_CLOSEMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1180 else
1181 return errorSyntax(USAGE_CLOSEMEDIUM, "error: %Rrs", c);
1182 }
1183 }
1184
1185 /* check for required options */
1186 if (cmd == CMD_NONE)
1187 return errorSyntax(USAGE_CLOSEMEDIUM, "Command variant disk/dvd/floppy required");
1188 if (!FilenameOrUuid)
1189 return errorSyntax(USAGE_CLOSEMEDIUM, "Disk name or UUID required");
1190
1191 ComPtr<IMedium> medium;
1192
1193 if (cmd == CMD_DISK)
1194 CHECK_ERROR(a->virtualBox, FindMedium(Bstr(FilenameOrUuid).raw(),
1195 DeviceType_HardDisk,
1196 medium.asOutParam()));
1197 else if (cmd == CMD_DVD)
1198 CHECK_ERROR(a->virtualBox, FindMedium(Bstr(FilenameOrUuid).raw(),
1199 DeviceType_DVD,
1200 medium.asOutParam()));
1201 else if (cmd == CMD_FLOPPY)
1202 CHECK_ERROR(a->virtualBox, FindMedium(Bstr(FilenameOrUuid).raw(),
1203 DeviceType_Floppy,
1204 medium.asOutParam()));
1205
1206 if (SUCCEEDED(rc) && medium)
1207 {
1208 if (fDelete)
1209 {
1210 ComPtr<IProgress> progress;
1211 CHECK_ERROR(medium, DeleteStorage(progress.asOutParam()));
1212 if (SUCCEEDED(rc))
1213 {
1214 rc = showProgress(progress);
1215 if (FAILED(rc))
1216 {
1217 com::ProgressErrorInfo info(progress);
1218 if (info.isBasicAvailable())
1219 RTMsgError("Failed to delete medium. Error message: %lS", info.getText().raw());
1220 else
1221 RTMsgError("Failed to delete medium. No error message available!");
1222 }
1223 }
1224 else
1225 RTMsgError("Failed to delete medium. Error code %Rrc", rc);
1226 }
1227 CHECK_ERROR(medium, Close());
1228 }
1229
1230 return SUCCEEDED(rc) ? 0 : 1;
1231}
1232#endif /* !VBOX_ONLY_DOCS */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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