VirtualBox

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

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

Main: The change ulong to ULONG inside the loops where enumeration MediumVariant is converted.

  • 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 44379 2013-01-25 13:50:03Z 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.

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