VirtualBox

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

Last change on this file since 31218 was 31152, checked in by vboxsync, 14 years ago

VBoxManage: another fix for VBoxManage createhd --type shareable --remember

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