VirtualBox

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

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

VD: Move the generic virtual disk framework + backends to src/VBox/Storage and rename the files to get rid of the HDD part because it supports floppy and DVD images too

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.2 KB
Line 
1/* $Id: VBoxManageDisk.cpp 33567 2010-10-28 15:37:21Z vboxsync $ */
2/** @file
3 * VBoxManage - The disk related commands.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#ifndef VBOX_ONLY_DOCS
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#include <VBox/com/com.h>
24#include <VBox/com/array.h>
25#include <VBox/com/ErrorInfo.h>
26#include <VBox/com/errorprint.h>
27#include <VBox/com/VirtualBox.h>
28
29#include <iprt/asm.h>
30#include <iprt/file.h>
31#include <iprt/path.h>
32#include <iprt/param.h>
33#include <iprt/stream.h>
34#include <iprt/string.h>
35#include <iprt/ctype.h>
36#include <iprt/getopt.h>
37#include <VBox/log.h>
38#include <VBox/vd.h>
39
40#include "VBoxManage.h"
41using namespace com;
42
43
44// funcs
45///////////////////////////////////////////////////////////////////////////////
46
47
48static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va)
49{
50 RTMsgError(pszFormat, va);
51 RTMsgError("Error code %Rrc at %s(%u) in function %s", rc, RT_SRC_POS_ARGS);
52}
53
54
55static int parseDiskVariant(const char *psz, MediumVariant_T *pDiskVariant)
56{
57 int rc = VINF_SUCCESS;
58 unsigned DiskVariant = (unsigned)(*pDiskVariant);
59 while (psz && *psz && RT_SUCCESS(rc))
60 {
61 size_t len;
62 const char *pszComma = strchr(psz, ',');
63 if (pszComma)
64 len = pszComma - psz;
65 else
66 len = strlen(psz);
67 if (len > 0)
68 {
69 // Parsing is intentionally inconsistent: "standard" resets the
70 // variant, whereas the other flags are cumulative.
71 if (!RTStrNICmp(psz, "standard", len))
72 DiskVariant = MediumVariant_Standard;
73 else if ( !RTStrNICmp(psz, "fixed", len)
74 || !RTStrNICmp(psz, "static", len))
75 DiskVariant |= MediumVariant_Fixed;
76 else if (!RTStrNICmp(psz, "Diff", len))
77 DiskVariant |= MediumVariant_Diff;
78 else if (!RTStrNICmp(psz, "split2g", len))
79 DiskVariant |= MediumVariant_VmdkSplit2G;
80 else if ( !RTStrNICmp(psz, "stream", len)
81 || !RTStrNICmp(psz, "streamoptimized", len))
82 DiskVariant |= MediumVariant_VmdkStreamOptimized;
83 else if (!RTStrNICmp(psz, "esx", len))
84 DiskVariant |= MediumVariant_VmdkESX;
85 else
86 rc = VERR_PARSE_ERROR;
87 }
88 if (pszComma)
89 psz += len + 1;
90 else
91 psz += len;
92 }
93
94 if (RT_SUCCESS(rc))
95 *pDiskVariant = (MediumVariant_T)DiskVariant;
96 return rc;
97}
98
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 (unofficial)
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 else if (SUCCEEDED(rc))
709 fDstUnknown = true;
710 else
711 {
712 com::GluePrintRCMessage(rc);
713 break;
714 }
715 }
716 else
717 fRemember = true;
718 if (SUCCEEDED(rc))
719 {
720 /* Perform accessibility check now. */
721 MediumState_T state;
722 CHECK_ERROR_BREAK(dstDisk, RefreshState(&state));
723 CHECK_ERROR_BREAK(dstDisk, COMGETTER(Format)(format.asOutParam()));
724 }
725 }
726 else
727 {
728 /* use the format of the source hard disk if unspecified */
729 if (format.isEmpty())
730 CHECK_ERROR_BREAK(srcDisk, COMGETTER(Format)(format.asOutParam()));
731 CHECK_ERROR_BREAK(a->virtualBox, CreateHardDisk(format.raw(),
732 dst.raw(),
733 dstDisk.asOutParam()));
734 }
735
736 ComPtr<IProgress> progress;
737 CHECK_ERROR_BREAK(srcDisk, CloneTo(dstDisk, DiskVariant, NULL, progress.asOutParam()));
738
739 rc = showProgress(progress);
740 if (FAILED(rc))
741 {
742 com::ProgressErrorInfo info(progress);
743 if (info.isBasicAvailable())
744 RTMsgError("Failed to clone hard disk. Error message: %lS", info.getText().raw());
745 else
746 RTMsgError("Failed to clone hard disk. No error message available!");
747 break;
748 }
749
750 Bstr uuid;
751 CHECK_ERROR_BREAK(dstDisk, COMGETTER(Id)(uuid.asOutParam()));
752
753 RTPrintf("Clone hard disk created in format '%ls'. UUID: %s\n",
754 format.raw(), Utf8Str(uuid).c_str());
755 }
756 while (0);
757
758 if (!fRemember && !dstDisk.isNull())
759 {
760 /* forget the created clone */
761 dstDisk->Close();
762 }
763 else if (fSetDiskType)
764 {
765 CHECK_ERROR(dstDisk, COMSETTER(Type)(DiskType));
766 }
767
768 if (fSrcUnknown)
769 {
770 /* close the unknown hard disk to forget it again */
771 srcDisk->Close();
772 }
773
774 return SUCCEEDED(rc) ? 0 : 1;
775}
776
777static const RTGETOPTDEF g_aConvertFromRawHardDiskOptions[] =
778{
779 { "--format", 'o', RTGETOPT_REQ_STRING },
780 { "-format", 'o', RTGETOPT_REQ_STRING },
781 { "--static", 'F', RTGETOPT_REQ_NOTHING },
782 { "-static", 'F', RTGETOPT_REQ_NOTHING },
783 { "--variant", 'm', RTGETOPT_REQ_STRING },
784 { "-variant", 'm', RTGETOPT_REQ_STRING },
785};
786
787int handleConvertFromRaw(int argc, char *argv[])
788{
789 int rc = VINF_SUCCESS;
790 bool fReadFromStdIn = false;
791 const char *format = "VDI";
792 const char *srcfilename = NULL;
793 const char *dstfilename = NULL;
794 const char *filesize = NULL;
795 unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
796 void *pvBuf = NULL;
797
798 int c;
799 RTGETOPTUNION ValueUnion;
800 RTGETOPTSTATE GetState;
801 // start at 0 because main() has hacked both the argc and argv given to us
802 RTGetOptInit(&GetState, argc, argv, g_aConvertFromRawHardDiskOptions, RT_ELEMENTS(g_aConvertFromRawHardDiskOptions),
803 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
804 while ((c = RTGetOpt(&GetState, &ValueUnion)))
805 {
806 switch (c)
807 {
808 case 'o': // --format
809 format = ValueUnion.psz;
810 break;
811
812 case 'm': // --variant
813 MediumVariant_T DiskVariant;
814 rc = parseDiskVariant(ValueUnion.psz, &DiskVariant);
815 if (RT_FAILURE(rc))
816 return errorArgument("Invalid hard disk variant '%s'", ValueUnion.psz);
817 /// @todo cleaner solution than assuming 1:1 mapping?
818 uImageFlags = (unsigned)DiskVariant;
819 break;
820
821 case VINF_GETOPT_NOT_OPTION:
822 if (!srcfilename)
823 {
824 srcfilename = ValueUnion.psz;
825// If you change the OS list here don't forget to update VBoxManageHelp.cpp.
826#ifndef RT_OS_WINDOWS
827 fReadFromStdIn = !strcmp(srcfilename, "stdin");
828#endif
829 }
830 else if (!dstfilename)
831 dstfilename = ValueUnion.psz;
832 else if (fReadFromStdIn && !filesize)
833 filesize = ValueUnion.psz;
834 else
835 return errorSyntax(USAGE_CONVERTFROMRAW, "Invalid parameter '%s'", ValueUnion.psz);
836 break;
837
838 default:
839 return errorGetOpt(USAGE_CONVERTFROMRAW, c, &ValueUnion);
840 }
841 }
842
843 if (!srcfilename || !dstfilename || (fReadFromStdIn && !filesize))
844 return errorSyntax(USAGE_CONVERTFROMRAW, "Incorrect number of parameters");
845 RTStrmPrintf(g_pStdErr, "Converting from raw image file=\"%s\" to file=\"%s\"...\n",
846 srcfilename, dstfilename);
847
848 PVBOXHDD pDisk = NULL;
849
850 PVDINTERFACE pVDIfs = NULL;
851 VDINTERFACE vdInterfaceError;
852 VDINTERFACEERROR vdInterfaceErrorCallbacks;
853 vdInterfaceErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
854 vdInterfaceErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
855 vdInterfaceErrorCallbacks.pfnError = handleVDError;
856 vdInterfaceErrorCallbacks.pfnMessage = NULL;
857
858 rc = VDInterfaceAdd(&vdInterfaceError, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
859 &vdInterfaceErrorCallbacks, NULL, &pVDIfs);
860 AssertRC(rc);
861
862 /* open raw image file. */
863 RTFILE File;
864 if (fReadFromStdIn)
865 File = 0;
866 else
867 rc = RTFileOpen(&File, srcfilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
868 if (RT_FAILURE(rc))
869 {
870 RTMsgError("Cannot open file \"%s\": %Rrc", srcfilename, rc);
871 goto out;
872 }
873
874 uint64_t cbFile;
875 /* get image size. */
876 if (fReadFromStdIn)
877 cbFile = RTStrToUInt64(filesize);
878 else
879 rc = RTFileGetSize(File, &cbFile);
880 if (RT_FAILURE(rc))
881 {
882 RTMsgError("Cannot get image size for file \"%s\": %Rrc", srcfilename, rc);
883 goto out;
884 }
885
886 RTStrmPrintf(g_pStdErr, "Creating %s image with size %RU64 bytes (%RU64MB)...\n",
887 (uImageFlags & VD_IMAGE_FLAGS_FIXED) ? "fixed" : "dynamic", cbFile, (cbFile + _1M - 1) / _1M);
888 char pszComment[256];
889 RTStrPrintf(pszComment, sizeof(pszComment), "Converted image from %s", srcfilename);
890 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
891 if (RT_FAILURE(rc))
892 {
893 RTMsgError("Cannot create the virtual disk container: %Rrc", rc);
894 goto out;
895 }
896
897 Assert(RT_MIN(cbFile / 512 / 16 / 63, 16383) -
898 (unsigned int)RT_MIN(cbFile / 512 / 16 / 63, 16383) == 0);
899 VDGEOMETRY PCHS, LCHS;
900 PCHS.cCylinders = (unsigned int)RT_MIN(cbFile / 512 / 16 / 63, 16383);
901 PCHS.cHeads = 16;
902 PCHS.cSectors = 63;
903 LCHS.cCylinders = 0;
904 LCHS.cHeads = 0;
905 LCHS.cSectors = 0;
906 rc = VDCreateBase(pDisk, format, dstfilename, cbFile,
907 uImageFlags, pszComment, &PCHS, &LCHS, NULL,
908 VD_OPEN_FLAGS_NORMAL, NULL, NULL);
909 if (RT_FAILURE(rc))
910 {
911 RTMsgError("Cannot create the disk image \"%s\": %Rrc", dstfilename, rc);
912 goto out;
913 }
914
915 size_t cbBuffer;
916 cbBuffer = _1M;
917 pvBuf = RTMemAlloc(cbBuffer);
918 if (!pvBuf)
919 {
920 rc = VERR_NO_MEMORY;
921 RTMsgError("Out of memory allocating buffers for image \"%s\": %Rrc", dstfilename, rc);
922 goto out;
923 }
924
925 uint64_t offFile;
926 offFile = 0;
927 while (offFile < cbFile)
928 {
929 size_t cbRead;
930 size_t cbToRead;
931 cbRead = 0;
932 cbToRead = cbFile - offFile >= (uint64_t)cbBuffer ?
933 cbBuffer : (size_t)(cbFile - offFile);
934 rc = RTFileRead(File, pvBuf, cbToRead, &cbRead);
935 if (RT_FAILURE(rc) || !cbRead)
936 break;
937 rc = VDWrite(pDisk, offFile, pvBuf, cbRead);
938 if (RT_FAILURE(rc))
939 {
940 RTMsgError("Failed to write to disk image \"%s\": %Rrc", dstfilename, rc);
941 goto out;
942 }
943 offFile += cbRead;
944 }
945
946out:
947 if (pvBuf)
948 RTMemFree(pvBuf);
949 if (pDisk)
950 VDClose(pDisk, RT_FAILURE(rc));
951 if (File != NIL_RTFILE)
952 RTFileClose(File);
953
954 return RT_FAILURE(rc);
955}
956
957static const RTGETOPTDEF g_aAddiSCSIDiskOptions[] =
958{
959 { "--server", 's', RTGETOPT_REQ_STRING },
960 { "-server", 's', RTGETOPT_REQ_STRING }, // deprecated
961 { "--target", 'T', RTGETOPT_REQ_STRING },
962 { "-target", 'T', RTGETOPT_REQ_STRING }, // deprecated
963 { "--port", 'p', RTGETOPT_REQ_STRING },
964 { "-port", 'p', RTGETOPT_REQ_STRING }, // deprecated
965 { "--lun", 'l', RTGETOPT_REQ_STRING },
966 { "-lun", 'l', RTGETOPT_REQ_STRING }, // deprecated
967 { "--encodedlun", 'L', RTGETOPT_REQ_STRING },
968 { "-encodedlun", 'L', RTGETOPT_REQ_STRING }, // deprecated
969 { "--username", 'u', RTGETOPT_REQ_STRING },
970 { "-username", 'u', RTGETOPT_REQ_STRING }, // deprecated
971 { "--password", 'P', RTGETOPT_REQ_STRING },
972 { "-password", 'P', RTGETOPT_REQ_STRING }, // deprecated
973 { "--type", 't', RTGETOPT_REQ_STRING },
974 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
975 { "--intnet", 'I', RTGETOPT_REQ_NOTHING },
976 { "-intnet", 'I', RTGETOPT_REQ_NOTHING }, // deprecated
977};
978
979int handleAddiSCSIDisk(HandlerArg *a)
980{
981 HRESULT rc;
982 int vrc;
983 Bstr server;
984 Bstr target;
985 Bstr port;
986 Bstr lun;
987 Bstr username;
988 Bstr password;
989 Bstr comment;
990 bool fIntNet = false;
991 MediumType_T DiskType = MediumType_Normal;
992
993 int c;
994 RTGETOPTUNION ValueUnion;
995 RTGETOPTSTATE GetState;
996 // start at 0 because main() has hacked both the argc and argv given to us
997 RTGetOptInit(&GetState, a->argc, a->argv, g_aAddiSCSIDiskOptions, RT_ELEMENTS(g_aAddiSCSIDiskOptions),
998 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
999 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1000 {
1001 switch (c)
1002 {
1003 case 's': // --server
1004 server = ValueUnion.psz;
1005 break;
1006
1007 case 'T': // --target
1008 target = ValueUnion.psz;
1009 break;
1010
1011 case 'p': // --port
1012 port = ValueUnion.psz;
1013 break;
1014
1015 case 'l': // --lun
1016 lun = ValueUnion.psz;
1017 break;
1018
1019 case 'L': // --encodedlun
1020 lun = BstrFmt("enc%s", ValueUnion.psz);
1021 break;
1022
1023 case 'u': // --username
1024 username = ValueUnion.psz;
1025 break;
1026
1027 case 'P': // --password
1028 password = ValueUnion.psz;
1029 break;
1030
1031 case 't': // --type
1032 vrc = parseDiskType(ValueUnion.psz, &DiskType);
1033 if (RT_FAILURE(vrc))
1034 return errorArgument("Invalid hard disk type '%s'", ValueUnion.psz);
1035 break;
1036
1037 case 'I': // --intnet
1038 fIntNet = true;
1039 break;
1040
1041 case VINF_GETOPT_NOT_OPTION:
1042 return errorSyntax(USAGE_ADDISCSIDISK, "Invalid parameter '%s'", ValueUnion.psz);
1043
1044 default:
1045 if (c > 0)
1046 {
1047 if (RT_C_IS_PRINT(c))
1048 return errorSyntax(USAGE_ADDISCSIDISK, "Invalid option -%c", c);
1049 else
1050 return errorSyntax(USAGE_ADDISCSIDISK, "Invalid option case %i", c);
1051 }
1052 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1053 return errorSyntax(USAGE_ADDISCSIDISK, "unknown option: %s\n", ValueUnion.psz);
1054 else if (ValueUnion.pDef)
1055 return errorSyntax(USAGE_ADDISCSIDISK, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1056 else
1057 return errorSyntax(USAGE_ADDISCSIDISK, "error: %Rrs", c);
1058 }
1059 }
1060
1061 /* check for required options */
1062 if (server.isEmpty() || target.isEmpty())
1063 return errorSyntax(USAGE_ADDISCSIDISK, "Parameters --server and --target are required");
1064
1065 do
1066 {
1067 ComPtr<IMedium> hardDisk;
1068 /** @todo move the location stuff to Main, which can use pfnComposeName
1069 * from the disk backends to construct the location properly. Also do
1070 * not use slashes to separate the parts, as otherwise only the last
1071 * element containing information will be shown. */
1072 if (lun.isEmpty() || lun == "0" || lun == "enc0")
1073 {
1074 CHECK_ERROR_BREAK(a->virtualBox, CreateHardDisk(Bstr("iSCSI").raw(),
1075 BstrFmt("%ls|%ls",
1076 server.raw(),
1077 target.raw()).raw(),
1078 hardDisk.asOutParam()));
1079 }
1080 else
1081 {
1082 CHECK_ERROR_BREAK(a->virtualBox, CreateHardDisk(Bstr("iSCSI").raw(),
1083 BstrFmt("%ls|%ls|%ls",
1084 server.raw(),
1085 target.raw(),
1086 lun.raw()).raw(),
1087 hardDisk.asOutParam()));
1088 }
1089 if (FAILED(rc)) break;
1090
1091 if (!port.isEmpty())
1092 server = BstrFmt("%ls:%ls", server.raw(), port.raw());
1093
1094 com::SafeArray <BSTR> names;
1095 com::SafeArray <BSTR> values;
1096
1097 Bstr("TargetAddress").detachTo(names.appendedRaw());
1098 server.detachTo(values.appendedRaw());
1099 Bstr("TargetName").detachTo(names.appendedRaw());
1100 target.detachTo(values.appendedRaw());
1101
1102 if (!lun.isEmpty())
1103 {
1104 Bstr("LUN").detachTo(names.appendedRaw());
1105 lun.detachTo(values.appendedRaw());
1106 }
1107 if (!username.isEmpty())
1108 {
1109 Bstr("InitiatorUsername").detachTo(names.appendedRaw());
1110 username.detachTo(values.appendedRaw());
1111 }
1112 if (!password.isEmpty())
1113 {
1114 Bstr("InitiatorSecret").detachTo(names.appendedRaw());
1115 password.detachTo(values.appendedRaw());
1116 }
1117
1118 /// @todo add --initiator option - until that happens rely on the
1119 // defaults of the iSCSI initiator code. Setting it to a constant
1120 // value does more harm than good, as the initiator name is supposed
1121 // to identify a particular initiator uniquely.
1122// Bstr("InitiatorName").detachTo(names.appendedRaw());
1123// Bstr("iqn.2008-04.com.sun.virtualbox.initiator").detachTo(values.appendedRaw());
1124
1125 /// @todo add --targetName and --targetPassword options
1126
1127 if (fIntNet)
1128 {
1129 Bstr("HostIPStack").detachTo(names.appendedRaw());
1130 Bstr("0").detachTo(values.appendedRaw());
1131 }
1132
1133 CHECK_ERROR_BREAK(hardDisk, SetProperties(ComSafeArrayAsInParam(names),
1134 ComSafeArrayAsInParam(values)));
1135
1136 if (DiskType != MediumType_Normal)
1137 {
1138 CHECK_ERROR(hardDisk, COMSETTER(Type)(DiskType));
1139 }
1140
1141 Bstr guid;
1142 CHECK_ERROR(hardDisk, COMGETTER(Id)(guid.asOutParam()));
1143 RTPrintf("iSCSI disk created. UUID: %s\n", Utf8Str(guid).c_str());
1144 }
1145 while (0);
1146
1147 return SUCCEEDED(rc) ? 0 : 1;
1148}
1149
1150static const RTGETOPTDEF g_aShowHardDiskInfoOptions[] =
1151{
1152 { "--dummy", 256, RTGETOPT_REQ_NOTHING }, // placeholder for C++
1153};
1154
1155int handleShowHardDiskInfo(HandlerArg *a)
1156{
1157 HRESULT rc;
1158 const char *FilenameOrUuid = NULL;
1159
1160 int c;
1161 RTGETOPTUNION ValueUnion;
1162 RTGETOPTSTATE GetState;
1163 // start at 0 because main() has hacked both the argc and argv given to us
1164 RTGetOptInit(&GetState, a->argc, a->argv, g_aShowHardDiskInfoOptions, RT_ELEMENTS(g_aShowHardDiskInfoOptions),
1165 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1166 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1167 {
1168 switch (c)
1169 {
1170 case VINF_GETOPT_NOT_OPTION:
1171 if (!FilenameOrUuid)
1172 FilenameOrUuid = ValueUnion.psz;
1173 else
1174 return errorSyntax(USAGE_SHOWHDINFO, "Invalid parameter '%s'", ValueUnion.psz);
1175 break;
1176
1177 default:
1178 if (c > 0)
1179 {
1180 if (RT_C_IS_PRINT(c))
1181 return errorSyntax(USAGE_SHOWHDINFO, "Invalid option -%c", c);
1182 else
1183 return errorSyntax(USAGE_SHOWHDINFO, "Invalid option case %i", c);
1184 }
1185 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1186 return errorSyntax(USAGE_SHOWHDINFO, "unknown option: %s\n", ValueUnion.psz);
1187 else if (ValueUnion.pDef)
1188 return errorSyntax(USAGE_SHOWHDINFO, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1189 else
1190 return errorSyntax(USAGE_SHOWHDINFO, "error: %Rrs", c);
1191 }
1192 }
1193
1194 /* check for required options */
1195 if (!FilenameOrUuid)
1196 return errorSyntax(USAGE_SHOWHDINFO, "Disk name or UUID required");
1197
1198 ComPtr<IMedium> hardDisk;
1199 bool unknown = false;
1200 /* first guess is that it's a UUID */
1201 rc = a->virtualBox->FindMedium(Bstr(FilenameOrUuid).raw(),
1202 DeviceType_HardDisk,
1203 hardDisk.asOutParam());
1204 /* no? well, then it's an unknown image */
1205 if (FAILED(rc))
1206 {
1207 rc = a->virtualBox->OpenMedium(Bstr(FilenameOrUuid).raw(),
1208 DeviceType_HardDisk,
1209 AccessMode_ReadWrite,
1210 hardDisk.asOutParam());
1211 if (rc == VBOX_E_FILE_ERROR)
1212 {
1213 char szFilenameAbs[RTPATH_MAX] = "";
1214 int vrc = RTPathAbs(FilenameOrUuid, szFilenameAbs, sizeof(szFilenameAbs));
1215 if (RT_FAILURE(vrc))
1216 {
1217 RTMsgError("Cannot convert filename \"%s\" to absolute path", FilenameOrUuid);
1218 return 1;
1219 }
1220 CHECK_ERROR(a->virtualBox, OpenMedium(Bstr(szFilenameAbs).raw(),
1221 DeviceType_HardDisk,
1222 AccessMode_ReadWrite,
1223 hardDisk.asOutParam()));
1224 }
1225 else if (SUCCEEDED(rc))
1226 unknown = true;
1227 else
1228 {
1229 com::GluePrintRCMessage(rc);
1230 return 1;
1231 }
1232 }
1233
1234 do
1235 {
1236 Bstr uuid;
1237 hardDisk->COMGETTER(Id)(uuid.asOutParam());
1238 RTPrintf("UUID: %s\n", Utf8Str(uuid).c_str());
1239
1240 /* check for accessibility */
1241 /// @todo NEWMEDIA check accessibility of all parents
1242 /// @todo NEWMEDIA print the full state value
1243 MediumState_T state;
1244 CHECK_ERROR_BREAK(hardDisk, RefreshState(&state));
1245 RTPrintf("Accessible: %s\n", state != MediumState_Inaccessible ? "yes" : "no");
1246
1247 if (state == MediumState_Inaccessible)
1248 {
1249 Bstr err;
1250 CHECK_ERROR_BREAK(hardDisk, COMGETTER(LastAccessError)(err.asOutParam()));
1251 RTPrintf("Access Error: %lS\n", err.raw());
1252 }
1253
1254 Bstr description;
1255 hardDisk->COMGETTER(Description)(description.asOutParam());
1256 if (!description.isEmpty())
1257 {
1258 RTPrintf("Description: %lS\n", description.raw());
1259 }
1260
1261 LONG64 logicalSize;
1262 hardDisk->COMGETTER(LogicalSize)(&logicalSize);
1263 RTPrintf("Logical size: %lld MBytes\n", logicalSize);
1264 LONG64 actualSize;
1265 hardDisk->COMGETTER(Size)(&actualSize);
1266 RTPrintf("Current size on disk: %lld MBytes\n", actualSize >> 20);
1267
1268 ComPtr <IMedium> parent;
1269 hardDisk->COMGETTER(Parent)(parent.asOutParam());
1270
1271 MediumType_T type;
1272 hardDisk->COMGETTER(Type)(&type);
1273 const char *typeStr = "unknown";
1274 switch (type)
1275 {
1276 case MediumType_Normal:
1277 if (!parent.isNull())
1278 typeStr = "normal (differencing)";
1279 else
1280 typeStr = "normal (base)";
1281 break;
1282 case MediumType_Immutable:
1283 typeStr = "immutable";
1284 break;
1285 case MediumType_Writethrough:
1286 typeStr = "writethrough";
1287 break;
1288 case MediumType_Shareable:
1289 typeStr = "shareable";
1290 break;
1291 }
1292 RTPrintf("Type: %s\n", typeStr);
1293
1294 Bstr format;
1295 hardDisk->COMGETTER(Format)(format.asOutParam());
1296 RTPrintf("Storage format: %lS\n", format.raw());
1297
1298 /// @todo also dump config parameters (iSCSI)
1299
1300 if (!unknown)
1301 {
1302 com::SafeArray<BSTR> machineIds;
1303 hardDisk->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(machineIds));
1304 for (size_t j = 0; j < machineIds.size(); ++ j)
1305 {
1306 ComPtr<IMachine> machine;
1307 CHECK_ERROR(a->virtualBox, FindMachine(machineIds[j], machine.asOutParam()));
1308 ASSERT(machine);
1309 Bstr name;
1310 machine->COMGETTER(Name)(name.asOutParam());
1311 machine->COMGETTER(Id)(uuid.asOutParam());
1312 RTPrintf("%s%lS (UUID: %lS)\n",
1313 j == 0 ? "In use by VMs: " : " ",
1314 name.raw(), machineIds[j]);
1315 }
1316 /// @todo NEWMEDIA check usage in snapshots too
1317 /// @todo NEWMEDIA also list children
1318 }
1319
1320 Bstr loc;
1321 hardDisk->COMGETTER(Location)(loc.asOutParam());
1322 RTPrintf("Location: %lS\n", loc.raw());
1323
1324 /* print out information specific for differencing hard disks */
1325 if (!parent.isNull())
1326 {
1327 BOOL autoReset = FALSE;
1328 hardDisk->COMGETTER(AutoReset)(&autoReset);
1329 RTPrintf("Auto-Reset: %s\n", autoReset ? "on" : "off");
1330 }
1331 }
1332 while (0);
1333
1334 if (unknown)
1335 {
1336 /* close the unknown hard disk to forget it again */
1337 hardDisk->Close();
1338 }
1339
1340 return SUCCEEDED(rc) ? 0 : 1;
1341}
1342
1343static const RTGETOPTDEF g_aCloseMediumOptions[] =
1344{
1345 { "disk", 'd', RTGETOPT_REQ_NOTHING },
1346 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
1347 { "floppy", 'f', RTGETOPT_REQ_NOTHING },
1348 { "--delete", 'r', RTGETOPT_REQ_NOTHING },
1349};
1350
1351int handleCloseMedium(HandlerArg *a)
1352{
1353 HRESULT rc = S_OK;
1354 enum {
1355 CMD_NONE,
1356 CMD_DISK,
1357 CMD_DVD,
1358 CMD_FLOPPY
1359 } cmd = CMD_NONE;
1360 const char *FilenameOrUuid = NULL;
1361 bool fDelete = false;
1362
1363 int c;
1364 RTGETOPTUNION ValueUnion;
1365 RTGETOPTSTATE GetState;
1366 // start at 0 because main() has hacked both the argc and argv given to us
1367 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloseMediumOptions, RT_ELEMENTS(g_aCloseMediumOptions),
1368 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1369 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1370 {
1371 switch (c)
1372 {
1373 case 'd': // disk
1374 if (cmd != CMD_NONE)
1375 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1376 cmd = CMD_DISK;
1377 break;
1378
1379 case 'D': // DVD
1380 if (cmd != CMD_NONE)
1381 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1382 cmd = CMD_DVD;
1383 break;
1384
1385 case 'f': // floppy
1386 if (cmd != CMD_NONE)
1387 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1388 cmd = CMD_FLOPPY;
1389 break;
1390
1391 case 'r': // --delete
1392 fDelete = true;
1393 break;
1394
1395 case VINF_GETOPT_NOT_OPTION:
1396 if (!FilenameOrUuid)
1397 FilenameOrUuid = ValueUnion.psz;
1398 else
1399 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
1400 break;
1401
1402 default:
1403 if (c > 0)
1404 {
1405 if (RT_C_IS_PRINT(c))
1406 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option -%c", c);
1407 else
1408 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option case %i", c);
1409 }
1410 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1411 return errorSyntax(USAGE_CLOSEMEDIUM, "unknown option: %s\n", ValueUnion.psz);
1412 else if (ValueUnion.pDef)
1413 return errorSyntax(USAGE_CLOSEMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1414 else
1415 return errorSyntax(USAGE_CLOSEMEDIUM, "error: %Rrs", c);
1416 }
1417 }
1418
1419 /* check for required options */
1420 if (cmd == CMD_NONE)
1421 return errorSyntax(USAGE_CLOSEMEDIUM, "Command variant disk/dvd/floppy required");
1422 if (!FilenameOrUuid)
1423 return errorSyntax(USAGE_CLOSEMEDIUM, "Disk name or UUID required");
1424
1425 ComPtr<IMedium> medium;
1426
1427 if (cmd == CMD_DISK)
1428 CHECK_ERROR(a->virtualBox, FindMedium(Bstr(FilenameOrUuid).raw(),
1429 DeviceType_HardDisk,
1430 medium.asOutParam()));
1431 else if (cmd == CMD_DVD)
1432 CHECK_ERROR(a->virtualBox, FindMedium(Bstr(FilenameOrUuid).raw(),
1433 DeviceType_DVD,
1434 medium.asOutParam()));
1435 else if (cmd == CMD_FLOPPY)
1436 CHECK_ERROR(a->virtualBox, FindMedium(Bstr(FilenameOrUuid).raw(),
1437 DeviceType_Floppy,
1438 medium.asOutParam()));
1439
1440 if (SUCCEEDED(rc) && medium)
1441 {
1442 if (fDelete)
1443 {
1444 ComPtr<IProgress> progress;
1445 CHECK_ERROR(medium, DeleteStorage(progress.asOutParam()));
1446 if (SUCCEEDED(rc))
1447 {
1448 rc = showProgress(progress);
1449 if (FAILED(rc))
1450 {
1451 com::ProgressErrorInfo info(progress);
1452 if (info.isBasicAvailable())
1453 RTMsgError("Failed to delete medium. Error message: %lS", info.getText().raw());
1454 else
1455 RTMsgError("Failed to delete medium. No error message available!");
1456 }
1457 }
1458 else
1459 RTMsgError("Failed to delete medium. Error code %Rrc", rc);
1460 }
1461 CHECK_ERROR(medium, Close());
1462 }
1463
1464 return SUCCEEDED(rc) ? 0 : 1;
1465}
1466#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