VirtualBox

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

Last change on this file since 25377 was 25149, checked in by vboxsync, 15 years ago

Main: cleanup: remove all CheckComRC* macros (no functional change)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 57.0 KB
RevLine 
[12599]1/* $Id: VBoxManageDisk.cpp 25149 2009-12-02 14:34:47Z vboxsync $ */
[1]2/** @file
[15492]3 * VBoxManage - The disk delated commands.
[1]4 */
5
6/*
[20928]7 * Copyright (C) 2006-2009 Sun Microsystems, Inc.
[1]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
[5999]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.
[8155]16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
[1]20 */
21
[15492]22#ifndef VBOX_ONLY_DOCS
[1]23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#include <VBox/com/com.h>
[7379]28#include <VBox/com/array.h>
[1]29#include <VBox/com/ErrorInfo.h>
[20928]30#include <VBox/com/errorprint.h>
[1]31#include <VBox/com/VirtualBox.h>
32
[14619]33#include <iprt/asm.h>
34#include <iprt/file.h>
[18814]35#include <iprt/path.h>
36#include <iprt/param.h>
[1]37#include <iprt/stream.h>
38#include <iprt/string.h>
[17970]39#include <iprt/ctype.h>
40#include <iprt/getopt.h>
[15492]41#include <VBox/log.h>
[16873]42#include <VBox/VBoxHDD.h>
[1]43
44#include "VBoxManage.h"
45using namespace com;
46
47
48// funcs
49///////////////////////////////////////////////////////////////////////////////
50
[3077]51
[15366]52static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va)
53{
54 RTPrintf("ERROR: ");
55 RTPrintfV(pszFormat, va);
56 RTPrintf("\n");
57 RTPrintf("Error code %Rrc at %s(%u) in function %s\n", rc, RT_SRC_POS_ARGS);
58}
59
60
[23223]61static int parseDiskVariant(const char *psz, MediumVariant_T *pDiskVariant)
[17970]62{
63 int rc = VINF_SUCCESS;
[17975]64 unsigned DiskVariant = (unsigned)(*pDiskVariant);
[17970]65 while (psz && *psz && RT_SUCCESS(rc))
66 {
67 size_t len;
68 const char *pszComma = strchr(psz, ',');
69 if (pszComma)
70 len = pszComma - psz;
71 else
72 len = strlen(psz);
73 if (len > 0)
74 {
75 // Parsing is intentionally inconsistent: "standard" resets the
76 // variant, whereas the other flags are cumulative.
77 if (!RTStrNICmp(psz, "standard", len))
[23223]78 DiskVariant = MediumVariant_Standard;
[17970]79 else if ( !RTStrNICmp(psz, "fixed", len)
80 || !RTStrNICmp(psz, "static", len))
[23223]81 DiskVariant |= MediumVariant_Fixed;
[17970]82 else if (!RTStrNICmp(psz, "Diff", len))
[23223]83 DiskVariant |= MediumVariant_Diff;
[17970]84 else if (!RTStrNICmp(psz, "split2g", len))
[23223]85 DiskVariant |= MediumVariant_VmdkSplit2G;
[17970]86 else if ( !RTStrNICmp(psz, "stream", len)
87 || !RTStrNICmp(psz, "streamoptimized", len))
[23223]88 DiskVariant |= MediumVariant_VmdkStreamOptimized;
[18566]89 else if (!RTStrNICmp(psz, "esx", len))
[23223]90 DiskVariant |= MediumVariant_VmdkESX;
[17970]91 else
92 rc = VERR_PARSE_ERROR;
93 }
94 if (pszComma)
95 psz += len + 1;
96 else
97 psz += len;
98 }
99
100 if (RT_SUCCESS(rc))
[23223]101 *pDiskVariant = (MediumVariant_T)DiskVariant;
[17970]102 return rc;
103}
104
[23223]105static int parseDiskType(const char *psz, MediumType_T *pDiskType)
[17970]106{
107 int rc = VINF_SUCCESS;
[23223]108 MediumType_T DiskType = MediumType_Normal;
[17970]109 if (!RTStrICmp(psz, "normal"))
[23223]110 DiskType = MediumType_Normal;
[18703]111 else if (!RTStrICmp(psz, "immutable"))
[23223]112 DiskType = MediumType_Immutable;
[18703]113 else if (!RTStrICmp(psz, "writethrough"))
[23223]114 DiskType = MediumType_Writethrough;
[17970]115 else
116 rc = VERR_PARSE_ERROR;
117
118 if (RT_SUCCESS(rc))
119 *pDiskType = DiskType;
120 return rc;
121}
122
[18759]123/** @todo move this into getopt, as getting bool values is generic */
[18703]124static int 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
[17970]151static const RTGETOPTDEF g_aCreateHardDiskOptions[] =
152{
153 { "--filename", 'f', RTGETOPT_REQ_STRING },
[18108]154 { "-filename", 'f', RTGETOPT_REQ_STRING }, // deprecated
[17970]155 { "--size", 's', RTGETOPT_REQ_UINT64 },
[18108]156 { "-size", 's', RTGETOPT_REQ_UINT64 }, // deprecated
[17970]157 { "--format", 'o', RTGETOPT_REQ_STRING },
[18108]158 { "-format", 'o', RTGETOPT_REQ_STRING }, // deprecated
[17970]159 { "--static", 'F', RTGETOPT_REQ_NOTHING },
[18108]160 { "-static", 'F', RTGETOPT_REQ_NOTHING }, // deprecated
[17970]161 { "--variant", 'm', RTGETOPT_REQ_STRING },
[18108]162 { "-variant", 'm', RTGETOPT_REQ_STRING }, // deprecated
[17970]163 { "--type", 't', RTGETOPT_REQ_STRING },
[18108]164 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
[17970]165 { "--comment", 'c', RTGETOPT_REQ_STRING },
[18108]166 { "-comment", 'c', RTGETOPT_REQ_STRING }, // deprecated
[17970]167 { "--remember", 'r', RTGETOPT_REQ_NOTHING },
[18108]168 { "-remember", 'r', RTGETOPT_REQ_NOTHING }, // deprecated
169 { "--register", 'r', RTGETOPT_REQ_NOTHING }, // deprecated (inofficial)
170 { "-register", 'r', RTGETOPT_REQ_NOTHING }, // deprecated
[17970]171};
172
[16052]173int handleCreateHardDisk(HandlerArg *a)
[1]174{
175 HRESULT rc;
[18814]176 int vrc;
[1]177 Bstr filename;
[132]178 uint64_t sizeMB = 0;
[15366]179 Bstr format = "VDI";
[23223]180 MediumVariant_T DiskVariant = MediumVariant_Standard;
[1]181 Bstr comment;
[17970]182 bool fRemember = false;
[23223]183 MediumType_T DiskType = MediumType_Normal;
[1]184
[17970]185 int c;
186 RTGETOPTUNION ValueUnion;
187 RTGETOPTSTATE GetState;
188 // start at 0 because main() has hacked both the argc and argv given to us
189 RTGetOptInit(&GetState, a->argc, a->argv, g_aCreateHardDiskOptions, RT_ELEMENTS(g_aCreateHardDiskOptions), 0, 0 /* fFlags */);
190 while ((c = RTGetOpt(&GetState, &ValueUnion)))
[1]191 {
[17970]192 switch (c)
[1]193 {
[17970]194 case 'f': // --filename
195 filename = ValueUnion.psz;
196 break;
197
198 case 's': // --size
199 sizeMB = ValueUnion.u64;
200 break;
201
202 case 'o': // --format
203 format = ValueUnion.psz;
204 break;
205
206 case 'F': // --static ("fixed"/"flat")
[17977]207 {
[17978]208 unsigned uDiskVariant = (unsigned)DiskVariant;
[23223]209 uDiskVariant |= MediumVariant_Fixed;
210 DiskVariant = (MediumVariant_T)uDiskVariant;
[17970]211 break;
[17977]212 }
[17970]213
214 case 'm': // --variant
[18814]215 vrc = parseDiskVariant(ValueUnion.psz, &DiskVariant);
216 if (RT_FAILURE(vrc))
[17970]217 return errorArgument("Invalid hard disk variant '%s'", ValueUnion.psz);
218 break;
219
220 case 'c': // --comment
221 comment = ValueUnion.psz;
222 break;
223
224 case 'r': // --remember
225 fRemember = true;
226 break;
227
228 case 't': // --type
[18814]229 vrc = parseDiskType(ValueUnion.psz, &DiskType);
230 if ( RT_FAILURE(vrc)
[23223]231 || (DiskType != MediumType_Normal && DiskType != MediumType_Writethrough))
[17970]232 return errorArgument("Invalid hard disk type '%s'", ValueUnion.psz);
233 break;
234
235 case VINF_GETOPT_NOT_OPTION:
236 return errorSyntax(USAGE_CREATEHD, "Invalid parameter '%s'", ValueUnion.psz);
237
238 default:
239 if (c > 0)
240 {
241 if (RT_C_IS_PRINT(c))
242 return errorSyntax(USAGE_CREATEHD, "Invalid option -%c", c);
243 else
244 return errorSyntax(USAGE_CREATEHD, "Invalid option case %i", c);
245 }
[18624]246 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
247 return errorSyntax(USAGE_CREATEHD, "unknown option: %s\n", ValueUnion.psz);
[17970]248 else if (ValueUnion.pDef)
249 return errorSyntax(USAGE_CREATEHD, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
250 else
251 return errorSyntax(USAGE_CREATEHD, "error: %Rrs", c);
[1]252 }
253 }
[17970]254
[1]255 /* check the outcome */
[24184]256 if ( !filename
257 || sizeMB == 0)
[18703]258 return errorSyntax(USAGE_CREATEHD, "Parameters --filename and --size are required");
[8373]259
[24184]260 /* check for filename extension */
261 Utf8Str strName(filename);
262 if (!RTPathHaveExt(strName.c_str()))
263 {
264 Utf8Str strFormat(format);
265 if (strFormat.compare("vmdk", iprt::MiniString::CaseInsensitive) == 0)
266 strName.append(".vmdk");
267 else if (strFormat.compare("vhd", iprt::MiniString::CaseInsensitive) == 0)
268 strName.append(".vhd");
269 else
270 strName.append(".vdi");
271 filename = Bstr(strName);
272 }
273
[23223]274 ComPtr<IMedium> hardDisk;
[16867]275 CHECK_ERROR(a->virtualBox, CreateHardDisk(format, filename, hardDisk.asOutParam()));
[1]276 if (SUCCEEDED(rc) && hardDisk)
277 {
[13580]278 /* we will close the hard disk after the storage has been successfully
[17970]279 * created unless fRemember is set */
[13580]280 bool doClose = false;
281
[15516]282 if (!comment.isNull())
[15514]283 {
284 CHECK_ERROR(hardDisk,COMSETTER(Description)(comment));
285 }
[1]286 ComPtr<IProgress> progress;
[17970]287 CHECK_ERROR(hardDisk, CreateBaseStorage(sizeMB, DiskVariant, progress.asOutParam()));
[1]288 if (SUCCEEDED(rc) && progress)
289 {
[24884]290 rc = showProgress(progress);
291 if (FAILED(rc))
[1]292 {
[24884]293 com::ProgressErrorInfo info(progress);
294 if (info.isBasicAvailable())
295 RTPrintf("Error: failed to create hard disk. Error message: %lS\n", info.getText().raw());
[1]296 else
[24884]297 RTPrintf("Error: failed to create hard disk. No error message available!\n");
298 }
299 else
300 {
301 doClose = !fRemember;
[13580]302
[24884]303 Bstr uuid;
304 CHECK_ERROR(hardDisk, COMGETTER(Id)(uuid.asOutParam()));
[1]305
[24884]306 if (DiskType == MediumType_Writethrough)
307 {
308 CHECK_ERROR(hardDisk, COMSETTER(Type)(MediumType_Writethrough));
309 }
[1]310
[24884]311 RTPrintf("Disk image created. UUID: %s\n", Utf8Str(uuid).raw());
[1]312 }
313 }
[13580]314 if (doClose)
[1]315 {
[13580]316 CHECK_ERROR(hardDisk, Close());
[1]317 }
318 }
319 return SUCCEEDED(rc) ? 0 : 1;
320}
321
[16873]322#if 0 /* disabled until disk shrinking is implemented based on VBoxHDD */
[13580]323static DECLCALLBACK(int) hardDiskProgressCallback(PVM pVM, unsigned uPercent, void *pvUser)
[383]324{
325 unsigned *pPercent = (unsigned *)pvUser;
326
327 if (*pPercent != uPercent)
328 {
329 *pPercent = uPercent;
330 RTPrintf(".");
331 if ((uPercent % 10) == 0 && uPercent)
332 RTPrintf("%d%%", uPercent);
333 RTStrmFlush(g_pStdOut);
334 }
335
336 return VINF_SUCCESS;
337}
[15366]338#endif
[383]339
[18703]340static const RTGETOPTDEF g_aModifyHardDiskOptions[] =
341{
342 { "--type", 't', RTGETOPT_REQ_STRING },
343 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
344 { "settype", 't', RTGETOPT_REQ_STRING }, // deprecated
345 { "--autoreset", 'z', RTGETOPT_REQ_STRING },
346 { "-autoreset", 'z', RTGETOPT_REQ_STRING }, // deprecated
347 { "autoreset", 'z', RTGETOPT_REQ_STRING }, // deprecated
348 { "--compact", 'c', RTGETOPT_REQ_NOTHING },
349 { "-compact", 'c', RTGETOPT_REQ_NOTHING }, // deprecated
350 { "compact", 'c', RTGETOPT_REQ_NOTHING }, // deprecated
351};
[383]352
[16052]353int handleModifyHardDisk(HandlerArg *a)
[1]354{
355 HRESULT rc;
[18814]356 int vrc;
[23223]357 ComPtr<IMedium> hardDisk;
358 MediumType_T DiskType;
[18703]359 bool AutoReset = false;
360 bool fModifyDiskType = false, fModifyAutoReset = false, fModifyCompact = false;;
361 const char *FilenameOrUuid = NULL;
[1]362
[18703]363 int c;
364 RTGETOPTUNION ValueUnion;
365 RTGETOPTSTATE GetState;
366 // start at 0 because main() has hacked both the argc and argv given to us
367 RTGetOptInit(&GetState, a->argc, a->argv, g_aModifyHardDiskOptions, RT_ELEMENTS(g_aModifyHardDiskOptions), 0, 0 /* fFlags */);
368 while ((c = RTGetOpt(&GetState, &ValueUnion)))
369 {
370 switch (c)
371 {
372 case 't': // --type
[18814]373 vrc = parseDiskType(ValueUnion.psz, &DiskType);
374 if (RT_FAILURE(vrc))
[18703]375 return errorArgument("Invalid hard disk type '%s'", ValueUnion.psz);
376 fModifyDiskType = true;
377 break;
[1]378
[18703]379 case 'z': // --autoreset
[18814]380 vrc = parseBool(ValueUnion.psz, &AutoReset);
381 if (RT_FAILURE(vrc))
[18703]382 return errorArgument("Invalid autoreset parameter '%s'", ValueUnion.psz);
383 fModifyAutoReset = true;
384 break;
[1]385
[18703]386 case 'c': // --compact
387 fModifyCompact = true;
388 break;
389
390 case VINF_GETOPT_NOT_OPTION:
391 if (!FilenameOrUuid)
392 FilenameOrUuid = ValueUnion.psz;
393 else
394 return errorSyntax(USAGE_CREATEHD, "Invalid parameter '%s'", ValueUnion.psz);
395 break;
396
397 default:
398 if (c > 0)
399 {
400 if (RT_C_IS_PRINT(c))
401 return errorSyntax(USAGE_MODIFYHD, "Invalid option -%c", c);
402 else
403 return errorSyntax(USAGE_MODIFYHD, "Invalid option case %i", c);
404 }
405 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
406 return errorSyntax(USAGE_MODIFYHD, "unknown option: %s\n", ValueUnion.psz);
407 else if (ValueUnion.pDef)
408 return errorSyntax(USAGE_MODIFYHD, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
409 else
410 return errorSyntax(USAGE_MODIFYHD, "error: %Rrs", c);
411 }
412 }
413
414 if (!FilenameOrUuid)
415 return errorSyntax(USAGE_MODIFYHD, "Disk name or UUID required");
416
417 if (!fModifyDiskType && !fModifyAutoReset && !fModifyCompact)
418 return errorSyntax(USAGE_MODIFYHD, "No operation specified");
419
[1]420 /* first guess is that it's a UUID */
[18703]421 Guid uuid(FilenameOrUuid);
[19239]422 rc = a->virtualBox->GetHardDisk(uuid.toUtf16(), hardDisk.asOutParam());
[1]423 /* no? then it must be a filename */
424 if (!hardDisk)
425 {
[18703]426 CHECK_ERROR(a->virtualBox, FindHardDisk(Bstr(FilenameOrUuid), hardDisk.asOutParam()));
[17402]427 if (FAILED(rc))
428 return 1;
[1]429 }
[383]430
[18703]431 if (fModifyDiskType)
[1]432 {
[383]433 /* hard disk must be registered */
[13580]434 if (SUCCEEDED(rc) && hardDisk)
[1]435 {
[23223]436 MediumType_T hddType;
[1]437 CHECK_ERROR(hardDisk, COMGETTER(Type)(&hddType));
438
[18703]439 if (hddType != DiskType)
440 CHECK_ERROR(hardDisk, COMSETTER(Type)(DiskType));
[1]441 }
[383]442 else
443 return errorArgument("Hard disk image not registered");
[1]444 }
[18703]445
446 if (fModifyAutoReset)
[17402]447 {
[18703]448 CHECK_ERROR(hardDisk, COMSETTER(AutoReset)(AutoReset));
449 }
[17402]450
[18703]451 if (fModifyCompact)
[383]452 {
[19037]453 bool unknown = false;
[383]454 /* the hard disk image might not be registered */
455 if (!hardDisk)
456 {
[19037]457 unknown = true;
[20842]458 rc = a->virtualBox->OpenHardDisk(Bstr(FilenameOrUuid), AccessMode_ReadWrite, false, Bstr(""), false, Bstr(""), hardDisk.asOutParam());
[19037]459 if (rc == VBOX_E_FILE_ERROR)
460 {
461 char szFilenameAbs[RTPATH_MAX] = "";
[24998]462 int irc = RTPathAbs(FilenameOrUuid, szFilenameAbs, sizeof(szFilenameAbs));
463 if (RT_FAILURE(irc))
[19037]464 {
465 RTPrintf("Cannot convert filename \"%s\" to absolute path\n", FilenameOrUuid);
466 return 1;
467 }
[20842]468 CHECK_ERROR(a->virtualBox, OpenHardDisk(Bstr(szFilenameAbs), AccessMode_ReadWrite, false, Bstr(""), false, Bstr(""), hardDisk.asOutParam()));
[19037]469 }
470 else if (FAILED(rc))
[20842]471 CHECK_ERROR(a->virtualBox, OpenHardDisk(Bstr(FilenameOrUuid), AccessMode_ReadWrite, false, Bstr(""), false, Bstr(""), hardDisk.asOutParam()));
[383]472 }
[19037]473 if (SUCCEEDED(rc) && hardDisk)
474 {
475 ComPtr<IProgress> progress;
476 CHECK_ERROR(hardDisk, Compact(progress.asOutParam()));
[19172]477 if (SUCCEEDED(rc))
[24884]478 rc = showProgress(progress);
[19037]479 if (FAILED(rc))
480 {
[19172]481 if (rc == E_NOTIMPL)
[19037]482 {
483 RTPrintf("Error: Compact hard disk operation is not implemented!\n");
484 RTPrintf("The functionality will be restored later.\n");
485 }
[19172]486 else if (rc == VBOX_E_NOT_SUPPORTED)
[19037]487 {
488 RTPrintf("Error: Compact hard disk operation for this format is not implemented yet!\n");
489 }
490 else
491 com::GluePrintRCMessage(rc);
492 }
493 if (unknown)
494 hardDisk->Close();
495 }
[383]496 }
[8373]497
[1]498 return SUCCEEDED(rc) ? 0 : 1;
499}
500
[17970]501static const RTGETOPTDEF g_aCloneHardDiskOptions[] =
502{
503 { "--format", 'o', RTGETOPT_REQ_STRING },
504 { "-format", 'o', RTGETOPT_REQ_STRING },
505 { "--static", 'F', RTGETOPT_REQ_NOTHING },
506 { "-static", 'F', RTGETOPT_REQ_NOTHING },
[21032]507 { "--existing", 'E', RTGETOPT_REQ_NOTHING },
[17970]508 { "--variant", 'm', RTGETOPT_REQ_STRING },
509 { "-variant", 'm', RTGETOPT_REQ_STRING },
510 { "--type", 't', RTGETOPT_REQ_STRING },
511 { "-type", 't', RTGETOPT_REQ_STRING },
512 { "--remember", 'r', RTGETOPT_REQ_NOTHING },
513 { "-remember", 'r', RTGETOPT_REQ_NOTHING },
514 { "--register", 'r', RTGETOPT_REQ_NOTHING },
515 { "-register", 'r', RTGETOPT_REQ_NOTHING },
516};
517
[16052]518int handleCloneHardDisk(HandlerArg *a)
[1]519{
[18814]520 HRESULT rc;
521 int vrc;
[15556]522 Bstr src, dst;
523 Bstr format;
[23223]524 MediumVariant_T DiskVariant = MediumVariant_Standard;
[21032]525 bool fExisting = false;
[17970]526 bool fRemember = false;
[21032]527 bool fSetDiskType = false;
[23223]528 MediumType_T DiskType = MediumType_Normal;
[15556]529
[17970]530 int c;
531 RTGETOPTUNION ValueUnion;
532 RTGETOPTSTATE GetState;
533 // start at 0 because main() has hacked both the argc and argv given to us
534 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloneHardDiskOptions, RT_ELEMENTS(g_aCloneHardDiskOptions), 0, 0 /* fFlags */);
535 while ((c = RTGetOpt(&GetState, &ValueUnion)))
[15556]536 {
[17970]537 switch (c)
[15556]538 {
[17970]539 case 'o': // --format
540 format = ValueUnion.psz;
541 break;
542
[21032]543 case 'F': // --static
[21038]544 {
[21037]545 unsigned uDiskVariant = (unsigned)DiskVariant;
[23223]546 uDiskVariant |= MediumVariant_Fixed;
547 DiskVariant = (MediumVariant_T)uDiskVariant;
[21032]548 break;
[21038]549 }
[21032]550
551 case 'E': // --existing
552 fExisting = true;
553 break;
554
[17970]555 case 'm': // --variant
[18814]556 vrc = parseDiskVariant(ValueUnion.psz, &DiskVariant);
557 if (RT_FAILURE(vrc))
[17970]558 return errorArgument("Invalid hard disk variant '%s'", ValueUnion.psz);
559 break;
560
561 case 'r': // --remember
562 fRemember = true;
563 break;
564
565 case 't': // --type
[18814]566 vrc = parseDiskType(ValueUnion.psz, &DiskType);
567 if (RT_FAILURE(vrc))
[17970]568 return errorArgument("Invalid hard disk type '%s'", ValueUnion.psz);
[21032]569 fSetDiskType = true;
[17970]570 break;
571
572 case VINF_GETOPT_NOT_OPTION:
573 if (src.isEmpty())
574 src = ValueUnion.psz;
575 else if (dst.isEmpty())
576 dst = ValueUnion.psz;
577 else
578 return errorSyntax(USAGE_CLONEHD, "Invalid parameter '%s'", ValueUnion.psz);
[17980]579 break;
[17970]580
581 default:
582 if (c > 0)
583 {
[18108]584 if (RT_C_IS_GRAPH(c))
585 return errorSyntax(USAGE_CLONEHD, "unhandled option: -%c", c);
[17970]586 else
[18108]587 return errorSyntax(USAGE_CLONEHD, "unhandled option: %i", c);
[17970]588 }
[18108]589 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
590 return errorSyntax(USAGE_CLONEHD, "unknown option: %s", ValueUnion.psz);
[17970]591 else if (ValueUnion.pDef)
592 return errorSyntax(USAGE_CLONEHD, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
593 else
594 return errorSyntax(USAGE_CLONEHD, "error: %Rrs", c);
[15556]595 }
596 }
[1]597
[15556]598 if (src.isEmpty())
599 return errorSyntax(USAGE_CLONEHD, "Mandatory UUID or input file parameter missing");
600 if (dst.isEmpty())
601 return errorSyntax(USAGE_CLONEHD, "Mandatory output file parameter missing");
[23223]602 if (fExisting && (!format.isEmpty() || DiskVariant != MediumType_Normal))
[21032]603 return errorSyntax(USAGE_CLONEHD, "Specified options which cannot be used with --existing");
[15556]604
[23223]605 ComPtr<IMedium> srcDisk;
606 ComPtr<IMedium> dstDisk;
[21032]607 bool fSrcUnknown = false;
608 bool fDstUnknown = false;
[15556]609
[1]610 /* first guess is that it's a UUID */
[19239]611 rc = a->virtualBox->GetHardDisk(src, srcDisk.asOutParam());
[15556]612 /* no? then it must be a filename */
613 if (FAILED (rc))
[21032]614 rc = a->virtualBox->FindHardDisk(src, srcDisk.asOutParam());
615 /* no? well, then it's an unknown image */
616 if (FAILED (rc))
[1]617 {
[21032]618 rc = a->virtualBox->OpenHardDisk(src, AccessMode_ReadWrite, false, Bstr(""), false, Bstr(""), srcDisk.asOutParam());
619 if (rc == VBOX_E_FILE_ERROR)
[15556]620 {
[21032]621 char szFilenameAbs[RTPATH_MAX] = "";
[24998]622 int irc = RTPathAbs(Utf8Str(src).c_str(), szFilenameAbs, sizeof(szFilenameAbs));
623 if (RT_FAILURE(irc))
[19037]624 {
[21032]625 RTPrintf("Cannot convert filename \"%s\" to absolute path\n", Utf8Str(src).raw());
626 return 1;
[19037]627 }
[21032]628 CHECK_ERROR(a->virtualBox, OpenHardDisk(Bstr(szFilenameAbs), AccessMode_ReadWrite, false, Bstr(""), false, Bstr(""), srcDisk.asOutParam()));
[15556]629 }
[21032]630 else if (FAILED(rc))
631 CHECK_ERROR(a->virtualBox, OpenHardDisk(src, AccessMode_ReadWrite, false, Bstr(""), false, Bstr(""), srcDisk.asOutParam()));
632 if (SUCCEEDED (rc))
633 fSrcUnknown = true;
[1]634 }
[15556]635
636 do
[1]637 {
[15556]638 if (!SUCCEEDED(rc))
639 break;
640
[21032]641 /* open/create destination hard disk */
642 if (fExisting)
[15556]643 {
[21032]644 /* first guess is that it's a UUID */
645 rc = a->virtualBox->GetHardDisk(dst, dstDisk.asOutParam());
646 /* no? then it must be a filename */
647 if (FAILED (rc))
648 rc = a->virtualBox->FindHardDisk(dst, dstDisk.asOutParam());
649 /* no? well, then it's an unknown image */
650 if (FAILED (rc))
651 {
652 rc = a->virtualBox->OpenHardDisk(dst, AccessMode_ReadWrite, false, Bstr(""), false, Bstr(""), dstDisk.asOutParam());
653 if (rc == VBOX_E_FILE_ERROR)
654 {
655 char szFilenameAbs[RTPATH_MAX] = "";
[24998]656 int irc = RTPathAbs(Utf8Str(dst).c_str(), szFilenameAbs, sizeof(szFilenameAbs));
657 if (RT_FAILURE(irc))
[21032]658 {
659 RTPrintf("Cannot convert filename \"%s\" to absolute path\n", Utf8Str(dst).raw());
660 return 1;
661 }
662 CHECK_ERROR_BREAK(a->virtualBox, OpenHardDisk(Bstr(szFilenameAbs), AccessMode_ReadWrite, false, Bstr(""), false, Bstr(""), dstDisk.asOutParam()));
663 }
664 else if (FAILED(rc))
665 CHECK_ERROR_BREAK(a->virtualBox, OpenHardDisk(dst, AccessMode_ReadWrite, false, Bstr(""), false, Bstr(""), dstDisk.asOutParam()));
666 if (SUCCEEDED (rc))
667 fDstUnknown = true;
668 }
669 else
670 fRemember = true;
671 if (SUCCEEDED(rc))
672 {
673 /* Perform accessibility check now. */
[23223]674 MediumState_T state;
[24258]675 CHECK_ERROR_BREAK(dstDisk, RefreshState(&state));
[21032]676 }
677 CHECK_ERROR_BREAK(dstDisk, COMGETTER(Format) (format.asOutParam()));
[15556]678 }
[21032]679 else
680 {
681 /* use the format of the source hard disk if unspecified */
682 if (format.isEmpty())
683 CHECK_ERROR_BREAK(srcDisk, COMGETTER(Format) (format.asOutParam()));
684 CHECK_ERROR_BREAK(a->virtualBox, CreateHardDisk(format, dst, dstDisk.asOutParam()));
685 }
[15556]686
[1]687 ComPtr<IProgress> progress;
[18388]688 CHECK_ERROR_BREAK(srcDisk, CloneTo(dstDisk, DiskVariant, NULL, progress.asOutParam()));
[15556]689
[24884]690 rc = showProgress(progress);
[15556]691 if (FAILED(rc))
[1]692 {
[15556]693 com::ProgressErrorInfo info(progress);
694 if (info.isBasicAvailable())
695 RTPrintf("Error: failed to clone hard disk. Error message: %lS\n", info.getText().raw());
696 else
697 RTPrintf("Error: failed to clone hard disk. No error message available!\n");
698 break;
[1]699 }
[15556]700
[19239]701 Bstr uuid;
[15556]702 CHECK_ERROR_BREAK(dstDisk, COMGETTER(Id)(uuid.asOutParam()));
703
704 RTPrintf("Clone hard disk created in format '%ls'. UUID: %s\n",
[19239]705 format.raw(), Utf8Str(uuid).raw());
[1]706 }
[15556]707 while (0);
708
[17970]709 if (!fRemember && !dstDisk.isNull())
[15556]710 {
711 /* forget the created clone */
712 dstDisk->Close();
713 }
[21032]714 else if (fSetDiskType)
715 {
716 CHECK_ERROR(dstDisk, COMSETTER(Type)(DiskType));
717 }
[15556]718
[21032]719 if (fSrcUnknown)
[15556]720 {
721 /* close the unknown hard disk to forget it again */
722 srcDisk->Close();
723 }
724
[1]725 return SUCCEEDED(rc) ? 0 : 1;
726}
727
[17970]728static const RTGETOPTDEF g_aConvertFromRawHardDiskOptions[] =
729{
730 { "--format", 'o', RTGETOPT_REQ_STRING },
731 { "-format", 'o', RTGETOPT_REQ_STRING },
732 { "--static", 'F', RTGETOPT_REQ_NOTHING },
733 { "-static", 'F', RTGETOPT_REQ_NOTHING },
734 { "--variant", 'm', RTGETOPT_REQ_STRING },
735 { "-variant", 'm', RTGETOPT_REQ_STRING },
736};
737
[15602]738int handleConvertFromRaw(int argc, char *argv[])
[1428]739{
[17970]740 int rc = VINF_SUCCESS;
[15366]741 bool fReadFromStdIn = false;
[15693]742 const char *format = "VDI";
[15366]743 const char *srcfilename = NULL;
744 const char *dstfilename = NULL;
745 const char *filesize = NULL;
[17970]746 unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
[15408]747 void *pvBuf = NULL;
[15366]748
[17970]749 int c;
750 RTGETOPTUNION ValueUnion;
751 RTGETOPTSTATE GetState;
752 // start at 0 because main() has hacked both the argc and argv given to us
[17980]753 RTGetOptInit(&GetState, argc, argv, g_aConvertFromRawHardDiskOptions, RT_ELEMENTS(g_aConvertFromRawHardDiskOptions), 0, 0 /* fFlags */);
[17970]754 while ((c = RTGetOpt(&GetState, &ValueUnion)))
[15366]755 {
[17970]756 switch (c)
[15366]757 {
[17970]758 case 'o': // --format
759 format = ValueUnion.psz;
760 break;
761
762 case 'm': // --variant
[23223]763 MediumVariant_T DiskVariant;
[17970]764 rc = parseDiskVariant(ValueUnion.psz, &DiskVariant);
765 if (RT_FAILURE(rc))
766 return errorArgument("Invalid hard disk variant '%s'", ValueUnion.psz);
767 /// @todo cleaner solution than assuming 1:1 mapping?
768 uImageFlags = (unsigned)DiskVariant;
769 break;
770
771 case VINF_GETOPT_NOT_OPTION:
772 if (!srcfilename)
[15366]773 {
[17970]774 srcfilename = ValueUnion.psz;
775#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
776 fReadFromStdIn = !strcmp(srcfilename, "stdin");
777#endif
778 }
779 else if (!dstfilename)
780 dstfilename = ValueUnion.psz;
781 else if (fReadFromStdIn && !filesize)
782 filesize = ValueUnion.psz;
783 else
784 return errorSyntax(USAGE_CONVERTFROMRAW, "Invalid parameter '%s'", ValueUnion.psz);
[17980]785 break;
[17970]786
787 default:
[24590]788 return errorGetOpt(USAGE_CONVERTFROMRAW, c, &ValueUnion);
[15366]789 }
790 }
[1916]791
[15692]792 if (!srcfilename || !dstfilename || (fReadFromStdIn && !filesize))
793 return errorSyntax(USAGE_CONVERTFROMRAW, "Incorrect number of parameters");
[15602]794 RTPrintf("Converting from raw image file=\"%s\" to file=\"%s\"...\n",
[15366]795 srcfilename, dstfilename);
[1428]796
[15366]797 PVBOXHDD pDisk = NULL;
798
799 PVDINTERFACE pVDIfs = NULL;
800 VDINTERFACE vdInterfaceError;
801 VDINTERFACEERROR vdInterfaceErrorCallbacks;
802 vdInterfaceErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
803 vdInterfaceErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
804 vdInterfaceErrorCallbacks.pfnError = handleVDError;
[21806]805 vdInterfaceErrorCallbacks.pfnMessage = NULL;
[15366]806
807 rc = VDInterfaceAdd(&vdInterfaceError, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
808 &vdInterfaceErrorCallbacks, NULL, &pVDIfs);
809 AssertRC(rc);
810
[1428]811 /* open raw image file. */
812 RTFILE File;
[1916]813 if (fReadFromStdIn)
814 File = 0;
815 else
[23973]816 rc = RTFileOpen(&File, srcfilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
[13835]817 if (RT_FAILURE(rc))
[1428]818 {
[15366]819 RTPrintf("File=\"%s\" open error: %Rrf\n", srcfilename, rc);
820 goto out;
[1428]821 }
822
[1916]823 uint64_t cbFile;
[1428]824 /* get image size. */
[1916]825 if (fReadFromStdIn)
[15366]826 cbFile = RTStrToUInt64(filesize);
[1916]827 else
828 rc = RTFileGetSize(File, &cbFile);
[15366]829 if (RT_FAILURE(rc))
[1428]830 {
[15366]831 RTPrintf("Error getting image size for file \"%s\": %Rrc\n", srcfilename, rc);
832 goto out;
833 }
[1428]834
[17970]835 RTPrintf("Creating %s image with size %RU64 bytes (%RU64MB)...\n", (uImageFlags & VD_IMAGE_FLAGS_FIXED) ? "fixed" : "dynamic", cbFile, (cbFile + _1M - 1) / _1M);
[15366]836 char pszComment[256];
837 RTStrPrintf(pszComment, sizeof(pszComment), "Converted image from %s", srcfilename);
838 rc = VDCreate(pVDIfs, &pDisk);
839 if (RT_FAILURE(rc))
840 {
841 RTPrintf("Error while creating the virtual disk container: %Rrc\n", rc);
842 goto out;
843 }
[1428]844
[15366]845 Assert(RT_MIN(cbFile / 512 / 16 / 63, 16383) -
846 (unsigned int)RT_MIN(cbFile / 512 / 16 / 63, 16383) == 0);
847 PDMMEDIAGEOMETRY PCHS, LCHS;
848 PCHS.cCylinders = (unsigned int)RT_MIN(cbFile / 512 / 16 / 63, 16383);
849 PCHS.cHeads = 16;
850 PCHS.cSectors = 63;
851 LCHS.cCylinders = 0;
852 LCHS.cHeads = 0;
853 LCHS.cSectors = 0;
[17970]854 rc = VDCreateBase(pDisk, format, dstfilename, cbFile,
[15366]855 uImageFlags, pszComment, &PCHS, &LCHS, NULL,
856 VD_OPEN_FLAGS_NORMAL, NULL, NULL);
857 if (RT_FAILURE(rc))
858 {
859 RTPrintf("Error while creating the disk image \"%s\": %Rrc\n", dstfilename, rc);
860 goto out;
861 }
[1428]862
[15366]863 size_t cbBuffer;
864 cbBuffer = _1M;
865 pvBuf = RTMemAlloc(cbBuffer);
866 if (!pvBuf)
867 {
868 rc = VERR_NO_MEMORY;
869 RTPrintf("Not enough memory allocating buffers for image \"%s\": %Rrc\n", dstfilename, rc);
870 goto out;
871 }
872
873 uint64_t offFile;
874 offFile = 0;
875 while (offFile < cbFile)
876 {
877 size_t cbRead;
878 size_t cbToRead;
879 cbRead = 0;
880 cbToRead = cbFile - offFile >= (uint64_t)cbBuffer ?
881 cbBuffer : (size_t) (cbFile - offFile);
882 rc = RTFileRead(File, pvBuf, cbToRead, &cbRead);
883 if (RT_FAILURE(rc) || !cbRead)
884 break;
885 rc = VDWrite(pDisk, offFile, pvBuf, cbRead);
886 if (RT_FAILURE(rc))
887 {
888 RTPrintf("Failed to write to disk image \"%s\": %Rrc\n", dstfilename, rc);
889 goto out;
[1428]890 }
[15366]891 offFile += cbRead;
[1428]892 }
893
[15366]894out:
895 if (pvBuf)
896 RTMemFree(pvBuf);
897 if (pDisk)
898 VDClose(pDisk, RT_FAILURE(rc));
899 if (File != NIL_RTFILE)
900 RTFileClose(File);
901
902 return RT_FAILURE(rc);
[1428]903}
904
[18703]905static const RTGETOPTDEF g_aAddiSCSIDiskOptions[] =
906{
907 { "--server", 's', RTGETOPT_REQ_STRING },
908 { "-server", 's', RTGETOPT_REQ_STRING }, // deprecated
909 { "--target", 'T', RTGETOPT_REQ_STRING },
910 { "-target", 'T', RTGETOPT_REQ_STRING }, // deprecated
911 { "--port", 'p', RTGETOPT_REQ_STRING },
912 { "-port", 'p', RTGETOPT_REQ_STRING }, // deprecated
913 { "--lun", 'l', RTGETOPT_REQ_STRING },
914 { "-lun", 'l', RTGETOPT_REQ_STRING }, // deprecated
915 { "--encodedlun", 'L', RTGETOPT_REQ_STRING },
916 { "-encodedlun", 'L', RTGETOPT_REQ_STRING }, // deprecated
917 { "--username", 'u', RTGETOPT_REQ_STRING },
918 { "-username", 'u', RTGETOPT_REQ_STRING }, // deprecated
919 { "--password", 'P', RTGETOPT_REQ_STRING },
920 { "-password", 'P', RTGETOPT_REQ_STRING }, // deprecated
921 { "--type", 't', RTGETOPT_REQ_STRING },
922 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
923 { "--intnet", 'I', RTGETOPT_REQ_NOTHING },
924 { "-intnet", 'I', RTGETOPT_REQ_NOTHING }, // deprecated
925};
926
[16052]927int handleAddiSCSIDisk(HandlerArg *a)
[1]928{
929 HRESULT rc;
[18814]930 int vrc;
[1]931 Bstr server;
932 Bstr target;
[14784]933 Bstr port;
934 Bstr lun;
[1]935 Bstr username;
936 Bstr password;
937 Bstr comment;
[15366]938 bool fIntNet = false;
[23223]939 MediumType_T DiskType = MediumType_Normal;
[1]940
[18703]941 int c;
942 RTGETOPTUNION ValueUnion;
943 RTGETOPTSTATE GetState;
944 // start at 0 because main() has hacked both the argc and argv given to us
945 RTGetOptInit(&GetState, a->argc, a->argv, g_aAddiSCSIDiskOptions, RT_ELEMENTS(g_aAddiSCSIDiskOptions), 0, 0 /* fFlags */);
946 while ((c = RTGetOpt(&GetState, &ValueUnion)))
[1]947 {
[18703]948 switch (c)
[1]949 {
[18703]950 case 's': // --server
951 server = ValueUnion.psz;
952 break;
953
954 case 'T': // --target
955 target = ValueUnion.psz;
956 break;
957
958 case 'p': // --port
959 port = ValueUnion.psz;
960 break;
961
962 case 'l': // --lun
963 lun = ValueUnion.psz;
964 break;
965
966 case 'L': // --encodedlun
967 lun = BstrFmt("enc%s", ValueUnion.psz);
968 break;
969
970 case 'u': // --username
971 username = ValueUnion.psz;
972 break;
973
974 case 'P': // --password
975 password = ValueUnion.psz;
976 break;
977
978 case 't': // --type
[18814]979 vrc = parseDiskType(ValueUnion.psz, &DiskType);
980 if (RT_FAILURE(vrc))
[18703]981 return errorArgument("Invalid hard disk type '%s'", ValueUnion.psz);
982 break;
983
984 case 'I': // --intnet
985 fIntNet = true;
986 break;
987
988 case VINF_GETOPT_NOT_OPTION:
989 return errorSyntax(USAGE_ADDISCSIDISK, "Invalid parameter '%s'", ValueUnion.psz);
990
991 default:
992 if (c > 0)
993 {
994 if (RT_C_IS_PRINT(c))
995 return errorSyntax(USAGE_ADDISCSIDISK, "Invalid option -%c", c);
996 else
997 return errorSyntax(USAGE_ADDISCSIDISK, "Invalid option case %i", c);
998 }
999 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1000 return errorSyntax(USAGE_ADDISCSIDISK, "unknown option: %s\n", ValueUnion.psz);
1001 else if (ValueUnion.pDef)
1002 return errorSyntax(USAGE_ADDISCSIDISK, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1003 else
1004 return errorSyntax(USAGE_ADDISCSIDISK, "error: %Rrs", c);
[1]1005 }
1006 }
1007
1008 /* check for required options */
1009 if (!server || !target)
[18703]1010 return errorSyntax(USAGE_ADDISCSIDISK, "Parameters --server and --target are required");
[1]1011
[14784]1012 do
[1]1013 {
[23223]1014 ComPtr<IMedium> hardDisk;
[17753]1015 /** @todo move the location stuff to Main, which can use pfnComposeName
[18703]1016 * from the disk backends to construct the location properly. Also do
1017 * not use slashes to separate the parts, as otherwise only the last
1018 * element comtaining information will be shown. */
[18836]1019 if (lun.isEmpty() || lun == "0" || lun == "enc0")
1020 {
1021 CHECK_ERROR_BREAK (a->virtualBox,
1022 CreateHardDisk(Bstr ("iSCSI"),
1023 BstrFmt ("%ls|%ls", server.raw(), target.raw()),
1024 hardDisk.asOutParam()));
1025 }
1026 else
1027 {
1028 CHECK_ERROR_BREAK (a->virtualBox,
1029 CreateHardDisk(Bstr ("iSCSI"),
1030 BstrFmt ("%ls|%ls|%ls", server.raw(), target.raw(), lun.raw()),
1031 hardDisk.asOutParam()));
1032 }
[25149]1033 if (FAILED(rc)) break;
[1]1034
[14784]1035 if (!port.isNull())
1036 server = BstrFmt ("%ls:%ls", server.raw(), port.raw());
1037
[15044]1038 com::SafeArray <BSTR> names;
1039 com::SafeArray <BSTR> values;
1040
1041 Bstr ("TargetAddress").detachTo (names.appendedRaw());
1042 server.detachTo (values.appendedRaw());
1043 Bstr ("TargetName").detachTo (names.appendedRaw());
1044 target.detachTo (values.appendedRaw());
1045
[14784]1046 if (!lun.isNull())
[15044]1047 {
1048 Bstr ("LUN").detachTo (names.appendedRaw());
1049 lun.detachTo (values.appendedRaw());
1050 }
[14784]1051 if (!username.isNull())
[15044]1052 {
1053 Bstr ("InitiatorUsername").detachTo (names.appendedRaw());
1054 username.detachTo (values.appendedRaw());
1055 }
[14784]1056 if (!password.isNull())
[15044]1057 {
1058 Bstr ("InitiatorSecret").detachTo (names.appendedRaw());
1059 password.detachTo (values.appendedRaw());
1060 }
[14784]1061
[18703]1062 /// @todo add --initiator option
[15044]1063 Bstr ("InitiatorName").detachTo (names.appendedRaw());
1064 Bstr ("iqn.2008-04.com.sun.virtualbox.initiator").detachTo (values.appendedRaw());
[14784]1065
[18703]1066 /// @todo add --targetName and --targetPassword options
[14784]1067
[15366]1068 if (fIntNet)
1069 {
1070 Bstr ("HostIPStack").detachTo (names.appendedRaw());
1071 Bstr ("0").detachTo (values.appendedRaw());
1072 }
1073
[15044]1074 CHECK_ERROR_BREAK (hardDisk,
1075 SetProperties (ComSafeArrayAsInParam (names),
1076 ComSafeArrayAsInParam (values)));
1077
[23223]1078 if (DiskType != MediumType_Normal)
[18703]1079 {
1080 CHECK_ERROR(hardDisk, COMSETTER(Type)(DiskType));
1081 }
1082
[19239]1083 Bstr guid;
[14784]1084 CHECK_ERROR(hardDisk, COMGETTER(Id)(guid.asOutParam()));
[19239]1085 RTPrintf("iSCSI disk created. UUID: %s\n", Utf8Str(guid).raw());
[1]1086 }
[14784]1087 while (0);
[1]1088
1089 return SUCCEEDED(rc) ? 0 : 1;
1090}
1091
[18703]1092static const RTGETOPTDEF g_aShowHardDiskInfoOptions[] =
1093{
[19026]1094 { "--dummy", 256, RTGETOPT_REQ_NOTHING }, // placeholder for C++
[18703]1095};
[1]1096
[16052]1097int handleShowHardDiskInfo(HandlerArg *a)
[1]1098{
1099 HRESULT rc;
[18703]1100 const char *FilenameOrUuid = NULL;
[1]1101
[18703]1102 int c;
1103 RTGETOPTUNION ValueUnion;
1104 RTGETOPTSTATE GetState;
1105 // start at 0 because main() has hacked both the argc and argv given to us
1106 RTGetOptInit(&GetState, a->argc, a->argv, g_aShowHardDiskInfoOptions, RT_ELEMENTS(g_aShowHardDiskInfoOptions), 0, 0 /* fFlags */);
1107 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1108 {
1109 switch (c)
1110 {
1111 case VINF_GETOPT_NOT_OPTION:
1112 if (!FilenameOrUuid)
1113 FilenameOrUuid = ValueUnion.psz;
1114 else
1115 return errorSyntax(USAGE_SHOWHDINFO, "Invalid parameter '%s'", ValueUnion.psz);
1116 break;
[1]1117
[18703]1118 default:
1119 if (c > 0)
1120 {
1121 if (RT_C_IS_PRINT(c))
1122 return errorSyntax(USAGE_SHOWHDINFO, "Invalid option -%c", c);
1123 else
1124 return errorSyntax(USAGE_SHOWHDINFO, "Invalid option case %i", c);
1125 }
1126 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1127 return errorSyntax(USAGE_SHOWHDINFO, "unknown option: %s\n", ValueUnion.psz);
1128 else if (ValueUnion.pDef)
1129 return errorSyntax(USAGE_SHOWHDINFO, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1130 else
1131 return errorSyntax(USAGE_SHOWHDINFO, "error: %Rrs", c);
1132 }
1133 }
1134
1135 /* check for required options */
1136 if (!FilenameOrUuid)
1137 return errorSyntax(USAGE_SHOWHDINFO, "Disk name or UUID required");
1138
[23223]1139 ComPtr<IMedium> hardDisk;
[13580]1140 bool unknown = false;
[1]1141 /* first guess is that it's a UUID */
[19239]1142 Bstr uuid(FilenameOrUuid);
[16867]1143 rc = a->virtualBox->GetHardDisk(uuid, hardDisk.asOutParam());
[1]1144 /* no? then it must be a filename */
[775]1145 if (FAILED (rc))
[1]1146 {
[18703]1147 rc = a->virtualBox->FindHardDisk(Bstr(FilenameOrUuid), hardDisk.asOutParam());
[13580]1148 /* no? well, then it's an unkwnown image */
[775]1149 if (FAILED (rc))
1150 {
[20842]1151 rc = a->virtualBox->OpenHardDisk(Bstr(FilenameOrUuid), AccessMode_ReadWrite, false, Bstr(""), false, Bstr(""), hardDisk.asOutParam());
[19037]1152 if (rc == VBOX_E_FILE_ERROR)
1153 {
1154 char szFilenameAbs[RTPATH_MAX] = "";
1155 int vrc = RTPathAbs(FilenameOrUuid, szFilenameAbs, sizeof(szFilenameAbs));
1156 if (RT_FAILURE(vrc))
1157 {
1158 RTPrintf("Cannot convert filename \"%s\" to absolute path\n", FilenameOrUuid);
1159 return 1;
1160 }
[20842]1161 CHECK_ERROR(a->virtualBox, OpenHardDisk(Bstr(szFilenameAbs), AccessMode_ReadWrite, false, Bstr(""), false, Bstr(""), hardDisk.asOutParam()));
[19037]1162 }
1163 else if (FAILED(rc))
[20842]1164 CHECK_ERROR(a->virtualBox, OpenHardDisk(Bstr(FilenameOrUuid), AccessMode_ReadWrite, false, Bstr(""), false, Bstr(""), hardDisk.asOutParam()));
[13580]1165 if (SUCCEEDED (rc))
1166 {
1167 unknown = true;
1168 }
[775]1169 }
[1]1170 }
[13580]1171 do
[1]1172 {
[13580]1173 if (!SUCCEEDED(rc))
1174 break;
[2920]1175
[1]1176 hardDisk->COMGETTER(Id)(uuid.asOutParam());
[19239]1177 RTPrintf("UUID: %s\n", Utf8Str(uuid).raw());
[1]1178
[775]1179 /* check for accessibility */
[13580]1180 /// @todo NEWMEDIA check accessibility of all parents
1181 /// @todo NEWMEDIA print the full state value
[23223]1182 MediumState_T state;
[24258]1183 CHECK_ERROR_BREAK (hardDisk, RefreshState(&state));
[23223]1184 RTPrintf("Accessible: %s\n", state != MediumState_Inaccessible ? "yes" : "no");
[775]1185
[23223]1186 if (state == MediumState_Inaccessible)
[1]1187 {
[775]1188 Bstr err;
[13580]1189 CHECK_ERROR_BREAK (hardDisk, COMGETTER(LastAccessError)(err.asOutParam()));
[775]1190 RTPrintf("Access Error: %lS\n", err.raw());
1191 }
[1]1192
[4095]1193 Bstr description;
1194 hardDisk->COMGETTER(Description)(description.asOutParam());
1195 if (description)
1196 {
1197 RTPrintf("Description: %lS\n", description.raw());
1198 }
1199
[13580]1200 ULONG64 logicalSize;
1201 hardDisk->COMGETTER(LogicalSize)(&logicalSize);
1202 RTPrintf("Logical size: %llu MBytes\n", logicalSize);
[4095]1203 ULONG64 actualSize;
[13580]1204 hardDisk->COMGETTER(Size)(&actualSize);
[4095]1205 RTPrintf("Current size on disk: %llu MBytes\n", actualSize >> 20);
1206
[23223]1207 ComPtr <IMedium> parent;
[17654]1208 hardDisk->COMGETTER(Parent) (parent.asOutParam());
1209
[23223]1210 MediumType_T type;
[1]1211 hardDisk->COMGETTER(Type)(&type);
[2333]1212 const char *typeStr = "unknown";
[1]1213 switch (type)
1214 {
[23223]1215 case MediumType_Normal:
[17654]1216 if (!parent.isNull())
1217 typeStr = "normal (differencing)";
1218 else
1219 typeStr = "normal (base)";
[1]1220 break;
[23223]1221 case MediumType_Immutable:
[1]1222 typeStr = "immutable";
1223 break;
[23223]1224 case MediumType_Writethrough:
[1]1225 typeStr = "writethrough";
1226 break;
1227 }
1228 RTPrintf("Type: %s\n", typeStr);
1229
[13580]1230 Bstr format;
1231 hardDisk->COMGETTER(Format)(format.asOutParam());
1232 RTPrintf("Storage format: %lS\n", format.raw());
[1]1233
[13580]1234 if (!unknown)
[775]1235 {
[19239]1236 com::SafeArray<BSTR> machineIds;
[13580]1237 hardDisk->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(machineIds));
1238 for (size_t j = 0; j < machineIds.size(); ++ j)
1239 {
1240 ComPtr<IMachine> machine;
[16052]1241 CHECK_ERROR(a->virtualBox, GetMachine(machineIds[j], machine.asOutParam()));
[13580]1242 ASSERT(machine);
1243 Bstr name;
1244 machine->COMGETTER(Name)(name.asOutParam());
1245 machine->COMGETTER(Id)(uuid.asOutParam());
[19239]1246 RTPrintf("%s%lS (UUID: %lS)\n",
[13580]1247 j == 0 ? "In use by VMs: " : " ",
[19239]1248 name.raw(), machineIds[j]);
[13580]1249 }
1250 /// @todo NEWMEDIA check usage in snapshots too
[17654]1251 /// @todo NEWMEDIA also list children
[775]1252 }
[1]1253
[13580]1254 Bstr loc;
1255 hardDisk->COMGETTER(Location)(loc.asOutParam());
1256 RTPrintf("Location: %lS\n", loc.raw());
[17654]1257
1258 /* print out information specific for differencing hard disks */
1259 if (!parent.isNull())
1260 {
1261 BOOL autoReset = FALSE;
1262 hardDisk->COMGETTER(AutoReset)(&autoReset);
1263 RTPrintf("Auto-Reset: %s\n", autoReset ? "on" : "off");
1264 }
[13580]1265 }
1266 while (0);
[1]1267
[13580]1268 if (unknown)
1269 {
1270 /* close the unknown hard disk to forget it again */
1271 hardDisk->Close();
[1]1272 }
[13580]1273
[1]1274 return SUCCEEDED(rc) ? 0 : 1;
1275}
1276
[18703]1277static const RTGETOPTDEF g_aOpenMediumOptions[] =
1278{
1279 { "disk", 'd', RTGETOPT_REQ_NOTHING },
1280 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
1281 { "floppy", 'f', RTGETOPT_REQ_NOTHING },
1282 { "--type", 't', RTGETOPT_REQ_STRING },
1283 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
[20842]1284 { "--uuid", 'u', RTGETOPT_REQ_UUID },
1285 { "--parentuuid", 'p', RTGETOPT_REQ_UUID },
[18703]1286};
1287
[16052]1288int handleOpenMedium(HandlerArg *a)
[1]1289{
[18703]1290 HRESULT rc = S_OK;
[18814]1291 int vrc;
[18703]1292 enum {
1293 CMD_NONE,
1294 CMD_DISK,
1295 CMD_DVD,
1296 CMD_FLOPPY
1297 } cmd = CMD_NONE;
1298 const char *Filename = NULL;
[23223]1299 MediumType_T DiskType = MediumType_Normal;
[18703]1300 bool fDiskType = false;
[20842]1301 bool fSetImageId = false;
1302 bool fSetParentId = false;
1303 Guid ImageId;
1304 ImageId.clear();
1305 Guid ParentId;
1306 ParentId.clear();
[1]1307
[18703]1308 int c;
1309 RTGETOPTUNION ValueUnion;
1310 RTGETOPTSTATE GetState;
1311 // start at 0 because main() has hacked both the argc and argv given to us
1312 RTGetOptInit(&GetState, a->argc, a->argv, g_aOpenMediumOptions, RT_ELEMENTS(g_aOpenMediumOptions), 0, 0 /* fFlags */);
1313 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1314 {
1315 switch (c)
1316 {
1317 case 'd': // disk
1318 if (cmd != CMD_NONE)
1319 return errorSyntax(USAGE_OPENMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1320 cmd = CMD_DISK;
1321 break;
[1]1322
[18703]1323 case 'D': // DVD
1324 if (cmd != CMD_NONE)
1325 return errorSyntax(USAGE_OPENMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1326 cmd = CMD_DVD;
1327 break;
[1]1328
[18703]1329 case 'f': // floppy
1330 if (cmd != CMD_NONE)
1331 return errorSyntax(USAGE_OPENMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1332 cmd = CMD_FLOPPY;
1333 break;
1334
1335 case 't': // --type
[18814]1336 vrc = parseDiskType(ValueUnion.psz, &DiskType);
1337 if (RT_FAILURE(vrc))
[18703]1338 return errorArgument("Invalid hard disk type '%s'", ValueUnion.psz);
1339 fDiskType = true;
1340 break;
1341
[20842]1342 case 'u': // --uuid
1343 ImageId = ValueUnion.Uuid;
1344 fSetImageId = true;
1345 break;
1346
1347 case 'p': // --parentuuid
1348 ParentId = ValueUnion.Uuid;
1349 fSetParentId = true;
1350 break;
1351
[18703]1352 case VINF_GETOPT_NOT_OPTION:
1353 if (!Filename)
1354 Filename = ValueUnion.psz;
1355 else
1356 return errorSyntax(USAGE_OPENMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
1357 break;
1358
1359 default:
1360 if (c > 0)
1361 {
1362 if (RT_C_IS_PRINT(c))
1363 return errorSyntax(USAGE_OPENMEDIUM, "Invalid option -%c", c);
1364 else
1365 return errorSyntax(USAGE_OPENMEDIUM, "Invalid option case %i", c);
1366 }
1367 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1368 return errorSyntax(USAGE_OPENMEDIUM, "unknown option: %s\n", ValueUnion.psz);
1369 else if (ValueUnion.pDef)
1370 return errorSyntax(USAGE_OPENMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1371 else
1372 return errorSyntax(USAGE_OPENMEDIUM, "error: %Rrs", c);
[1]1373 }
[18703]1374 }
[1]1375
[18703]1376 /* check for required options */
1377 if (cmd == CMD_NONE)
1378 return errorSyntax(USAGE_OPENMEDIUM, "Command variant disk/dvd/floppy required");
1379 if (!Filename)
1380 return errorSyntax(USAGE_OPENMEDIUM, "Disk name required");
1381
[18814]1382 /** @todo remove this hack!
1383 * First try opening the image as is (using the regular API semantics for
1384 * images with relative path or without path), and if that fails with a
1385 * file related error then try it again with what the client thinks the
1386 * relative path would mean. Requires doing the command twice in certain
1387 * cases. This is an ugly hack and needs to be removed whevever we have a
1388 * chance to clean up the API semantics. */
[18703]1389 if (cmd == CMD_DISK)
1390 {
[23223]1391 ComPtr<IMedium> hardDisk;
[20842]1392 Bstr ImageIdStr = BstrFmt("%RTuuid", &ImageId);
1393 Bstr ParentIdStr = BstrFmt("%RTuuid", &ParentId);
1394 rc = a->virtualBox->OpenHardDisk(Bstr(Filename), AccessMode_ReadWrite, fSetImageId, ImageIdStr, fSetParentId, ParentIdStr, hardDisk.asOutParam());
[18814]1395 if (rc == VBOX_E_FILE_ERROR)
1396 {
1397 char szFilenameAbs[RTPATH_MAX] = "";
[24998]1398 int irc = RTPathAbs(Filename, szFilenameAbs, sizeof(szFilenameAbs));
1399 if (RT_FAILURE(irc))
[18814]1400 {
1401 RTPrintf("Cannot convert filename \"%s\" to absolute path\n", Filename);
1402 return 1;
1403 }
[20842]1404 CHECK_ERROR(a->virtualBox, OpenHardDisk(Bstr(szFilenameAbs), AccessMode_ReadWrite, fSetImageId, ImageIdStr, fSetParentId, ParentIdStr, hardDisk.asOutParam()));
[18814]1405 }
[18901]1406 else if (FAILED(rc))
[20842]1407 CHECK_ERROR(a->virtualBox, OpenHardDisk(Bstr(Filename), AccessMode_ReadWrite, fSetImageId, ImageIdStr, fSetParentId, ParentIdStr, hardDisk.asOutParam()));
[2918]1408 if (SUCCEEDED(rc) && hardDisk)
[1]1409 {
1410 /* change the type if requested */
[23223]1411 if (DiskType != MediumType_Normal)
[2918]1412 {
[18703]1413 CHECK_ERROR(hardDisk, COMSETTER(Type)(DiskType));
[2918]1414 }
[1]1415 }
1416 }
[18703]1417 else if (cmd == CMD_DVD)
[1]1418 {
[24884]1419 if (fDiskType || fSetParentId)
[18703]1420 return errorSyntax(USAGE_OPENMEDIUM, "Invalid option for DVD images");
[24884]1421 Bstr ImageIdStr = BstrFmt("%RTuuid", &ImageId);
[23223]1422 ComPtr<IMedium> dvdImage;
[24884]1423 rc = a->virtualBox->OpenDVDImage(Bstr(Filename), ImageIdStr, dvdImage.asOutParam());
[18814]1424 if (rc == VBOX_E_FILE_ERROR)
1425 {
1426 char szFilenameAbs[RTPATH_MAX] = "";
[24998]1427 int irc = RTPathAbs(Filename, szFilenameAbs, sizeof(szFilenameAbs));
1428 if (RT_FAILURE(irc))
[18814]1429 {
1430 RTPrintf("Cannot convert filename \"%s\" to absolute path\n", Filename);
1431 return 1;
1432 }
[24884]1433 CHECK_ERROR(a->virtualBox, OpenDVDImage(Bstr(szFilenameAbs), ImageIdStr, dvdImage.asOutParam()));
[18814]1434 }
[18901]1435 else if (FAILED(rc))
[24884]1436 CHECK_ERROR(a->virtualBox, OpenDVDImage(Bstr(Filename), ImageIdStr, dvdImage.asOutParam()));
[1]1437 }
[18703]1438 else if (cmd == CMD_FLOPPY)
[1]1439 {
[20842]1440 if (fDiskType || fSetImageId || fSetParentId)
1441 return errorSyntax(USAGE_OPENMEDIUM, "Invalid option for floppy images");
[24884]1442 Bstr ImageIdStr = BstrFmt("%RTuuid", &ImageId);
[23223]1443 ComPtr<IMedium> floppyImage;
[24884]1444 rc = a->virtualBox->OpenFloppyImage(Bstr(Filename), ImageIdStr, floppyImage.asOutParam());
[18814]1445 if (rc == VBOX_E_FILE_ERROR)
1446 {
1447 char szFilenameAbs[RTPATH_MAX] = "";
[24998]1448 int irc = RTPathAbs(Filename, szFilenameAbs, sizeof(szFilenameAbs));
1449 if (RT_FAILURE(irc))
[18814]1450 {
1451 RTPrintf("Cannot convert filename \"%s\" to absolute path\n", Filename);
1452 return 1;
1453 }
[24884]1454 CHECK_ERROR(a->virtualBox, OpenFloppyImage(Bstr(szFilenameAbs), ImageIdStr, floppyImage.asOutParam()));
[18814]1455 }
[18901]1456 else if (FAILED(rc))
[24884]1457 CHECK_ERROR(a->virtualBox, OpenFloppyImage(Bstr(Filename), ImageIdStr, floppyImage.asOutParam()));
[1]1458 }
[8373]1459
[1]1460 return SUCCEEDED(rc) ? 0 : 1;
1461}
1462
[18703]1463static const RTGETOPTDEF g_aCloseMediumOptions[] =
1464{
1465 { "disk", 'd', RTGETOPT_REQ_NOTHING },
1466 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
1467 { "floppy", 'f', RTGETOPT_REQ_NOTHING },
[24884]1468 { "--delete", 'r', RTGETOPT_REQ_NOTHING },
[18703]1469};
1470
[16052]1471int handleCloseMedium(HandlerArg *a)
[1]1472{
[18703]1473 HRESULT rc = S_OK;
1474 enum {
1475 CMD_NONE,
1476 CMD_DISK,
1477 CMD_DVD,
1478 CMD_FLOPPY
1479 } cmd = CMD_NONE;
1480 const char *FilenameOrUuid = NULL;
[24884]1481 bool fDelete = false;
[1]1482
[18703]1483 int c;
1484 RTGETOPTUNION ValueUnion;
1485 RTGETOPTSTATE GetState;
1486 // start at 0 because main() has hacked both the argc and argv given to us
1487 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloseMediumOptions, RT_ELEMENTS(g_aCloseMediumOptions), 0, 0 /* fFlags */);
1488 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1489 {
1490 switch (c)
1491 {
1492 case 'd': // disk
1493 if (cmd != CMD_NONE)
1494 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1495 cmd = CMD_DISK;
1496 break;
[1]1497
[18703]1498 case 'D': // DVD
1499 if (cmd != CMD_NONE)
1500 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1501 cmd = CMD_DVD;
1502 break;
1503
1504 case 'f': // floppy
1505 if (cmd != CMD_NONE)
1506 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1507 cmd = CMD_FLOPPY;
1508 break;
1509
[24884]1510 case 'r': // --delete
1511 fDelete = true;
1512 break;
1513
[18703]1514 case VINF_GETOPT_NOT_OPTION:
1515 if (!FilenameOrUuid)
1516 FilenameOrUuid = ValueUnion.psz;
1517 else
1518 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
1519 break;
1520
1521 default:
1522 if (c > 0)
1523 {
1524 if (RT_C_IS_PRINT(c))
1525 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option -%c", c);
1526 else
1527 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option case %i", c);
1528 }
1529 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1530 return errorSyntax(USAGE_CLOSEMEDIUM, "unknown option: %s\n", ValueUnion.psz);
1531 else if (ValueUnion.pDef)
1532 return errorSyntax(USAGE_CLOSEMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1533 else
1534 return errorSyntax(USAGE_CLOSEMEDIUM, "error: %Rrs", c);
1535 }
1536 }
1537
1538 /* check for required options */
1539 if (cmd == CMD_NONE)
1540 return errorSyntax(USAGE_CLOSEMEDIUM, "Command variant disk/dvd/floppy required");
1541 if (!FilenameOrUuid)
1542 return errorSyntax(USAGE_CLOSEMEDIUM, "Disk name or UUID required");
1543
[24884]1544 ComPtr<IMedium> medium;
1545
[1]1546 /* first guess is that it's a UUID */
[19239]1547 Bstr uuid(FilenameOrUuid);
[1]1548
[18703]1549 if (cmd == CMD_DISK)
[1]1550 {
[24884]1551 rc = a->virtualBox->GetHardDisk(uuid, medium.asOutParam());
[1]1552 /* not a UUID or not registered? Then it must be a filename */
[24884]1553 if (!medium)
[1]1554 {
[24884]1555 CHECK_ERROR(a->virtualBox, FindHardDisk(Bstr(FilenameOrUuid), medium.asOutParam()));
[1]1556 }
1557 }
1558 else
[18703]1559 if (cmd == CMD_DVD)
[1]1560 {
[24884]1561 rc = a->virtualBox->GetDVDImage(uuid, medium.asOutParam());
[1]1562 /* not a UUID or not registered? Then it must be a filename */
[24884]1563 if (!medium)
[1]1564 {
[24884]1565 CHECK_ERROR(a->virtualBox, FindDVDImage(Bstr(FilenameOrUuid), medium.asOutParam()));
[1]1566 }
1567 }
1568 else
[18703]1569 if (cmd == CMD_FLOPPY)
[1]1570 {
[24884]1571 rc = a->virtualBox->GetFloppyImage(uuid, medium.asOutParam());
[1]1572 /* not a UUID or not registered? Then it must be a filename */
[24884]1573 if (!medium)
[1]1574 {
[24884]1575 CHECK_ERROR(a->virtualBox, FindFloppyImage(Bstr(FilenameOrUuid), medium.asOutParam()));
[1]1576 }
[24884]1577 }
1578
1579 if (SUCCEEDED(rc) && medium)
1580 {
1581 if (fDelete)
[1]1582 {
[24884]1583 ComPtr<IProgress> progress;
1584 CHECK_ERROR(medium, DeleteStorage(progress.asOutParam()));
1585 if (SUCCEEDED(rc))
1586 {
1587 rc = showProgress(progress);
1588 if (FAILED(rc))
1589 {
1590 com::ProgressErrorInfo info(progress);
1591 if (info.isBasicAvailable())
1592 RTPrintf("Error: failed to delete medium. Error message: %lS\n", info.getText().raw());
1593 else
1594 RTPrintf("Error: failed to delete medium. No error message available!\n");
1595 }
1596 }
1597 else
1598 RTPrintf("Error: failed to delete medium. Error code %Rrc\n", rc);
[1]1599 }
[24884]1600 CHECK_ERROR(medium, Close());
[1]1601 }
[8373]1602
[1]1603 return SUCCEEDED(rc) ? 0 : 1;
1604}
[11703]1605#endif /* !VBOX_ONLY_DOCS */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use