VirtualBox

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

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

Frontends/VBoxManage: fix error handling code path in clonehd

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