VirtualBox

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

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

Main: new method Medium::RefreshState() which refreshes medium state; make the ::state attribute return the state only without refreshing

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