VirtualBox

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

Last change on this file since 35273 was 35261, checked in by vboxsync, 13 years ago

Main+Frontends: fix windows burns

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

© 2023 Oracle
ContactPrivacy policyTerms of Use