VirtualBox

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

Last change on this file since 44376 was 44376, checked in by vboxsync, 12 years ago

The changes inside loops where enumeration MediumVariant is converted. For correct work under both systems Windows and Linux because enumeration is defined differently on these systems.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.4 KB
Line 
1/* $Id: VBoxManageDisk.cpp 44376 2013-01-25 13:35:31Z vboxsync $ */
2/** @file
3 * VBoxManage - The disk related commands.
4 */
5
6/*
7 * Copyright (C) 2006-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#ifndef VBOX_ONLY_DOCS
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#include <VBox/com/com.h>
24#include <VBox/com/array.h>
25#include <VBox/com/ErrorInfo.h>
26#include <VBox/com/errorprint.h>
27#include <VBox/com/VirtualBox.h>
28
29#include <iprt/asm.h>
30#include <iprt/file.h>
31#include <iprt/path.h>
32#include <iprt/param.h>
33#include <iprt/stream.h>
34#include <iprt/string.h>
35#include <iprt/ctype.h>
36#include <iprt/getopt.h>
37#include <VBox/log.h>
38#include <VBox/vd.h>
39
40#include "VBoxManage.h"
41using namespace com;
42
43
44// funcs
45///////////////////////////////////////////////////////////////////////////////
46
47
48static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va)
49{
50 RTMsgError(pszFormat, va);
51 RTMsgError("Error code %Rrc at %s(%u) in function %s", rc, RT_SRC_POS_ARGS);
52}
53
54
55static int parseDiskVariant(const char *psz, MediumVariant_T *pDiskVariant)
56{
57 int rc = VINF_SUCCESS;
58 unsigned DiskVariant = (unsigned)(*pDiskVariant);
59 while (psz && *psz && RT_SUCCESS(rc))
60 {
61 size_t len;
62 const char *pszComma = strchr(psz, ',');
63 if (pszComma)
64 len = pszComma - psz;
65 else
66 len = strlen(psz);
67 if (len > 0)
68 {
69 // Parsing is intentionally inconsistent: "standard" resets the
70 // variant, whereas the other flags are cumulative.
71 if (!RTStrNICmp(psz, "standard", len))
72 DiskVariant = MediumVariant_Standard;
73 else if ( !RTStrNICmp(psz, "fixed", len)
74 || !RTStrNICmp(psz, "static", len))
75 DiskVariant |= MediumVariant_Fixed;
76 else if (!RTStrNICmp(psz, "Diff", len))
77 DiskVariant |= MediumVariant_Diff;
78 else if (!RTStrNICmp(psz, "split2g", len))
79 DiskVariant |= MediumVariant_VmdkSplit2G;
80 else if ( !RTStrNICmp(psz, "stream", len)
81 || !RTStrNICmp(psz, "streamoptimized", len))
82 DiskVariant |= MediumVariant_VmdkStreamOptimized;
83 else if (!RTStrNICmp(psz, "esx", len))
84 DiskVariant |= MediumVariant_VmdkESX;
85 else
86 rc = VERR_PARSE_ERROR;
87 }
88 if (pszComma)
89 psz += len + 1;
90 else
91 psz += len;
92 }
93
94 if (RT_SUCCESS(rc))
95 *pDiskVariant = (MediumVariant_T)DiskVariant;
96 return rc;
97}
98
99int parseDiskType(const char *psz, MediumType_T *pDiskType)
100{
101 int rc = VINF_SUCCESS;
102 MediumType_T DiskType = MediumType_Normal;
103 if (!RTStrICmp(psz, "normal"))
104 DiskType = MediumType_Normal;
105 else if (!RTStrICmp(psz, "immutable"))
106 DiskType = MediumType_Immutable;
107 else if (!RTStrICmp(psz, "writethrough"))
108 DiskType = MediumType_Writethrough;
109 else if (!RTStrICmp(psz, "shareable"))
110 DiskType = MediumType_Shareable;
111 else if (!RTStrICmp(psz, "readonly"))
112 DiskType = MediumType_Readonly;
113 else if (!RTStrICmp(psz, "multiattach"))
114 DiskType = MediumType_MultiAttach;
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 */
124int 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
151HRESULT openMedium(HandlerArg *a, const char *pszFilenameOrUuid,
152 DeviceType_T enmDevType, AccessMode_T enmAccessMode,
153 ComPtr<IMedium> &pMedium, bool fForceNewUuidOnOpen,
154 bool fSilent)
155{
156 HRESULT rc;
157 Guid id(pszFilenameOrUuid);
158 char szFilenameAbs[RTPATH_MAX] = "";
159
160 /* If it is no UUID, convert the filename to an absolute one. */
161 if (!id.isValid())
162 {
163 int irc = RTPathAbs(pszFilenameOrUuid, szFilenameAbs, sizeof(szFilenameAbs));
164 if (RT_FAILURE(irc))
165 {
166 if (!fSilent)
167 RTMsgError("Cannot convert filename \"%s\" to absolute path", pszFilenameOrUuid);
168 return E_FAIL;
169 }
170 pszFilenameOrUuid = szFilenameAbs;
171 }
172
173 if (!fSilent)
174 CHECK_ERROR(a->virtualBox, OpenMedium(Bstr(pszFilenameOrUuid).raw(),
175 enmDevType,
176 enmAccessMode,
177 fForceNewUuidOnOpen,
178 pMedium.asOutParam()));
179 else
180 rc = a->virtualBox->OpenMedium(Bstr(pszFilenameOrUuid).raw(),
181 enmDevType,
182 enmAccessMode,
183 fForceNewUuidOnOpen,
184 pMedium.asOutParam());
185
186 return rc;
187}
188
189static HRESULT createHardDisk(HandlerArg *a, const char *pszFormat,
190 const char *pszFilename, ComPtr<IMedium> &pMedium)
191{
192 HRESULT rc;
193 char szFilenameAbs[RTPATH_MAX] = "";
194
195 /** @todo laziness shortcut. should really check the MediumFormatCapabilities */
196 if (RTStrICmp(pszFormat, "iSCSI"))
197 {
198 int irc = RTPathAbs(pszFilename, szFilenameAbs, sizeof(szFilenameAbs));
199 if (RT_FAILURE(irc))
200 {
201 RTMsgError("Cannot convert filename \"%s\" to absolute path", pszFilename);
202 return E_FAIL;
203 }
204 pszFilename = szFilenameAbs;
205 }
206
207 CHECK_ERROR(a->virtualBox, CreateHardDisk(Bstr(pszFormat).raw(),
208 Bstr(pszFilename).raw(),
209 pMedium.asOutParam()));
210 return rc;
211}
212
213static const RTGETOPTDEF g_aCreateHardDiskOptions[] =
214{
215 { "--filename", 'f', RTGETOPT_REQ_STRING },
216 { "-filename", 'f', RTGETOPT_REQ_STRING }, // deprecated
217 { "--diffparent", 'd', RTGETOPT_REQ_STRING },
218 { "--size", 's', RTGETOPT_REQ_UINT64 },
219 { "-size", 's', RTGETOPT_REQ_UINT64 }, // deprecated
220 { "--sizebyte", 'S', RTGETOPT_REQ_UINT64 },
221 { "--format", 'o', RTGETOPT_REQ_STRING },
222 { "-format", 'o', RTGETOPT_REQ_STRING }, // deprecated
223 { "--static", 'F', RTGETOPT_REQ_NOTHING },
224 { "-static", 'F', RTGETOPT_REQ_NOTHING }, // deprecated
225 { "--variant", 'm', RTGETOPT_REQ_STRING },
226 { "-variant", 'm', RTGETOPT_REQ_STRING }, // deprecated
227};
228
229int handleCreateHardDisk(HandlerArg *a)
230{
231 HRESULT rc;
232 int vrc;
233 const char *filename = NULL;
234 const char *diffparent = NULL;
235 uint64_t size = 0;
236 const char *format = NULL;
237 bool fBase = true;
238 MediumVariant_T DiskVariant = MediumVariant_Standard;
239
240 int c;
241 RTGETOPTUNION ValueUnion;
242 RTGETOPTSTATE GetState;
243 // start at 0 because main() has hacked both the argc and argv given to us
244 RTGetOptInit(&GetState, a->argc, a->argv, g_aCreateHardDiskOptions, RT_ELEMENTS(g_aCreateHardDiskOptions),
245 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
246 while ((c = RTGetOpt(&GetState, &ValueUnion)))
247 {
248 switch (c)
249 {
250 case 'f': // --filename
251 filename = ValueUnion.psz;
252 break;
253
254 case 'd': // --diffparent
255 diffparent = ValueUnion.psz;
256 fBase = false;
257 break;
258
259 case 's': // --size
260 size = ValueUnion.u64 * _1M;
261 break;
262
263 case 'S': // --sizebyte
264 size = ValueUnion.u64;
265 break;
266
267 case 'o': // --format
268 format = ValueUnion.psz;
269 break;
270
271 case 'F': // --static ("fixed"/"flat")
272 {
273 unsigned uDiskVariant = (unsigned)DiskVariant;
274 uDiskVariant |= MediumVariant_Fixed;
275 DiskVariant = (MediumVariant_T)uDiskVariant;
276 break;
277 }
278
279 case 'm': // --variant
280 vrc = parseDiskVariant(ValueUnion.psz, &DiskVariant);
281 if (RT_FAILURE(vrc))
282 return errorArgument("Invalid hard disk variant '%s'", ValueUnion.psz);
283 break;
284
285 case VINF_GETOPT_NOT_OPTION:
286 return errorSyntax(USAGE_CREATEHD, "Invalid parameter '%s'", ValueUnion.psz);
287
288 default:
289 if (c > 0)
290 {
291 if (RT_C_IS_PRINT(c))
292 return errorSyntax(USAGE_CREATEHD, "Invalid option -%c", c);
293 else
294 return errorSyntax(USAGE_CREATEHD, "Invalid option case %i", c);
295 }
296 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
297 return errorSyntax(USAGE_CREATEHD, "unknown option: %s\n", ValueUnion.psz);
298 else if (ValueUnion.pDef)
299 return errorSyntax(USAGE_CREATEHD, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
300 else
301 return errorSyntax(USAGE_CREATEHD, "error: %Rrs", c);
302 }
303 }
304
305 /* check the outcome */
306 ComPtr<IMedium> parentHardDisk;
307 if (fBase)
308 {
309 if ( !filename
310 || !*filename
311 || size == 0)
312 return errorSyntax(USAGE_CREATEHD, "Parameters --filename and --size are required");
313 if (!format || !*format)
314 format = "VDI";
315 }
316 else
317 {
318 if ( !filename
319 || !*filename)
320 return errorSyntax(USAGE_CREATEHD, "Parameters --filename is required");
321 size = 0;
322 DiskVariant = MediumVariant_Diff;
323 if (!format || !*format)
324 {
325 const char *pszExt = RTPathExt(filename);
326 /* Skip over . if there is an extension. */
327 if (pszExt)
328 pszExt++;
329 if (!pszExt || !*pszExt)
330 format = "VDI";
331 else
332 format = pszExt;
333 }
334 rc = openMedium(a, diffparent, DeviceType_HardDisk,
335 AccessMode_ReadWrite, parentHardDisk,
336 false /* fForceNewUuidOnOpen */, false /* fSilent */);
337 if (FAILED(rc))
338 return 1;
339 if (parentHardDisk.isNull())
340 {
341 RTMsgError("Invalid parent hard disk reference, avoiding crash");
342 return 1;
343 }
344 MediumState_T state;
345 CHECK_ERROR(parentHardDisk, COMGETTER(State)(&state));
346 if (FAILED(rc))
347 return 1;
348 if (state == MediumState_Inaccessible)
349 {
350 CHECK_ERROR(parentHardDisk, RefreshState(&state));
351 if (FAILED(rc))
352 return 1;
353 }
354 }
355 /* check for filename extension */
356 /** @todo use IMediumFormat to cover all extensions generically */
357 Utf8Str strName(filename);
358 if (!RTPathHaveExt(strName.c_str()))
359 {
360 Utf8Str strFormat(format);
361 if (strFormat.compare("vmdk", RTCString::CaseInsensitive) == 0)
362 strName.append(".vmdk");
363 else if (strFormat.compare("vhd", RTCString::CaseInsensitive) == 0)
364 strName.append(".vhd");
365 else
366 strName.append(".vdi");
367 filename = strName.c_str();
368 }
369
370 ComPtr<IMedium> hardDisk;
371 rc = createHardDisk(a, format, filename, hardDisk);
372 if (SUCCEEDED(rc) && hardDisk)
373 {
374 ComPtr<IProgress> progress;
375 com::SafeArray<MediumVariant_T> l_variants(sizeof(MediumVariant_T)*8);
376
377 for (ulong i = 0; i < l_variants.size(); ++i)
378 {
379 ulong temp = DiskVariant;
380 temp &= 1<<i;
381 l_variants [i] = (MediumVariant_T)temp;
382 }
383
384 if (fBase)
385 CHECK_ERROR(hardDisk, CreateBaseStorage(size, ComSafeArrayAsInParam(l_variants), progress.asOutParam()));
386 else
387 CHECK_ERROR(parentHardDisk, CreateDiffStorage(hardDisk, ComSafeArrayAsInParam(l_variants), progress.asOutParam()));
388 if (SUCCEEDED(rc) && progress)
389 {
390 rc = showProgress(progress);
391 CHECK_PROGRESS_ERROR(progress, ("Failed to create hard disk"));
392 if (SUCCEEDED(rc))
393 {
394 Bstr uuid;
395 CHECK_ERROR(hardDisk, COMGETTER(Id)(uuid.asOutParam()));
396 RTPrintf("Disk image created. UUID: %s\n", Utf8Str(uuid).c_str());
397 }
398 }
399
400 CHECK_ERROR(hardDisk, Close());
401 }
402 return SUCCEEDED(rc) ? 0 : 1;
403}
404
405static const RTGETOPTDEF g_aModifyHardDiskOptions[] =
406{
407 { "--type", 't', RTGETOPT_REQ_STRING },
408 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
409 { "settype", 't', RTGETOPT_REQ_STRING }, // deprecated
410 { "--autoreset", 'z', RTGETOPT_REQ_STRING },
411 { "-autoreset", 'z', RTGETOPT_REQ_STRING }, // deprecated
412 { "autoreset", 'z', RTGETOPT_REQ_STRING }, // deprecated
413 { "--compact", 'c', RTGETOPT_REQ_NOTHING },
414 { "-compact", 'c', RTGETOPT_REQ_NOTHING }, // deprecated
415 { "compact", 'c', RTGETOPT_REQ_NOTHING }, // deprecated
416 { "--resize", 'r', RTGETOPT_REQ_UINT64 },
417 { "--resizebyte", 'R', RTGETOPT_REQ_UINT64 }
418};
419
420int handleModifyHardDisk(HandlerArg *a)
421{
422 HRESULT rc;
423 int vrc;
424 ComPtr<IMedium> hardDisk;
425 MediumType_T DiskType;
426 bool AutoReset = false;
427 bool fModifyDiskType = false, fModifyAutoReset = false, fModifyCompact = false;
428 bool fModifyResize = false;
429 uint64_t cbResize = 0;
430 const char *FilenameOrUuid = NULL;
431
432 int c;
433 RTGETOPTUNION ValueUnion;
434 RTGETOPTSTATE GetState;
435 // start at 0 because main() has hacked both the argc and argv given to us
436 RTGetOptInit(&GetState, a->argc, a->argv, g_aModifyHardDiskOptions, RT_ELEMENTS(g_aModifyHardDiskOptions),
437 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
438 while ((c = RTGetOpt(&GetState, &ValueUnion)))
439 {
440 switch (c)
441 {
442 case 't': // --type
443 vrc = parseDiskType(ValueUnion.psz, &DiskType);
444 if (RT_FAILURE(vrc))
445 return errorArgument("Invalid hard disk type '%s'", ValueUnion.psz);
446 fModifyDiskType = true;
447 break;
448
449 case 'z': // --autoreset
450 vrc = parseBool(ValueUnion.psz, &AutoReset);
451 if (RT_FAILURE(vrc))
452 return errorArgument("Invalid autoreset parameter '%s'", ValueUnion.psz);
453 fModifyAutoReset = true;
454 break;
455
456 case 'c': // --compact
457 fModifyCompact = true;
458 break;
459
460 case 'r': // --resize
461 cbResize = ValueUnion.u64 * _1M;
462 fModifyResize = true;
463 break;
464
465 case 'R': // --resizebyte
466 cbResize = ValueUnion.u64;
467 fModifyResize = true;
468 break;
469
470 case VINF_GETOPT_NOT_OPTION:
471 if (!FilenameOrUuid)
472 FilenameOrUuid = ValueUnion.psz;
473 else
474 return errorSyntax(USAGE_MODIFYHD, "Invalid parameter '%s'", ValueUnion.psz);
475 break;
476
477 default:
478 if (c > 0)
479 {
480 if (RT_C_IS_PRINT(c))
481 return errorSyntax(USAGE_MODIFYHD, "Invalid option -%c", c);
482 else
483 return errorSyntax(USAGE_MODIFYHD, "Invalid option case %i", c);
484 }
485 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
486 return errorSyntax(USAGE_MODIFYHD, "unknown option: %s\n", ValueUnion.psz);
487 else if (ValueUnion.pDef)
488 return errorSyntax(USAGE_MODIFYHD, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
489 else
490 return errorSyntax(USAGE_MODIFYHD, "error: %Rrs", c);
491 }
492 }
493
494 if (!FilenameOrUuid)
495 return errorSyntax(USAGE_MODIFYHD, "Disk name or UUID required");
496
497 if (!fModifyDiskType && !fModifyAutoReset && !fModifyCompact && !fModifyResize)
498 return errorSyntax(USAGE_MODIFYHD, "No operation specified");
499
500 /* Always open the medium if necessary, there is no other way. */
501 rc = openMedium(a, FilenameOrUuid, DeviceType_HardDisk,
502 AccessMode_ReadWrite, hardDisk,
503 false /* fForceNewUuidOnOpen */, false /* fSilent */);
504 if (FAILED(rc))
505 return 1;
506 if (hardDisk.isNull())
507 {
508 RTMsgError("Invalid hard disk reference, avoiding crash");
509 return 1;
510 }
511
512 if (fModifyDiskType)
513 {
514 MediumType_T hddType;
515 CHECK_ERROR(hardDisk, COMGETTER(Type)(&hddType));
516
517 if (hddType != DiskType)
518 CHECK_ERROR(hardDisk, COMSETTER(Type)(DiskType));
519 }
520
521 if (fModifyAutoReset)
522 {
523 CHECK_ERROR(hardDisk, COMSETTER(AutoReset)(AutoReset));
524 }
525
526 if (fModifyCompact)
527 {
528 ComPtr<IProgress> progress;
529 CHECK_ERROR(hardDisk, Compact(progress.asOutParam()));
530 if (SUCCEEDED(rc))
531 rc = showProgress(progress);
532 if (FAILED(rc))
533 {
534 if (rc == E_NOTIMPL)
535 RTMsgError("Compact hard disk operation is not implemented!");
536 else if (rc == VBOX_E_NOT_SUPPORTED)
537 RTMsgError("Compact hard disk operation for this format is not implemented yet!");
538 else if (!progress.isNull())
539 CHECK_PROGRESS_ERROR(progress, ("Failed to compact hard disk"));
540 else
541 RTMsgError("Failed to compact hard disk!");
542 }
543 }
544
545 if (fModifyResize)
546 {
547 ComPtr<IProgress> progress;
548 CHECK_ERROR(hardDisk, Resize(cbResize, progress.asOutParam()));
549 if (SUCCEEDED(rc))
550 rc = showProgress(progress);
551 if (FAILED(rc))
552 {
553 if (rc == E_NOTIMPL)
554 RTMsgError("Resize hard disk operation is not implemented!");
555 else if (rc == VBOX_E_NOT_SUPPORTED)
556 RTMsgError("Resize hard disk operation for this format is not implemented yet!");
557 else
558 CHECK_PROGRESS_ERROR(progress, ("Failed to resize hard disk"));
559 }
560 }
561
562 return SUCCEEDED(rc) ? 0 : 1;
563}
564
565static const RTGETOPTDEF g_aCloneHardDiskOptions[] =
566{
567 { "--format", 'o', RTGETOPT_REQ_STRING },
568 { "-format", 'o', RTGETOPT_REQ_STRING },
569 { "--static", 'F', RTGETOPT_REQ_NOTHING },
570 { "-static", 'F', RTGETOPT_REQ_NOTHING },
571 { "--existing", 'E', RTGETOPT_REQ_NOTHING },
572 { "--variant", 'm', RTGETOPT_REQ_STRING },
573 { "-variant", 'm', RTGETOPT_REQ_STRING },
574};
575
576int handleCloneHardDisk(HandlerArg *a)
577{
578 HRESULT rc;
579 int vrc;
580 const char *pszSrc = NULL;
581 const char *pszDst = NULL;
582 Bstr format;
583 MediumVariant_T DiskVariant = MediumVariant_Standard;
584 bool fExisting = false;
585
586 int c;
587 RTGETOPTUNION ValueUnion;
588 RTGETOPTSTATE GetState;
589 // start at 0 because main() has hacked both the argc and argv given to us
590 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloneHardDiskOptions, RT_ELEMENTS(g_aCloneHardDiskOptions),
591 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
592 while ((c = RTGetOpt(&GetState, &ValueUnion)))
593 {
594 switch (c)
595 {
596 case 'o': // --format
597 format = ValueUnion.psz;
598 break;
599
600 case 'F': // --static
601 {
602 unsigned uDiskVariant = (unsigned)DiskVariant;
603 uDiskVariant |= MediumVariant_Fixed;
604 DiskVariant = (MediumVariant_T)uDiskVariant;
605 break;
606 }
607
608 case 'E': // --existing
609 fExisting = true;
610 break;
611
612 case 'm': // --variant
613 vrc = parseDiskVariant(ValueUnion.psz, &DiskVariant);
614 if (RT_FAILURE(vrc))
615 return errorArgument("Invalid hard disk variant '%s'", ValueUnion.psz);
616 break;
617
618 case VINF_GETOPT_NOT_OPTION:
619 if (!pszSrc)
620 pszSrc = ValueUnion.psz;
621 else if (!pszDst)
622 pszDst = ValueUnion.psz;
623 else
624 return errorSyntax(USAGE_CLONEHD, "Invalid parameter '%s'", ValueUnion.psz);
625 break;
626
627 default:
628 if (c > 0)
629 {
630 if (RT_C_IS_GRAPH(c))
631 return errorSyntax(USAGE_CLONEHD, "unhandled option: -%c", c);
632 else
633 return errorSyntax(USAGE_CLONEHD, "unhandled option: %i", c);
634 }
635 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
636 return errorSyntax(USAGE_CLONEHD, "unknown option: %s", ValueUnion.psz);
637 else if (ValueUnion.pDef)
638 return errorSyntax(USAGE_CLONEHD, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
639 else
640 return errorSyntax(USAGE_CLONEHD, "error: %Rrs", c);
641 }
642 }
643
644 if (!pszSrc)
645 return errorSyntax(USAGE_CLONEHD, "Mandatory UUID or input file parameter missing");
646 if (!pszDst)
647 return errorSyntax(USAGE_CLONEHD, "Mandatory output file parameter missing");
648 if (fExisting && (!format.isEmpty() || DiskVariant != MediumType_Normal))
649 return errorSyntax(USAGE_CLONEHD, "Specified options which cannot be used with --existing");
650
651 ComPtr<IMedium> srcDisk;
652 ComPtr<IMedium> dstDisk;
653
654 rc = openMedium(a, pszSrc, DeviceType_HardDisk, AccessMode_ReadOnly,
655 srcDisk, false /* fForceNewUuidOnOpen */,
656 false /* fSilent */);
657 if (FAILED(rc))
658 return 1;
659
660 do
661 {
662 /* open/create destination hard disk */
663 if (fExisting)
664 {
665 rc = openMedium(a, pszDst, DeviceType_HardDisk,
666 AccessMode_ReadWrite, dstDisk,
667 false /* fForceNewUuidOnOpen */,
668 false /* fSilent */);
669 if (FAILED(rc))
670 break;
671
672 /* Perform accessibility check now. */
673 MediumState_T state;
674 CHECK_ERROR_BREAK(dstDisk, RefreshState(&state));
675 CHECK_ERROR_BREAK(dstDisk, COMGETTER(Format)(format.asOutParam()));
676 }
677 else
678 {
679 /* use the format of the source hard disk if unspecified */
680 if (format.isEmpty())
681 CHECK_ERROR_BREAK(srcDisk, COMGETTER(Format)(format.asOutParam()));
682 rc = createHardDisk(a, Utf8Str(format).c_str(), pszDst, dstDisk);
683 if (FAILED(rc))
684 break;
685 }
686
687 ComPtr<IProgress> progress;
688 com::SafeArray<MediumVariant_T> l_variants(sizeof(MediumVariant_T)*8);
689
690 for (ulong i = 0; i < l_variants.size(); ++i)
691 {
692 ulong temp = DiskVariant;
693 temp &= 1<<i;
694 l_variants [i] = (MediumVariant_T)temp;
695 }
696
697 CHECK_ERROR_BREAK(srcDisk, CloneTo(dstDisk, ComSafeArrayAsInParam(l_variants), NULL, progress.asOutParam()));
698
699 rc = showProgress(progress);
700 CHECK_PROGRESS_ERROR_BREAK(progress, ("Failed to clone hard disk"));
701
702 Bstr uuid;
703 CHECK_ERROR_BREAK(dstDisk, COMGETTER(Id)(uuid.asOutParam()));
704
705 RTPrintf("Clone hard disk created in format '%ls'. UUID: %s\n",
706 format.raw(), Utf8Str(uuid).c_str());
707 }
708 while (0);
709
710 return SUCCEEDED(rc) ? 0 : 1;
711}
712
713static const RTGETOPTDEF g_aConvertFromRawHardDiskOptions[] =
714{
715 { "--format", 'o', RTGETOPT_REQ_STRING },
716 { "-format", 'o', RTGETOPT_REQ_STRING },
717 { "--static", 'F', RTGETOPT_REQ_NOTHING },
718 { "-static", 'F', RTGETOPT_REQ_NOTHING },
719 { "--variant", 'm', RTGETOPT_REQ_STRING },
720 { "-variant", 'm', RTGETOPT_REQ_STRING },
721 { "--uuid", 'u', RTGETOPT_REQ_STRING },
722};
723
724RTEXITCODE handleConvertFromRaw(int argc, char *argv[])
725{
726 int rc = VINF_SUCCESS;
727 bool fReadFromStdIn = false;
728 const char *format = "VDI";
729 const char *srcfilename = NULL;
730 const char *dstfilename = NULL;
731 const char *filesize = NULL;
732 unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
733 void *pvBuf = NULL;
734 RTUUID uuid;
735 PCRTUUID pUuid = NULL;
736
737 int c;
738 RTGETOPTUNION ValueUnion;
739 RTGETOPTSTATE GetState;
740 // start at 0 because main() has hacked both the argc and argv given to us
741 RTGetOptInit(&GetState, argc, argv, g_aConvertFromRawHardDiskOptions, RT_ELEMENTS(g_aConvertFromRawHardDiskOptions),
742 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
743 while ((c = RTGetOpt(&GetState, &ValueUnion)))
744 {
745 switch (c)
746 {
747 case 'u': // --uuid
748 if (RT_FAILURE(RTUuidFromStr(&uuid, ValueUnion.psz)))
749 return errorSyntax(USAGE_CONVERTFROMRAW, "Invalid UUID '%s'", ValueUnion.psz);
750 pUuid = &uuid;
751 break;
752 case 'o': // --format
753 format = ValueUnion.psz;
754 break;
755
756 case 'm': // --variant
757 {
758 MediumVariant_T DiskVariant = MediumVariant_Standard;
759 rc = parseDiskVariant(ValueUnion.psz, &DiskVariant);
760 if (RT_FAILURE(rc))
761 return errorArgument("Invalid hard disk variant '%s'", ValueUnion.psz);
762 /// @todo cleaner solution than assuming 1:1 mapping?
763 uImageFlags = (unsigned)DiskVariant;
764 break;
765 }
766 case VINF_GETOPT_NOT_OPTION:
767 if (!srcfilename)
768 {
769 srcfilename = ValueUnion.psz;
770 fReadFromStdIn = !strcmp(srcfilename, "stdin");
771 }
772 else if (!dstfilename)
773 dstfilename = ValueUnion.psz;
774 else if (fReadFromStdIn && !filesize)
775 filesize = ValueUnion.psz;
776 else
777 return errorSyntax(USAGE_CONVERTFROMRAW, "Invalid parameter '%s'", ValueUnion.psz);
778 break;
779
780 default:
781 return errorGetOpt(USAGE_CONVERTFROMRAW, c, &ValueUnion);
782 }
783 }
784
785 if (!srcfilename || !dstfilename || (fReadFromStdIn && !filesize))
786 return errorSyntax(USAGE_CONVERTFROMRAW, "Incorrect number of parameters");
787 RTStrmPrintf(g_pStdErr, "Converting from raw image file=\"%s\" to file=\"%s\"...\n",
788 srcfilename, dstfilename);
789
790 PVBOXHDD pDisk = NULL;
791
792 PVDINTERFACE pVDIfs = NULL;
793 VDINTERFACEERROR vdInterfaceError;
794 vdInterfaceError.pfnError = handleVDError;
795 vdInterfaceError.pfnMessage = NULL;
796
797 rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
798 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
799 AssertRC(rc);
800
801 /* open raw image file. */
802 RTFILE File;
803 if (fReadFromStdIn)
804 rc = RTFileFromNative(&File, RTFILE_NATIVE_STDIN);
805 else
806 rc = RTFileOpen(&File, srcfilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
807 if (RT_FAILURE(rc))
808 {
809 RTMsgError("Cannot open file \"%s\": %Rrc", srcfilename, rc);
810 goto out;
811 }
812
813 uint64_t cbFile;
814 /* get image size. */
815 if (fReadFromStdIn)
816 cbFile = RTStrToUInt64(filesize);
817 else
818 rc = RTFileGetSize(File, &cbFile);
819 if (RT_FAILURE(rc))
820 {
821 RTMsgError("Cannot get image size for file \"%s\": %Rrc", srcfilename, rc);
822 goto out;
823 }
824
825 RTStrmPrintf(g_pStdErr, "Creating %s image with size %RU64 bytes (%RU64MB)...\n",
826 (uImageFlags & VD_IMAGE_FLAGS_FIXED) ? "fixed" : "dynamic", cbFile, (cbFile + _1M - 1) / _1M);
827 char pszComment[256];
828 RTStrPrintf(pszComment, sizeof(pszComment), "Converted image from %s", srcfilename);
829 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
830 if (RT_FAILURE(rc))
831 {
832 RTMsgError("Cannot create the virtual disk container: %Rrc", rc);
833 goto out;
834 }
835
836 Assert(RT_MIN(cbFile / 512 / 16 / 63, 16383) -
837 (unsigned int)RT_MIN(cbFile / 512 / 16 / 63, 16383) == 0);
838 VDGEOMETRY PCHS, LCHS;
839 PCHS.cCylinders = (unsigned int)RT_MIN(cbFile / 512 / 16 / 63, 16383);
840 PCHS.cHeads = 16;
841 PCHS.cSectors = 63;
842 LCHS.cCylinders = 0;
843 LCHS.cHeads = 0;
844 LCHS.cSectors = 0;
845 rc = VDCreateBase(pDisk, format, dstfilename, cbFile,
846 uImageFlags, pszComment, &PCHS, &LCHS, pUuid,
847 VD_OPEN_FLAGS_NORMAL, NULL, NULL);
848 if (RT_FAILURE(rc))
849 {
850 RTMsgError("Cannot create the disk image \"%s\": %Rrc", dstfilename, rc);
851 goto out;
852 }
853
854 size_t cbBuffer;
855 cbBuffer = _1M;
856 pvBuf = RTMemAlloc(cbBuffer);
857 if (!pvBuf)
858 {
859 rc = VERR_NO_MEMORY;
860 RTMsgError("Out of memory allocating buffers for image \"%s\": %Rrc", dstfilename, rc);
861 goto out;
862 }
863
864 uint64_t offFile;
865 offFile = 0;
866 while (offFile < cbFile)
867 {
868 size_t cbRead;
869 size_t cbToRead;
870 cbRead = 0;
871 cbToRead = cbFile - offFile >= (uint64_t)cbBuffer ?
872 cbBuffer : (size_t)(cbFile - offFile);
873 rc = RTFileRead(File, pvBuf, cbToRead, &cbRead);
874 if (RT_FAILURE(rc) || !cbRead)
875 break;
876 rc = VDWrite(pDisk, offFile, pvBuf, cbRead);
877 if (RT_FAILURE(rc))
878 {
879 RTMsgError("Failed to write to disk image \"%s\": %Rrc", dstfilename, rc);
880 goto out;
881 }
882 offFile += cbRead;
883 }
884
885out:
886 if (pvBuf)
887 RTMemFree(pvBuf);
888 if (pDisk)
889 VDClose(pDisk, RT_FAILURE(rc));
890 if (File != NIL_RTFILE)
891 RTFileClose(File);
892
893 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
894}
895
896static const RTGETOPTDEF g_aShowHardDiskInfoOptions[] =
897{
898 { "--dummy", 256, RTGETOPT_REQ_NOTHING }, // placeholder for C++
899};
900
901int handleShowHardDiskInfo(HandlerArg *a)
902{
903 HRESULT rc;
904 const char *FilenameOrUuid = NULL;
905
906 int c;
907 RTGETOPTUNION ValueUnion;
908 RTGETOPTSTATE GetState;
909 // start at 0 because main() has hacked both the argc and argv given to us
910 RTGetOptInit(&GetState, a->argc, a->argv, g_aShowHardDiskInfoOptions, RT_ELEMENTS(g_aShowHardDiskInfoOptions),
911 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
912 while ((c = RTGetOpt(&GetState, &ValueUnion)))
913 {
914 switch (c)
915 {
916 case VINF_GETOPT_NOT_OPTION:
917 if (!FilenameOrUuid)
918 FilenameOrUuid = ValueUnion.psz;
919 else
920 return errorSyntax(USAGE_SHOWHDINFO, "Invalid parameter '%s'", ValueUnion.psz);
921 break;
922
923 default:
924 if (c > 0)
925 {
926 if (RT_C_IS_PRINT(c))
927 return errorSyntax(USAGE_SHOWHDINFO, "Invalid option -%c", c);
928 else
929 return errorSyntax(USAGE_SHOWHDINFO, "Invalid option case %i", c);
930 }
931 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
932 return errorSyntax(USAGE_SHOWHDINFO, "unknown option: %s\n", ValueUnion.psz);
933 else if (ValueUnion.pDef)
934 return errorSyntax(USAGE_SHOWHDINFO, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
935 else
936 return errorSyntax(USAGE_SHOWHDINFO, "error: %Rrs", c);
937 }
938 }
939
940 /* check for required options */
941 if (!FilenameOrUuid)
942 return errorSyntax(USAGE_SHOWHDINFO, "Disk name or UUID required");
943
944 ComPtr<IMedium> hardDisk;
945
946 rc = openMedium(a, FilenameOrUuid, DeviceType_HardDisk,
947 AccessMode_ReadOnly, hardDisk,
948 false /* fForceNewUuidOnOpen */, false /* fSilent */);
949 if (FAILED(rc))
950 return 1;
951
952 do
953 {
954 Bstr uuid;
955 hardDisk->COMGETTER(Id)(uuid.asOutParam());
956 RTPrintf("UUID: %s\n", Utf8Str(uuid).c_str());
957
958 /* check for accessibility */
959 /// @todo NEWMEDIA check accessibility of all parents
960 /// @todo NEWMEDIA print the full state value
961 MediumState_T state;
962 CHECK_ERROR_BREAK(hardDisk, RefreshState(&state));
963 RTPrintf("Accessible: %s\n", state != MediumState_Inaccessible ? "yes" : "no");
964
965 if (state == MediumState_Inaccessible)
966 {
967 Bstr err;
968 CHECK_ERROR_BREAK(hardDisk, COMGETTER(LastAccessError)(err.asOutParam()));
969 RTPrintf("Access Error: %ls\n", err.raw());
970 }
971
972 Bstr description;
973 hardDisk->COMGETTER(Description)(description.asOutParam());
974 if (!description.isEmpty())
975 {
976 RTPrintf("Description: %ls\n", description.raw());
977 }
978
979 LONG64 logicalSize;
980 hardDisk->COMGETTER(LogicalSize)(&logicalSize);
981 RTPrintf("Logical size: %lld MBytes\n", logicalSize >> 20);
982 LONG64 actualSize;
983 hardDisk->COMGETTER(Size)(&actualSize);
984 RTPrintf("Current size on disk: %lld MBytes\n", actualSize >> 20);
985
986 ComPtr <IMedium> parent;
987 hardDisk->COMGETTER(Parent)(parent.asOutParam());
988
989 MediumType_T type;
990 hardDisk->COMGETTER(Type)(&type);
991 const char *typeStr = "unknown";
992 switch (type)
993 {
994 case MediumType_Normal:
995 if (!parent.isNull())
996 typeStr = "normal (differencing)";
997 else
998 typeStr = "normal (base)";
999 break;
1000 case MediumType_Immutable:
1001 typeStr = "immutable";
1002 break;
1003 case MediumType_Writethrough:
1004 typeStr = "writethrough";
1005 break;
1006 case MediumType_Shareable:
1007 typeStr = "shareable";
1008 break;
1009 case MediumType_Readonly:
1010 typeStr = "readonly";
1011 break;
1012 case MediumType_MultiAttach:
1013 typeStr = "multiattach";
1014 break;
1015 }
1016 RTPrintf("Type: %s\n", typeStr);
1017
1018 Bstr format;
1019 hardDisk->COMGETTER(Format)(format.asOutParam());
1020 RTPrintf("Storage format: %ls\n", format.raw());
1021
1022 com::SafeArray<MediumVariant_T> safeArray_variant;
1023
1024 hardDisk->COMGETTER(Variant)(ComSafeArrayAsOutParam(safeArray_variant));
1025 ULONG variant=0;
1026 for (size_t i = 0; i < safeArray_variant.size(); i++)
1027 variant |= safeArray_variant[i];
1028
1029 const char *variantStr = "unknown";
1030 switch (variant & ~(MediumVariant_Fixed | MediumVariant_Diff))
1031 {
1032 case MediumVariant_VmdkSplit2G:
1033 variantStr = "split2G";
1034 break;
1035 case MediumVariant_VmdkStreamOptimized:
1036 variantStr = "streamOptimized";
1037 break;
1038 case MediumVariant_VmdkESX:
1039 variantStr = "ESX";
1040 break;
1041 case MediumVariant_Standard:
1042 variantStr = "default";
1043 break;
1044 }
1045 const char *variantTypeStr = "dynamic";
1046 if (variant & MediumVariant_Fixed)
1047 variantTypeStr = "fixed";
1048 else if (variant & MediumVariant_Diff)
1049 variantTypeStr = "differencing";
1050 RTPrintf("Format variant: %s %s\n", variantTypeStr, variantStr);
1051
1052 /// @todo also dump config parameters (iSCSI)
1053
1054 com::SafeArray<BSTR> machineIds;
1055 hardDisk->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(machineIds));
1056 for (size_t j = 0; j < machineIds.size(); ++ j)
1057 {
1058 ComPtr<IMachine> machine;
1059 CHECK_ERROR(a->virtualBox, FindMachine(machineIds[j], machine.asOutParam()));
1060 ASSERT(machine);
1061 Bstr name;
1062 machine->COMGETTER(Name)(name.asOutParam());
1063 machine->COMGETTER(Id)(uuid.asOutParam());
1064 RTPrintf("%s%ls (UUID: %ls)\n",
1065 j == 0 ? "In use by VMs: " : " ",
1066 name.raw(), machineIds[j]);
1067 }
1068 /// @todo NEWMEDIA check usage in snapshots too
1069 /// @todo NEWMEDIA also list children
1070
1071 Bstr loc;
1072 hardDisk->COMGETTER(Location)(loc.asOutParam());
1073 RTPrintf("Location: %ls\n", loc.raw());
1074
1075 /* print out information specific for differencing hard disks */
1076 if (!parent.isNull())
1077 {
1078 BOOL autoReset = FALSE;
1079 hardDisk->COMGETTER(AutoReset)(&autoReset);
1080 RTPrintf("Auto-Reset: %s\n", autoReset ? "on" : "off");
1081 }
1082 }
1083 while (0);
1084
1085 return SUCCEEDED(rc) ? 0 : 1;
1086}
1087
1088static const RTGETOPTDEF g_aCloseMediumOptions[] =
1089{
1090 { "disk", 'd', RTGETOPT_REQ_NOTHING },
1091 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
1092 { "floppy", 'f', RTGETOPT_REQ_NOTHING },
1093 { "--delete", 'r', RTGETOPT_REQ_NOTHING },
1094};
1095
1096int handleCloseMedium(HandlerArg *a)
1097{
1098 HRESULT rc = S_OK;
1099 enum {
1100 CMD_NONE,
1101 CMD_DISK,
1102 CMD_DVD,
1103 CMD_FLOPPY
1104 } cmd = CMD_NONE;
1105 const char *FilenameOrUuid = NULL;
1106 bool fDelete = false;
1107
1108 int c;
1109 RTGETOPTUNION ValueUnion;
1110 RTGETOPTSTATE GetState;
1111 // start at 0 because main() has hacked both the argc and argv given to us
1112 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloseMediumOptions, RT_ELEMENTS(g_aCloseMediumOptions),
1113 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1114 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1115 {
1116 switch (c)
1117 {
1118 case 'd': // disk
1119 if (cmd != CMD_NONE)
1120 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1121 cmd = CMD_DISK;
1122 break;
1123
1124 case 'D': // DVD
1125 if (cmd != CMD_NONE)
1126 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1127 cmd = CMD_DVD;
1128 break;
1129
1130 case 'f': // floppy
1131 if (cmd != CMD_NONE)
1132 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1133 cmd = CMD_FLOPPY;
1134 break;
1135
1136 case 'r': // --delete
1137 fDelete = true;
1138 break;
1139
1140 case VINF_GETOPT_NOT_OPTION:
1141 if (!FilenameOrUuid)
1142 FilenameOrUuid = ValueUnion.psz;
1143 else
1144 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
1145 break;
1146
1147 default:
1148 if (c > 0)
1149 {
1150 if (RT_C_IS_PRINT(c))
1151 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option -%c", c);
1152 else
1153 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option case %i", c);
1154 }
1155 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1156 return errorSyntax(USAGE_CLOSEMEDIUM, "unknown option: %s\n", ValueUnion.psz);
1157 else if (ValueUnion.pDef)
1158 return errorSyntax(USAGE_CLOSEMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1159 else
1160 return errorSyntax(USAGE_CLOSEMEDIUM, "error: %Rrs", c);
1161 }
1162 }
1163
1164 /* check for required options */
1165 if (cmd == CMD_NONE)
1166 return errorSyntax(USAGE_CLOSEMEDIUM, "Command variant disk/dvd/floppy required");
1167 if (!FilenameOrUuid)
1168 return errorSyntax(USAGE_CLOSEMEDIUM, "Disk name or UUID required");
1169
1170 ComPtr<IMedium> medium;
1171
1172 if (cmd == CMD_DISK)
1173 rc = openMedium(a, FilenameOrUuid, DeviceType_HardDisk,
1174 AccessMode_ReadWrite, medium,
1175 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1176 else if (cmd == CMD_DVD)
1177 rc = openMedium(a, FilenameOrUuid, DeviceType_DVD,
1178 AccessMode_ReadOnly, medium,
1179 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1180 else if (cmd == CMD_FLOPPY)
1181 rc = openMedium(a, FilenameOrUuid, DeviceType_Floppy,
1182 AccessMode_ReadWrite, medium,
1183 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1184
1185 if (SUCCEEDED(rc) && medium)
1186 {
1187 if (fDelete)
1188 {
1189 ComPtr<IProgress> progress;
1190 CHECK_ERROR(medium, DeleteStorage(progress.asOutParam()));
1191 if (SUCCEEDED(rc))
1192 {
1193 rc = showProgress(progress);
1194 CHECK_PROGRESS_ERROR(progress, ("Failed to delete medium"));
1195 }
1196 else
1197 RTMsgError("Failed to delete medium. Error code %Rrc", rc);
1198 }
1199 CHECK_ERROR(medium, Close());
1200 }
1201
1202 return SUCCEEDED(rc) ? 0 : 1;
1203}
1204#endif /* !VBOX_ONLY_DOCS */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette