VirtualBox

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

Last change on this file since 44028 was 44028, checked in by vboxsync, 12 years ago

Frontends/VBoxManage: improve snapshot information, unify wrappers for opening a medium object, cleanup

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

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