VirtualBox

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

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

VBoxManage: Add option for resizing an image

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