VirtualBox

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

Last change on this file since 30111 was 28888, checked in by vboxsync, 15 years ago

Main/Medium: new stub medium type "Shareable", plus assorted frontend changes to prepare its use.

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