VirtualBox

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

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

Storage/iSCSI: implement automatically setting InitiatorName and ISID to globally unique values

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 57.5 KB
Line 
1/* $Id: VBoxManageDisk.cpp 26917 2010-03-01 14:55:10Z 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 - until that happens rely on the
1069 // defaults of the iSCSI initiator code. Setting it to a constant
1070 // value does more harm than good, as the initiator name is supposed
1071 // to identify a particular initiator uniquely.
1072// Bstr ("InitiatorName").detachTo (names.appendedRaw());
1073// Bstr ("iqn.2008-04.com.sun.virtualbox.initiator").detachTo (values.appendedRaw());
1074
1075 /// @todo add --targetName and --targetPassword options
1076
1077 if (fIntNet)
1078 {
1079 Bstr ("HostIPStack").detachTo (names.appendedRaw());
1080 Bstr ("0").detachTo (values.appendedRaw());
1081 }
1082
1083 CHECK_ERROR_BREAK (hardDisk,
1084 SetProperties (ComSafeArrayAsInParam (names),
1085 ComSafeArrayAsInParam (values)));
1086
1087 if (DiskType != MediumType_Normal)
1088 {
1089 CHECK_ERROR(hardDisk, COMSETTER(Type)(DiskType));
1090 }
1091
1092 Bstr guid;
1093 CHECK_ERROR(hardDisk, COMGETTER(Id)(guid.asOutParam()));
1094 RTPrintf("iSCSI disk created. UUID: %s\n", Utf8Str(guid).raw());
1095 }
1096 while (0);
1097
1098 return SUCCEEDED(rc) ? 0 : 1;
1099}
1100
1101static const RTGETOPTDEF g_aShowHardDiskInfoOptions[] =
1102{
1103 { "--dummy", 256, RTGETOPT_REQ_NOTHING }, // placeholder for C++
1104};
1105
1106int handleShowHardDiskInfo(HandlerArg *a)
1107{
1108 HRESULT rc;
1109 const char *FilenameOrUuid = NULL;
1110
1111 int c;
1112 RTGETOPTUNION ValueUnion;
1113 RTGETOPTSTATE GetState;
1114 // start at 0 because main() has hacked both the argc and argv given to us
1115 RTGetOptInit(&GetState, a->argc, a->argv, g_aShowHardDiskInfoOptions, RT_ELEMENTS(g_aShowHardDiskInfoOptions),
1116 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1117 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1118 {
1119 switch (c)
1120 {
1121 case VINF_GETOPT_NOT_OPTION:
1122 if (!FilenameOrUuid)
1123 FilenameOrUuid = ValueUnion.psz;
1124 else
1125 return errorSyntax(USAGE_SHOWHDINFO, "Invalid parameter '%s'", ValueUnion.psz);
1126 break;
1127
1128 default:
1129 if (c > 0)
1130 {
1131 if (RT_C_IS_PRINT(c))
1132 return errorSyntax(USAGE_SHOWHDINFO, "Invalid option -%c", c);
1133 else
1134 return errorSyntax(USAGE_SHOWHDINFO, "Invalid option case %i", c);
1135 }
1136 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1137 return errorSyntax(USAGE_SHOWHDINFO, "unknown option: %s\n", ValueUnion.psz);
1138 else if (ValueUnion.pDef)
1139 return errorSyntax(USAGE_SHOWHDINFO, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1140 else
1141 return errorSyntax(USAGE_SHOWHDINFO, "error: %Rrs", c);
1142 }
1143 }
1144
1145 /* check for required options */
1146 if (!FilenameOrUuid)
1147 return errorSyntax(USAGE_SHOWHDINFO, "Disk name or UUID required");
1148
1149 ComPtr<IMedium> hardDisk;
1150 bool unknown = false;
1151 /* first guess is that it's a UUID */
1152 Bstr uuid(FilenameOrUuid);
1153 rc = a->virtualBox->GetHardDisk(uuid, hardDisk.asOutParam());
1154 /* no? then it must be a filename */
1155 if (FAILED (rc))
1156 {
1157 rc = a->virtualBox->FindHardDisk(Bstr(FilenameOrUuid), hardDisk.asOutParam());
1158 /* no? well, then it's an unkwnown image */
1159 if (FAILED (rc))
1160 {
1161 rc = a->virtualBox->OpenHardDisk(Bstr(FilenameOrUuid), AccessMode_ReadWrite, false, Bstr(""), false, Bstr(""), hardDisk.asOutParam());
1162 if (rc == VBOX_E_FILE_ERROR)
1163 {
1164 char szFilenameAbs[RTPATH_MAX] = "";
1165 int vrc = RTPathAbs(FilenameOrUuid, szFilenameAbs, sizeof(szFilenameAbs));
1166 if (RT_FAILURE(vrc))
1167 {
1168 RTPrintf("Cannot convert filename \"%s\" to absolute path\n", FilenameOrUuid);
1169 return 1;
1170 }
1171 CHECK_ERROR(a->virtualBox, OpenHardDisk(Bstr(szFilenameAbs), AccessMode_ReadWrite, false, Bstr(""), false, Bstr(""), hardDisk.asOutParam()));
1172 }
1173 else if (FAILED(rc))
1174 CHECK_ERROR(a->virtualBox, OpenHardDisk(Bstr(FilenameOrUuid), AccessMode_ReadWrite, false, Bstr(""), false, Bstr(""), hardDisk.asOutParam()));
1175 if (SUCCEEDED (rc))
1176 {
1177 unknown = true;
1178 }
1179 }
1180 }
1181 do
1182 {
1183 if (!SUCCEEDED(rc))
1184 break;
1185
1186 hardDisk->COMGETTER(Id)(uuid.asOutParam());
1187 RTPrintf("UUID: %s\n", Utf8Str(uuid).raw());
1188
1189 /* check for accessibility */
1190 /// @todo NEWMEDIA check accessibility of all parents
1191 /// @todo NEWMEDIA print the full state value
1192 MediumState_T state;
1193 CHECK_ERROR_BREAK (hardDisk, RefreshState(&state));
1194 RTPrintf("Accessible: %s\n", state != MediumState_Inaccessible ? "yes" : "no");
1195
1196 if (state == MediumState_Inaccessible)
1197 {
1198 Bstr err;
1199 CHECK_ERROR_BREAK (hardDisk, COMGETTER(LastAccessError)(err.asOutParam()));
1200 RTPrintf("Access Error: %lS\n", err.raw());
1201 }
1202
1203 Bstr description;
1204 hardDisk->COMGETTER(Description)(description.asOutParam());
1205 if (description)
1206 {
1207 RTPrintf("Description: %lS\n", description.raw());
1208 }
1209
1210 ULONG64 logicalSize;
1211 hardDisk->COMGETTER(LogicalSize)(&logicalSize);
1212 RTPrintf("Logical size: %llu MBytes\n", logicalSize);
1213 ULONG64 actualSize;
1214 hardDisk->COMGETTER(Size)(&actualSize);
1215 RTPrintf("Current size on disk: %llu MBytes\n", actualSize >> 20);
1216
1217 ComPtr <IMedium> parent;
1218 hardDisk->COMGETTER(Parent) (parent.asOutParam());
1219
1220 MediumType_T type;
1221 hardDisk->COMGETTER(Type)(&type);
1222 const char *typeStr = "unknown";
1223 switch (type)
1224 {
1225 case MediumType_Normal:
1226 if (!parent.isNull())
1227 typeStr = "normal (differencing)";
1228 else
1229 typeStr = "normal (base)";
1230 break;
1231 case MediumType_Immutable:
1232 typeStr = "immutable";
1233 break;
1234 case MediumType_Writethrough:
1235 typeStr = "writethrough";
1236 break;
1237 }
1238 RTPrintf("Type: %s\n", typeStr);
1239
1240 Bstr format;
1241 hardDisk->COMGETTER(Format)(format.asOutParam());
1242 RTPrintf("Storage format: %lS\n", format.raw());
1243
1244 if (!unknown)
1245 {
1246 com::SafeArray<BSTR> machineIds;
1247 hardDisk->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(machineIds));
1248 for (size_t j = 0; j < machineIds.size(); ++ j)
1249 {
1250 ComPtr<IMachine> machine;
1251 CHECK_ERROR(a->virtualBox, GetMachine(machineIds[j], machine.asOutParam()));
1252 ASSERT(machine);
1253 Bstr name;
1254 machine->COMGETTER(Name)(name.asOutParam());
1255 machine->COMGETTER(Id)(uuid.asOutParam());
1256 RTPrintf("%s%lS (UUID: %lS)\n",
1257 j == 0 ? "In use by VMs: " : " ",
1258 name.raw(), machineIds[j]);
1259 }
1260 /// @todo NEWMEDIA check usage in snapshots too
1261 /// @todo NEWMEDIA also list children
1262 }
1263
1264 Bstr loc;
1265 hardDisk->COMGETTER(Location)(loc.asOutParam());
1266 RTPrintf("Location: %lS\n", loc.raw());
1267
1268 /* print out information specific for differencing hard disks */
1269 if (!parent.isNull())
1270 {
1271 BOOL autoReset = FALSE;
1272 hardDisk->COMGETTER(AutoReset)(&autoReset);
1273 RTPrintf("Auto-Reset: %s\n", autoReset ? "on" : "off");
1274 }
1275 }
1276 while (0);
1277
1278 if (unknown)
1279 {
1280 /* close the unknown hard disk to forget it again */
1281 hardDisk->Close();
1282 }
1283
1284 return SUCCEEDED(rc) ? 0 : 1;
1285}
1286
1287static const RTGETOPTDEF g_aOpenMediumOptions[] =
1288{
1289 { "disk", 'd', RTGETOPT_REQ_NOTHING },
1290 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
1291 { "floppy", 'f', RTGETOPT_REQ_NOTHING },
1292 { "--type", 't', RTGETOPT_REQ_STRING },
1293 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
1294 { "--uuid", 'u', RTGETOPT_REQ_UUID },
1295 { "--parentuuid", 'p', RTGETOPT_REQ_UUID },
1296};
1297
1298int handleOpenMedium(HandlerArg *a)
1299{
1300 HRESULT rc = S_OK;
1301 int vrc;
1302 enum {
1303 CMD_NONE,
1304 CMD_DISK,
1305 CMD_DVD,
1306 CMD_FLOPPY
1307 } cmd = CMD_NONE;
1308 const char *Filename = NULL;
1309 MediumType_T DiskType = MediumType_Normal;
1310 bool fDiskType = false;
1311 bool fSetImageId = false;
1312 bool fSetParentId = false;
1313 Guid ImageId;
1314 ImageId.clear();
1315 Guid ParentId;
1316 ParentId.clear();
1317
1318 int c;
1319 RTGETOPTUNION ValueUnion;
1320 RTGETOPTSTATE GetState;
1321 // start at 0 because main() has hacked both the argc and argv given to us
1322 RTGetOptInit(&GetState, a->argc, a->argv, g_aOpenMediumOptions, RT_ELEMENTS(g_aOpenMediumOptions),
1323 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1324 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1325 {
1326 switch (c)
1327 {
1328 case 'd': // disk
1329 if (cmd != CMD_NONE)
1330 return errorSyntax(USAGE_OPENMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1331 cmd = CMD_DISK;
1332 break;
1333
1334 case 'D': // DVD
1335 if (cmd != CMD_NONE)
1336 return errorSyntax(USAGE_OPENMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1337 cmd = CMD_DVD;
1338 break;
1339
1340 case 'f': // floppy
1341 if (cmd != CMD_NONE)
1342 return errorSyntax(USAGE_OPENMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1343 cmd = CMD_FLOPPY;
1344 break;
1345
1346 case 't': // --type
1347 vrc = parseDiskType(ValueUnion.psz, &DiskType);
1348 if (RT_FAILURE(vrc))
1349 return errorArgument("Invalid hard disk type '%s'", ValueUnion.psz);
1350 fDiskType = true;
1351 break;
1352
1353 case 'u': // --uuid
1354 ImageId = ValueUnion.Uuid;
1355 fSetImageId = true;
1356 break;
1357
1358 case 'p': // --parentuuid
1359 ParentId = ValueUnion.Uuid;
1360 fSetParentId = true;
1361 break;
1362
1363 case VINF_GETOPT_NOT_OPTION:
1364 if (!Filename)
1365 Filename = ValueUnion.psz;
1366 else
1367 return errorSyntax(USAGE_OPENMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
1368 break;
1369
1370 default:
1371 if (c > 0)
1372 {
1373 if (RT_C_IS_PRINT(c))
1374 return errorSyntax(USAGE_OPENMEDIUM, "Invalid option -%c", c);
1375 else
1376 return errorSyntax(USAGE_OPENMEDIUM, "Invalid option case %i", c);
1377 }
1378 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1379 return errorSyntax(USAGE_OPENMEDIUM, "unknown option: %s\n", ValueUnion.psz);
1380 else if (ValueUnion.pDef)
1381 return errorSyntax(USAGE_OPENMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1382 else
1383 return errorSyntax(USAGE_OPENMEDIUM, "error: %Rrs", c);
1384 }
1385 }
1386
1387 /* check for required options */
1388 if (cmd == CMD_NONE)
1389 return errorSyntax(USAGE_OPENMEDIUM, "Command variant disk/dvd/floppy required");
1390 if (!Filename)
1391 return errorSyntax(USAGE_OPENMEDIUM, "Disk name required");
1392
1393 /** @todo remove this hack!
1394 * First try opening the image as is (using the regular API semantics for
1395 * images with relative path or without path), and if that fails with a
1396 * file related error then try it again with what the client thinks the
1397 * relative path would mean. Requires doing the command twice in certain
1398 * cases. This is an ugly hack and needs to be removed whevever we have a
1399 * chance to clean up the API semantics. */
1400 if (cmd == CMD_DISK)
1401 {
1402 ComPtr<IMedium> hardDisk;
1403 Bstr ImageIdStr = BstrFmt("%RTuuid", &ImageId);
1404 Bstr ParentIdStr = BstrFmt("%RTuuid", &ParentId);
1405 rc = a->virtualBox->OpenHardDisk(Bstr(Filename), AccessMode_ReadWrite, fSetImageId, ImageIdStr, fSetParentId, ParentIdStr, hardDisk.asOutParam());
1406 if (rc == VBOX_E_FILE_ERROR)
1407 {
1408 char szFilenameAbs[RTPATH_MAX] = "";
1409 int irc = RTPathAbs(Filename, szFilenameAbs, sizeof(szFilenameAbs));
1410 if (RT_FAILURE(irc))
1411 {
1412 RTPrintf("Cannot convert filename \"%s\" to absolute path\n", Filename);
1413 return 1;
1414 }
1415 CHECK_ERROR(a->virtualBox, OpenHardDisk(Bstr(szFilenameAbs), AccessMode_ReadWrite, fSetImageId, ImageIdStr, fSetParentId, ParentIdStr, hardDisk.asOutParam()));
1416 }
1417 else if (FAILED(rc))
1418 CHECK_ERROR(a->virtualBox, OpenHardDisk(Bstr(Filename), AccessMode_ReadWrite, fSetImageId, ImageIdStr, fSetParentId, ParentIdStr, hardDisk.asOutParam()));
1419 if (SUCCEEDED(rc) && hardDisk)
1420 {
1421 /* change the type if requested */
1422 if (DiskType != MediumType_Normal)
1423 {
1424 CHECK_ERROR(hardDisk, COMSETTER(Type)(DiskType));
1425 }
1426 }
1427 }
1428 else if (cmd == CMD_DVD)
1429 {
1430 if (fDiskType || fSetParentId)
1431 return errorSyntax(USAGE_OPENMEDIUM, "Invalid option for DVD images");
1432 Bstr ImageIdStr = BstrFmt("%RTuuid", &ImageId);
1433 ComPtr<IMedium> dvdImage;
1434 rc = a->virtualBox->OpenDVDImage(Bstr(Filename), ImageIdStr, dvdImage.asOutParam());
1435 if (rc == VBOX_E_FILE_ERROR)
1436 {
1437 char szFilenameAbs[RTPATH_MAX] = "";
1438 int irc = RTPathAbs(Filename, szFilenameAbs, sizeof(szFilenameAbs));
1439 if (RT_FAILURE(irc))
1440 {
1441 RTPrintf("Cannot convert filename \"%s\" to absolute path\n", Filename);
1442 return 1;
1443 }
1444 CHECK_ERROR(a->virtualBox, OpenDVDImage(Bstr(szFilenameAbs), ImageIdStr, dvdImage.asOutParam()));
1445 }
1446 else if (FAILED(rc))
1447 CHECK_ERROR(a->virtualBox, OpenDVDImage(Bstr(Filename), ImageIdStr, dvdImage.asOutParam()));
1448 }
1449 else if (cmd == CMD_FLOPPY)
1450 {
1451 if (fDiskType || fSetImageId || fSetParentId)
1452 return errorSyntax(USAGE_OPENMEDIUM, "Invalid option for floppy images");
1453 Bstr ImageIdStr = BstrFmt("%RTuuid", &ImageId);
1454 ComPtr<IMedium> floppyImage;
1455 rc = a->virtualBox->OpenFloppyImage(Bstr(Filename), ImageIdStr, floppyImage.asOutParam());
1456 if (rc == VBOX_E_FILE_ERROR)
1457 {
1458 char szFilenameAbs[RTPATH_MAX] = "";
1459 int irc = RTPathAbs(Filename, szFilenameAbs, sizeof(szFilenameAbs));
1460 if (RT_FAILURE(irc))
1461 {
1462 RTPrintf("Cannot convert filename \"%s\" to absolute path\n", Filename);
1463 return 1;
1464 }
1465 CHECK_ERROR(a->virtualBox, OpenFloppyImage(Bstr(szFilenameAbs), ImageIdStr, floppyImage.asOutParam()));
1466 }
1467 else if (FAILED(rc))
1468 CHECK_ERROR(a->virtualBox, OpenFloppyImage(Bstr(Filename), ImageIdStr, floppyImage.asOutParam()));
1469 }
1470
1471 return SUCCEEDED(rc) ? 0 : 1;
1472}
1473
1474static const RTGETOPTDEF g_aCloseMediumOptions[] =
1475{
1476 { "disk", 'd', RTGETOPT_REQ_NOTHING },
1477 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
1478 { "floppy", 'f', RTGETOPT_REQ_NOTHING },
1479 { "--delete", 'r', RTGETOPT_REQ_NOTHING },
1480};
1481
1482int handleCloseMedium(HandlerArg *a)
1483{
1484 HRESULT rc = S_OK;
1485 enum {
1486 CMD_NONE,
1487 CMD_DISK,
1488 CMD_DVD,
1489 CMD_FLOPPY
1490 } cmd = CMD_NONE;
1491 const char *FilenameOrUuid = NULL;
1492 bool fDelete = false;
1493
1494 int c;
1495 RTGETOPTUNION ValueUnion;
1496 RTGETOPTSTATE GetState;
1497 // start at 0 because main() has hacked both the argc and argv given to us
1498 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloseMediumOptions, RT_ELEMENTS(g_aCloseMediumOptions),
1499 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1500 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1501 {
1502 switch (c)
1503 {
1504 case 'd': // disk
1505 if (cmd != CMD_NONE)
1506 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1507 cmd = CMD_DISK;
1508 break;
1509
1510 case 'D': // DVD
1511 if (cmd != CMD_NONE)
1512 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1513 cmd = CMD_DVD;
1514 break;
1515
1516 case 'f': // floppy
1517 if (cmd != CMD_NONE)
1518 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1519 cmd = CMD_FLOPPY;
1520 break;
1521
1522 case 'r': // --delete
1523 fDelete = true;
1524 break;
1525
1526 case VINF_GETOPT_NOT_OPTION:
1527 if (!FilenameOrUuid)
1528 FilenameOrUuid = ValueUnion.psz;
1529 else
1530 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
1531 break;
1532
1533 default:
1534 if (c > 0)
1535 {
1536 if (RT_C_IS_PRINT(c))
1537 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option -%c", c);
1538 else
1539 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option case %i", c);
1540 }
1541 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1542 return errorSyntax(USAGE_CLOSEMEDIUM, "unknown option: %s\n", ValueUnion.psz);
1543 else if (ValueUnion.pDef)
1544 return errorSyntax(USAGE_CLOSEMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1545 else
1546 return errorSyntax(USAGE_CLOSEMEDIUM, "error: %Rrs", c);
1547 }
1548 }
1549
1550 /* check for required options */
1551 if (cmd == CMD_NONE)
1552 return errorSyntax(USAGE_CLOSEMEDIUM, "Command variant disk/dvd/floppy required");
1553 if (!FilenameOrUuid)
1554 return errorSyntax(USAGE_CLOSEMEDIUM, "Disk name or UUID required");
1555
1556 ComPtr<IMedium> medium;
1557
1558 /* first guess is that it's a UUID */
1559 Bstr uuid(FilenameOrUuid);
1560
1561 if (cmd == CMD_DISK)
1562 {
1563 rc = a->virtualBox->GetHardDisk(uuid, medium.asOutParam());
1564 /* not a UUID or not registered? Then it must be a filename */
1565 if (!medium)
1566 {
1567 CHECK_ERROR(a->virtualBox, FindHardDisk(Bstr(FilenameOrUuid), medium.asOutParam()));
1568 }
1569 }
1570 else
1571 if (cmd == CMD_DVD)
1572 {
1573 rc = a->virtualBox->GetDVDImage(uuid, medium.asOutParam());
1574 /* not a UUID or not registered? Then it must be a filename */
1575 if (!medium)
1576 {
1577 CHECK_ERROR(a->virtualBox, FindDVDImage(Bstr(FilenameOrUuid), medium.asOutParam()));
1578 }
1579 }
1580 else
1581 if (cmd == CMD_FLOPPY)
1582 {
1583 rc = a->virtualBox->GetFloppyImage(uuid, medium.asOutParam());
1584 /* not a UUID or not registered? Then it must be a filename */
1585 if (!medium)
1586 {
1587 CHECK_ERROR(a->virtualBox, FindFloppyImage(Bstr(FilenameOrUuid), medium.asOutParam()));
1588 }
1589 }
1590
1591 if (SUCCEEDED(rc) && medium)
1592 {
1593 if (fDelete)
1594 {
1595 ComPtr<IProgress> progress;
1596 CHECK_ERROR(medium, DeleteStorage(progress.asOutParam()));
1597 if (SUCCEEDED(rc))
1598 {
1599 rc = showProgress(progress);
1600 if (FAILED(rc))
1601 {
1602 com::ProgressErrorInfo info(progress);
1603 if (info.isBasicAvailable())
1604 RTPrintf("Error: failed to delete medium. Error message: %lS\n", info.getText().raw());
1605 else
1606 RTPrintf("Error: failed to delete medium. No error message available!\n");
1607 }
1608 }
1609 else
1610 RTPrintf("Error: failed to delete medium. Error code %Rrc\n", rc);
1611 }
1612 CHECK_ERROR(medium, Close());
1613 }
1614
1615 return SUCCEEDED(rc) ? 0 : 1;
1616}
1617#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