VirtualBox

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

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

Frontends/VBoxManage: Error printing cleanup, use stderr and consistent formatting. Small cleanups (like using Keyboard::PutScancodes instead of the more clumsy Keyboard::PutScancode and fixing the incorrect comment which attracted my attention).

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