VirtualBox

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

Last change on this file since 37722 was 37525, checked in by vboxsync, 14 years ago

Main/VirtualBox+Medium: resurrect the feature of changing the medium UUID when opening the image, which allows to resolve duplicate UUIDs without using external tools. Also fixes Medium::setIDs, which wasn't correctly working.
Frontends/*: corresponding changes.

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

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