VirtualBox

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

Last change on this file since 26753 was 26753, checked in by vboxsync, 15 years ago

Main: Bstr makeover (third attempt) -- make Bstr(NULL) and Bstr() behave the same; resulting cleanup; make some more internal methods use Utf8Str instead of Bstr; fix a lot of CheckComArgNotNull??() usage

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