VirtualBox

source: vbox/trunk/src/VBox/Main/HardDiskImpl.cpp@ 18052

Last change on this file since 18052 was 17974, checked in by vboxsync, 16 years ago

ugly hack

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 140.0 KB
Line 
1/* $Id: HardDiskImpl.cpp 17974 2009-03-16 19:53:46Z vboxsync $ */
2
3/** @file
4 *
5 * VirtualBox COM class implementation
6 */
7
8/*
9 * Copyright (C) 2008 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24#include "HardDiskImpl.h"
25
26#include "ProgressImpl.h"
27#include "SystemPropertiesImpl.h"
28
29#include "Logging.h"
30
31#include <VBox/com/array.h>
32#include <VBox/com/SupportErrorInfo.h>
33
34#include <VBox/err.h>
35#include <VBox/settings.h>
36
37#include <iprt/param.h>
38#include <iprt/path.h>
39#include <iprt/file.h>
40#include <iprt/tcp.h>
41
42#include <list>
43#include <memory>
44
45////////////////////////////////////////////////////////////////////////////////
46// Globals
47////////////////////////////////////////////////////////////////////////////////
48
49/**
50 * Asynchronous task thread parameter bucket.
51 *
52 * Note that instances of this class must be created using new() because the
53 * task thread function will delete them when the task is complete!
54 *
55 * @note The constructor of this class adds a caller on the managed HardDisk
56 * object which is automatically released upon destruction.
57 */
58struct HardDisk::Task : public com::SupportErrorInfoBase
59{
60 enum Operation { CreateBase, CreateDiff,
61 Merge, Clone, Delete, Reset };
62
63 HardDisk *that;
64 VirtualBoxBaseProto::AutoCaller autoCaller;
65
66 ComObjPtr <Progress> progress;
67 Operation operation;
68
69 /** Where to save the result when executed using #runNow(). */
70 HRESULT rc;
71
72 Task (HardDisk *aThat, Progress *aProgress, Operation aOperation)
73 : that (aThat), autoCaller (aThat)
74 , progress (aProgress)
75 , operation (aOperation)
76 , rc (S_OK) {}
77
78 ~Task();
79
80 void setData (HardDisk *aTarget)
81 {
82 d.target = aTarget;
83 HRESULT rc = d.target->addCaller();
84 AssertComRC (rc);
85 }
86
87 void setData (MergeChain *aChain)
88 {
89 AssertReturnVoid (aChain != NULL);
90 d.chain.reset (aChain);
91 }
92
93 HRESULT startThread();
94 HRESULT runNow();
95
96 struct Data
97 {
98 Data() : size (0) {}
99
100 /* CreateBase */
101
102 uint64_t size;
103
104 /* CreateBase, CreateDiff, Clone */
105
106 HardDiskVariant_T variant;
107
108 /* CreateDiff */
109
110 ComObjPtr<HardDisk> target;
111
112 /* Merge */
113
114 /** Hard disks to merge, in {parent,child} order */
115 std::auto_ptr <MergeChain> chain;
116 }
117 d;
118
119protected:
120
121 // SupportErrorInfoBase interface
122 const GUID &mainInterfaceID() const { return COM_IIDOF (IHardDisk); }
123 const char *componentName() const { return HardDisk::ComponentName(); }
124};
125
126HardDisk::Task::~Task()
127{
128 /* remove callers added by setData() */
129 if (!d.target.isNull())
130 d.target->releaseCaller();
131}
132
133/**
134 * Starts a new thread driven by the HardDisk::taskThread() function and passes
135 * this Task instance as an argument.
136 *
137 * Note that if this method returns success, this Task object becomes an ownee
138 * of the started thread and will be automatically deleted when the thread
139 * terminates.
140 *
141 * @note When the task is executed by this method, IProgress::notifyComplete()
142 * is automatically called for the progress object associated with this
143 * task when the task is finished to signal the operation completion for
144 * other threads asynchronously waiting for it.
145 */
146HRESULT HardDisk::Task::startThread()
147{
148 int vrc = RTThreadCreate (NULL, HardDisk::taskThread, this,
149 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
150 "HardDisk::Task");
151 ComAssertMsgRCRet (vrc,
152 ("Could not create HardDisk::Task thread (%Rrc)\n", vrc), E_FAIL);
153
154 return S_OK;
155}
156
157/**
158 * Runs HardDisk::taskThread() by passing it this Task instance as an argument
159 * on the current thread instead of creating a new one.
160 *
161 * This call implies that it is made on another temporary thread created for
162 * some asynchronous task. Avoid calling it from a normal thread since the task
163 * operatinos are potentially lengthy and will block the calling thread in this
164 * case.
165 *
166 * Note that this Task object will be deleted by taskThread() when this method
167 * returns!
168 *
169 * @note When the task is executed by this method, IProgress::notifyComplete()
170 * is not called for the progress object associated with this task when
171 * the task is finished. Instead, the result of the operation is returned
172 * by this method directly and it's the caller's responsibility to
173 * complete the progress object in this case.
174 */
175HRESULT HardDisk::Task::runNow()
176{
177 HardDisk::taskThread (NIL_RTTHREAD, this);
178
179 return rc;
180}
181
182////////////////////////////////////////////////////////////////////////////////
183
184/**
185 * Helper class for merge operations.
186 *
187 * @note It is assumed that when modifying methods of this class are called,
188 * HardDisk::treeLock() is held in read mode.
189 */
190class HardDisk::MergeChain : public HardDisk::List,
191 public com::SupportErrorInfoBase
192{
193public:
194
195 MergeChain (bool aForward, bool aIgnoreAttachments)
196 : mForward (aForward)
197 , mIgnoreAttachments (aIgnoreAttachments) {}
198
199 ~MergeChain()
200 {
201 for (iterator it = mChildren.begin(); it != mChildren.end(); ++ it)
202 {
203 HRESULT rc = (*it)->UnlockWrite (NULL);
204 AssertComRC (rc);
205
206 (*it)->releaseCaller();
207 }
208
209 for (iterator it = begin(); it != end(); ++ it)
210 {
211 AutoWriteLock alock (*it);
212 Assert ((*it)->m.state == MediaState_LockedWrite ||
213 (*it)->m.state == MediaState_Deleting);
214 if ((*it)->m.state == MediaState_LockedWrite)
215 (*it)->UnlockWrite (NULL);
216 else
217 (*it)->m.state = MediaState_Created;
218
219 (*it)->releaseCaller();
220 }
221
222 if (!mParent.isNull())
223 mParent->releaseCaller();
224 }
225
226 HRESULT addSource (HardDisk *aHardDisk)
227 {
228 HRESULT rc = aHardDisk->addCaller();
229 CheckComRCReturnRC (rc);
230
231 AutoWriteLock alock (aHardDisk);
232
233 if (mForward)
234 {
235 rc = checkChildrenAndAttachmentsAndImmutable (aHardDisk);
236 if (FAILED (rc))
237 {
238 aHardDisk->releaseCaller();
239 return rc;
240 }
241 }
242
243 /* go to Deleting */
244 switch (aHardDisk->m.state)
245 {
246 case MediaState_Created:
247 aHardDisk->m.state = MediaState_Deleting;
248 break;
249 default:
250 aHardDisk->releaseCaller();
251 return aHardDisk->setStateError();
252 }
253
254 push_front (aHardDisk);
255
256 if (mForward)
257 {
258 /* we will need parent to reparent target */
259 if (!aHardDisk->mParent.isNull())
260 {
261 rc = aHardDisk->mParent->addCaller();
262 CheckComRCReturnRC (rc);
263
264 mParent = aHardDisk->mParent;
265 }
266 }
267 else
268 {
269 /* we will need to reparent children */
270 for (List::const_iterator it = aHardDisk->children().begin();
271 it != aHardDisk->children().end(); ++ it)
272 {
273 rc = (*it)->addCaller();
274 CheckComRCReturnRC (rc);
275
276 rc = (*it)->LockWrite (NULL);
277 if (FAILED (rc))
278 {
279 (*it)->releaseCaller();
280 return rc;
281 }
282
283 mChildren.push_back (*it);
284 }
285 }
286
287 return S_OK;
288 }
289
290 HRESULT addTarget (HardDisk *aHardDisk)
291 {
292 HRESULT rc = aHardDisk->addCaller();
293 CheckComRCReturnRC (rc);
294
295 AutoWriteLock alock (aHardDisk);
296
297 if (!mForward)
298 {
299 rc = checkChildrenAndImmutable (aHardDisk);
300 if (FAILED (rc))
301 {
302 aHardDisk->releaseCaller();
303 return rc;
304 }
305 }
306
307 /* go to LockedWrite */
308 rc = aHardDisk->LockWrite (NULL);
309 if (FAILED (rc))
310 {
311 aHardDisk->releaseCaller();
312 return rc;
313 }
314
315 push_front (aHardDisk);
316
317 return S_OK;
318 }
319
320 HRESULT addIntermediate (HardDisk *aHardDisk)
321 {
322 HRESULT rc = aHardDisk->addCaller();
323 CheckComRCReturnRC (rc);
324
325 AutoWriteLock alock (aHardDisk);
326
327 rc = checkChildrenAndAttachments (aHardDisk);
328 if (FAILED (rc))
329 {
330 aHardDisk->releaseCaller();
331 return rc;
332 }
333
334 /* go to Deleting */
335 switch (aHardDisk->m.state)
336 {
337 case MediaState_Created:
338 aHardDisk->m.state = MediaState_Deleting;
339 break;
340 default:
341 aHardDisk->releaseCaller();
342 return aHardDisk->setStateError();
343 }
344
345 push_front (aHardDisk);
346
347 return S_OK;
348 }
349
350 bool isForward() const { return mForward; }
351 HardDisk *parent() const { return mParent; }
352 const List &children() const { return mChildren; }
353
354 HardDisk *source() const
355 { AssertReturn (size() > 0, NULL); return mForward ? front() : back(); }
356
357 HardDisk *target() const
358 { AssertReturn (size() > 0, NULL); return mForward ? back() : front(); }
359
360protected:
361
362 // SupportErrorInfoBase interface
363 const GUID &mainInterfaceID() const { return COM_IIDOF (IHardDisk); }
364 const char *componentName() const { return HardDisk::ComponentName(); }
365
366private:
367
368 HRESULT check (HardDisk *aHardDisk, bool aChildren, bool aAttachments,
369 bool aImmutable)
370 {
371 if (aChildren)
372 {
373 /* not going to multi-merge as it's too expensive */
374 if (aHardDisk->children().size() > 1)
375 {
376 return setError (E_FAIL,
377 tr ("Hard disk '%ls' involved in the merge operation "
378 "has more than one child hard disk (%d)"),
379 aHardDisk->m.locationFull.raw(),
380 aHardDisk->children().size());
381 }
382 }
383
384 if (aAttachments && !mIgnoreAttachments)
385 {
386 if (aHardDisk->m.backRefs.size() != 0)
387 return setError (E_FAIL,
388 tr ("Hard disk '%ls' is attached to %d virtual machines"),
389 aHardDisk->m.locationFull.raw(),
390 aHardDisk->m.backRefs.size());
391 }
392
393 if (aImmutable)
394 {
395 if (aHardDisk->mm.type == HardDiskType_Immutable)
396 return setError (E_FAIL,
397 tr ("Hard disk '%ls' is immutable"),
398 aHardDisk->m.locationFull.raw());
399 }
400
401 return S_OK;
402 }
403
404 HRESULT checkChildren (HardDisk *aHardDisk)
405 { return check (aHardDisk, true, false, false); }
406
407 HRESULT checkChildrenAndImmutable (HardDisk *aHardDisk)
408 { return check (aHardDisk, true, false, true); }
409
410 HRESULT checkChildrenAndAttachments (HardDisk *aHardDisk)
411 { return check (aHardDisk, true, true, false); }
412
413 HRESULT checkChildrenAndAttachmentsAndImmutable (HardDisk *aHardDisk)
414 { return check (aHardDisk, true, true, true); }
415
416 /** true if forward merge, false if backward */
417 bool mForward : 1;
418 /** true to not perform attachment checks */
419 bool mIgnoreAttachments : 1;
420
421 /** Parent of the source when forward merge (if any) */
422 ComObjPtr <HardDisk> mParent;
423 /** Children of the source when backward merge (if any) */
424 List mChildren;
425};
426
427////////////////////////////////////////////////////////////////////////////////
428// HardDisk class
429////////////////////////////////////////////////////////////////////////////////
430
431// constructor / destructor
432////////////////////////////////////////////////////////////////////////////////
433
434DEFINE_EMPTY_CTOR_DTOR (HardDisk)
435
436HRESULT HardDisk::FinalConstruct()
437{
438 /* Initialize the callbacks of the VD error interface */
439 mm.vdIfCallsError.cbSize = sizeof (VDINTERFACEERROR);
440 mm.vdIfCallsError.enmInterface = VDINTERFACETYPE_ERROR;
441 mm.vdIfCallsError.pfnError = vdErrorCall;
442
443 /* Initialize the callbacks of the VD progress interface */
444 mm.vdIfCallsProgress.cbSize = sizeof (VDINTERFACEPROGRESS);
445 mm.vdIfCallsProgress.enmInterface = VDINTERFACETYPE_PROGRESS;
446 mm.vdIfCallsProgress.pfnProgress = vdProgressCall;
447
448 /* Initialize the callbacks of the VD config interface */
449 mm.vdIfCallsConfig.cbSize = sizeof (VDINTERFACECONFIG);
450 mm.vdIfCallsConfig.enmInterface = VDINTERFACETYPE_CONFIG;
451 mm.vdIfCallsConfig.pfnAreKeysValid = vdConfigAreKeysValid;
452 mm.vdIfCallsConfig.pfnQuerySize = vdConfigQuerySize;
453 mm.vdIfCallsConfig.pfnQuery = vdConfigQuery;
454
455 /* Initialize the callbacks of the VD TCP interface (we always use the host
456 * IP stack for now) */
457 mm.vdIfCallsTcpNet.cbSize = sizeof (VDINTERFACETCPNET);
458 mm.vdIfCallsTcpNet.enmInterface = VDINTERFACETYPE_TCPNET;
459 mm.vdIfCallsTcpNet.pfnClientConnect = RTTcpClientConnect;
460 mm.vdIfCallsTcpNet.pfnClientClose = RTTcpClientClose;
461 mm.vdIfCallsTcpNet.pfnSelectOne = RTTcpSelectOne;
462 mm.vdIfCallsTcpNet.pfnRead = RTTcpRead;
463 mm.vdIfCallsTcpNet.pfnWrite = RTTcpWrite;
464 mm.vdIfCallsTcpNet.pfnFlush = RTTcpFlush;
465
466 /* Initialize the per-disk interface chain */
467 int vrc;
468 vrc = VDInterfaceAdd (&mm.vdIfError,
469 "HardDisk::vdInterfaceError",
470 VDINTERFACETYPE_ERROR,
471 &mm.vdIfCallsError, this, &mm.vdDiskIfaces);
472 AssertRCReturn (vrc, E_FAIL);
473
474 vrc = VDInterfaceAdd (&mm.vdIfProgress,
475 "HardDisk::vdInterfaceProgress",
476 VDINTERFACETYPE_PROGRESS,
477 &mm.vdIfCallsProgress, this, &mm.vdDiskIfaces);
478 AssertRCReturn (vrc, E_FAIL);
479
480 vrc = VDInterfaceAdd (&mm.vdIfConfig,
481 "HardDisk::vdInterfaceConfig",
482 VDINTERFACETYPE_CONFIG,
483 &mm.vdIfCallsConfig, this, &mm.vdDiskIfaces);
484 AssertRCReturn (vrc, E_FAIL);
485
486 vrc = VDInterfaceAdd (&mm.vdIfTcpNet,
487 "HardDisk::vdInterfaceTcpNet",
488 VDINTERFACETYPE_TCPNET,
489 &mm.vdIfCallsTcpNet, this, &mm.vdDiskIfaces);
490 AssertRCReturn (vrc, E_FAIL);
491
492 return S_OK;
493}
494
495void HardDisk::FinalRelease()
496{
497 uninit();
498}
499
500// public initializer/uninitializer for internal purposes only
501////////////////////////////////////////////////////////////////////////////////
502
503/**
504 * Initializes the hard disk object without creating or opening an associated
505 * storage unit.
506 *
507 * For hard disks that don't have the VD_CAP_CREATE_FIXED or
508 * VD_CAP_CREATE_DYNAMIC capability (and therefore cannot be created or deleted
509 * with the means of VirtualBox) the associated storage unit is assumed to be
510 * ready for use so the state of the hard disk object will be set to Created.
511 *
512 * @param aVirtualBox VirtualBox object.
513 * @param aLocaiton Storage unit location.
514 */
515HRESULT HardDisk::init (VirtualBox *aVirtualBox, CBSTR aFormat,
516 CBSTR aLocation)
517{
518 AssertReturn (aVirtualBox != NULL, E_FAIL);
519 AssertReturn (aFormat != NULL && *aFormat != '\0', E_FAIL);
520
521 /* Enclose the state transition NotReady->InInit->Ready */
522 AutoInitSpan autoInitSpan (this);
523 AssertReturn (autoInitSpan.isOk(), E_FAIL);
524
525 HRESULT rc = S_OK;
526
527 /* share VirtualBox weakly (parent remains NULL so far) */
528 unconst (mVirtualBox) = aVirtualBox;
529
530 /* register with VirtualBox early, since uninit() will
531 * unconditionally unregister on failure */
532 aVirtualBox->addDependentChild (this);
533
534 /* no storage yet */
535 m.state = MediaState_NotCreated;
536
537 /* No storage unit is created yet, no need to queryInfo() */
538
539 rc = setFormat (aFormat);
540 CheckComRCReturnRC (rc);
541
542 if (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File)
543 {
544 rc = setLocation (aLocation);
545 CheckComRCReturnRC (rc);
546 }
547 else
548 {
549 rc = setLocation (aLocation);
550 CheckComRCReturnRC (rc);
551
552 /// @todo later we may want to use a pfnComposeLocation backend info
553 /// callback to generate a well-formed location value (based on the hard
554 /// disk properties we have) rather than allowing each caller to invent
555 /// its own (pseudo-)location.
556 }
557
558 if (!(mm.formatObj->capabilities() &
559 (HardDiskFormatCapabilities_CreateFixed |
560 HardDiskFormatCapabilities_CreateDynamic)))
561 {
562 /* storage for hard disks of this format can neither be explicitly
563 * created by VirtualBox nor deleted, so we place the hard disk to
564 * Created state here and also add it to the registry */
565 m.state = MediaState_Created;
566 unconst (m.id).create();
567 rc = mVirtualBox->registerHardDisk (this);
568
569 /// @todo later we may want to use a pfnIsConfigSufficient backend info
570 /// callback that would tell us when we have enough properties to work
571 /// with the hard disk and this information could be used to actually
572 /// move such hard disks from NotCreated to Created state. Instead of
573 /// pfnIsConfigSufficient we can use HardDiskFormat property
574 /// descriptions to see which properties are mandatory
575 }
576
577 /* Confirm a successful initialization when it's the case */
578 if (SUCCEEDED (rc))
579 autoInitSpan.setSucceeded();
580
581 return rc;
582}
583
584/**
585 * Initializes the hard disk object by opening the storage unit at the specified
586 * location.
587 *
588 * Note that the UUID, format and the parent of this hard disk will be
589 * determined when reading the hard disk storage unit. If the detected parent is
590 * not known to VirtualBox, then this method will fail.
591 *
592 * @param aVirtualBox VirtualBox object.
593 * @param aLocaiton Storage unit location.
594 */
595HRESULT HardDisk::init (VirtualBox *aVirtualBox, CBSTR aLocation)
596{
597 AssertReturn (aVirtualBox, E_INVALIDARG);
598 AssertReturn (aLocation, E_INVALIDARG);
599
600 /* Enclose the state transition NotReady->InInit->Ready */
601 AutoInitSpan autoInitSpan (this);
602 AssertReturn (autoInitSpan.isOk(), E_FAIL);
603
604 HRESULT rc = S_OK;
605
606 /* share VirtualBox weakly (parent remains NULL so far) */
607 unconst (mVirtualBox) = aVirtualBox;
608
609 /* register with VirtualBox early, since uninit() will
610 * unconditionally unregister on failure */
611 aVirtualBox->addDependentChild (this);
612
613 /* there must be a storage unit */
614 m.state = MediaState_Created;
615
616 rc = setLocation (aLocation);
617 CheckComRCReturnRC (rc);
618
619 /* get all the information about the medium from the storage unit */
620 rc = queryInfo();
621 if (SUCCEEDED (rc))
622 {
623 /* if the storage unit is not accessible, it's not acceptable for the
624 * newly opened media so convert this into an error */
625 if (m.state == MediaState_Inaccessible)
626 {
627 Assert (!m.lastAccessError.isEmpty());
628 rc = setError (E_FAIL, Utf8Str (m.lastAccessError));
629 }
630 else
631 {
632 /* storage format must be detected by queryInfo() if the medium is
633 * accessible */
634 AssertReturn (!m.id.isEmpty() && !mm.format.isNull(), E_FAIL);
635 }
636 }
637
638 /* Confirm a successful initialization when it's the case */
639 if (SUCCEEDED (rc))
640 autoInitSpan.setSucceeded();
641
642 return rc;
643}
644
645/**
646 * Initializes the hard disk object by loading its data from the given settings
647 * node.
648 *
649 * @param aVirtualBox VirtualBox object.
650 * @param aParent Parent hard disk or NULL for a root (base) hard disk.
651 * @param aNode <HardDisk> settings node.
652 *
653 * @note Locks VirtualBox lock for writing, treeLock() for writing.
654 */
655HRESULT HardDisk::init (VirtualBox *aVirtualBox, HardDisk *aParent,
656 const settings::Key &aNode)
657{
658 using namespace settings;
659
660 AssertReturn (aVirtualBox, E_INVALIDARG);
661
662 /* Enclose the state transition NotReady->InInit->Ready */
663 AutoInitSpan autoInitSpan (this);
664 AssertReturn (autoInitSpan.isOk(), E_FAIL);
665
666 HRESULT rc = S_OK;
667
668 /* share VirtualBox and parent weakly */
669 unconst (mVirtualBox) = aVirtualBox;
670
671 /* register with VirtualBox/parent early, since uninit() will
672 * unconditionally unregister on failure */
673 if (aParent == NULL)
674 aVirtualBox->addDependentChild (this);
675 else
676 {
677 /* we set mParent */
678 AutoWriteLock treeLock (this->treeLock());
679
680 mParent = aParent;
681 aParent->addDependentChild (this);
682 }
683
684 /* see below why we don't call queryInfo() (and therefore treat the medium
685 * as inaccessible for now */
686 m.state = MediaState_Inaccessible;
687 m.lastAccessError = tr ("Accessibility check was not yet performed");
688
689 /* required */
690 unconst (m.id) = aNode.value <Guid> ("uuid");
691
692 /* optional */
693 {
694 settings::Key descNode = aNode.findKey ("Description");
695 if (!descNode.isNull())
696 m.description = descNode.keyStringValue();
697 }
698
699 /* required */
700 Bstr format = aNode.stringValue ("format");
701 AssertReturn (!format.isNull(), E_FAIL);
702 rc = setFormat (format);
703 CheckComRCReturnRC (rc);
704
705 /* optional, only for diffs, default is false */
706 if (aParent != NULL)
707 mm.autoReset = aNode.value <bool> ("autoReset");
708 else
709 mm.autoReset = false;
710
711 /* properties (after setting the format as it populates the map). Note that
712 * if some properties are not supported but preseint in the settings file,
713 * they will still be read and accessible (for possible backward
714 * compatibility; we can also clean them up from the XML upon next
715 * XML format versino change if we wish) */
716 Key::List properties = aNode.keys ("Property");
717 for (Key::List::const_iterator it = properties.begin();
718 it != properties.end(); ++ it)
719 {
720 mm.properties [Bstr (it->stringValue ("name"))] =
721 Bstr (it->stringValue ("value"));
722 }
723
724 /* required */
725 Bstr location = aNode.stringValue ("location");
726 rc = setLocation (location);
727 CheckComRCReturnRC (rc);
728
729 /* type is only for base hard disks */
730 if (mParent.isNull())
731 {
732 const char *type = aNode.stringValue ("type");
733 if (strcmp (type, "Normal") == 0)
734 mm.type = HardDiskType_Normal;
735 else if (strcmp (type, "Immutable") == 0)
736 mm.type = HardDiskType_Immutable;
737 else if (strcmp (type, "Writethrough") == 0)
738 mm.type = HardDiskType_Writethrough;
739 else
740 AssertFailed();
741 }
742
743 LogFlowThisFunc (("m.locationFull='%ls', mm.format=%ls, m.id={%RTuuid}\n",
744 m.locationFull.raw(), mm.format.raw(), m.id.raw()));
745
746 /* Don't call queryInfo() for registered media to prevent the calling
747 * thread (i.e. the VirtualBox server startup thread) from an unexpected
748 * freeze but mark it as initially inaccessible instead. The vital UUID,
749 * location and format properties are read from the registry file above; to
750 * get the actual state and the rest of the data, the user will have to call
751 * COMGETTER(State). */
752
753 /* load all children */
754 Key::List hardDisks = aNode.keys ("HardDisk");
755 for (Key::List::const_iterator it = hardDisks.begin();
756 it != hardDisks.end(); ++ it)
757 {
758 ComObjPtr<HardDisk> hardDisk;
759 hardDisk.createObject();
760 rc = hardDisk->init (aVirtualBox, this, *it);
761 CheckComRCBreakRC (rc);
762
763 rc = mVirtualBox->registerHardDisk(hardDisk, false /* aSaveRegistry */);
764 CheckComRCBreakRC (rc);
765 }
766
767 /* Confirm a successful initialization when it's the case */
768 if (SUCCEEDED (rc))
769 autoInitSpan.setSucceeded();
770
771 return rc;
772}
773
774/**
775 * Uninitializes the instance.
776 *
777 * Called either from FinalRelease() or by the parent when it gets destroyed.
778 *
779 * @note All children of this hard disk get uninitialized by calling their
780 * uninit() methods.
781 *
782 * @note Locks treeLock() for writing, VirtualBox for writing.
783 */
784void HardDisk::uninit()
785{
786 /* Enclose the state transition Ready->InUninit->NotReady */
787 AutoUninitSpan autoUninitSpan (this);
788 if (autoUninitSpan.uninitDone())
789 return;
790
791 if (!mm.formatObj.isNull())
792 {
793 /* remove the caller reference we added in setFormat() */
794 mm.formatObj->releaseCaller();
795 mm.formatObj.setNull();
796 }
797
798 if (m.state == MediaState_Deleting)
799 {
800 /* we are being uninitialized after've been deleted by merge.
801 * Reparenting has already been done so don't touch it here (we are
802 * now orphans and remoeDependentChild() will assert) */
803
804 Assert (mParent.isNull());
805 }
806 else
807 {
808 /* we uninit children and reset mParent
809 * and VirtualBox::removeDependentChild() needs a write lock */
810 AutoMultiWriteLock2 alock (mVirtualBox->lockHandle(), this->treeLock());
811
812 uninitDependentChildren();
813
814 if (!mParent.isNull())
815 {
816 mParent->removeDependentChild (this);
817 mParent.setNull();
818 }
819 else
820 mVirtualBox->removeDependentChild (this);
821 }
822
823 unconst (mVirtualBox).setNull();
824}
825
826// IHardDisk properties
827////////////////////////////////////////////////////////////////////////////////
828
829STDMETHODIMP HardDisk::COMGETTER(Format) (BSTR *aFormat)
830{
831 if (aFormat == NULL)
832 return E_POINTER;
833
834 AutoCaller autoCaller (this);
835 CheckComRCReturnRC (autoCaller.rc());
836
837 /* no need to lock, mm.format is const */
838 mm.format.cloneTo (aFormat);
839
840 return S_OK;
841}
842
843STDMETHODIMP HardDisk::COMGETTER(Type) (HardDiskType_T *aType)
844{
845 if (aType == NULL)
846 return E_POINTER;
847
848 AutoCaller autoCaller (this);
849 CheckComRCReturnRC (autoCaller.rc());
850
851 AutoReadLock alock (this);
852
853 *aType = mm.type;
854
855 return S_OK;
856}
857
858STDMETHODIMP HardDisk::COMSETTER(Type) (HardDiskType_T aType)
859{
860 AutoCaller autoCaller (this);
861 CheckComRCReturnRC (autoCaller.rc());
862
863 /* VirtualBox::saveSettings() needs a write lock */
864 AutoMultiWriteLock2 alock (mVirtualBox, this);
865
866 switch (m.state)
867 {
868 case MediaState_Created:
869 case MediaState_Inaccessible:
870 break;
871 default:
872 return setStateError();
873 }
874
875 if (mm.type == aType)
876 {
877 /* Nothing to do */
878 return S_OK;
879 }
880
881 /* we access mParent & children() */
882 AutoReadLock treeLock (this->treeLock());
883
884 /* cannot change the type of a differencing hard disk */
885 if (!mParent.isNull())
886 return setError (E_FAIL,
887 tr ("Hard disk '%ls' is a differencing hard disk"),
888 m.locationFull.raw());
889
890 /* cannot change the type of a hard disk being in use */
891 if (m.backRefs.size() != 0)
892 return setError (E_FAIL,
893 tr ("Hard disk '%ls' is attached to %d virtual machines"),
894 m.locationFull.raw(), m.backRefs.size());
895
896 switch (aType)
897 {
898 case HardDiskType_Normal:
899 case HardDiskType_Immutable:
900 {
901 /* normal can be easily converted to imutable and vice versa even
902 * if they have children as long as they are not attached to any
903 * machine themselves */
904 break;
905 }
906 case HardDiskType_Writethrough:
907 {
908 /* cannot change to writethrough if there are children */
909 if (children().size() != 0)
910 return setError (E_FAIL,
911 tr ("Hard disk '%ls' has %d child hard disks"),
912 children().size());
913 break;
914 }
915 default:
916 AssertFailedReturn (E_FAIL);
917 }
918
919 mm.type = aType;
920
921 HRESULT rc = mVirtualBox->saveSettings();
922
923 return rc;
924}
925
926STDMETHODIMP HardDisk::COMGETTER(Parent) (IHardDisk **aParent)
927{
928 if (aParent == NULL)
929 return E_POINTER;
930
931 AutoCaller autoCaller (this);
932 CheckComRCReturnRC (autoCaller.rc());
933
934 /* we access mParent */
935 AutoReadLock treeLock (this->treeLock());
936
937 mParent.queryInterfaceTo (aParent);
938
939 return S_OK;
940}
941
942STDMETHODIMP HardDisk::COMGETTER(Children) (ComSafeArrayOut (IHardDisk *, aChildren))
943{
944 if (ComSafeArrayOutIsNull (aChildren))
945 return E_POINTER;
946
947 AutoCaller autoCaller (this);
948 CheckComRCReturnRC (autoCaller.rc());
949
950 /* we access children */
951 AutoReadLock treeLock (this->treeLock());
952
953 SafeIfaceArray<IHardDisk> children (this->children());
954 children.detachTo (ComSafeArrayOutArg (aChildren));
955
956 return S_OK;
957}
958
959STDMETHODIMP HardDisk::COMGETTER(Root)(IHardDisk **aRoot)
960{
961 if (aRoot == NULL)
962 return E_POINTER;
963
964 /* root() will do callers/locking */
965
966 root().queryInterfaceTo (aRoot);
967
968 return S_OK;
969}
970
971STDMETHODIMP HardDisk::COMGETTER(ReadOnly) (BOOL *aReadOnly)
972{
973 if (aReadOnly == NULL)
974 return E_POINTER;
975
976 AutoCaller autoCaller (this);
977 CheckComRCReturnRC (autoCaller.rc());
978
979 /* isRadOnly() will do locking */
980
981 *aReadOnly = isReadOnly();
982
983 return S_OK;
984}
985
986STDMETHODIMP HardDisk::COMGETTER(LogicalSize) (ULONG64 *aLogicalSize)
987{
988 CheckComArgOutPointerValid (aLogicalSize);
989
990 {
991 AutoCaller autoCaller (this);
992 CheckComRCReturnRC (autoCaller.rc());
993
994 AutoReadLock alock (this);
995
996 /* we access mParent */
997 AutoReadLock treeLock (this->treeLock());
998
999 if (mParent.isNull())
1000 {
1001 *aLogicalSize = mm.logicalSize;
1002
1003 return S_OK;
1004 }
1005 }
1006
1007 /* We assume that some backend may decide to return a meaningless value in
1008 * response to VDGetSize() for differencing hard disks and therefore
1009 * always ask the base hard disk ourselves. */
1010
1011 /* root() will do callers/locking */
1012
1013 return root()->COMGETTER (LogicalSize) (aLogicalSize);
1014}
1015
1016STDMETHODIMP HardDisk::COMGETTER(AutoReset) (BOOL *aAutoReset)
1017{
1018 CheckComArgOutPointerValid (aAutoReset);
1019
1020 AutoCaller autoCaller (this);
1021 CheckComRCReturnRC (autoCaller.rc());
1022
1023 AutoReadLock alock (this);
1024
1025 if (mParent.isNull())
1026 *aAutoReset = FALSE;
1027
1028 *aAutoReset = mm.autoReset;
1029
1030 return S_OK;
1031}
1032
1033STDMETHODIMP HardDisk::COMSETTER(AutoReset) (BOOL aAutoReset)
1034{
1035 AutoCaller autoCaller (this);
1036 CheckComRCReturnRC (autoCaller.rc());
1037
1038 /* VirtualBox::saveSettings() needs a write lock */
1039 AutoMultiWriteLock2 alock (mVirtualBox, this);
1040
1041 if (mParent.isNull())
1042 return setError (VBOX_E_NOT_SUPPORTED,
1043 tr ("Hard disk '%ls' is not differencing"),
1044 m.locationFull.raw());
1045
1046 if (mm.autoReset != aAutoReset)
1047 {
1048 mm.autoReset = aAutoReset;
1049
1050 return mVirtualBox->saveSettings();
1051 }
1052
1053 return S_OK;
1054}
1055
1056// IHardDisk methods
1057////////////////////////////////////////////////////////////////////////////////
1058
1059STDMETHODIMP HardDisk::GetProperty (IN_BSTR aName, BSTR *aValue)
1060{
1061 CheckComArgStrNotEmptyOrNull (aName);
1062 CheckComArgOutPointerValid (aValue);
1063
1064 AutoCaller autoCaller (this);
1065 CheckComRCReturnRC (autoCaller.rc());
1066
1067 AutoReadLock alock (this);
1068
1069 Data::PropertyMap::const_iterator it = mm.properties.find (Bstr (aName));
1070 if (it == mm.properties.end())
1071 return setError (VBOX_E_OBJECT_NOT_FOUND,
1072 tr ("Property '%ls' does not exist"), aName);
1073
1074 it->second.cloneTo (aValue);
1075
1076 return S_OK;
1077}
1078
1079STDMETHODIMP HardDisk::SetProperty (IN_BSTR aName, IN_BSTR aValue)
1080{
1081 CheckComArgStrNotEmptyOrNull (aName);
1082
1083 AutoCaller autoCaller (this);
1084 CheckComRCReturnRC (autoCaller.rc());
1085
1086 /* VirtualBox::saveSettings() needs a write lock */
1087 AutoMultiWriteLock2 alock (mVirtualBox, this);
1088
1089 switch (m.state)
1090 {
1091 case MediaState_Created:
1092 case MediaState_Inaccessible:
1093 break;
1094 default:
1095 return setStateError();
1096 }
1097
1098 Data::PropertyMap::iterator it = mm.properties.find (Bstr (aName));
1099 if (it == mm.properties.end())
1100 return setError (VBOX_E_OBJECT_NOT_FOUND,
1101 tr ("Property '%ls' does not exist"), aName);
1102
1103 it->second = aValue;
1104
1105 HRESULT rc = mVirtualBox->saveSettings();
1106
1107 return rc;
1108}
1109
1110STDMETHODIMP HardDisk::GetProperties(IN_BSTR aNames,
1111 ComSafeArrayOut (BSTR, aReturnNames),
1112 ComSafeArrayOut (BSTR, aReturnValues))
1113{
1114 CheckComArgOutSafeArrayPointerValid (aReturnNames);
1115 CheckComArgOutSafeArrayPointerValid (aReturnValues);
1116
1117 AutoCaller autoCaller (this);
1118 CheckComRCReturnRC (autoCaller.rc());
1119
1120 AutoReadLock alock (this);
1121
1122 /// @todo make use of aNames according to the documentation
1123 NOREF (aNames);
1124
1125 com::SafeArray <BSTR> names (mm.properties.size());
1126 com::SafeArray <BSTR> values (mm.properties.size());
1127 size_t i = 0;
1128
1129 for (Data::PropertyMap::const_iterator it = mm.properties.begin();
1130 it != mm.properties.end(); ++ it)
1131 {
1132 it->first.cloneTo (&names [i]);
1133 it->second.cloneTo (&values [i]);
1134 ++ i;
1135 }
1136
1137 names.detachTo (ComSafeArrayOutArg (aReturnNames));
1138 values.detachTo (ComSafeArrayOutArg (aReturnValues));
1139
1140 return S_OK;
1141}
1142
1143STDMETHODIMP HardDisk::SetProperties(ComSafeArrayIn (IN_BSTR, aNames),
1144 ComSafeArrayIn (IN_BSTR, aValues))
1145{
1146 CheckComArgSafeArrayNotNull (aNames);
1147 CheckComArgSafeArrayNotNull (aValues);
1148
1149 AutoCaller autoCaller (this);
1150 CheckComRCReturnRC (autoCaller.rc());
1151
1152 /* VirtualBox::saveSettings() needs a write lock */
1153 AutoMultiWriteLock2 alock (mVirtualBox, this);
1154
1155 com::SafeArray <IN_BSTR> names (ComSafeArrayInArg (aNames));
1156 com::SafeArray <IN_BSTR> values (ComSafeArrayInArg (aValues));
1157
1158 /* first pass: validate names */
1159 for (size_t i = 0; i < names.size(); ++ i)
1160 {
1161 if (mm.properties.find (Bstr (names [i])) == mm.properties.end())
1162 return setError (VBOX_E_OBJECT_NOT_FOUND,
1163 tr ("Property '%ls' does not exist"), names [i]);
1164 }
1165
1166 /* second pass: assign */
1167 for (size_t i = 0; i < names.size(); ++ i)
1168 {
1169 Data::PropertyMap::iterator it = mm.properties.find (Bstr (names [i]));
1170 AssertReturn (it != mm.properties.end(), E_FAIL);
1171
1172 it->second = values [i];
1173 }
1174
1175 HRESULT rc = mVirtualBox->saveSettings();
1176
1177 return rc;
1178}
1179
1180STDMETHODIMP HardDisk::CreateBaseStorage(ULONG64 aLogicalSize,
1181 HardDiskVariant_T aVariant,
1182 IProgress **aProgress)
1183{
1184 CheckComArgOutPointerValid (aProgress);
1185
1186 AutoCaller autoCaller (this);
1187 CheckComRCReturnRC (autoCaller.rc());
1188
1189 AutoWriteLock alock (this);
1190
1191 aVariant = (HardDiskVariant_T)((unsigned)aVariant & (unsigned)~HardDiskVariant_Diff);
1192 if ( !(aVariant & HardDiskVariant_Fixed)
1193 && !(mm.formatObj->capabilities() & HardDiskFormatCapabilities_CreateDynamic))
1194 return setError (VBOX_E_NOT_SUPPORTED,
1195 tr ("Hard disk format '%ls' does not support dynamic storage "
1196 "creation"), mm.format.raw());
1197 if ( (aVariant & HardDiskVariant_Fixed)
1198 && !(mm.formatObj->capabilities() & HardDiskFormatCapabilities_CreateDynamic))
1199 return setError (VBOX_E_NOT_SUPPORTED,
1200 tr ("Hard disk format '%ls' does not support fixed storage "
1201 "creation"), mm.format.raw());
1202
1203 switch (m.state)
1204 {
1205 case MediaState_NotCreated:
1206 break;
1207 default:
1208 return setStateError();
1209 }
1210
1211 ComObjPtr <Progress> progress;
1212 progress.createObject();
1213 /// @todo include fixed/dynamic
1214 HRESULT rc = progress->init (mVirtualBox, static_cast<IHardDisk*>(this),
1215 (aVariant & HardDiskVariant_Fixed)
1216 ? BstrFmt (tr ("Creating fixed hard disk storage unit '%ls'"), m.locationFull.raw())
1217 : BstrFmt (tr ("Creating dynamic hard disk storage unit '%ls'"), m.locationFull.raw()),
1218 FALSE /* aCancelable */);
1219 CheckComRCReturnRC (rc);
1220
1221 /* setup task object and thread to carry out the operation
1222 * asynchronously */
1223
1224 std::auto_ptr <Task> task (new Task (this, progress, Task::CreateBase));
1225 AssertComRCReturnRC (task->autoCaller.rc());
1226
1227 task->d.size = aLogicalSize;
1228 task->d.variant = aVariant;
1229
1230 rc = task->startThread();
1231 CheckComRCReturnRC (rc);
1232
1233 /* go to Creating state on success */
1234 m.state = MediaState_Creating;
1235
1236 /* task is now owned by taskThread() so release it */
1237 task.release();
1238
1239 /* return progress to the caller */
1240 progress.queryInterfaceTo (aProgress);
1241
1242 return S_OK;
1243}
1244
1245STDMETHODIMP HardDisk::DeleteStorage (IProgress **aProgress)
1246{
1247 CheckComArgOutPointerValid (aProgress);
1248
1249 AutoCaller autoCaller (this);
1250 CheckComRCReturnRC (autoCaller.rc());
1251
1252 ComObjPtr <Progress> progress;
1253
1254 HRESULT rc = deleteStorageNoWait (progress);
1255 if (SUCCEEDED (rc))
1256 {
1257 /* return progress to the caller */
1258 progress.queryInterfaceTo (aProgress);
1259 }
1260
1261 return rc;
1262}
1263
1264STDMETHODIMP HardDisk::CreateDiffStorage (IHardDisk *aTarget,
1265 HardDiskVariant_T aVariant,
1266 IProgress **aProgress)
1267{
1268 CheckComArgNotNull (aTarget);
1269 CheckComArgOutPointerValid (aProgress);
1270
1271 AutoCaller autoCaller (this);
1272 CheckComRCReturnRC (autoCaller.rc());
1273
1274 ComObjPtr<HardDisk> diff;
1275 HRESULT rc = mVirtualBox->cast (aTarget, diff);
1276 CheckComRCReturnRC (rc);
1277
1278 AutoWriteLock alock (this);
1279
1280 if (mm.type == HardDiskType_Writethrough)
1281 return setError (E_FAIL,
1282 tr ("Hard disk '%ls' is Writethrough"),
1283 m.locationFull.raw());
1284
1285 /* We want to be locked for reading as long as our diff child is being
1286 * created */
1287 rc = LockRead (NULL);
1288 CheckComRCReturnRC (rc);
1289
1290 ComObjPtr <Progress> progress;
1291
1292 rc = createDiffStorageNoWait (diff, aVariant, progress);
1293 if (FAILED (rc))
1294 {
1295 HRESULT rc2 = UnlockRead (NULL);
1296 AssertComRC (rc2);
1297 /* Note: on success, taskThread() will unlock this */
1298 }
1299 else
1300 {
1301 /* return progress to the caller */
1302 progress.queryInterfaceTo (aProgress);
1303 }
1304
1305 return rc;
1306}
1307
1308STDMETHODIMP HardDisk::MergeTo (IN_GUID /* aTargetId */, IProgress ** /* aProgress */)
1309{
1310 AutoCaller autoCaller (this);
1311 CheckComRCReturnRC (autoCaller.rc());
1312
1313 ReturnComNotImplemented();
1314}
1315
1316STDMETHODIMP HardDisk::CloneTo (IHardDisk *aTarget,
1317 HardDiskVariant_T aVariant,
1318 IProgress **aProgress)
1319{
1320 CheckComArgNotNull (aTarget);
1321 CheckComArgOutPointerValid (aProgress);
1322
1323 AutoCaller autoCaller (this);
1324 CheckComRCReturnRC (autoCaller.rc());
1325
1326 ComObjPtr <HardDisk> target;
1327 HRESULT rc = mVirtualBox->cast (aTarget, target);
1328 CheckComRCReturnRC (rc);
1329
1330 AutoMultiWriteLock2 alock (this, target);
1331
1332 /* We want to be locked for reading as long as the clone hard disk is being
1333 * created*/
1334 rc = LockRead (NULL);
1335 CheckComRCReturnRC (rc);
1336
1337 ComObjPtr <Progress> progress;
1338
1339 try
1340 {
1341 if (target->m.state != MediaState_NotCreated)
1342 throw target->setStateError();
1343
1344 progress.createObject();
1345 rc = progress->init (mVirtualBox, static_cast <IHardDisk *> (this),
1346 BstrFmt (tr ("Creating clone hard disk '%ls'"),
1347 target->m.locationFull.raw()),
1348 FALSE /* aCancelable */);
1349 CheckComRCThrowRC (rc);
1350
1351 /* setup task object and thread to carry out the operation
1352 * asynchronously */
1353
1354 std::auto_ptr <Task> task (new Task (this, progress, Task::Clone));
1355 AssertComRCThrowRC (task->autoCaller.rc());
1356
1357 task->setData (target);
1358 task->d.variant = aVariant;
1359
1360 rc = task->startThread();
1361 CheckComRCThrowRC (rc);
1362
1363 /* go to Creating state before leaving the lock */
1364 target->m.state = MediaState_Creating;
1365
1366 /* task is now owned (or already deleted) by taskThread() so release it */
1367 task.release();
1368 }
1369 catch (HRESULT aRC)
1370 {
1371 rc = aRC;
1372 }
1373
1374 if (FAILED (rc))
1375 {
1376 HRESULT rc2 = UnlockRead (NULL);
1377 AssertComRC (rc2);
1378 /* Note: on success, taskThread() will unlock this */
1379 }
1380 else
1381 {
1382 /* return progress to the caller */
1383 progress.queryInterfaceTo (aProgress);
1384 }
1385
1386 return rc;
1387}
1388
1389STDMETHODIMP HardDisk::FlattenTo (IHardDisk *aTarget,
1390 HardDiskVariant_T /* aVariant */,
1391 IProgress **aProgress)
1392{
1393 CheckComArgNotNull (aTarget);
1394 CheckComArgOutPointerValid (aProgress);
1395
1396 AutoCaller autoCaller (this);
1397 CheckComRCReturnRC (autoCaller.rc());
1398
1399 ReturnComNotImplemented();
1400}
1401
1402STDMETHODIMP HardDisk::Compact (IProgress **aProgress)
1403{
1404 CheckComArgOutPointerValid (aProgress);
1405
1406 AutoCaller autoCaller (this);
1407 CheckComRCReturnRC (autoCaller.rc());
1408
1409 ReturnComNotImplemented();
1410}
1411
1412STDMETHODIMP HardDisk::Reset (IProgress **aProgress)
1413{
1414 CheckComArgOutPointerValid (aProgress);
1415
1416 AutoCaller autoCaller (this);
1417 CheckComRCReturnRC (autoCaller.rc());
1418
1419 AutoWriteLock alock (this);
1420
1421 if (mParent.isNull())
1422 return setError (VBOX_E_NOT_SUPPORTED,
1423 tr ("Hard disk '%ls' is not differencing"),
1424 m.locationFull.raw());
1425
1426 HRESULT rc = canClose();
1427 CheckComRCReturnRC (rc);
1428
1429 rc = LockWrite (NULL);
1430 CheckComRCReturnRC (rc);
1431
1432 ComObjPtr <Progress> progress;
1433
1434 try
1435 {
1436 progress.createObject();
1437 rc = progress->init (mVirtualBox, static_cast <IHardDisk *> (this),
1438 BstrFmt (tr ("Resetting differencing hard disk '%ls'"),
1439 m.locationFull.raw()),
1440 FALSE /* aCancelable */);
1441 CheckComRCThrowRC (rc);
1442
1443 /* setup task object and thread to carry out the operation
1444 * asynchronously */
1445
1446 std::auto_ptr <Task> task (new Task (this, progress, Task::Reset));
1447 AssertComRCThrowRC (task->autoCaller.rc());
1448
1449 rc = task->startThread();
1450 CheckComRCThrowRC (rc);
1451
1452 /* task is now owned (or already deleted) by taskThread() so release it */
1453 task.release();
1454 }
1455 catch (HRESULT aRC)
1456 {
1457 rc = aRC;
1458 }
1459
1460 if (FAILED (rc))
1461 {
1462 HRESULT rc2 = UnlockWrite (NULL);
1463 AssertComRC (rc2);
1464 /* Note: on success, taskThread() will unlock this */
1465 }
1466 else
1467 {
1468 /* return progress to the caller */
1469 progress.queryInterfaceTo (aProgress);
1470 }
1471
1472 return rc;
1473}
1474
1475// public methods for internal purposes only
1476////////////////////////////////////////////////////////////////////////////////
1477
1478/**
1479 * Checks if the given change of \a aOldPath to \a aNewPath affects the location
1480 * of this hard disk or any its child and updates the paths if necessary to
1481 * reflect the new location.
1482 *
1483 * @param aOldPath Old path (full).
1484 * @param aNewPath New path (full).
1485 *
1486 * @note Locks treeLock() for reading, this object and all children for writing.
1487 */
1488void HardDisk::updatePaths (const char *aOldPath, const char *aNewPath)
1489{
1490 AssertReturnVoid (aOldPath);
1491 AssertReturnVoid (aNewPath);
1492
1493 AutoCaller autoCaller (this);
1494 AssertComRCReturnVoid (autoCaller.rc());
1495
1496 AutoWriteLock alock (this);
1497
1498 /* we access children() */
1499 AutoReadLock treeLock (this->treeLock());
1500
1501 updatePath (aOldPath, aNewPath);
1502
1503 /* update paths of all children */
1504 for (List::const_iterator it = children().begin();
1505 it != children().end();
1506 ++ it)
1507 {
1508 (*it)->updatePaths (aOldPath, aNewPath);
1509 }
1510}
1511
1512/**
1513 * Returns the base hard disk of the hard disk chain this hard disk is part of.
1514 *
1515 * The root hard disk is found by walking up the parent-child relationship axis.
1516 * If the hard disk doesn't have a parent (i.e. it's a base hard disk), it
1517 * returns itself in response to this method.
1518 *
1519 * @param aLevel Where to store the number of ancestors of this hard disk
1520 * (zero for the root), may be @c NULL.
1521 *
1522 * @note Locks treeLock() for reading.
1523 */
1524ComObjPtr <HardDisk> HardDisk::root (uint32_t *aLevel /*= NULL*/)
1525{
1526 ComObjPtr <HardDisk> root;
1527 uint32_t level;
1528
1529 AutoCaller autoCaller (this);
1530 AssertReturn (autoCaller.isOk(), root);
1531
1532 /* we access mParent */
1533 AutoReadLock treeLock (this->treeLock());
1534
1535 root = this;
1536 level = 0;
1537
1538 if (!mParent.isNull())
1539 {
1540 for (;;)
1541 {
1542 AutoCaller rootCaller (root);
1543 AssertReturn (rootCaller.isOk(), root);
1544
1545 if (root->mParent.isNull())
1546 break;
1547
1548 root = root->mParent;
1549 ++ level;
1550 }
1551 }
1552
1553 if (aLevel != NULL)
1554 *aLevel = level;
1555
1556 return root;
1557}
1558
1559/**
1560 * Returns @c true if this hard disk cannot be modified because it has
1561 * dependants (children) or is part of the snapshot. Related to the hard disk
1562 * type and posterity, not to the current media state.
1563 *
1564 * @note Locks this object and treeLock() for reading.
1565 */
1566bool HardDisk::isReadOnly()
1567{
1568 AutoCaller autoCaller (this);
1569 AssertComRCReturn (autoCaller.rc(), false);
1570
1571 AutoReadLock alock (this);
1572
1573 /* we access children */
1574 AutoReadLock treeLock (this->treeLock());
1575
1576 switch (mm.type)
1577 {
1578 case HardDiskType_Normal:
1579 {
1580 if (children().size() != 0)
1581 return true;
1582
1583 for (BackRefList::const_iterator it = m.backRefs.begin();
1584 it != m.backRefs.end(); ++ it)
1585 if (it->snapshotIds.size() != 0)
1586 return true;
1587
1588 return false;
1589 }
1590 case HardDiskType_Immutable:
1591 {
1592 return true;
1593 }
1594 case HardDiskType_Writethrough:
1595 {
1596 return false;
1597 }
1598 default:
1599 break;
1600 }
1601
1602 AssertFailedReturn (false);
1603}
1604
1605/**
1606 * Saves hard disk data by appending a new <HardDisk> child node to the given
1607 * parent node which can be either <HardDisks> or <HardDisk>.
1608 *
1609 * @param aaParentNode Parent <HardDisks> or <HardDisk> node.
1610 *
1611 * @note Locks this object, treeLock() and children for reading.
1612 */
1613HRESULT HardDisk::saveSettings (settings::Key &aParentNode)
1614{
1615 using namespace settings;
1616
1617 AssertReturn (!aParentNode.isNull(), E_FAIL);
1618
1619 AutoCaller autoCaller (this);
1620 CheckComRCReturnRC (autoCaller.rc());
1621
1622 AutoReadLock alock (this);
1623
1624 /* we access mParent */
1625 AutoReadLock treeLock (this->treeLock());
1626
1627 Key diskNode = aParentNode.appendKey ("HardDisk");
1628 /* required */
1629 diskNode.setValue <Guid> ("uuid", m.id);
1630 /* required (note: the original locaiton, not full) */
1631 diskNode.setValue <Bstr> ("location", m.location);
1632 /* required */
1633 diskNode.setValue <Bstr> ("format", mm.format);
1634 /* optional, only for diffs, default is false */
1635 if (!mParent.isNull())
1636 diskNode.setValueOr <bool> ("autoReset", !!mm.autoReset, false);
1637 /* optional */
1638 if (!m.description.isNull())
1639 {
1640 Key descNode = diskNode.createKey ("Description");
1641 descNode.setKeyValue <Bstr> (m.description);
1642 }
1643
1644 /* optional properties */
1645 for (Data::PropertyMap::const_iterator it = mm.properties.begin();
1646 it != mm.properties.end(); ++ it)
1647 {
1648 /* only save properties that have non-default values */
1649 if (!it->second.isNull())
1650 {
1651 Key propNode = diskNode.appendKey ("Property");
1652 propNode.setValue <Bstr> ("name", it->first);
1653 propNode.setValue <Bstr> ("value", it->second);
1654 }
1655 }
1656
1657 /* only for base hard disks */
1658 if (mParent.isNull())
1659 {
1660 const char *type =
1661 mm.type == HardDiskType_Normal ? "Normal" :
1662 mm.type == HardDiskType_Immutable ? "Immutable" :
1663 mm.type == HardDiskType_Writethrough ? "Writethrough" : NULL;
1664 Assert (type != NULL);
1665 diskNode.setStringValue ("type", type);
1666 }
1667
1668 /* save all children */
1669 for (List::const_iterator it = children().begin();
1670 it != children().end();
1671 ++ it)
1672 {
1673 HRESULT rc = (*it)->saveSettings (diskNode);
1674 AssertComRCReturnRC (rc);
1675 }
1676
1677 return S_OK;
1678}
1679
1680/**
1681 * Compares the location of this hard disk to the given location.
1682 *
1683 * The comparison takes the location details into account. For example, if the
1684 * location is a file in the host's filesystem, a case insensitive comparison
1685 * will be performed for case insensitive filesystems.
1686 *
1687 * @param aLocation Location to compare to (as is).
1688 * @param aResult Where to store the result of comparison: 0 if locations
1689 * are equal, 1 if this object's location is greater than
1690 * the specified location, and -1 otherwise.
1691 */
1692HRESULT HardDisk::compareLocationTo (const char *aLocation, int &aResult)
1693{
1694 AutoCaller autoCaller (this);
1695 AssertComRCReturnRC (autoCaller.rc());
1696
1697 AutoReadLock alock (this);
1698
1699 Utf8Str locationFull (m.locationFull);
1700
1701 /// @todo NEWMEDIA delegate the comparison to the backend?
1702
1703 if (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File)
1704 {
1705 Utf8Str location (aLocation);
1706
1707 /* For locations represented by files, append the default path if
1708 * only the name is given, and then get the full path. */
1709 if (!RTPathHavePath (aLocation))
1710 {
1711 AutoReadLock propsLock (mVirtualBox->systemProperties());
1712 location = Utf8StrFmt ("%ls%c%s",
1713 mVirtualBox->systemProperties()->defaultHardDiskFolder().raw(),
1714 RTPATH_DELIMITER, aLocation);
1715 }
1716
1717 int vrc = mVirtualBox->calculateFullPath (location, location);
1718 if (RT_FAILURE (vrc))
1719 return setError (E_FAIL,
1720 tr ("Invalid hard disk storage file location '%s' (%Rrc)"),
1721 location.raw(), vrc);
1722
1723 aResult = RTPathCompare (locationFull, location);
1724 }
1725 else
1726 aResult = locationFull.compare (aLocation);
1727
1728 return S_OK;
1729}
1730
1731/**
1732 * Returns a short version of the location attribute.
1733 *
1734 * Reimplements MediumBase::name() to specially treat non-FS-path locations.
1735 *
1736 * @note Must be called from under this object's read or write lock.
1737 */
1738Utf8Str HardDisk::name()
1739{
1740 /// @todo NEWMEDIA treat non-FS-paths specially! (may require to requiest
1741 /// this information from the VD backend)
1742
1743 Utf8Str location (m.locationFull);
1744
1745 Utf8Str name = RTPathFilename (location);
1746 return name;
1747}
1748
1749/**
1750 * Checks that this hard disk may be discarded and performs necessary state
1751 * changes.
1752 *
1753 * This method is to be called prior to calling the #discrad() to perform
1754 * necessary consistency checks and place involved hard disks to appropriate
1755 * states. If #discard() is not called or fails, the state modifications
1756 * performed by this method must be undone by #cancelDiscard().
1757 *
1758 * See #discard() for more info about discarding hard disks.
1759 *
1760 * @param aChain Where to store the created merge chain (may return NULL
1761 * if no real merge is necessary).
1762 *
1763 * @note Locks treeLock() for reading. Locks this object, aTarget and all
1764 * intermediate hard disks for writing.
1765 */
1766HRESULT HardDisk::prepareDiscard (MergeChain * &aChain)
1767{
1768 AutoCaller autoCaller (this);
1769 AssertComRCReturnRC (autoCaller.rc());
1770
1771 aChain = NULL;
1772
1773 AutoWriteLock alock (this);
1774
1775 /* we access mParent & children() */
1776 AutoReadLock treeLock (this->treeLock());
1777
1778 AssertReturn (mm.type == HardDiskType_Normal, E_FAIL);
1779
1780 if (children().size() == 0)
1781 {
1782 /* special treatment of the last hard disk in the chain: */
1783
1784 if (mParent.isNull())
1785 {
1786 /* lock only, to prevent any usage; discard() will unlock */
1787 return LockWrite (NULL);
1788 }
1789
1790 /* the differencing hard disk w/o children will be deleted, protect it
1791 * from attaching to other VMs (this is why Deleting) */
1792
1793 switch (m.state)
1794 {
1795 case MediaState_Created:
1796 m.state = MediaState_Deleting;
1797 break;
1798 default:
1799 return setStateError();
1800 }
1801
1802 /* aChain is intentionally NULL here */
1803
1804 return S_OK;
1805 }
1806
1807 /* not going multi-merge as it's too expensive */
1808 if (children().size() > 1)
1809 return setError (E_FAIL,
1810 tr ("Hard disk '%ls' has more than one child hard disk (%d)"),
1811 m.locationFull.raw(), children().size());
1812
1813 /* this is a read-only hard disk with children; it must be associated with
1814 * exactly one snapshot (when the snapshot is being taken, none of the
1815 * current VM's hard disks may be attached to other VMs). Note that by the
1816 * time when discard() is called, there must be no any attachments at all
1817 * (the code calling prepareDiscard() should detach). */
1818 AssertReturn (m.backRefs.size() == 1 &&
1819 !m.backRefs.front().inCurState &&
1820 m.backRefs.front().snapshotIds.size() == 1, E_FAIL);
1821
1822 ComObjPtr<HardDisk> child = children().front();
1823
1824 /* we keep this locked, so lock the affected child to make sure the lock
1825 * order is correct when calling prepareMergeTo() */
1826 AutoWriteLock childLock (child);
1827
1828 /* delegate the rest to the profi */
1829 if (mParent.isNull())
1830 {
1831 /* base hard disk, backward merge */
1832
1833 Assert (child->m.backRefs.size() == 1);
1834 if (child->m.backRefs.front().machineId != m.backRefs.front().machineId)
1835 {
1836 /* backward merge is too tricky, we'll just detach on discard, so
1837 * lock only, to prevent any usage; discard() will only unlock
1838 * (since we return NULL in aChain) */
1839 return LockWrite (NULL);
1840 }
1841
1842 return child->prepareMergeTo (this, aChain,
1843 true /* aIgnoreAttachments */);
1844 }
1845 else
1846 {
1847 /* forward merge */
1848 return prepareMergeTo (child, aChain,
1849 true /* aIgnoreAttachments */);
1850 }
1851}
1852
1853/**
1854 * Discards this hard disk.
1855 *
1856 * Discarding the hard disk is merging its contents to its differencing child
1857 * hard disk (forward merge) or contents of its child hard disk to itself
1858 * (backward merge) if this hard disk is a base hard disk. If this hard disk is
1859 * a differencing hard disk w/o children, then it will be simply deleted.
1860 * Calling this method on a base hard disk w/o children will do nothing and
1861 * silently succeed. If this hard disk has more than one child, the method will
1862 * currently return an error (since merging in this case would be too expensive
1863 * and result in data duplication).
1864 *
1865 * When the backward merge takes place (i.e. this hard disk is a target) then,
1866 * on success, this hard disk will automatically replace the differencing child
1867 * hard disk used as a source (which will then be deleted) in the attachment
1868 * this child hard disk is associated with. This will happen only if both hard
1869 * disks belong to the same machine because otherwise such a replace would be
1870 * too tricky and could be not expected by the other machine. Same relates to a
1871 * case when the child hard disk is not associated with any machine at all. When
1872 * the backward merge is not applied, the method behaves as if the base hard
1873 * disk were not attached at all -- i.e. simply detaches it from the machine but
1874 * leaves the hard disk chain intact.
1875 *
1876 * This method is basically a wrapper around #mergeTo() that selects the correct
1877 * merge direction and performs additional actions as described above and.
1878 *
1879 * Note that this method will not return until the merge operation is complete
1880 * (which may be quite time consuming depending on the size of the merged hard
1881 * disks).
1882 *
1883 * Note that #prepareDiscard() must be called before calling this method. If
1884 * this method returns a failure, the caller must call #cancelDiscard(). On
1885 * success, #cancelDiscard() must not be called (this method will perform all
1886 * necessary steps such as resetting states of all involved hard disks and
1887 * deleting @a aChain).
1888 *
1889 * @param aChain Merge chain created by #prepareDiscard() (may be NULL if
1890 * no real merge takes place).
1891 *
1892 * @note Locks the hard disks from the chain for writing. Locks the machine
1893 * object when the backward merge takes place. Locks treeLock() lock for
1894 * reading or writing.
1895 */
1896HRESULT HardDisk::discard (ComObjPtr <Progress> &aProgress, MergeChain *aChain)
1897{
1898 AssertReturn (!aProgress.isNull(), E_FAIL);
1899
1900 ComObjPtr <HardDisk> hdFrom;
1901
1902 HRESULT rc = S_OK;
1903
1904 {
1905 AutoCaller autoCaller (this);
1906 AssertComRCReturnRC (autoCaller.rc());
1907
1908 aProgress->advanceOperation (BstrFmt (
1909 tr ("Discarding hard disk '%s'"), name().raw()));
1910
1911 if (aChain == NULL)
1912 {
1913 AutoWriteLock alock (this);
1914
1915 /* we access mParent & children() */
1916 AutoReadLock treeLock (this->treeLock());
1917
1918 Assert (children().size() == 0);
1919
1920 /* special treatment of the last hard disk in the chain: */
1921
1922 if (mParent.isNull())
1923 {
1924 rc = UnlockWrite (NULL);
1925 AssertComRC (rc);
1926 return rc;
1927 }
1928
1929 /* delete the differencing hard disk w/o children */
1930
1931 Assert (m.state == MediaState_Deleting);
1932
1933 /* go back to Created since deleteStorage() expects this state */
1934 m.state = MediaState_Created;
1935
1936 hdFrom = this;
1937
1938 rc = deleteStorageAndWait (&aProgress);
1939 }
1940 else
1941 {
1942 hdFrom = aChain->source();
1943
1944 rc = hdFrom->mergeToAndWait (aChain, &aProgress);
1945 }
1946 }
1947
1948 if (SUCCEEDED (rc))
1949 {
1950 /* mergeToAndWait() cannot uninitialize the initiator because of
1951 * possible AutoCallers on the current thread, deleteStorageAndWait()
1952 * doesn't do it either; do it ourselves */
1953 hdFrom->uninit();
1954 }
1955
1956 return rc;
1957}
1958
1959/**
1960 * Undoes what #prepareDiscard() did. Must be called if #discard() is not called
1961 * or fails. Frees memory occupied by @a aChain.
1962 *
1963 * @param aChain Merge chain created by #prepareDiscard() (may be NULL if
1964 * no real merge takes place).
1965 *
1966 * @note Locks the hard disks from the chain for writing. Locks treeLock() for
1967 * reading.
1968 */
1969void HardDisk::cancelDiscard (MergeChain *aChain)
1970{
1971 AutoCaller autoCaller (this);
1972 AssertComRCReturnVoid (autoCaller.rc());
1973
1974 if (aChain == NULL)
1975 {
1976 AutoWriteLock alock (this);
1977
1978 /* we access mParent & children() */
1979 AutoReadLock treeLock (this->treeLock());
1980
1981 Assert (children().size() == 0);
1982
1983 /* special treatment of the last hard disk in the chain: */
1984
1985 if (mParent.isNull())
1986 {
1987 HRESULT rc = UnlockWrite (NULL);
1988 AssertComRC (rc);
1989 return;
1990 }
1991
1992 /* the differencing hard disk w/o children will be deleted, protect it
1993 * from attaching to other VMs (this is why Deleting) */
1994
1995 Assert (m.state == MediaState_Deleting);
1996 m.state = MediaState_Created;
1997
1998 return;
1999 }
2000
2001 /* delegate the rest to the profi */
2002 cancelMergeTo (aChain);
2003}
2004
2005/**
2006 * Returns a preferred format for differencing hard disks.
2007 */
2008Bstr HardDisk::preferredDiffFormat()
2009{
2010 Bstr format;
2011
2012 AutoCaller autoCaller (this);
2013 AssertComRCReturn (autoCaller.rc(), format);
2014
2015 /* mm.format is const, no need to lock */
2016 format = mm.format;
2017
2018 /* check that our own format supports diffs */
2019 if (!(mm.formatObj->capabilities() & HardDiskFormatCapabilities_Differencing))
2020 {
2021 /* use the default format if not */
2022 AutoReadLock propsLock (mVirtualBox->systemProperties());
2023 format = mVirtualBox->systemProperties()->defaultHardDiskFormat();
2024 }
2025
2026 return format;
2027}
2028
2029// protected methods
2030////////////////////////////////////////////////////////////////////////////////
2031
2032/**
2033 * Deletes the hard disk storage unit.
2034 *
2035 * If @a aProgress is not NULL but the object it points to is @c null then a new
2036 * progress object will be created and assigned to @a *aProgress on success,
2037 * otherwise the existing progress object is used. If Progress is NULL, then no
2038 * progress object is created/used at all.
2039 *
2040 * When @a aWait is @c false, this method will create a thread to perform the
2041 * delete operation asynchronously and will return immediately. Otherwise, it
2042 * will perform the operation on the calling thread and will not return to the
2043 * caller until the operation is completed. Note that @a aProgress cannot be
2044 * NULL when @a aWait is @c false (this method will assert in this case).
2045 *
2046 * @param aProgress Where to find/store a Progress object to track operation
2047 * completion.
2048 * @param aWait @c true if this method should block instead of creating
2049 * an asynchronous thread.
2050 *
2051 * @note Locks mVirtualBox and this object for writing. Locks treeLock() for
2052 * writing.
2053 */
2054HRESULT HardDisk::deleteStorage (ComObjPtr <Progress> *aProgress, bool aWait)
2055{
2056 AssertReturn (aProgress != NULL || aWait == true, E_FAIL);
2057
2058 /* unregisterWithVirtualBox() needs a write lock. We want to unregister
2059 * ourselves atomically after detecting that deletion is possible to make
2060 * sure that we don't do that after another thread has done
2061 * VirtualBox::findHardDisk() but before it starts using us (provided that
2062 * it holds a mVirtualBox lock too of course). */
2063
2064 AutoWriteLock vboxLock (mVirtualBox);
2065
2066 AutoWriteLock alock (this);
2067
2068 if (!(mm.formatObj->capabilities() &
2069 (HardDiskFormatCapabilities_CreateDynamic |
2070 HardDiskFormatCapabilities_CreateFixed)))
2071 return setError (VBOX_E_NOT_SUPPORTED,
2072 tr ("Hard disk format '%ls' does not support storage deletion"),
2073 mm.format.raw());
2074
2075 /* Note that we are fine with Inaccessible state too: a) for symmetry with
2076 * create calls and b) because it doesn't really harm to try, if it is
2077 * really inaccessibke, the delete operation will fail anyway. Accepting
2078 * Inaccessible state is especially important because all registered hard
2079 * disks are initially Inaccessible upon VBoxSVC startup until
2080 * COMGETTER(State) is called. */
2081
2082 switch (m.state)
2083 {
2084 case MediaState_Created:
2085 case MediaState_Inaccessible:
2086 break;
2087 default:
2088 return setStateError();
2089 }
2090
2091 if (m.backRefs.size() != 0)
2092 return setError (VBOX_E_OBJECT_IN_USE,
2093 tr ("Hard disk '%ls' is attached to %d virtual machines"),
2094 m.locationFull.raw(), m.backRefs.size());
2095
2096 HRESULT rc = canClose();
2097 CheckComRCReturnRC (rc);
2098
2099 /* go to Deleting state before leaving the lock */
2100 m.state = MediaState_Deleting;
2101
2102 /* we need to leave this object's write lock now because of
2103 * unregisterWithVirtualBox() that locks treeLock() for writing */
2104 alock.leave();
2105
2106 /* try to remove from the list of known hard disks before performing actual
2107 * deletion (we favor the consistency of the media registry in the first
2108 * place which would have been broken if unregisterWithVirtualBox() failed
2109 * after we successfully deleted the storage) */
2110
2111 rc = unregisterWithVirtualBox();
2112
2113 alock.enter();
2114
2115 /* restore the state because we may fail below; we will set it later again*/
2116 m.state = MediaState_Created;
2117
2118 CheckComRCReturnRC (rc);
2119
2120 ComObjPtr <Progress> progress;
2121
2122 if (aProgress != NULL)
2123 {
2124 /* use the existing progress object... */
2125 progress = *aProgress;
2126
2127 /* ...but create a new one if it is null */
2128 if (progress.isNull())
2129 {
2130 progress.createObject();
2131 rc = progress->init (mVirtualBox, static_cast<IHardDisk*>(this),
2132 BstrFmt (tr ("Deleting hard disk storage unit '%ls'"),
2133 m.locationFull.raw()),
2134 FALSE /* aCancelable */);
2135 CheckComRCReturnRC (rc);
2136 }
2137 }
2138
2139 std::auto_ptr <Task> task (new Task (this, progress, Task::Delete));
2140 AssertComRCReturnRC (task->autoCaller.rc());
2141
2142 if (aWait)
2143 {
2144 /* go to Deleting state before starting the task */
2145 m.state = MediaState_Deleting;
2146
2147 rc = task->runNow();
2148 }
2149 else
2150 {
2151 rc = task->startThread();
2152 CheckComRCReturnRC (rc);
2153
2154 /* go to Deleting state before leaving the lock */
2155 m.state = MediaState_Deleting;
2156 }
2157
2158 /* task is now owned (or already deleted) by taskThread() so release it */
2159 task.release();
2160
2161 if (aProgress != NULL)
2162 {
2163 /* return progress to the caller */
2164 *aProgress = progress;
2165 }
2166
2167 return rc;
2168}
2169
2170/**
2171 * Creates a new differencing storage unit using the given target hard disk's
2172 * format and the location. Note that @c aTarget must be NotCreated.
2173 *
2174 * As opposed to the CreateDiffStorage() method, this method doesn't try to lock
2175 * this hard disk for reading assuming that the caller has already done so. This
2176 * is used when taking an online snaopshot (where all original hard disks are
2177 * locked for writing and must remain such). Note however that if @a aWait is
2178 * @c false and this method returns a success then the thread started by
2179 * this method will unlock the hard disk (unless it is in
2180 * MediaState_LockedWrite state) so make sure the hard disk is either in
2181 * MediaState_LockedWrite or call #LockRead() before calling this method! If @a
2182 * aWait is @c true then this method neither locks nor unlocks the hard disk, so
2183 * make sure you do it yourself as needed.
2184 *
2185 * If @a aProgress is not NULL but the object it points to is @c null then a new
2186 * progress object will be created and assigned to @a *aProgress on success,
2187 * otherwise the existing progress object is used. If @a aProgress is NULL, then no
2188 * progress object is created/used at all.
2189 *
2190 * When @a aWait is @c false, this method will create a thread to perform the
2191 * create operation asynchronously and will return immediately. Otherwise, it
2192 * will perform the operation on the calling thread and will not return to the
2193 * caller until the operation is completed. Note that @a aProgress cannot be
2194 * NULL when @a aWait is @c false (this method will assert in this case).
2195 *
2196 * @param aTarget Target hard disk.
2197 * @param aVariant Precise image variant to create.
2198 * @param aProgress Where to find/store a Progress object to track operation
2199 * completion.
2200 * @param aWait @c true if this method should block instead of creating
2201 * an asynchronous thread.
2202 *
2203 * @note Locks this object and @a aTarget for writing.
2204 */
2205HRESULT HardDisk::createDiffStorage(ComObjPtr<HardDisk> &aTarget,
2206 HardDiskVariant_T aVariant,
2207 ComObjPtr<Progress> *aProgress,
2208 bool aWait)
2209{
2210 AssertReturn (!aTarget.isNull(), E_FAIL);
2211 AssertReturn (aProgress != NULL || aWait == true, E_FAIL);
2212
2213 AutoCaller autoCaller (this);
2214 CheckComRCReturnRC (autoCaller.rc());
2215
2216 AutoCaller targetCaller (aTarget);
2217 CheckComRCReturnRC (targetCaller.rc());
2218
2219 AutoMultiWriteLock2 alock (this, aTarget);
2220
2221 AssertReturn (mm.type != HardDiskType_Writethrough, E_FAIL);
2222
2223 /* Note: MediaState_LockedWrite is ok when taking an online snapshot */
2224 AssertReturn (m.state == MediaState_LockedRead ||
2225 m.state == MediaState_LockedWrite, E_FAIL);
2226
2227 if (aTarget->m.state != MediaState_NotCreated)
2228 return aTarget->setStateError();
2229
2230 HRESULT rc = S_OK;
2231
2232 /* check that the hard disk is not attached to any VM in the current state*/
2233 for (BackRefList::const_iterator it = m.backRefs.begin();
2234 it != m.backRefs.end(); ++ it)
2235 {
2236 if (it->inCurState)
2237 {
2238 /* Note: when a VM snapshot is being taken, all normal hard disks
2239 * attached to the VM in the current state will be, as an exception,
2240 * also associated with the snapshot which is about to create (see
2241 * SnapshotMachine::init()) before deassociating them from the
2242 * current state (which takes place only on success in
2243 * Machine::fixupHardDisks()), so that the size of snapshotIds
2244 * will be 1 in this case. The given condition is used to filter out
2245 * this legal situatinon and do not report an error. */
2246
2247 if (it->snapshotIds.size() == 0)
2248 {
2249 return setError (VBOX_E_INVALID_OBJECT_STATE,
2250 tr ("Hard disk '%ls' is attached to a virtual machine "
2251 "with UUID {%RTuuid}. No differencing hard disks "
2252 "based on it may be created until it is detached"),
2253 m.locationFull.raw(), it->machineId.raw());
2254 }
2255
2256 Assert (it->snapshotIds.size() == 1);
2257 }
2258 }
2259
2260 ComObjPtr <Progress> progress;
2261
2262 if (aProgress != NULL)
2263 {
2264 /* use the existing progress object... */
2265 progress = *aProgress;
2266
2267 /* ...but create a new one if it is null */
2268 if (progress.isNull())
2269 {
2270 progress.createObject();
2271 rc = progress->init (mVirtualBox, static_cast<IHardDisk*> (this),
2272 BstrFmt (tr ("Creating differencing hard disk storage unit '%ls'"),
2273 aTarget->m.locationFull.raw()),
2274 FALSE /* aCancelable */);
2275 CheckComRCReturnRC (rc);
2276 }
2277 }
2278
2279 /* setup task object and thread to carry out the operation
2280 * asynchronously */
2281
2282 std::auto_ptr <Task> task (new Task (this, progress, Task::CreateDiff));
2283 AssertComRCReturnRC (task->autoCaller.rc());
2284
2285 task->setData (aTarget);
2286 task->d.variant = aVariant;
2287
2288 /* register a task (it will deregister itself when done) */
2289 ++ mm.numCreateDiffTasks;
2290 Assert (mm.numCreateDiffTasks != 0); /* overflow? */
2291
2292 if (aWait)
2293 {
2294 /* go to Creating state before starting the task */
2295 aTarget->m.state = MediaState_Creating;
2296
2297 rc = task->runNow();
2298 }
2299 else
2300 {
2301 rc = task->startThread();
2302 CheckComRCReturnRC (rc);
2303
2304 /* go to Creating state before leaving the lock */
2305 aTarget->m.state = MediaState_Creating;
2306 }
2307
2308 /* task is now owned (or already deleted) by taskThread() so release it */
2309 task.release();
2310
2311 if (aProgress != NULL)
2312 {
2313 /* return progress to the caller */
2314 *aProgress = progress;
2315 }
2316
2317 return rc;
2318}
2319
2320/**
2321 * Prepares this (source) hard disk, target hard disk and all intermediate hard
2322 * disks for the merge operation.
2323 *
2324 * This method is to be called prior to calling the #mergeTo() to perform
2325 * necessary consistency checks and place involved hard disks to appropriate
2326 * states. If #mergeTo() is not called or fails, the state modifications
2327 * performed by this method must be undone by #cancelMergeTo().
2328 *
2329 * Note that when @a aIgnoreAttachments is @c true then it's the caller's
2330 * responsibility to detach the source and all intermediate hard disks before
2331 * calling #mergeTo() (which will fail otherwise).
2332 *
2333 * See #mergeTo() for more information about merging.
2334 *
2335 * @param aTarget Target hard disk.
2336 * @param aChain Where to store the created merge chain.
2337 * @param aIgnoreAttachments Don't check if the source or any intermediate
2338 * hard disk is attached to any VM.
2339 *
2340 * @note Locks treeLock() for reading. Locks this object, aTarget and all
2341 * intermediate hard disks for writing.
2342 */
2343HRESULT HardDisk::prepareMergeTo(HardDisk *aTarget,
2344 MergeChain * &aChain,
2345 bool aIgnoreAttachments /*= false*/)
2346{
2347 AssertReturn (aTarget != NULL, E_FAIL);
2348
2349 AutoCaller autoCaller (this);
2350 AssertComRCReturnRC (autoCaller.rc());
2351
2352 AutoCaller targetCaller (aTarget);
2353 AssertComRCReturnRC (targetCaller.rc());
2354
2355 aChain = NULL;
2356
2357 /* we walk the tree */
2358 AutoReadLock treeLock (this->treeLock());
2359
2360 HRESULT rc = S_OK;
2361
2362 /* detect the merge direction */
2363 bool forward;
2364 {
2365 HardDisk *parent = mParent;
2366 while (parent != NULL && parent != aTarget)
2367 parent = parent->mParent;
2368 if (parent == aTarget)
2369 forward = false;
2370 else
2371 {
2372 parent = aTarget->mParent;
2373 while (parent != NULL && parent != this)
2374 parent = parent->mParent;
2375 if (parent == this)
2376 forward = true;
2377 else
2378 {
2379 Bstr tgtLoc;
2380 {
2381 AutoReadLock alock (this);
2382 tgtLoc = aTarget->locationFull();
2383 }
2384
2385 AutoReadLock alock (this);
2386 return setError (E_FAIL,
2387 tr ("Hard disks '%ls' and '%ls' are unrelated"),
2388 m.locationFull.raw(), tgtLoc.raw());
2389 }
2390 }
2391 }
2392
2393 /* build the chain (will do necessary checks and state changes) */
2394 std::auto_ptr <MergeChain> chain (new MergeChain (forward,
2395 aIgnoreAttachments));
2396 {
2397 HardDisk *last = forward ? aTarget : this;
2398 HardDisk *first = forward ? this : aTarget;
2399
2400 for (;;)
2401 {
2402 if (last == aTarget)
2403 rc = chain->addTarget (last);
2404 else if (last == this)
2405 rc = chain->addSource (last);
2406 else
2407 rc = chain->addIntermediate (last);
2408 CheckComRCReturnRC (rc);
2409
2410 if (last == first)
2411 break;
2412
2413 last = last->mParent;
2414 }
2415 }
2416
2417 aChain = chain.release();
2418
2419 return S_OK;
2420}
2421
2422/**
2423 * Merges this hard disk to the specified hard disk which must be either its
2424 * direct ancestor or descendant.
2425 *
2426 * Given this hard disk is SOURCE and the specified hard disk is TARGET, we will
2427 * get two varians of the merge operation:
2428 *
2429 * forward merge
2430 * ------------------------->
2431 * [Extra] <- SOURCE <- Intermediate <- TARGET
2432 * Any Del Del LockWr
2433 *
2434 *
2435 * backward merge
2436 * <-------------------------
2437 * TARGET <- Intermediate <- SOURCE <- [Extra]
2438 * LockWr Del Del LockWr
2439 *
2440 * Each scheme shows the involved hard disks on the hard disk chain where
2441 * SOURCE and TARGET belong. Under each hard disk there is a state value which
2442 * the hard disk must have at a time of the mergeTo() call.
2443 *
2444 * The hard disks in the square braces may be absent (e.g. when the forward
2445 * operation takes place and SOURCE is the base hard disk, or when the backward
2446 * merge operation takes place and TARGET is the last child in the chain) but if
2447 * they present they are involved too as shown.
2448 *
2449 * Nor the source hard disk neither intermediate hard disks may be attached to
2450 * any VM directly or in the snapshot, otherwise this method will assert.
2451 *
2452 * The #prepareMergeTo() method must be called prior to this method to place all
2453 * involved to necessary states and perform other consistency checks.
2454 *
2455 * If @a aWait is @c true then this method will perform the operation on the
2456 * calling thread and will not return to the caller until the operation is
2457 * completed. When this method succeeds, all intermediate hard disk objects in
2458 * the chain will be uninitialized, the state of the target hard disk (and all
2459 * involved extra hard disks) will be restored and @a aChain will be deleted.
2460 * Note that this (source) hard disk is not uninitialized because of possible
2461 * AutoCaller instances held by the caller of this method on the current thread.
2462 * It's therefore the responsibility of the caller to call HardDisk::uninit()
2463 * after releasing all callers in this case!
2464 *
2465 * If @a aWait is @c false then this method will crea,te a thread to perform the
2466 * create operation asynchronously and will return immediately. If the operation
2467 * succeeds, the thread will uninitialize the source hard disk object and all
2468 * intermediate hard disk objects in the chain, reset the state of the target
2469 * hard disk (and all involved extra hard disks) and delete @a aChain. If the
2470 * operation fails, the thread will only reset the states of all involved hard
2471 * disks and delete @a aChain.
2472 *
2473 * When this method fails (regardless of the @a aWait mode), it is a caller's
2474 * responsiblity to undo state changes and delete @a aChain using
2475 * #cancelMergeTo().
2476 *
2477 * If @a aProgress is not NULL but the object it points to is @c null then a new
2478 * progress object will be created and assigned to @a *aProgress on success,
2479 * otherwise the existing progress object is used. If Progress is NULL, then no
2480 * progress object is created/used at all. Note that @a aProgress cannot be
2481 * NULL when @a aWait is @c false (this method will assert in this case).
2482 *
2483 * @param aChain Merge chain created by #prepareMergeTo().
2484 * @param aProgress Where to find/store a Progress object to track operation
2485 * completion.
2486 * @param aWait @c true if this method should block instead of creating
2487 * an asynchronous thread.
2488 *
2489 * @note Locks the branch lock for writing. Locks the hard disks from the chain
2490 * for writing.
2491 */
2492HRESULT HardDisk::mergeTo(MergeChain *aChain,
2493 ComObjPtr <Progress> *aProgress,
2494 bool aWait)
2495{
2496 AssertReturn (aChain != NULL, E_FAIL);
2497 AssertReturn (aProgress != NULL || aWait == true, E_FAIL);
2498
2499 AutoCaller autoCaller (this);
2500 CheckComRCReturnRC (autoCaller.rc());
2501
2502 HRESULT rc = S_OK;
2503
2504 ComObjPtr <Progress> progress;
2505
2506 if (aProgress != NULL)
2507 {
2508 /* use the existing progress object... */
2509 progress = *aProgress;
2510
2511 /* ...but create a new one if it is null */
2512 if (progress.isNull())
2513 {
2514 AutoReadLock alock (this);
2515
2516 progress.createObject();
2517 rc = progress->init (mVirtualBox, static_cast<IHardDisk*>(this),
2518 BstrFmt (tr ("Merging hard disk '%s' to '%s'"),
2519 name().raw(), aChain->target()->name().raw()),
2520 FALSE /* aCancelable */);
2521 CheckComRCReturnRC (rc);
2522 }
2523 }
2524
2525 /* setup task object and thread to carry out the operation
2526 * asynchronously */
2527
2528 std::auto_ptr <Task> task (new Task (this, progress, Task::Merge));
2529 AssertComRCReturnRC (task->autoCaller.rc());
2530
2531 task->setData (aChain);
2532
2533 /* Note: task owns aChain (will delete it when not needed) in all cases
2534 * except when @a aWait is @c true and runNow() fails -- in this case
2535 * aChain will be left away because cancelMergeTo() will be applied by the
2536 * caller on it as it is required in the documentation above */
2537
2538 if (aWait)
2539 {
2540 rc = task->runNow();
2541 }
2542 else
2543 {
2544 rc = task->startThread();
2545 CheckComRCReturnRC (rc);
2546 }
2547
2548 /* task is now owned (or already deleted) by taskThread() so release it */
2549 task.release();
2550
2551 if (aProgress != NULL)
2552 {
2553 /* return progress to the caller */
2554 *aProgress = progress;
2555 }
2556
2557 return rc;
2558}
2559
2560/**
2561 * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not called
2562 * or fails. Frees memory occupied by @a aChain.
2563 *
2564 * @param aChain Merge chain created by #prepareMergeTo().
2565 *
2566 * @note Locks the hard disks from the chain for writing.
2567 */
2568void HardDisk::cancelMergeTo (MergeChain *aChain)
2569{
2570 AutoCaller autoCaller (this);
2571 AssertComRCReturnVoid (autoCaller.rc());
2572
2573 AssertReturnVoid (aChain != NULL);
2574
2575 /* the destructor will do the thing */
2576 delete aChain;
2577}
2578
2579// private methods
2580////////////////////////////////////////////////////////////////////////////////
2581
2582/**
2583 * Sets the value of m.location and calculates the value of m.locationFull.
2584 *
2585 * Reimplements MediumBase::setLocation() to specially treat non-FS-path
2586 * locations and to prepend the default hard disk folder if the given location
2587 * string does not contain any path information at all.
2588 *
2589 * Also, if the specified location is a file path that ends with '/' then the
2590 * file name part will be generated by this method automatically in the format
2591 * '{<uuid>}.<ext>' where <uuid> is a fresh UUID that this method will generate
2592 * and assign to this medium, and <ext> is the default extension for this
2593 * medium's storage format. Note that this procedure requires the media state to
2594 * be NotCreated and will return a faiulre otherwise.
2595 *
2596 * @param aLocation Location of the storage unit. If the locaiton is a FS-path,
2597 * then it can be relative to the VirtualBox home directory.
2598 *
2599 * @note Must be called from under this object's write lock.
2600 */
2601HRESULT HardDisk::setLocation (CBSTR aLocation)
2602{
2603 /// @todo so far, we assert but later it makes sense to support null
2604 /// locations for hard disks that are not yet created fail to create a
2605 /// storage unit instead
2606 CheckComArgStrNotEmptyOrNull (aLocation);
2607
2608 AutoCaller autoCaller (this);
2609 AssertComRCReturnRC (autoCaller.rc());
2610
2611 /* formatObj may be null only when initializing from an existing path and
2612 * no format is known yet */
2613 AssertReturn ((!mm.format.isNull() && !mm.formatObj.isNull()) ||
2614 (autoCaller.state() == InInit &&
2615 m.state != MediaState_NotCreated && m.id.isEmpty() &&
2616 mm.format.isNull() && mm.formatObj.isNull()),
2617 E_FAIL);
2618
2619 /* are we dealing with a new hard disk constructed using the existing
2620 * location? */
2621 bool isImport = mm.format.isNull();
2622
2623 if (isImport ||
2624 (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File))
2625 {
2626 Guid id;
2627
2628 Utf8Str location (aLocation);
2629
2630 if (m.state == MediaState_NotCreated)
2631 {
2632 /* must be a file (formatObj must be already known) */
2633 Assert (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File);
2634
2635 if (RTPathFilename (location) == NULL)
2636 {
2637 /* no file name is given (either an empty string or ends with a
2638 * slash), generate a new UUID + file name if the state allows
2639 * this */
2640
2641 ComAssertMsgRet (!mm.formatObj->fileExtensions().empty(),
2642 ("Must be at least one extension if it is "
2643 "HardDiskFormatCapabilities_File\n"),
2644 E_FAIL);
2645
2646 Bstr ext = mm.formatObj->fileExtensions().front();
2647 ComAssertMsgRet (!ext.isEmpty(),
2648 ("Default extension must not be empty\n"),
2649 E_FAIL);
2650
2651 id.create();
2652
2653 location = Utf8StrFmt ("%s{%RTuuid}.%ls",
2654 location.raw(), id.raw(), ext.raw());
2655 }
2656 }
2657
2658 /* append the default folder if no path is given */
2659 if (!RTPathHavePath (location))
2660 {
2661 AutoReadLock propsLock (mVirtualBox->systemProperties());
2662 location = Utf8StrFmt ("%ls%c%s",
2663 mVirtualBox->systemProperties()->defaultHardDiskFolder().raw(),
2664 RTPATH_DELIMITER,
2665 location.raw());
2666 }
2667
2668 /* get the full file name */
2669 Utf8Str locationFull;
2670 int vrc = mVirtualBox->calculateFullPath (location, locationFull);
2671 if (RT_FAILURE (vrc))
2672 return setError (VBOX_E_FILE_ERROR,
2673 tr ("Invalid hard disk storage file location '%s' (%Rrc)"),
2674 location.raw(), vrc);
2675
2676 /* detect the backend from the storage unit if importing */
2677 if (isImport)
2678 {
2679 char *backendName = NULL;
2680
2681 /* is it a file? */
2682 {
2683 RTFILE file;
2684 vrc = RTFileOpen (&file, locationFull, RTFILE_O_READ);
2685 if (RT_SUCCESS (vrc))
2686 RTFileClose (file);
2687 }
2688 if (RT_SUCCESS (vrc))
2689 {
2690 vrc = VDGetFormat (locationFull, &backendName);
2691 }
2692 else if (vrc != VERR_FILE_NOT_FOUND && vrc != VERR_PATH_NOT_FOUND)
2693 {
2694 /* assume it's not a file, restore the original location */
2695 location = locationFull = aLocation;
2696 vrc = VDGetFormat (locationFull, &backendName);
2697 }
2698
2699 if (RT_FAILURE (vrc))
2700 return setError (VBOX_E_IPRT_ERROR,
2701 tr ("Could not get the storage format of the hard disk "
2702 "'%s' (%Rrc)"), locationFull.raw(), vrc);
2703
2704 ComAssertRet (backendName != NULL && *backendName != '\0', E_FAIL);
2705
2706 HRESULT rc = setFormat (Bstr (backendName));
2707 RTStrFree (backendName);
2708
2709 /* setFormat() must not fail since we've just used the backend so
2710 * the format object must be there */
2711 AssertComRCReturnRC (rc);
2712 }
2713
2714 /* is it still a file? */
2715 if (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File)
2716 {
2717 m.location = location;
2718 m.locationFull = locationFull;
2719
2720 if (m.state == MediaState_NotCreated)
2721 {
2722 /* assign a new UUID (this UUID will be used when calling
2723 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
2724 * also do that if we didn't generate it to make sure it is
2725 * either generated by us or reset to null */
2726 unconst (m.id) = id;
2727 }
2728 }
2729 else
2730 {
2731 m.location = locationFull;
2732 m.locationFull = locationFull;
2733 }
2734 }
2735 else
2736 {
2737 m.location = aLocation;
2738 m.locationFull = aLocation;
2739 }
2740
2741 return S_OK;
2742}
2743
2744/**
2745 * Checks that the format ID is valid and sets it on success.
2746 *
2747 * Note that this method will caller-reference the format object on success!
2748 * This reference must be released somewhere to let the HardDiskFormat object be
2749 * uninitialized.
2750 *
2751 * @note Must be called from under this object's write lock.
2752 */
2753HRESULT HardDisk::setFormat (CBSTR aFormat)
2754{
2755 /* get the format object first */
2756 {
2757 AutoReadLock propsLock (mVirtualBox->systemProperties());
2758
2759 unconst (mm.formatObj)
2760 = mVirtualBox->systemProperties()->hardDiskFormat (aFormat);
2761 if (mm.formatObj.isNull())
2762 return setError (E_INVALIDARG,
2763 tr ("Invalid hard disk storage format '%ls'"), aFormat);
2764
2765 /* reference the format permanently to prevent its unexpected
2766 * uninitialization */
2767 HRESULT rc = mm.formatObj->addCaller();
2768 AssertComRCReturnRC (rc);
2769
2770 /* get properties (preinsert them as keys in the map). Note that the
2771 * map doesn't grow over the object life time since the set of
2772 * properties is meant to be constant. */
2773
2774 Assert (mm.properties.empty());
2775
2776 for (HardDiskFormat::PropertyList::const_iterator it =
2777 mm.formatObj->properties().begin();
2778 it != mm.formatObj->properties().end();
2779 ++ it)
2780 {
2781 mm.properties.insert (std::make_pair (it->name, Bstr::Null));
2782 }
2783 }
2784
2785 unconst (mm.format) = aFormat;
2786
2787 return S_OK;
2788}
2789
2790/**
2791 * Queries information from the image file.
2792 *
2793 * As a result of this call, the accessibility state and data members such as
2794 * size and description will be updated with the current information.
2795 *
2796 * Reimplements MediumBase::queryInfo() to query hard disk information using the
2797 * VD backend interface.
2798 *
2799 * @note This method may block during a system I/O call that checks storage
2800 * accessibility.
2801 *
2802 * @note Locks treeLock() for reading and writing (for new diff media checked
2803 * for the first time). Locks mParent for reading. Locks this object for
2804 * writing.
2805 */
2806HRESULT HardDisk::queryInfo()
2807{
2808 AutoWriteLock alock (this);
2809
2810 AssertReturn (m.state == MediaState_Created ||
2811 m.state == MediaState_Inaccessible ||
2812 m.state == MediaState_LockedRead ||
2813 m.state == MediaState_LockedWrite,
2814 E_FAIL);
2815
2816 HRESULT rc = S_OK;
2817
2818 int vrc = VINF_SUCCESS;
2819
2820 /* check if a blocking queryInfo() call is in progress on some other thread,
2821 * and wait for it to finish if so instead of querying data ourselves */
2822 if (m.queryInfoSem != NIL_RTSEMEVENTMULTI)
2823 {
2824 Assert (m.state == MediaState_LockedRead);
2825
2826 ++ m.queryInfoCallers;
2827 alock.leave();
2828
2829 vrc = RTSemEventMultiWait (m.queryInfoSem, RT_INDEFINITE_WAIT);
2830
2831 alock.enter();
2832 -- m.queryInfoCallers;
2833
2834 if (m.queryInfoCallers == 0)
2835 {
2836 /* last waiting caller deletes the semaphore */
2837 RTSemEventMultiDestroy (m.queryInfoSem);
2838 m.queryInfoSem = NIL_RTSEMEVENTMULTI;
2839 }
2840
2841 AssertRC (vrc);
2842
2843 return S_OK;
2844 }
2845
2846 /* lazily create a semaphore for possible callers */
2847 vrc = RTSemEventMultiCreate (&m.queryInfoSem);
2848 ComAssertRCRet (vrc, E_FAIL);
2849
2850 bool tempStateSet = false;
2851 if (m.state != MediaState_LockedRead &&
2852 m.state != MediaState_LockedWrite)
2853 {
2854 /* Cause other methods to prevent any modifications before leaving the
2855 * lock. Note that clients will never see this temporary state change
2856 * since any COMGETTER(State) is (or will be) blocked until we finish
2857 * and restore the actual state. */
2858 m.state = MediaState_LockedRead;
2859 tempStateSet = true;
2860 }
2861
2862 /* leave the lock before a blocking operation */
2863 alock.leave();
2864
2865 bool success = false;
2866 Utf8Str lastAccessError;
2867
2868 try
2869 {
2870 Utf8Str location (m.locationFull);
2871
2872 /* are we dealing with a new hard disk constructed using the existing
2873 * location? */
2874 bool isImport = m.id.isEmpty();
2875
2876 PVBOXHDD hdd;
2877 vrc = VDCreate (mm.vdDiskIfaces, &hdd);
2878 ComAssertRCThrow (vrc, E_FAIL);
2879
2880 try
2881 {
2882 unsigned flags = VD_OPEN_FLAGS_INFO;
2883
2884 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
2885 * hard disks because that would prevent necessary modifications
2886 * when opening hard disks of some third-party formats for the first
2887 * time in VirtualBox (such as VMDK for which VDOpen() needs to
2888 * generate an UUID if it is missing) */
2889 if (!isImport)
2890 flags |= VD_OPEN_FLAGS_READONLY;
2891
2892 vrc = VDOpen (hdd, Utf8Str (mm.format), location, flags,
2893 mm.vdDiskIfaces);
2894 if (RT_FAILURE (vrc))
2895 {
2896 lastAccessError = Utf8StrFmt (
2897 tr ("Could not open the hard disk '%ls'%s"),
2898 m.locationFull.raw(), vdError (vrc).raw());
2899 throw S_OK;
2900 }
2901
2902 if (mm.formatObj->capabilities() & HardDiskFormatCapabilities_Uuid)
2903 {
2904 /* check the UUID */
2905 RTUUID uuid;
2906 vrc = VDGetUuid (hdd, 0, &uuid);
2907 ComAssertRCThrow (vrc, E_FAIL);
2908
2909 if (isImport)
2910 {
2911 unconst (m.id) = uuid;
2912 }
2913 else
2914 {
2915 Assert (!m.id.isEmpty());
2916
2917 if (m.id != uuid)
2918 {
2919 lastAccessError = Utf8StrFmt (
2920 tr ("UUID {%RTuuid} of the hard disk '%ls' does "
2921 "not match the value {%RTuuid} stored in the "
2922 "media registry ('%ls')"),
2923 &uuid, m.locationFull.raw(), m.id.raw(),
2924 mVirtualBox->settingsFileName().raw());
2925 throw S_OK;
2926 }
2927 }
2928 }
2929 else
2930 {
2931 /* the backend does not support storing UUIDs within the
2932 * underlying storage so use what we store in XML */
2933
2934 /* generate an UUID for an imported UUID-less hard disk */
2935 if (isImport)
2936 unconst (m.id).create();
2937 }
2938
2939 /* check the type */
2940 unsigned uImageFlags;
2941 vrc = VDGetImageFlags (hdd, 0, &uImageFlags);
2942 ComAssertRCThrow (vrc, E_FAIL);
2943
2944 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
2945 {
2946 RTUUID parentId;
2947 vrc = VDGetParentUuid (hdd, 0, &parentId);
2948 ComAssertRCThrow (vrc, E_FAIL);
2949
2950 if (isImport)
2951 {
2952 /* the parent must be known to us. Note that we freely
2953 * call locking methods of mVirtualBox and parent from the
2954 * write lock (breaking the {parent,child} lock order)
2955 * because there may be no concurrent access to the just
2956 * opened hard disk on ther threads yet (and init() will
2957 * fail if this method reporst MediaState_Inaccessible) */
2958
2959 Guid id = parentId;
2960 ComObjPtr<HardDisk> parent;
2961 rc = mVirtualBox->findHardDisk(&id, NULL,
2962 false /* aSetError */,
2963 &parent);
2964 if (FAILED (rc))
2965 {
2966 lastAccessError = Utf8StrFmt (
2967 tr ("Parent hard disk with UUID {%RTuuid} of the "
2968 "hard disk '%ls' is not found in the media "
2969 "registry ('%ls')"),
2970 &parentId, m.locationFull.raw(),
2971 mVirtualBox->settingsFileName().raw());
2972 throw S_OK;
2973 }
2974
2975 /* deassociate from VirtualBox, associate with parent */
2976
2977 mVirtualBox->removeDependentChild (this);
2978
2979 /* we set mParent & children() */
2980 AutoWriteLock treeLock (this->treeLock());
2981
2982 Assert (mParent.isNull());
2983 mParent = parent;
2984 mParent->addDependentChild (this);
2985 }
2986 else
2987 {
2988 /* we access mParent */
2989 AutoReadLock treeLock (this->treeLock());
2990
2991 /* check that parent UUIDs match. Note that there's no need
2992 * for the parent's AutoCaller (our lifetime is bound to
2993 * it) */
2994
2995 if (mParent.isNull())
2996 {
2997 lastAccessError = Utf8StrFmt (
2998 tr ("Hard disk '%ls' is differencing but it is not "
2999 "associated with any parent hard disk in the "
3000 "media registry ('%ls')"),
3001 m.locationFull.raw(),
3002 mVirtualBox->settingsFileName().raw());
3003 throw S_OK;
3004 }
3005
3006 AutoReadLock parentLock (mParent);
3007 if (mParent->state() != MediaState_Inaccessible &&
3008 mParent->id() != parentId)
3009 {
3010 lastAccessError = Utf8StrFmt (
3011 tr ("Parent UUID {%RTuuid} of the hard disk '%ls' "
3012 "does not match UUID {%RTuuid} of its parent "
3013 "hard disk stored in the media registry ('%ls')"),
3014 &parentId, m.locationFull.raw(),
3015 mParent->id().raw(),
3016 mVirtualBox->settingsFileName().raw());
3017 throw S_OK;
3018 }
3019
3020 /// @todo NEWMEDIA what to do if the parent is not
3021 /// accessible while the diff is? Probably, nothing. The
3022 /// real code will detect the mismatch anyway.
3023 }
3024 }
3025
3026 m.size = VDGetFileSize (hdd, 0);
3027 mm.logicalSize = VDGetSize (hdd, 0) / _1M;
3028
3029 success = true;
3030 }
3031 catch (HRESULT aRC)
3032 {
3033 rc = aRC;
3034 }
3035
3036 VDDestroy (hdd);
3037
3038 }
3039 catch (HRESULT aRC)
3040 {
3041 rc = aRC;
3042 }
3043
3044 alock.enter();
3045
3046 if (success)
3047 m.lastAccessError.setNull();
3048 else
3049 {
3050 m.lastAccessError = lastAccessError;
3051 LogWarningFunc (("'%ls' is not accessible (error='%ls', "
3052 "rc=%Rhrc, vrc=%Rrc)\n",
3053 m.locationFull.raw(), m.lastAccessError.raw(),
3054 rc, vrc));
3055 }
3056
3057 /* inform other callers if there are any */
3058 if (m.queryInfoCallers > 0)
3059 {
3060 RTSemEventMultiSignal (m.queryInfoSem);
3061 }
3062 else
3063 {
3064 /* delete the semaphore ourselves */
3065 RTSemEventMultiDestroy (m.queryInfoSem);
3066 m.queryInfoSem = NIL_RTSEMEVENTMULTI;
3067 }
3068
3069 if (tempStateSet)
3070 {
3071 /* Set the proper state according to the result of the check */
3072 if (success)
3073 m.state = MediaState_Created;
3074 else
3075 m.state = MediaState_Inaccessible;
3076 }
3077 else
3078 {
3079 /* we're locked, use a special field to store the result */
3080 m.accessibleInLock = success;
3081 }
3082
3083 return rc;
3084}
3085
3086/**
3087 * @note Called from this object's AutoMayUninitSpan and from under mVirtualBox
3088 * write lock.
3089 *
3090 * @note Also reused by HardDisk::Reset().
3091 *
3092 * @note Locks treeLock() for reading.
3093 */
3094HRESULT HardDisk::canClose()
3095{
3096 /* we access children */
3097 AutoReadLock treeLock (this->treeLock());
3098
3099 if (children().size() != 0)
3100 return setError (E_FAIL,
3101 tr ("Hard disk '%ls' has %d child hard disks"),
3102 children().size());
3103
3104 return S_OK;
3105}
3106
3107/**
3108 * @note Called from within this object's AutoWriteLock.
3109 */
3110HRESULT HardDisk::canAttach(const Guid & /* aMachineId */,
3111 const Guid & /* aSnapshotId */)
3112{
3113 if (mm.numCreateDiffTasks > 0)
3114 return setError (E_FAIL,
3115 tr ("One or more differencing child hard disks are "
3116 "being created for the hard disk '%ls' (%u)"),
3117 m.locationFull.raw(), mm.numCreateDiffTasks);
3118
3119 return S_OK;
3120}
3121
3122/**
3123 * @note Called from within this object's AutoMayUninitSpan (or AutoCaller) and
3124 * from under mVirtualBox write lock.
3125 *
3126 * @note Locks treeLock() for writing.
3127 */
3128HRESULT HardDisk::unregisterWithVirtualBox()
3129{
3130 /* Note that we need to de-associate ourselves from the parent to let
3131 * unregisterHardDisk() properly save the registry */
3132
3133 /* we modify mParent and access children */
3134 AutoWriteLock treeLock (this->treeLock());
3135
3136 const ComObjPtr<HardDisk, ComWeakRef> parent = mParent;
3137
3138 AssertReturn (children().size() == 0, E_FAIL);
3139
3140 if (!mParent.isNull())
3141 {
3142 /* deassociate from the parent, associate with VirtualBox */
3143 mVirtualBox->addDependentChild (this);
3144 mParent->removeDependentChild (this);
3145 mParent.setNull();
3146 }
3147
3148 HRESULT rc = mVirtualBox->unregisterHardDisk(this);
3149
3150 if (FAILED (rc))
3151 {
3152 if (!parent.isNull())
3153 {
3154 /* re-associate with the parent as we are still relatives in the
3155 * registry */
3156 mParent = parent;
3157 mParent->addDependentChild (this);
3158 mVirtualBox->removeDependentChild (this);
3159 }
3160 }
3161
3162 return rc;
3163}
3164
3165/**
3166 * Returns the last error message collected by the vdErrorCall callback and
3167 * resets it.
3168 *
3169 * The error message is returned prepended with a dot and a space, like this:
3170 * <code>
3171 * ". <error_text> (%Rrc)"
3172 * </code>
3173 * to make it easily appendable to a more general error message. The @c %Rrc
3174 * format string is given @a aVRC as an argument.
3175 *
3176 * If there is no last error message collected by vdErrorCall or if it is a
3177 * null or empty string, then this function returns the following text:
3178 * <code>
3179 * " (%Rrc)"
3180 * </code>
3181 *
3182 * @note Doesn't do any object locking; it is assumed that the caller makes sure
3183 * the callback isn't called by more than one thread at a time.
3184 *
3185 * @param aVRC VBox error code to use when no error message is provided.
3186 */
3187Utf8Str HardDisk::vdError (int aVRC)
3188{
3189 Utf8Str error;
3190
3191 if (mm.vdError.isEmpty())
3192 error = Utf8StrFmt (" (%Rrc)", aVRC);
3193 else
3194 error = Utf8StrFmt (".\n%s", mm.vdError.raw());
3195
3196 mm.vdError.setNull();
3197
3198 return error;
3199}
3200
3201/**
3202 * Error message callback.
3203 *
3204 * Puts the reported error message to the mm.vdError field.
3205 *
3206 * @note Doesn't do any object locking; it is assumed that the caller makes sure
3207 * the callback isn't called by more than one thread at a time.
3208 *
3209 * @param pvUser The opaque data passed on container creation.
3210 * @param rc The VBox error code.
3211 * @param RT_SRC_POS_DECL Use RT_SRC_POS.
3212 * @param pszFormat Error message format string.
3213 * @param va Error message arguments.
3214 */
3215/*static*/
3216DECLCALLBACK(void) HardDisk::vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
3217 const char *pszFormat, va_list va)
3218{
3219 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
3220
3221 HardDisk *that = static_cast<HardDisk*>(pvUser);
3222 AssertReturnVoid (that != NULL);
3223
3224 if (that->mm.vdError.isEmpty())
3225 that->mm.vdError =
3226 Utf8StrFmt ("%s (%Rrc)", Utf8StrFmtVA (pszFormat, va).raw(), rc);
3227 else
3228 that->mm.vdError =
3229 Utf8StrFmt ("%s.\n%s (%Rrc)", that->mm.vdError.raw(),
3230 Utf8StrFmtVA (pszFormat, va).raw(), rc);
3231}
3232
3233/**
3234 * PFNVMPROGRESS callback handler for Task operations.
3235 *
3236 * @param uPercent Completetion precentage (0-100).
3237 * @param pvUser Pointer to the Progress instance.
3238 */
3239/*static*/
3240DECLCALLBACK(int) HardDisk::vdProgressCall(PVM /* pVM */, unsigned uPercent,
3241 void *pvUser)
3242{
3243 HardDisk *that = static_cast<HardDisk*>(pvUser);
3244 AssertReturn (that != NULL, VERR_GENERAL_FAILURE);
3245
3246 if (that->mm.vdProgress != NULL)
3247 {
3248 /* update the progress object, capping it at 99% as the final percent
3249 * is used for additional operations like setting the UUIDs and similar. */
3250 that->mm.vdProgress->notifyProgress (RT_MIN (uPercent, 99));
3251 }
3252
3253 return VINF_SUCCESS;
3254}
3255
3256/* static */
3257DECLCALLBACK(bool) HardDisk::vdConfigAreKeysValid (void *pvUser,
3258 const char * /* pszzValid */)
3259{
3260 HardDisk *that = static_cast<HardDisk*>(pvUser);
3261 AssertReturn (that != NULL, false);
3262
3263 /* we always return true since the only keys we have are those found in
3264 * VDBACKENDINFO */
3265 return true;
3266}
3267
3268/* static */
3269DECLCALLBACK(int) HardDisk::vdConfigQuerySize(void *pvUser, const char *pszName,
3270 size_t *pcbValue)
3271{
3272 AssertReturn (VALID_PTR (pcbValue), VERR_INVALID_POINTER);
3273
3274 HardDisk *that = static_cast<HardDisk*>(pvUser);
3275 AssertReturn (that != NULL, VERR_GENERAL_FAILURE);
3276
3277 Data::PropertyMap::const_iterator it =
3278 that->mm.properties.find (Bstr (pszName));
3279 if (it == that->mm.properties.end())
3280 return VERR_CFGM_VALUE_NOT_FOUND;
3281
3282 /* we interpret null values as "no value" in HardDisk */
3283 if (it->second.isNull())
3284 return VERR_CFGM_VALUE_NOT_FOUND;
3285
3286 *pcbValue = it->second.length() + 1 /* include terminator */;
3287
3288 return VINF_SUCCESS;
3289}
3290
3291/* static */
3292DECLCALLBACK(int) HardDisk::vdConfigQuery (void *pvUser, const char *pszName,
3293 char *pszValue, size_t cchValue)
3294{
3295 AssertReturn (VALID_PTR (pszValue), VERR_INVALID_POINTER);
3296
3297 HardDisk *that = static_cast<HardDisk*>(pvUser);
3298 AssertReturn (that != NULL, VERR_GENERAL_FAILURE);
3299
3300 Data::PropertyMap::const_iterator it =
3301 that->mm.properties.find (Bstr (pszName));
3302 if (it == that->mm.properties.end())
3303 return VERR_CFGM_VALUE_NOT_FOUND;
3304
3305 Utf8Str value = it->second;
3306 if (value.length() >= cchValue)
3307 return VERR_CFGM_NOT_ENOUGH_SPACE;
3308
3309 /* we interpret null values as "no value" in HardDisk */
3310 if (it->second.isNull())
3311 return VERR_CFGM_VALUE_NOT_FOUND;
3312
3313 memcpy (pszValue, value, value.length() + 1);
3314
3315 return VINF_SUCCESS;
3316}
3317
3318/**
3319 * Thread function for time-consuming tasks.
3320 *
3321 * The Task structure passed to @a pvUser must be allocated using new and will
3322 * be freed by this method before it returns.
3323 *
3324 * @param pvUser Pointer to the Task instance.
3325 */
3326/* static */
3327DECLCALLBACK(int) HardDisk::taskThread (RTTHREAD thread, void *pvUser)
3328{
3329 std::auto_ptr <Task> task (static_cast <Task *> (pvUser));
3330 AssertReturn (task.get(), VERR_GENERAL_FAILURE);
3331
3332 bool isAsync = thread != NIL_RTTHREAD;
3333
3334 HardDisk *that = task->that;
3335
3336 /// @todo ugly hack, fix ComAssert... later
3337 #define setError that->setError
3338
3339 /* Note: no need in AutoCaller because Task does that */
3340
3341 LogFlowFuncEnter();
3342 LogFlowFunc (("{%p}: operation=%d\n", that, task->operation));
3343
3344 HRESULT rc = S_OK;
3345
3346 switch (task->operation)
3347 {
3348 ////////////////////////////////////////////////////////////////////////
3349
3350 case Task::CreateBase:
3351 {
3352 /* The lock is also used as a signal from the task initiator (which
3353 * releases it only after RTThreadCreate()) that we can start the job */
3354 AutoWriteLock thatLock (that);
3355
3356 /* these parameters we need after creation */
3357 uint64_t size = 0, logicalSize = 0;
3358
3359 /* The object may request a specific UUID (through a special form of
3360 * the setLocation() argument). Otherwise we have to generate it */
3361 Guid id = that->m.id;
3362 bool generateUuid = id.isEmpty();
3363 if (generateUuid)
3364 {
3365 id.create();
3366 /* VirtualBox::registerHardDisk() will need UUID */
3367 unconst (that->m.id) = id;
3368 }
3369
3370 try
3371 {
3372 PVBOXHDD hdd;
3373 int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
3374 ComAssertRCThrow (vrc, E_FAIL);
3375
3376 Utf8Str format (that->mm.format);
3377 Utf8Str location (that->m.locationFull);
3378 /* uint64_t capabilities = */ that->mm.formatObj->capabilities();
3379
3380 /* unlock before the potentially lengthy operation */
3381 Assert (that->m.state == MediaState_Creating);
3382 thatLock.leave();
3383
3384 try
3385 {
3386 /* ensure the directory exists */
3387 rc = VirtualBox::ensureFilePathExists (location);
3388 CheckComRCThrowRC (rc);
3389
3390 PDMMEDIAGEOMETRY geo = { 0 }; /* auto-detect */
3391
3392 /* needed for vdProgressCallback */
3393 that->mm.vdProgress = task->progress;
3394
3395 vrc = VDCreateBase (hdd, format, location,
3396 task->d.size * _1M,
3397 task->d.variant,
3398 NULL, &geo, &geo, id.raw(),
3399 VD_OPEN_FLAGS_NORMAL,
3400 NULL, that->mm.vdDiskIfaces);
3401
3402 if (RT_FAILURE (vrc))
3403 {
3404 throw setError (E_FAIL,
3405 tr ("Could not create the hard disk storage "
3406 "unit '%s'%s"),
3407 location.raw(), that->vdError (vrc).raw());
3408 }
3409
3410 size = VDGetFileSize (hdd, 0);
3411 logicalSize = VDGetSize (hdd, 0) / _1M;
3412 }
3413 catch (HRESULT aRC) { rc = aRC; }
3414
3415 VDDestroy (hdd);
3416 }
3417 catch (HRESULT aRC) { rc = aRC; }
3418
3419 if (SUCCEEDED (rc))
3420 {
3421 /* register with mVirtualBox as the last step and move to
3422 * Created state only on success (leaving an orphan file is
3423 * better than breaking media registry consistency) */
3424 rc = that->mVirtualBox->registerHardDisk(that);
3425 }
3426
3427 thatLock.maybeEnter();
3428
3429 if (SUCCEEDED (rc))
3430 {
3431 that->m.state = MediaState_Created;
3432
3433 that->m.size = size;
3434 that->mm.logicalSize = logicalSize;
3435 }
3436 else
3437 {
3438 /* back to NotCreated on failiure */
3439 that->m.state = MediaState_NotCreated;
3440
3441 /* reset UUID to prevent it from being reused next time */
3442 if (generateUuid)
3443 unconst (that->m.id).clear();
3444 }
3445
3446 break;
3447 }
3448
3449 ////////////////////////////////////////////////////////////////////////
3450
3451 case Task::CreateDiff:
3452 {
3453 ComObjPtr<HardDisk> &target = task->d.target;
3454
3455 /* Lock both in {parent,child} order. The lock is also used as a
3456 * signal from the task initiator (which releases it only after
3457 * RTThreadCreate()) that we can start the job*/
3458 AutoMultiWriteLock2 thatLock (that, target);
3459
3460 uint64_t size = 0, logicalSize = 0;
3461
3462 /* The object may request a specific UUID (through a special form of
3463 * the setLocation() argument). Otherwise we have to generate it */
3464 Guid targetId = target->m.id;
3465 bool generateUuid = targetId.isEmpty();
3466 if (generateUuid)
3467 {
3468 targetId.create();
3469 /* VirtualBox::registerHardDisk() will need UUID */
3470 unconst (target->m.id) = targetId;
3471 }
3472
3473 try
3474 {
3475 PVBOXHDD hdd;
3476 int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
3477 ComAssertRCThrow (vrc, E_FAIL);
3478
3479 Guid id = that->m.id;
3480 Utf8Str format (that->mm.format);
3481 Utf8Str location (that->m.locationFull);
3482
3483 Utf8Str targetFormat (target->mm.format);
3484 Utf8Str targetLocation (target->m.locationFull);
3485
3486 Assert (target->m.state == MediaState_Creating);
3487
3488 /* Note: MediaState_LockedWrite is ok when taking an online
3489 * snapshot */
3490 Assert (that->m.state == MediaState_LockedRead ||
3491 that->m.state == MediaState_LockedWrite);
3492
3493 /* unlock before the potentially lengthy operation */
3494 thatLock.leave();
3495
3496 try
3497 {
3498 vrc = VDOpen (hdd, format, location,
3499 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
3500 that->mm.vdDiskIfaces);
3501 if (RT_FAILURE (vrc))
3502 {
3503 throw setError (E_FAIL,
3504 tr ("Could not open the hard disk storage "
3505 "unit '%s'%s"),
3506 location.raw(), that->vdError (vrc).raw());
3507 }
3508
3509 /* ensure the target directory exists */
3510 rc = VirtualBox::ensureFilePathExists (targetLocation);
3511 CheckComRCThrowRC (rc);
3512
3513 /* needed for vdProgressCallback */
3514 that->mm.vdProgress = task->progress;
3515
3516 vrc = VDCreateDiff (hdd, targetFormat, targetLocation,
3517 task->d.variant,
3518 NULL, targetId.raw(),
3519 id.raw(),
3520 VD_OPEN_FLAGS_NORMAL,
3521 target->mm.vdDiskIfaces,
3522 that->mm.vdDiskIfaces);
3523
3524 that->mm.vdProgress = NULL;
3525
3526 if (RT_FAILURE (vrc))
3527 {
3528 throw setError (E_FAIL,
3529 tr ("Could not create the differencing hard disk "
3530 "storage unit '%s'%s"),
3531 targetLocation.raw(), that->vdError (vrc).raw());
3532 }
3533
3534 size = VDGetFileSize (hdd, 1);
3535 logicalSize = VDGetSize (hdd, 1) / _1M;
3536 }
3537 catch (HRESULT aRC) { rc = aRC; }
3538
3539 VDDestroy (hdd);
3540 }
3541 catch (HRESULT aRC) { rc = aRC; }
3542
3543 if (SUCCEEDED (rc))
3544 {
3545 /* we set mParent & children() (note that thatLock is released
3546 * here), but lock VirtualBox first to follow the rule */
3547 AutoMultiWriteLock2 alock (that->mVirtualBox->lockHandle(),
3548 that->treeLock());
3549
3550 Assert (target->mParent.isNull());
3551
3552 /* associate the child with the parent and deassociate from
3553 * VirtualBox */
3554 target->mParent = that;
3555 that->addDependentChild (target);
3556 target->mVirtualBox->removeDependentChild (target);
3557
3558 /* diffs for immutable hard disks are auto-reset by default */
3559 target->mm.autoReset =
3560 that->root()->mm.type == HardDiskType_Immutable ?
3561 TRUE : FALSE;
3562
3563 /* register with mVirtualBox as the last step and move to
3564 * Created state only on success (leaving an orphan file is
3565 * better than breaking media registry consistency) */
3566 rc = that->mVirtualBox->registerHardDisk (target);
3567
3568 if (FAILED (rc))
3569 {
3570 /* break the parent association on failure to register */
3571 target->mVirtualBox->addDependentChild (target);
3572 that->removeDependentChild (target);
3573 target->mParent.setNull();
3574 }
3575 }
3576
3577 thatLock.maybeEnter();
3578
3579 if (SUCCEEDED (rc))
3580 {
3581 target->m.state = MediaState_Created;
3582
3583 target->m.size = size;
3584 target->mm.logicalSize = logicalSize;
3585 }
3586 else
3587 {
3588 /* back to NotCreated on failiure */
3589 target->m.state = MediaState_NotCreated;
3590
3591 target->mm.autoReset = FALSE;
3592
3593 /* reset UUID to prevent it from being reused next time */
3594 if (generateUuid)
3595 unconst (target->m.id).clear();
3596 }
3597
3598 if (isAsync)
3599 {
3600 /* unlock ourselves when done (unless in MediaState_LockedWrite
3601 * state because of taking the online snapshot*/
3602 if (that->m.state != MediaState_LockedWrite)
3603 {
3604 HRESULT rc2 = that->UnlockRead (NULL);
3605 AssertComRC (rc2);
3606 }
3607 }
3608
3609 /* deregister the task registered in createDiffStorage() */
3610 Assert (that->mm.numCreateDiffTasks != 0);
3611 -- that->mm.numCreateDiffTasks;
3612
3613 /* Note that in sync mode, it's the caller's responsibility to
3614 * unlock the hard disk */
3615
3616 break;
3617 }
3618
3619 ////////////////////////////////////////////////////////////////////////
3620
3621 case Task::Merge:
3622 {
3623 /* The lock is also used as a signal from the task initiator (which
3624 * releases it only after RTThreadCreate()) that we can start the
3625 * job. We don't actually need the lock for anything else since the
3626 * object is protected by MediaState_Deleting and we don't modify
3627 * its sensitive fields below */
3628 {
3629 AutoWriteLock thatLock (that);
3630 }
3631
3632 MergeChain *chain = task->d.chain.get();
3633
3634#if 0
3635 LogFlow (("*** MERGE forward = %RTbool\n", chain->isForward()));
3636#endif
3637
3638 try
3639 {
3640 PVBOXHDD hdd;
3641 int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
3642 ComAssertRCThrow (vrc, E_FAIL);
3643
3644 try
3645 {
3646 /* open all hard disks in the chain (they are in the
3647 * {parent,child} order in there. Note that we don't lock
3648 * objects in this chain since they must be in states
3649 * (Deleting and LockedWrite) that prevent from chaning
3650 * their format and location fields from outside. */
3651
3652 for (MergeChain::const_iterator it = chain->begin();
3653 it != chain->end(); ++ it)
3654 {
3655 /* complex sanity (sane complexity) */
3656 Assert ((chain->isForward() &&
3657 ((*it != chain->back() &&
3658 (*it)->m.state == MediaState_Deleting) ||
3659 (*it == chain->back() &&
3660 (*it)->m.state == MediaState_LockedWrite))) ||
3661 (!chain->isForward() &&
3662 ((*it != chain->front() &&
3663 (*it)->m.state == MediaState_Deleting) ||
3664 (*it == chain->front() &&
3665 (*it)->m.state == MediaState_LockedWrite))));
3666
3667 Assert (*it == chain->target() ||
3668 (*it)->m.backRefs.size() == 0);
3669
3670 /* open the first image with VDOPEN_FLAGS_INFO because
3671 * it's not necessarily the base one */
3672 vrc = VDOpen (hdd, Utf8Str ((*it)->mm.format),
3673 Utf8Str ((*it)->m.locationFull),
3674 it == chain->begin() ?
3675 VD_OPEN_FLAGS_INFO : 0,
3676 (*it)->mm.vdDiskIfaces);
3677 if (RT_FAILURE (vrc))
3678 throw vrc;
3679#if 0
3680 LogFlow (("*** MERGE disk = %ls\n",
3681 (*it)->m.locationFull.raw()));
3682#endif
3683 }
3684
3685 /* needed for vdProgressCallback */
3686 that->mm.vdProgress = task->progress;
3687
3688 unsigned start = chain->isForward() ?
3689 0 : chain->size() - 1;
3690 unsigned end = chain->isForward() ?
3691 chain->size() - 1 : 0;
3692#if 0
3693 LogFlow (("*** MERGE from %d to %d\n", start, end));
3694#endif
3695 vrc = VDMerge (hdd, start, end, that->mm.vdDiskIfaces);
3696
3697 that->mm.vdProgress = NULL;
3698
3699 if (RT_FAILURE (vrc))
3700 throw vrc;
3701
3702 /* update parent UUIDs */
3703 /// @todo VDMerge should be taught to do so, including the
3704 /// multiple children case
3705 if (chain->isForward())
3706 {
3707 /* target's UUID needs to be updated (note that target
3708 * is the only image in the container on success) */
3709 vrc = VDSetParentUuid (hdd, 0, chain->parent()->m.id);
3710 if (RT_FAILURE (vrc))
3711 throw vrc;
3712 }
3713 else
3714 {
3715 /* we need to update UUIDs of all source's children
3716 * which cannot be part of the container at once so
3717 * add each one in there individually */
3718 if (chain->children().size() > 0)
3719 {
3720 for (List::const_iterator it = chain->children().begin();
3721 it != chain->children().end(); ++ it)
3722 {
3723 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
3724 vrc = VDOpen (hdd, Utf8Str ((*it)->mm.format),
3725 Utf8Str ((*it)->m.locationFull),
3726 VD_OPEN_FLAGS_INFO,
3727 (*it)->mm.vdDiskIfaces);
3728 if (RT_FAILURE (vrc))
3729 throw vrc;
3730
3731 vrc = VDSetParentUuid (hdd, 1,
3732 chain->target()->m.id);
3733 if (RT_FAILURE (vrc))
3734 throw vrc;
3735
3736 vrc = VDClose (hdd, false /* fDelete */);
3737 if (RT_FAILURE (vrc))
3738 throw vrc;
3739 }
3740 }
3741 }
3742 }
3743 catch (HRESULT aRC) { rc = aRC; }
3744 catch (int aVRC)
3745 {
3746 throw setError (E_FAIL,
3747 tr ("Could not merge the hard disk '%ls' to '%ls'%s"),
3748 chain->source()->m.locationFull.raw(),
3749 chain->target()->m.locationFull.raw(),
3750 that->vdError (aVRC).raw());
3751 }
3752
3753 VDDestroy (hdd);
3754 }
3755 catch (HRESULT aRC) { rc = aRC; }
3756
3757 HRESULT rc2;
3758
3759 bool saveSettingsFailed = false;
3760
3761 if (SUCCEEDED (rc))
3762 {
3763 /* all hard disks but the target were successfully deleted by
3764 * VDMerge; reparent the last one and uninitialize deleted */
3765
3766 /* we set mParent & children() (note that thatLock is released
3767 * here), but lock VirtualBox first to follow the rule */
3768 AutoMultiWriteLock2 alock (that->mVirtualBox->lockHandle(),
3769 that->treeLock());
3770
3771 HardDisk *source = chain->source();
3772 HardDisk *target = chain->target();
3773
3774 if (chain->isForward())
3775 {
3776 /* first, unregister the target since it may become a base
3777 * hard disk which needs re-registration */
3778 rc2 = target->mVirtualBox->
3779 unregisterHardDisk (target, false /* aSaveSettings */);
3780 AssertComRC (rc2);
3781
3782 /* then, reparent it and disconnect the deleted branch at
3783 * both ends (chain->parent() is source's parent) */
3784 target->mParent->removeDependentChild (target);
3785 target->mParent = chain->parent();
3786 if (!target->mParent.isNull())
3787 {
3788 target->mParent->addDependentChild (target);
3789 target->mParent->removeDependentChild (source);
3790 source->mParent.setNull();
3791 }
3792 else
3793 {
3794 target->mVirtualBox->addDependentChild (target);
3795 target->mVirtualBox->removeDependentChild (source);
3796 }
3797
3798 /* then, register again */
3799 rc2 = target->mVirtualBox->
3800 registerHardDisk (target, false /* aSaveSettings */);
3801 AssertComRC (rc2);
3802 }
3803 else
3804 {
3805 Assert (target->children().size() == 1);
3806 HardDisk *targetChild = target->children().front();
3807
3808 /* disconnect the deleted branch at the elder end */
3809 target->removeDependentChild (targetChild);
3810 targetChild->mParent.setNull();
3811
3812 const List &children = chain->children();
3813
3814 /* reparent source's chidren and disconnect the deleted
3815 * branch at the younger end m*/
3816 if (children.size() > 0)
3817 {
3818 /* obey {parent,child} lock order */
3819 AutoWriteLock sourceLock (source);
3820
3821 for (List::const_iterator it = children.begin();
3822 it != children.end(); ++ it)
3823 {
3824 AutoWriteLock childLock (*it);
3825
3826 (*it)->mParent = target;
3827 (*it)->mParent->addDependentChild (*it);
3828 source->removeDependentChild (*it);
3829 }
3830 }
3831 }
3832
3833 /* try to save the hard disk registry */
3834 rc = that->mVirtualBox->saveSettings();
3835
3836 if (SUCCEEDED (rc))
3837 {
3838 /* unregister and uninitialize all hard disks in the chain
3839 * but the target */
3840
3841 for (MergeChain::iterator it = chain->begin();
3842 it != chain->end();)
3843 {
3844 if (*it == chain->target())
3845 {
3846 ++ it;
3847 continue;
3848 }
3849
3850 rc2 = (*it)->mVirtualBox->
3851 unregisterHardDisk(*it, false /* aSaveSettings */);
3852 AssertComRC (rc2);
3853
3854 /* now, uninitialize the deleted hard disk (note that
3855 * due to the Deleting state, uninit() will not touch
3856 * the parent-child relationship so we need to
3857 * uninitialize each disk individually) */
3858
3859 /* note that the operation initiator hard disk (which is
3860 * normally also the source hard disk) is a special case
3861 * -- there is one more caller added by Task to it which
3862 * we must release. Also, if we are in sync mode, the
3863 * caller may still hold an AutoCaller instance for it
3864 * and therefore we cannot uninit() it (it's therefore
3865 * the caller's responsibility) */
3866 if (*it == that)
3867 task->autoCaller.release();
3868
3869 /* release the caller added by MergeChain before
3870 * uninit() */
3871 (*it)->releaseCaller();
3872
3873 if (isAsync || *it != that)
3874 (*it)->uninit();
3875
3876 /* delete (to prevent uninitialization in MergeChain
3877 * dtor) and advance to the next item */
3878 it = chain->erase (it);
3879 }
3880
3881 /* Note that states of all other hard disks (target, parent,
3882 * children) will be restored by the MergeChain dtor */
3883 }
3884 else
3885 {
3886 /* too bad if we fail, but we'll need to rollback everything
3887 * we did above to at least keep the HD tree in sync with
3888 * the current registry on disk */
3889
3890 saveSettingsFailed = true;
3891
3892 /// @todo NEWMEDIA implement a proper undo
3893
3894 AssertFailed();
3895 }
3896 }
3897
3898 if (FAILED (rc))
3899 {
3900 /* Here we come if either VDMerge() failed (in which case we
3901 * assume that it tried to do everything to make a further
3902 * retry possible -- e.g. not deleted intermediate hard disks
3903 * and so on) or VirtualBox::saveSettings() failed (where we
3904 * should have the original tree but with intermediate storage
3905 * units deleted by VDMerge()). We have to only restore states
3906 * (through the MergeChain dtor) unless we are run synchronously
3907 * in which case it's the responsibility of the caller as stated
3908 * in the mergeTo() docs. The latter also implies that we
3909 * don't own the merge chain, so release it in this case. */
3910
3911 if (!isAsync)
3912 task->d.chain.release();
3913
3914 NOREF (saveSettingsFailed);
3915 }
3916
3917 break;
3918 }
3919
3920 ////////////////////////////////////////////////////////////////////////
3921
3922 case Task::Clone:
3923 {
3924 ComObjPtr<HardDisk> &target = task->d.target;
3925
3926 /* Lock both in {parent,child} order. The lock is also used as a
3927 * signal from the task initiator (which releases it only after
3928 * RTThreadCreate()) that we can start the job*/
3929 AutoMultiWriteLock2 thatLock (that, target);
3930
3931 uint64_t size = 0, logicalSize = 0;
3932
3933 /* The object may request a specific UUID (through a special form of
3934 * the setLocation() argument). Otherwise we have to generate it */
3935 Guid targetId = target->m.id;
3936 bool generateUuid = targetId.isEmpty();
3937 if (generateUuid)
3938 {
3939 targetId.create();
3940 /* VirtualBox::registerHardDisk() will need UUID */
3941 unconst (target->m.id) = targetId;
3942 }
3943
3944 try
3945 {
3946 PVBOXHDD hdd;
3947 int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
3948 ComAssertRCThrow (vrc, E_FAIL);
3949
3950 Utf8Str format (that->mm.format);
3951 Utf8Str location (that->m.locationFull);
3952
3953 Utf8Str targetFormat (target->mm.format);
3954 Utf8Str targetLocation (target->m.locationFull);
3955
3956 Assert (target->m.state == MediaState_Creating);
3957
3958 Assert (that->m.state == MediaState_LockedRead);
3959
3960 /* unlock before the potentially lengthy operation */
3961 thatLock.leave();
3962
3963 try
3964 {
3965 vrc = VDOpen (hdd, format, location,
3966 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
3967 that->mm.vdDiskIfaces);
3968 if (RT_FAILURE (vrc))
3969 {
3970 throw setError (E_FAIL,
3971 tr ("Could not open the hard disk storage "
3972 "unit '%s'%s"),
3973 location.raw(), that->vdError (vrc).raw());
3974 }
3975
3976 /* ensure the target directory exists */
3977 rc = VirtualBox::ensureFilePathExists (targetLocation);
3978 CheckComRCThrowRC (rc);
3979
3980 /* needed for vdProgressCallback */
3981 that->mm.vdProgress = task->progress;
3982
3983 PVBOXHDD targetHdd;
3984 int vrc = VDCreate (that->mm.vdDiskIfaces, &targetHdd);
3985 ComAssertRCThrow (vrc, E_FAIL);
3986
3987 vrc = VDCopy (hdd, 0, targetHdd, targetFormat,
3988 targetLocation, false, 0, task->d.variant,
3989 targetId.raw(), NULL,
3990 target->mm.vdDiskIfaces,
3991 that->mm.vdDiskIfaces);
3992
3993 that->mm.vdProgress = NULL;
3994
3995 if (RT_FAILURE (vrc))
3996 {
3997 VDDestroy (targetHdd);
3998
3999 throw setError (E_FAIL,
4000 tr ("Could not create the clone hard disk "
4001 "'%s'%s"),
4002 targetLocation.raw(), that->vdError (vrc).raw());
4003 }
4004
4005 size = VDGetFileSize (targetHdd, 0);
4006 logicalSize = VDGetSize (targetHdd, 0) / _1M;
4007
4008 VDDestroy (targetHdd);
4009 }
4010 catch (HRESULT aRC) { rc = aRC; }
4011
4012 VDDestroy (hdd);
4013 }
4014 catch (HRESULT aRC) { rc = aRC; }
4015
4016 if (SUCCEEDED (rc))
4017 {
4018 /* we set mParent & children() (note that thatLock is released
4019 * here), but lock VirtualBox first to follow the rule */
4020 AutoMultiWriteLock2 alock (that->mVirtualBox->lockHandle(),
4021 that->treeLock());
4022
4023 Assert (target->mParent.isNull());
4024
4025 if (!that->mParent.isNull())
4026 {
4027 /* associate the clone with the original's parent and
4028 * deassociate from VirtualBox */
4029 target->mParent = that->mParent;
4030 that->mParent->addDependentChild (target);
4031 target->mVirtualBox->removeDependentChild (target);
4032
4033 /* register with mVirtualBox as the last step and move to
4034 * Created state only on success (leaving an orphan file is
4035 * better than breaking media registry consistency) */
4036 rc = that->mVirtualBox->registerHardDisk(target);
4037
4038 if (FAILED (rc))
4039 {
4040 /* break the parent association on failure to register */
4041 target->mVirtualBox->addDependentChild (target);
4042 that->mParent->removeDependentChild (target);
4043 target->mParent.setNull();
4044 }
4045 }
4046 else
4047 {
4048 /* just register */
4049 rc = that->mVirtualBox->registerHardDisk(target);
4050 }
4051 }
4052
4053 thatLock.maybeEnter();
4054
4055 if (SUCCEEDED (rc))
4056 {
4057 target->m.state = MediaState_Created;
4058
4059 target->m.size = size;
4060 target->mm.logicalSize = logicalSize;
4061 }
4062 else
4063 {
4064 /* back to NotCreated on failiure */
4065 target->m.state = MediaState_NotCreated;
4066
4067 /* reset UUID to prevent it from being reused next time */
4068 if (generateUuid)
4069 unconst (target->m.id).clear();
4070 }
4071
4072 if (isAsync)
4073 {
4074 /* unlock ourselves when done (unless in MediaState_LockedWrite
4075 * state because of taking the online snapshot*/
4076 if (that->m.state != MediaState_LockedWrite)
4077 {
4078 HRESULT rc2 = that->UnlockRead (NULL);
4079 AssertComRC (rc2);
4080 }
4081 }
4082
4083 /* Note that in sync mode, it's the caller's responsibility to
4084 * unlock the hard disk */
4085
4086 break;
4087 }
4088
4089 ////////////////////////////////////////////////////////////////////////
4090
4091 case Task::Delete:
4092 {
4093 /* The lock is also used as a signal from the task initiator (which
4094 * releases it only after RTThreadCreate()) that we can start the job */
4095 AutoWriteLock thatLock (that);
4096
4097 try
4098 {
4099 PVBOXHDD hdd;
4100 int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
4101 ComAssertRCThrow (vrc, E_FAIL);
4102
4103 Utf8Str format (that->mm.format);
4104 Utf8Str location (that->m.locationFull);
4105
4106 /* unlock before the potentially lengthy operation */
4107 Assert (that->m.state == MediaState_Deleting);
4108 thatLock.leave();
4109
4110 try
4111 {
4112 vrc = VDOpen (hdd, format, location,
4113 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
4114 that->mm.vdDiskIfaces);
4115 if (RT_SUCCESS (vrc))
4116 vrc = VDClose (hdd, true /* fDelete */);
4117
4118 if (RT_FAILURE (vrc))
4119 {
4120 throw setError (E_FAIL,
4121 tr ("Could not delete the hard disk storage "
4122 "unit '%s'%s"),
4123 location.raw(), that->vdError (vrc).raw());
4124 }
4125
4126 }
4127 catch (HRESULT aRC) { rc = aRC; }
4128
4129 VDDestroy (hdd);
4130 }
4131 catch (HRESULT aRC) { rc = aRC; }
4132
4133 thatLock.maybeEnter();
4134
4135 /* go to the NotCreated state even on failure since the storage
4136 * may have been already partially deleted and cannot be used any
4137 * more. One will be able to manually re-open the storage if really
4138 * needed to re-register it. */
4139 that->m.state = MediaState_NotCreated;
4140
4141 /* Reset UUID to prevent Create* from reusing it again */
4142 unconst (that->m.id).clear();
4143
4144 break;
4145 }
4146
4147 case Task::Reset:
4148 {
4149 /* The lock is also used as a signal from the task initiator (which
4150 * releases it only after RTThreadCreate()) that we can start the job */
4151 AutoWriteLock thatLock (that);
4152
4153 /// @todo Below we use a pair of delete/create operations to reset
4154 /// the diff contents but the most efficient way will of course be
4155 /// to add a VDResetDiff() API call
4156
4157 uint64_t size = 0, logicalSize = 0;
4158
4159 try
4160 {
4161 PVBOXHDD hdd;
4162 int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
4163 ComAssertRCThrow (vrc, E_FAIL);
4164
4165 Guid id = that->m.id;
4166 Utf8Str format (that->mm.format);
4167 Utf8Str location (that->m.locationFull);
4168
4169 Guid parentId = that->mParent->m.id;
4170 Utf8Str parentFormat (that->mParent->mm.format);
4171 Utf8Str parentLocation (that->mParent->m.locationFull);
4172
4173 Assert (that->m.state == MediaState_LockedWrite);
4174
4175 /* unlock before the potentially lengthy operation */
4176 thatLock.leave();
4177
4178 try
4179 {
4180 /* first, delete the storage unit */
4181 vrc = VDOpen (hdd, format, location,
4182 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
4183 that->mm.vdDiskIfaces);
4184 if (RT_SUCCESS (vrc))
4185 vrc = VDClose (hdd, true /* fDelete */);
4186
4187 if (RT_FAILURE (vrc))
4188 {
4189 throw setError (E_FAIL,
4190 tr ("Could not delete the hard disk storage "
4191 "unit '%s'%s"),
4192 location.raw(), that->vdError (vrc).raw());
4193 }
4194
4195 /* next, create it again */
4196 vrc = VDOpen (hdd, parentFormat, parentLocation,
4197 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
4198 that->mm.vdDiskIfaces);
4199 if (RT_FAILURE (vrc))
4200 {
4201 throw setError (E_FAIL,
4202 tr ("Could not open the hard disk storage "
4203 "unit '%s'%s"),
4204 parentLocation.raw(), that->vdError (vrc).raw());
4205 }
4206
4207 /* needed for vdProgressCallback */
4208 that->mm.vdProgress = task->progress;
4209
4210 vrc = VDCreateDiff (hdd, format, location,
4211 /// @todo use the same image variant as before
4212 VD_IMAGE_FLAGS_NONE,
4213 NULL, id.raw(),
4214 parentId.raw(),
4215 VD_OPEN_FLAGS_NORMAL,
4216 that->mm.vdDiskIfaces,
4217 that->mm.vdDiskIfaces);
4218
4219 that->mm.vdProgress = NULL;
4220
4221 if (RT_FAILURE (vrc))
4222 {
4223 throw setError (E_FAIL,
4224 tr ("Could not create the differencing hard disk "
4225 "storage unit '%s'%s"),
4226 location.raw(), that->vdError (vrc).raw());
4227 }
4228
4229 size = VDGetFileSize (hdd, 1);
4230 logicalSize = VDGetSize (hdd, 1) / _1M;
4231 }
4232 catch (HRESULT aRC) { rc = aRC; }
4233
4234 VDDestroy (hdd);
4235 }
4236 catch (HRESULT aRC) { rc = aRC; }
4237
4238 thatLock.enter();
4239
4240 that->m.size = size;
4241 that->mm.logicalSize = logicalSize;
4242
4243 if (isAsync)
4244 {
4245 /* unlock ourselves when done */
4246 HRESULT rc2 = that->UnlockWrite (NULL);
4247 AssertComRC (rc2);
4248 }
4249
4250 /* Note that in sync mode, it's the caller's responsibility to
4251 * unlock the hard disk */
4252
4253 break;
4254 }
4255
4256 default:
4257 AssertFailedReturn (VERR_GENERAL_FAILURE);
4258 }
4259
4260 /* complete the progress if run asynchronously */
4261 if (isAsync)
4262 {
4263 if (!task->progress.isNull())
4264 task->progress->notifyComplete (rc);
4265 }
4266 else
4267 {
4268 task->rc = rc;
4269 }
4270
4271 LogFlowFunc (("rc=%Rhrc\n", rc));
4272 LogFlowFuncLeave();
4273
4274 return VINF_SUCCESS;
4275
4276 /// @todo ugly hack, fix ComAssert... later
4277 #undef setError
4278}
4279/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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