VirtualBox

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

Last change on this file since 19073 was 19037, checked in by vboxsync, 16 years ago

VBoxManage/modifyhd: implement compacting hard disks, printing an appropriate error if not available.

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