VirtualBox

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

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

#3686: “Main: fix unused var warnings”

  • Garbage collect; use NOREF(); comment out; (depending on situation)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 141.4 KB
Line 
1/* $Id: HardDiskImpl.cpp 17911 2009-03-16 10:30:55Z 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 { CreateDynamic, CreateFixed, 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 /* CreateDynamic, CreateStatic */
101
102 uint64_t size;
103
104 /* CreateDynamic, CreateStatic, 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::CreateDynamicStorage(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 if (!(mm.formatObj->capabilities() &
1192 HardDiskFormatCapabilities_CreateDynamic))
1193 return setError (VBOX_E_NOT_SUPPORTED,
1194 tr ("Hard disk format '%ls' does not support dynamic storage "
1195 "creation"), mm.format.raw());
1196
1197 switch (m.state)
1198 {
1199 case MediaState_NotCreated:
1200 break;
1201 default:
1202 return setStateError();
1203 }
1204
1205 ComObjPtr <Progress> progress;
1206 progress.createObject();
1207 HRESULT rc = progress->init (mVirtualBox, static_cast<IHardDisk*>(this),
1208 BstrFmt (tr ("Creating dynamic hard disk storage unit '%ls'"),
1209 m.locationFull.raw()),
1210 FALSE /* aCancelable */);
1211 CheckComRCReturnRC (rc);
1212
1213 /* setup task object and thread to carry out the operation
1214 * asynchronously */
1215
1216 std::auto_ptr <Task> task (new Task (this, progress, Task::CreateDynamic));
1217 AssertComRCReturnRC (task->autoCaller.rc());
1218
1219 task->d.size = aLogicalSize;
1220 task->d.variant = aVariant;
1221
1222 rc = task->startThread();
1223 CheckComRCReturnRC (rc);
1224
1225 /* go to Creating state on success */
1226 m.state = MediaState_Creating;
1227
1228 /* task is now owned by taskThread() so release it */
1229 task.release();
1230
1231 /* return progress to the caller */
1232 progress.queryInterfaceTo (aProgress);
1233
1234 return S_OK;
1235}
1236
1237STDMETHODIMP HardDisk::CreateFixedStorage(ULONG64 aLogicalSize,
1238 HardDiskVariant_T aVariant,
1239 IProgress **aProgress)
1240{
1241 CheckComArgOutPointerValid (aProgress);
1242
1243 AutoCaller autoCaller (this);
1244 CheckComRCReturnRC (autoCaller.rc());
1245
1246 AutoWriteLock alock (this);
1247
1248 if (!(mm.formatObj->capabilities() &
1249 HardDiskFormatCapabilities_CreateFixed))
1250 return setError (VBOX_E_NOT_SUPPORTED,
1251 tr ("Hard disk format '%ls' does not support fixed storage "
1252 "creation"), mm.format.raw());
1253
1254 switch (m.state)
1255 {
1256 case MediaState_NotCreated:
1257 break;
1258 default:
1259 return setStateError();
1260 }
1261
1262 ComObjPtr <Progress> progress;
1263 progress.createObject();
1264 HRESULT rc = progress->init (mVirtualBox, static_cast<IHardDisk*>(this),
1265 BstrFmt (tr ("Creating fixed hard disk storage unit '%ls'"),
1266 m.locationFull.raw()),
1267 FALSE /* aCancelable */);
1268 CheckComRCReturnRC (rc);
1269
1270 /* setup task object and thread to carry out the operation
1271 * asynchronously */
1272
1273 std::auto_ptr <Task> task (new Task (this, progress, Task::CreateFixed));
1274 AssertComRCReturnRC (task->autoCaller.rc());
1275
1276 task->d.size = aLogicalSize;
1277 task->d.variant = aVariant;
1278
1279 rc = task->startThread();
1280 CheckComRCReturnRC (rc);
1281
1282 /* go to Creating state on success */
1283 m.state = MediaState_Creating;
1284
1285 /* task is now owned by taskThread() so release it */
1286 task.release();
1287
1288 /* return progress to the caller */
1289 progress.queryInterfaceTo (aProgress);
1290
1291 return S_OK;
1292}
1293
1294STDMETHODIMP HardDisk::DeleteStorage (IProgress **aProgress)
1295{
1296 CheckComArgOutPointerValid (aProgress);
1297
1298 AutoCaller autoCaller (this);
1299 CheckComRCReturnRC (autoCaller.rc());
1300
1301 ComObjPtr <Progress> progress;
1302
1303 HRESULT rc = deleteStorageNoWait (progress);
1304 if (SUCCEEDED (rc))
1305 {
1306 /* return progress to the caller */
1307 progress.queryInterfaceTo (aProgress);
1308 }
1309
1310 return rc;
1311}
1312
1313STDMETHODIMP HardDisk::CreateDiffStorage (IHardDisk *aTarget,
1314 HardDiskVariant_T aVariant,
1315 IProgress **aProgress)
1316{
1317 CheckComArgNotNull (aTarget);
1318 CheckComArgOutPointerValid (aProgress);
1319
1320 AutoCaller autoCaller (this);
1321 CheckComRCReturnRC (autoCaller.rc());
1322
1323 ComObjPtr<HardDisk> diff;
1324 HRESULT rc = mVirtualBox->cast (aTarget, diff);
1325 CheckComRCReturnRC (rc);
1326
1327 AutoWriteLock alock (this);
1328
1329 if (mm.type == HardDiskType_Writethrough)
1330 return setError (E_FAIL,
1331 tr ("Hard disk '%ls' is Writethrough"),
1332 m.locationFull.raw());
1333
1334 /* We want to be locked for reading as long as our diff child is being
1335 * created */
1336 rc = LockRead (NULL);
1337 CheckComRCReturnRC (rc);
1338
1339 ComObjPtr <Progress> progress;
1340
1341 rc = createDiffStorageNoWait (diff, aVariant, progress);
1342 if (FAILED (rc))
1343 {
1344 HRESULT rc2 = UnlockRead (NULL);
1345 AssertComRC (rc2);
1346 /* Note: on success, taskThread() will unlock this */
1347 }
1348 else
1349 {
1350 /* return progress to the caller */
1351 progress.queryInterfaceTo (aProgress);
1352 }
1353
1354 return rc;
1355}
1356
1357STDMETHODIMP HardDisk::MergeTo (IN_GUID /* aTargetId */, IProgress ** /* aProgress */)
1358{
1359 AutoCaller autoCaller (this);
1360 CheckComRCReturnRC (autoCaller.rc());
1361
1362 ReturnComNotImplemented();
1363}
1364
1365STDMETHODIMP HardDisk::CloneTo (IHardDisk *aTarget,
1366 HardDiskVariant_T aVariant,
1367 IProgress **aProgress)
1368{
1369 CheckComArgNotNull (aTarget);
1370 CheckComArgOutPointerValid (aProgress);
1371
1372 AutoCaller autoCaller (this);
1373 CheckComRCReturnRC (autoCaller.rc());
1374
1375 ComObjPtr <HardDisk> target;
1376 HRESULT rc = mVirtualBox->cast (aTarget, target);
1377 CheckComRCReturnRC (rc);
1378
1379 AutoMultiWriteLock2 alock (this, target);
1380
1381 /* We want to be locked for reading as long as the clone hard disk is being
1382 * created*/
1383 rc = LockRead (NULL);
1384 CheckComRCReturnRC (rc);
1385
1386 ComObjPtr <Progress> progress;
1387
1388 try
1389 {
1390 if (target->m.state != MediaState_NotCreated)
1391 throw target->setStateError();
1392
1393 progress.createObject();
1394 rc = progress->init (mVirtualBox, static_cast <IHardDisk *> (this),
1395 BstrFmt (tr ("Creating clone hard disk '%ls'"),
1396 target->m.locationFull.raw()),
1397 FALSE /* aCancelable */);
1398 CheckComRCThrowRC (rc);
1399
1400 /* setup task object and thread to carry out the operation
1401 * asynchronously */
1402
1403 std::auto_ptr <Task> task (new Task (this, progress, Task::Clone));
1404 AssertComRCThrowRC (task->autoCaller.rc());
1405
1406 task->setData (target);
1407 task->d.variant = aVariant;
1408
1409 rc = task->startThread();
1410 CheckComRCThrowRC (rc);
1411
1412 /* go to Creating state before leaving the lock */
1413 target->m.state = MediaState_Creating;
1414
1415 /* task is now owned (or already deleted) by taskThread() so release it */
1416 task.release();
1417 }
1418 catch (HRESULT aRC)
1419 {
1420 rc = aRC;
1421 }
1422
1423 if (FAILED (rc))
1424 {
1425 HRESULT rc2 = UnlockRead (NULL);
1426 AssertComRC (rc2);
1427 /* Note: on success, taskThread() will unlock this */
1428 }
1429 else
1430 {
1431 /* return progress to the caller */
1432 progress.queryInterfaceTo (aProgress);
1433 }
1434
1435 return rc;
1436}
1437
1438STDMETHODIMP HardDisk::FlattenTo (IHardDisk *aTarget,
1439 HardDiskVariant_T /* aVariant */,
1440 IProgress **aProgress)
1441{
1442 CheckComArgNotNull (aTarget);
1443 CheckComArgOutPointerValid (aProgress);
1444
1445 AutoCaller autoCaller (this);
1446 CheckComRCReturnRC (autoCaller.rc());
1447
1448 ReturnComNotImplemented();
1449}
1450
1451STDMETHODIMP HardDisk::Compact (IProgress **aProgress)
1452{
1453 CheckComArgOutPointerValid (aProgress);
1454
1455 AutoCaller autoCaller (this);
1456 CheckComRCReturnRC (autoCaller.rc());
1457
1458 ReturnComNotImplemented();
1459}
1460
1461STDMETHODIMP HardDisk::Reset (IProgress **aProgress)
1462{
1463 CheckComArgOutPointerValid (aProgress);
1464
1465 AutoCaller autoCaller (this);
1466 CheckComRCReturnRC (autoCaller.rc());
1467
1468 AutoWriteLock alock (this);
1469
1470 if (mParent.isNull())
1471 return setError (VBOX_E_NOT_SUPPORTED,
1472 tr ("Hard disk '%ls' is not differencing"),
1473 m.locationFull.raw());
1474
1475 HRESULT rc = canClose();
1476 CheckComRCReturnRC (rc);
1477
1478 rc = LockWrite (NULL);
1479 CheckComRCReturnRC (rc);
1480
1481 ComObjPtr <Progress> progress;
1482
1483 try
1484 {
1485 progress.createObject();
1486 rc = progress->init (mVirtualBox, static_cast <IHardDisk *> (this),
1487 BstrFmt (tr ("Resetting differencing hard disk '%ls'"),
1488 m.locationFull.raw()),
1489 FALSE /* aCancelable */);
1490 CheckComRCThrowRC (rc);
1491
1492 /* setup task object and thread to carry out the operation
1493 * asynchronously */
1494
1495 std::auto_ptr <Task> task (new Task (this, progress, Task::Reset));
1496 AssertComRCThrowRC (task->autoCaller.rc());
1497
1498 rc = task->startThread();
1499 CheckComRCThrowRC (rc);
1500
1501 /* task is now owned (or already deleted) by taskThread() so release it */
1502 task.release();
1503 }
1504 catch (HRESULT aRC)
1505 {
1506 rc = aRC;
1507 }
1508
1509 if (FAILED (rc))
1510 {
1511 HRESULT rc2 = UnlockWrite (NULL);
1512 AssertComRC (rc2);
1513 /* Note: on success, taskThread() will unlock this */
1514 }
1515 else
1516 {
1517 /* return progress to the caller */
1518 progress.queryInterfaceTo (aProgress);
1519 }
1520
1521 return rc;
1522}
1523
1524// public methods for internal purposes only
1525////////////////////////////////////////////////////////////////////////////////
1526
1527/**
1528 * Checks if the given change of \a aOldPath to \a aNewPath affects the location
1529 * of this hard disk or any its child and updates the paths if necessary to
1530 * reflect the new location.
1531 *
1532 * @param aOldPath Old path (full).
1533 * @param aNewPath New path (full).
1534 *
1535 * @note Locks treeLock() for reading, this object and all children for writing.
1536 */
1537void HardDisk::updatePaths (const char *aOldPath, const char *aNewPath)
1538{
1539 AssertReturnVoid (aOldPath);
1540 AssertReturnVoid (aNewPath);
1541
1542 AutoCaller autoCaller (this);
1543 AssertComRCReturnVoid (autoCaller.rc());
1544
1545 AutoWriteLock alock (this);
1546
1547 /* we access children() */
1548 AutoReadLock treeLock (this->treeLock());
1549
1550 updatePath (aOldPath, aNewPath);
1551
1552 /* update paths of all children */
1553 for (List::const_iterator it = children().begin();
1554 it != children().end();
1555 ++ it)
1556 {
1557 (*it)->updatePaths (aOldPath, aNewPath);
1558 }
1559}
1560
1561/**
1562 * Returns the base hard disk of the hard disk chain this hard disk is part of.
1563 *
1564 * The root hard disk is found by walking up the parent-child relationship axis.
1565 * If the hard disk doesn't have a parent (i.e. it's a base hard disk), it
1566 * returns itself in response to this method.
1567 *
1568 * @param aLevel Where to store the number of ancestors of this hard disk
1569 * (zero for the root), may be @c NULL.
1570 *
1571 * @note Locks treeLock() for reading.
1572 */
1573ComObjPtr <HardDisk> HardDisk::root (uint32_t *aLevel /*= NULL*/)
1574{
1575 ComObjPtr <HardDisk> root;
1576 uint32_t level;
1577
1578 AutoCaller autoCaller (this);
1579 AssertReturn (autoCaller.isOk(), root);
1580
1581 /* we access mParent */
1582 AutoReadLock treeLock (this->treeLock());
1583
1584 root = this;
1585 level = 0;
1586
1587 if (!mParent.isNull())
1588 {
1589 for (;;)
1590 {
1591 AutoCaller rootCaller (root);
1592 AssertReturn (rootCaller.isOk(), root);
1593
1594 if (root->mParent.isNull())
1595 break;
1596
1597 root = root->mParent;
1598 ++ level;
1599 }
1600 }
1601
1602 if (aLevel != NULL)
1603 *aLevel = level;
1604
1605 return root;
1606}
1607
1608/**
1609 * Returns @c true if this hard disk cannot be modified because it has
1610 * dependants (children) or is part of the snapshot. Related to the hard disk
1611 * type and posterity, not to the current media state.
1612 *
1613 * @note Locks this object and treeLock() for reading.
1614 */
1615bool HardDisk::isReadOnly()
1616{
1617 AutoCaller autoCaller (this);
1618 AssertComRCReturn (autoCaller.rc(), false);
1619
1620 AutoReadLock alock (this);
1621
1622 /* we access children */
1623 AutoReadLock treeLock (this->treeLock());
1624
1625 switch (mm.type)
1626 {
1627 case HardDiskType_Normal:
1628 {
1629 if (children().size() != 0)
1630 return true;
1631
1632 for (BackRefList::const_iterator it = m.backRefs.begin();
1633 it != m.backRefs.end(); ++ it)
1634 if (it->snapshotIds.size() != 0)
1635 return true;
1636
1637 return false;
1638 }
1639 case HardDiskType_Immutable:
1640 {
1641 return true;
1642 }
1643 case HardDiskType_Writethrough:
1644 {
1645 return false;
1646 }
1647 default:
1648 break;
1649 }
1650
1651 AssertFailedReturn (false);
1652}
1653
1654/**
1655 * Saves hard disk data by appending a new <HardDisk> child node to the given
1656 * parent node which can be either <HardDisks> or <HardDisk>.
1657 *
1658 * @param aaParentNode Parent <HardDisks> or <HardDisk> node.
1659 *
1660 * @note Locks this object, treeLock() and children for reading.
1661 */
1662HRESULT HardDisk::saveSettings (settings::Key &aParentNode)
1663{
1664 using namespace settings;
1665
1666 AssertReturn (!aParentNode.isNull(), E_FAIL);
1667
1668 AutoCaller autoCaller (this);
1669 CheckComRCReturnRC (autoCaller.rc());
1670
1671 AutoReadLock alock (this);
1672
1673 /* we access mParent */
1674 AutoReadLock treeLock (this->treeLock());
1675
1676 Key diskNode = aParentNode.appendKey ("HardDisk");
1677 /* required */
1678 diskNode.setValue <Guid> ("uuid", m.id);
1679 /* required (note: the original locaiton, not full) */
1680 diskNode.setValue <Bstr> ("location", m.location);
1681 /* required */
1682 diskNode.setValue <Bstr> ("format", mm.format);
1683 /* optional, only for diffs, default is false */
1684 if (!mParent.isNull())
1685 diskNode.setValueOr <bool> ("autoReset", !!mm.autoReset, false);
1686 /* optional */
1687 if (!m.description.isNull())
1688 {
1689 Key descNode = diskNode.createKey ("Description");
1690 descNode.setKeyValue <Bstr> (m.description);
1691 }
1692
1693 /* optional properties */
1694 for (Data::PropertyMap::const_iterator it = mm.properties.begin();
1695 it != mm.properties.end(); ++ it)
1696 {
1697 /* only save properties that have non-default values */
1698 if (!it->second.isNull())
1699 {
1700 Key propNode = diskNode.appendKey ("Property");
1701 propNode.setValue <Bstr> ("name", it->first);
1702 propNode.setValue <Bstr> ("value", it->second);
1703 }
1704 }
1705
1706 /* only for base hard disks */
1707 if (mParent.isNull())
1708 {
1709 const char *type =
1710 mm.type == HardDiskType_Normal ? "Normal" :
1711 mm.type == HardDiskType_Immutable ? "Immutable" :
1712 mm.type == HardDiskType_Writethrough ? "Writethrough" : NULL;
1713 Assert (type != NULL);
1714 diskNode.setStringValue ("type", type);
1715 }
1716
1717 /* save all children */
1718 for (List::const_iterator it = children().begin();
1719 it != children().end();
1720 ++ it)
1721 {
1722 HRESULT rc = (*it)->saveSettings (diskNode);
1723 AssertComRCReturnRC (rc);
1724 }
1725
1726 return S_OK;
1727}
1728
1729/**
1730 * Compares the location of this hard disk to the given location.
1731 *
1732 * The comparison takes the location details into account. For example, if the
1733 * location is a file in the host's filesystem, a case insensitive comparison
1734 * will be performed for case insensitive filesystems.
1735 *
1736 * @param aLocation Location to compare to (as is).
1737 * @param aResult Where to store the result of comparison: 0 if locations
1738 * are equal, 1 if this object's location is greater than
1739 * the specified location, and -1 otherwise.
1740 */
1741HRESULT HardDisk::compareLocationTo (const char *aLocation, int &aResult)
1742{
1743 AutoCaller autoCaller (this);
1744 AssertComRCReturnRC (autoCaller.rc());
1745
1746 AutoReadLock alock (this);
1747
1748 Utf8Str locationFull (m.locationFull);
1749
1750 /// @todo NEWMEDIA delegate the comparison to the backend?
1751
1752 if (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File)
1753 {
1754 Utf8Str location (aLocation);
1755
1756 /* For locations represented by files, append the default path if
1757 * only the name is given, and then get the full path. */
1758 if (!RTPathHavePath (aLocation))
1759 {
1760 AutoReadLock propsLock (mVirtualBox->systemProperties());
1761 location = Utf8StrFmt ("%ls%c%s",
1762 mVirtualBox->systemProperties()->defaultHardDiskFolder().raw(),
1763 RTPATH_DELIMITER, aLocation);
1764 }
1765
1766 int vrc = mVirtualBox->calculateFullPath (location, location);
1767 if (RT_FAILURE (vrc))
1768 return setError (E_FAIL,
1769 tr ("Invalid hard disk storage file location '%s' (%Rrc)"),
1770 location.raw(), vrc);
1771
1772 aResult = RTPathCompare (locationFull, location);
1773 }
1774 else
1775 aResult = locationFull.compare (aLocation);
1776
1777 return S_OK;
1778}
1779
1780/**
1781 * Returns a short version of the location attribute.
1782 *
1783 * Reimplements MediumBase::name() to specially treat non-FS-path locations.
1784 *
1785 * @note Must be called from under this object's read or write lock.
1786 */
1787Utf8Str HardDisk::name()
1788{
1789 /// @todo NEWMEDIA treat non-FS-paths specially! (may require to requiest
1790 /// this information from the VD backend)
1791
1792 Utf8Str location (m.locationFull);
1793
1794 Utf8Str name = RTPathFilename (location);
1795 return name;
1796}
1797
1798/**
1799 * Checks that this hard disk may be discarded and performs necessary state
1800 * changes.
1801 *
1802 * This method is to be called prior to calling the #discrad() to perform
1803 * necessary consistency checks and place involved hard disks to appropriate
1804 * states. If #discard() is not called or fails, the state modifications
1805 * performed by this method must be undone by #cancelDiscard().
1806 *
1807 * See #discard() for more info about discarding hard disks.
1808 *
1809 * @param aChain Where to store the created merge chain (may return NULL
1810 * if no real merge is necessary).
1811 *
1812 * @note Locks treeLock() for reading. Locks this object, aTarget and all
1813 * intermediate hard disks for writing.
1814 */
1815HRESULT HardDisk::prepareDiscard (MergeChain * &aChain)
1816{
1817 AutoCaller autoCaller (this);
1818 AssertComRCReturnRC (autoCaller.rc());
1819
1820 aChain = NULL;
1821
1822 AutoWriteLock alock (this);
1823
1824 /* we access mParent & children() */
1825 AutoReadLock treeLock (this->treeLock());
1826
1827 AssertReturn (mm.type == HardDiskType_Normal, E_FAIL);
1828
1829 if (children().size() == 0)
1830 {
1831 /* special treatment of the last hard disk in the chain: */
1832
1833 if (mParent.isNull())
1834 {
1835 /* lock only, to prevent any usage; discard() will unlock */
1836 return LockWrite (NULL);
1837 }
1838
1839 /* the differencing hard disk w/o children will be deleted, protect it
1840 * from attaching to other VMs (this is why Deleting) */
1841
1842 switch (m.state)
1843 {
1844 case MediaState_Created:
1845 m.state = MediaState_Deleting;
1846 break;
1847 default:
1848 return setStateError();
1849 }
1850
1851 /* aChain is intentionally NULL here */
1852
1853 return S_OK;
1854 }
1855
1856 /* not going multi-merge as it's too expensive */
1857 if (children().size() > 1)
1858 return setError (E_FAIL,
1859 tr ("Hard disk '%ls' has more than one child hard disk (%d)"),
1860 m.locationFull.raw(), children().size());
1861
1862 /* this is a read-only hard disk with children; it must be associated with
1863 * exactly one snapshot (when the snapshot is being taken, none of the
1864 * current VM's hard disks may be attached to other VMs). Note that by the
1865 * time when discard() is called, there must be no any attachments at all
1866 * (the code calling prepareDiscard() should detach). */
1867 AssertReturn (m.backRefs.size() == 1 &&
1868 !m.backRefs.front().inCurState &&
1869 m.backRefs.front().snapshotIds.size() == 1, E_FAIL);
1870
1871 ComObjPtr<HardDisk> child = children().front();
1872
1873 /* we keep this locked, so lock the affected child to make sure the lock
1874 * order is correct when calling prepareMergeTo() */
1875 AutoWriteLock childLock (child);
1876
1877 /* delegate the rest to the profi */
1878 if (mParent.isNull())
1879 {
1880 /* base hard disk, backward merge */
1881
1882 Assert (child->m.backRefs.size() == 1);
1883 if (child->m.backRefs.front().machineId != m.backRefs.front().machineId)
1884 {
1885 /* backward merge is too tricky, we'll just detach on discard, so
1886 * lock only, to prevent any usage; discard() will only unlock
1887 * (since we return NULL in aChain) */
1888 return LockWrite (NULL);
1889 }
1890
1891 return child->prepareMergeTo (this, aChain,
1892 true /* aIgnoreAttachments */);
1893 }
1894 else
1895 {
1896 /* forward merge */
1897 return prepareMergeTo (child, aChain,
1898 true /* aIgnoreAttachments */);
1899 }
1900}
1901
1902/**
1903 * Discards this hard disk.
1904 *
1905 * Discarding the hard disk is merging its contents to its differencing child
1906 * hard disk (forward merge) or contents of its child hard disk to itself
1907 * (backward merge) if this hard disk is a base hard disk. If this hard disk is
1908 * a differencing hard disk w/o children, then it will be simply deleted.
1909 * Calling this method on a base hard disk w/o children will do nothing and
1910 * silently succeed. If this hard disk has more than one child, the method will
1911 * currently return an error (since merging in this case would be too expensive
1912 * and result in data duplication).
1913 *
1914 * When the backward merge takes place (i.e. this hard disk is a target) then,
1915 * on success, this hard disk will automatically replace the differencing child
1916 * hard disk used as a source (which will then be deleted) in the attachment
1917 * this child hard disk is associated with. This will happen only if both hard
1918 * disks belong to the same machine because otherwise such a replace would be
1919 * too tricky and could be not expected by the other machine. Same relates to a
1920 * case when the child hard disk is not associated with any machine at all. When
1921 * the backward merge is not applied, the method behaves as if the base hard
1922 * disk were not attached at all -- i.e. simply detaches it from the machine but
1923 * leaves the hard disk chain intact.
1924 *
1925 * This method is basically a wrapper around #mergeTo() that selects the correct
1926 * merge direction and performs additional actions as described above and.
1927 *
1928 * Note that this method will not return until the merge operation is complete
1929 * (which may be quite time consuming depending on the size of the merged hard
1930 * disks).
1931 *
1932 * Note that #prepareDiscard() must be called before calling this method. If
1933 * this method returns a failure, the caller must call #cancelDiscard(). On
1934 * success, #cancelDiscard() must not be called (this method will perform all
1935 * necessary steps such as resetting states of all involved hard disks and
1936 * deleting @a aChain).
1937 *
1938 * @param aChain Merge chain created by #prepareDiscard() (may be NULL if
1939 * no real merge takes place).
1940 *
1941 * @note Locks the hard disks from the chain for writing. Locks the machine
1942 * object when the backward merge takes place. Locks treeLock() lock for
1943 * reading or writing.
1944 */
1945HRESULT HardDisk::discard (ComObjPtr <Progress> &aProgress, MergeChain *aChain)
1946{
1947 AssertReturn (!aProgress.isNull(), E_FAIL);
1948
1949 ComObjPtr <HardDisk> hdFrom;
1950
1951 HRESULT rc = S_OK;
1952
1953 {
1954 AutoCaller autoCaller (this);
1955 AssertComRCReturnRC (autoCaller.rc());
1956
1957 aProgress->advanceOperation (BstrFmt (
1958 tr ("Discarding hard disk '%s'"), name().raw()));
1959
1960 if (aChain == NULL)
1961 {
1962 AutoWriteLock alock (this);
1963
1964 /* we access mParent & children() */
1965 AutoReadLock treeLock (this->treeLock());
1966
1967 Assert (children().size() == 0);
1968
1969 /* special treatment of the last hard disk in the chain: */
1970
1971 if (mParent.isNull())
1972 {
1973 rc = UnlockWrite (NULL);
1974 AssertComRC (rc);
1975 return rc;
1976 }
1977
1978 /* delete the differencing hard disk w/o children */
1979
1980 Assert (m.state == MediaState_Deleting);
1981
1982 /* go back to Created since deleteStorage() expects this state */
1983 m.state = MediaState_Created;
1984
1985 hdFrom = this;
1986
1987 rc = deleteStorageAndWait (&aProgress);
1988 }
1989 else
1990 {
1991 hdFrom = aChain->source();
1992
1993 rc = hdFrom->mergeToAndWait (aChain, &aProgress);
1994 }
1995 }
1996
1997 if (SUCCEEDED (rc))
1998 {
1999 /* mergeToAndWait() cannot uninitialize the initiator because of
2000 * possible AutoCallers on the current thread, deleteStorageAndWait()
2001 * doesn't do it either; do it ourselves */
2002 hdFrom->uninit();
2003 }
2004
2005 return rc;
2006}
2007
2008/**
2009 * Undoes what #prepareDiscard() did. Must be called if #discard() is not called
2010 * or fails. Frees memory occupied by @a aChain.
2011 *
2012 * @param aChain Merge chain created by #prepareDiscard() (may be NULL if
2013 * no real merge takes place).
2014 *
2015 * @note Locks the hard disks from the chain for writing. Locks treeLock() for
2016 * reading.
2017 */
2018void HardDisk::cancelDiscard (MergeChain *aChain)
2019{
2020 AutoCaller autoCaller (this);
2021 AssertComRCReturnVoid (autoCaller.rc());
2022
2023 if (aChain == NULL)
2024 {
2025 AutoWriteLock alock (this);
2026
2027 /* we access mParent & children() */
2028 AutoReadLock treeLock (this->treeLock());
2029
2030 Assert (children().size() == 0);
2031
2032 /* special treatment of the last hard disk in the chain: */
2033
2034 if (mParent.isNull())
2035 {
2036 HRESULT rc = UnlockWrite (NULL);
2037 AssertComRC (rc);
2038 return;
2039 }
2040
2041 /* the differencing hard disk w/o children will be deleted, protect it
2042 * from attaching to other VMs (this is why Deleting) */
2043
2044 Assert (m.state == MediaState_Deleting);
2045 m.state = MediaState_Created;
2046
2047 return;
2048 }
2049
2050 /* delegate the rest to the profi */
2051 cancelMergeTo (aChain);
2052}
2053
2054/**
2055 * Returns a preferred format for differencing hard disks.
2056 */
2057Bstr HardDisk::preferredDiffFormat()
2058{
2059 Bstr format;
2060
2061 AutoCaller autoCaller (this);
2062 AssertComRCReturn (autoCaller.rc(), format);
2063
2064 /* mm.format is const, no need to lock */
2065 format = mm.format;
2066
2067 /* check that our own format supports diffs */
2068 if (!(mm.formatObj->capabilities() & HardDiskFormatCapabilities_Differencing))
2069 {
2070 /* use the default format if not */
2071 AutoReadLock propsLock (mVirtualBox->systemProperties());
2072 format = mVirtualBox->systemProperties()->defaultHardDiskFormat();
2073 }
2074
2075 return format;
2076}
2077
2078// protected methods
2079////////////////////////////////////////////////////////////////////////////////
2080
2081/**
2082 * Deletes the hard disk storage unit.
2083 *
2084 * If @a aProgress is not NULL but the object it points to is @c null then a new
2085 * progress object will be created and assigned to @a *aProgress on success,
2086 * otherwise the existing progress object is used. If Progress is NULL, then no
2087 * progress object is created/used at all.
2088 *
2089 * When @a aWait is @c false, this method will create a thread to perform the
2090 * delete operation asynchronously and will return immediately. Otherwise, it
2091 * will perform the operation on the calling thread and will not return to the
2092 * caller until the operation is completed. Note that @a aProgress cannot be
2093 * NULL when @a aWait is @c false (this method will assert in this case).
2094 *
2095 * @param aProgress Where to find/store a Progress object to track operation
2096 * completion.
2097 * @param aWait @c true if this method should block instead of creating
2098 * an asynchronous thread.
2099 *
2100 * @note Locks mVirtualBox and this object for writing. Locks treeLock() for
2101 * writing.
2102 */
2103HRESULT HardDisk::deleteStorage (ComObjPtr <Progress> *aProgress, bool aWait)
2104{
2105 AssertReturn (aProgress != NULL || aWait == true, E_FAIL);
2106
2107 /* unregisterWithVirtualBox() needs a write lock. We want to unregister
2108 * ourselves atomically after detecting that deletion is possible to make
2109 * sure that we don't do that after another thread has done
2110 * VirtualBox::findHardDisk() but before it starts using us (provided that
2111 * it holds a mVirtualBox lock too of course). */
2112
2113 AutoWriteLock vboxLock (mVirtualBox);
2114
2115 AutoWriteLock alock (this);
2116
2117 if (!(mm.formatObj->capabilities() &
2118 (HardDiskFormatCapabilities_CreateDynamic |
2119 HardDiskFormatCapabilities_CreateFixed)))
2120 return setError (VBOX_E_NOT_SUPPORTED,
2121 tr ("Hard disk format '%ls' does not support storage deletion"),
2122 mm.format.raw());
2123
2124 /* Note that we are fine with Inaccessible state too: a) for symmetry with
2125 * create calls and b) because it doesn't really harm to try, if it is
2126 * really inaccessibke, the delete operation will fail anyway. Accepting
2127 * Inaccessible state is especially important because all registered hard
2128 * disks are initially Inaccessible upon VBoxSVC startup until
2129 * COMGETTER(State) is called. */
2130
2131 switch (m.state)
2132 {
2133 case MediaState_Created:
2134 case MediaState_Inaccessible:
2135 break;
2136 default:
2137 return setStateError();
2138 }
2139
2140 if (m.backRefs.size() != 0)
2141 return setError (VBOX_E_OBJECT_IN_USE,
2142 tr ("Hard disk '%ls' is attached to %d virtual machines"),
2143 m.locationFull.raw(), m.backRefs.size());
2144
2145 HRESULT rc = canClose();
2146 CheckComRCReturnRC (rc);
2147
2148 /* go to Deleting state before leaving the lock */
2149 m.state = MediaState_Deleting;
2150
2151 /* we need to leave this object's write lock now because of
2152 * unregisterWithVirtualBox() that locks treeLock() for writing */
2153 alock.leave();
2154
2155 /* try to remove from the list of known hard disks before performing actual
2156 * deletion (we favor the consistency of the media registry in the first
2157 * place which would have been broken if unregisterWithVirtualBox() failed
2158 * after we successfully deleted the storage) */
2159
2160 rc = unregisterWithVirtualBox();
2161
2162 alock.enter();
2163
2164 /* restore the state because we may fail below; we will set it later again*/
2165 m.state = MediaState_Created;
2166
2167 CheckComRCReturnRC (rc);
2168
2169 ComObjPtr <Progress> progress;
2170
2171 if (aProgress != NULL)
2172 {
2173 /* use the existing progress object... */
2174 progress = *aProgress;
2175
2176 /* ...but create a new one if it is null */
2177 if (progress.isNull())
2178 {
2179 progress.createObject();
2180 rc = progress->init (mVirtualBox, static_cast<IHardDisk*>(this),
2181 BstrFmt (tr ("Deleting hard disk storage unit '%ls'"),
2182 m.locationFull.raw()),
2183 FALSE /* aCancelable */);
2184 CheckComRCReturnRC (rc);
2185 }
2186 }
2187
2188 std::auto_ptr <Task> task (new Task (this, progress, Task::Delete));
2189 AssertComRCReturnRC (task->autoCaller.rc());
2190
2191 if (aWait)
2192 {
2193 /* go to Deleting state before starting the task */
2194 m.state = MediaState_Deleting;
2195
2196 rc = task->runNow();
2197 }
2198 else
2199 {
2200 rc = task->startThread();
2201 CheckComRCReturnRC (rc);
2202
2203 /* go to Deleting state before leaving the lock */
2204 m.state = MediaState_Deleting;
2205 }
2206
2207 /* task is now owned (or already deleted) by taskThread() so release it */
2208 task.release();
2209
2210 if (aProgress != NULL)
2211 {
2212 /* return progress to the caller */
2213 *aProgress = progress;
2214 }
2215
2216 return rc;
2217}
2218
2219/**
2220 * Creates a new differencing storage unit using the given target hard disk's
2221 * format and the location. Note that @c aTarget must be NotCreated.
2222 *
2223 * As opposed to the CreateDiffStorage() method, this method doesn't try to lock
2224 * this hard disk for reading assuming that the caller has already done so. This
2225 * is used when taking an online snaopshot (where all original hard disks are
2226 * locked for writing and must remain such). Note however that if @a aWait is
2227 * @c false and this method returns a success then the thread started by
2228 * this method will unlock the hard disk (unless it is in
2229 * MediaState_LockedWrite state) so make sure the hard disk is either in
2230 * MediaState_LockedWrite or call #LockRead() before calling this method! If @a
2231 * aWait is @c true then this method neither locks nor unlocks the hard disk, so
2232 * make sure you do it yourself as needed.
2233 *
2234 * If @a aProgress is not NULL but the object it points to is @c null then a new
2235 * progress object will be created and assigned to @a *aProgress on success,
2236 * otherwise the existing progress object is used. If @a aProgress is NULL, then no
2237 * progress object is created/used at all.
2238 *
2239 * When @a aWait is @c false, this method will create a thread to perform the
2240 * create operation asynchronously and will return immediately. Otherwise, it
2241 * will perform the operation on the calling thread and will not return to the
2242 * caller until the operation is completed. Note that @a aProgress cannot be
2243 * NULL when @a aWait is @c false (this method will assert in this case).
2244 *
2245 * @param aTarget Target hard disk.
2246 * @param aVariant Precise image variant to create.
2247 * @param aProgress Where to find/store a Progress object to track operation
2248 * completion.
2249 * @param aWait @c true if this method should block instead of creating
2250 * an asynchronous thread.
2251 *
2252 * @note Locks this object and @a aTarget for writing.
2253 */
2254HRESULT HardDisk::createDiffStorage(ComObjPtr<HardDisk> &aTarget,
2255 HardDiskVariant_T aVariant,
2256 ComObjPtr<Progress> *aProgress,
2257 bool aWait)
2258{
2259 AssertReturn (!aTarget.isNull(), E_FAIL);
2260 AssertReturn (aProgress != NULL || aWait == true, E_FAIL);
2261
2262 AutoCaller autoCaller (this);
2263 CheckComRCReturnRC (autoCaller.rc());
2264
2265 AutoCaller targetCaller (aTarget);
2266 CheckComRCReturnRC (targetCaller.rc());
2267
2268 AutoMultiWriteLock2 alock (this, aTarget);
2269
2270 AssertReturn (mm.type != HardDiskType_Writethrough, E_FAIL);
2271
2272 /* Note: MediaState_LockedWrite is ok when taking an online snapshot */
2273 AssertReturn (m.state == MediaState_LockedRead ||
2274 m.state == MediaState_LockedWrite, E_FAIL);
2275
2276 if (aTarget->m.state != MediaState_NotCreated)
2277 return aTarget->setStateError();
2278
2279 HRESULT rc = S_OK;
2280
2281 /* check that the hard disk is not attached to any VM in the current state*/
2282 for (BackRefList::const_iterator it = m.backRefs.begin();
2283 it != m.backRefs.end(); ++ it)
2284 {
2285 if (it->inCurState)
2286 {
2287 /* Note: when a VM snapshot is being taken, all normal hard disks
2288 * attached to the VM in the current state will be, as an exception,
2289 * also associated with the snapshot which is about to create (see
2290 * SnapshotMachine::init()) before deassociating them from the
2291 * current state (which takes place only on success in
2292 * Machine::fixupHardDisks()), so that the size of snapshotIds
2293 * will be 1 in this case. The given condition is used to filter out
2294 * this legal situatinon and do not report an error. */
2295
2296 if (it->snapshotIds.size() == 0)
2297 {
2298 return setError (VBOX_E_INVALID_OBJECT_STATE,
2299 tr ("Hard disk '%ls' is attached to a virtual machine "
2300 "with UUID {%RTuuid}. No differencing hard disks "
2301 "based on it may be created until it is detached"),
2302 m.locationFull.raw(), it->machineId.raw());
2303 }
2304
2305 Assert (it->snapshotIds.size() == 1);
2306 }
2307 }
2308
2309 ComObjPtr <Progress> progress;
2310
2311 if (aProgress != NULL)
2312 {
2313 /* use the existing progress object... */
2314 progress = *aProgress;
2315
2316 /* ...but create a new one if it is null */
2317 if (progress.isNull())
2318 {
2319 progress.createObject();
2320 rc = progress->init (mVirtualBox, static_cast<IHardDisk*> (this),
2321 BstrFmt (tr ("Creating differencing hard disk storage unit '%ls'"),
2322 aTarget->m.locationFull.raw()),
2323 FALSE /* aCancelable */);
2324 CheckComRCReturnRC (rc);
2325 }
2326 }
2327
2328 /* setup task object and thread to carry out the operation
2329 * asynchronously */
2330
2331 std::auto_ptr <Task> task (new Task (this, progress, Task::CreateDiff));
2332 AssertComRCReturnRC (task->autoCaller.rc());
2333
2334 task->setData (aTarget);
2335 task->d.variant = aVariant;
2336
2337 /* register a task (it will deregister itself when done) */
2338 ++ mm.numCreateDiffTasks;
2339 Assert (mm.numCreateDiffTasks != 0); /* overflow? */
2340
2341 if (aWait)
2342 {
2343 /* go to Creating state before starting the task */
2344 aTarget->m.state = MediaState_Creating;
2345
2346 rc = task->runNow();
2347 }
2348 else
2349 {
2350 rc = task->startThread();
2351 CheckComRCReturnRC (rc);
2352
2353 /* go to Creating state before leaving the lock */
2354 aTarget->m.state = MediaState_Creating;
2355 }
2356
2357 /* task is now owned (or already deleted) by taskThread() so release it */
2358 task.release();
2359
2360 if (aProgress != NULL)
2361 {
2362 /* return progress to the caller */
2363 *aProgress = progress;
2364 }
2365
2366 return rc;
2367}
2368
2369/**
2370 * Prepares this (source) hard disk, target hard disk and all intermediate hard
2371 * disks for the merge operation.
2372 *
2373 * This method is to be called prior to calling the #mergeTo() to perform
2374 * necessary consistency checks and place involved hard disks to appropriate
2375 * states. If #mergeTo() is not called or fails, the state modifications
2376 * performed by this method must be undone by #cancelMergeTo().
2377 *
2378 * Note that when @a aIgnoreAttachments is @c true then it's the caller's
2379 * responsibility to detach the source and all intermediate hard disks before
2380 * calling #mergeTo() (which will fail otherwise).
2381 *
2382 * See #mergeTo() for more information about merging.
2383 *
2384 * @param aTarget Target hard disk.
2385 * @param aChain Where to store the created merge chain.
2386 * @param aIgnoreAttachments Don't check if the source or any intermediate
2387 * hard disk is attached to any VM.
2388 *
2389 * @note Locks treeLock() for reading. Locks this object, aTarget and all
2390 * intermediate hard disks for writing.
2391 */
2392HRESULT HardDisk::prepareMergeTo(HardDisk *aTarget,
2393 MergeChain * &aChain,
2394 bool aIgnoreAttachments /*= false*/)
2395{
2396 AssertReturn (aTarget != NULL, E_FAIL);
2397
2398 AutoCaller autoCaller (this);
2399 AssertComRCReturnRC (autoCaller.rc());
2400
2401 AutoCaller targetCaller (aTarget);
2402 AssertComRCReturnRC (targetCaller.rc());
2403
2404 aChain = NULL;
2405
2406 /* we walk the tree */
2407 AutoReadLock treeLock (this->treeLock());
2408
2409 HRESULT rc = S_OK;
2410
2411 /* detect the merge direction */
2412 bool forward;
2413 {
2414 HardDisk *parent = mParent;
2415 while (parent != NULL && parent != aTarget)
2416 parent = parent->mParent;
2417 if (parent == aTarget)
2418 forward = false;
2419 else
2420 {
2421 parent = aTarget->mParent;
2422 while (parent != NULL && parent != this)
2423 parent = parent->mParent;
2424 if (parent == this)
2425 forward = true;
2426 else
2427 {
2428 Bstr tgtLoc;
2429 {
2430 AutoReadLock alock (this);
2431 tgtLoc = aTarget->locationFull();
2432 }
2433
2434 AutoReadLock alock (this);
2435 return setError (E_FAIL,
2436 tr ("Hard disks '%ls' and '%ls' are unrelated"),
2437 m.locationFull.raw(), tgtLoc.raw());
2438 }
2439 }
2440 }
2441
2442 /* build the chain (will do necessary checks and state changes) */
2443 std::auto_ptr <MergeChain> chain (new MergeChain (forward,
2444 aIgnoreAttachments));
2445 {
2446 HardDisk *last = forward ? aTarget : this;
2447 HardDisk *first = forward ? this : aTarget;
2448
2449 for (;;)
2450 {
2451 if (last == aTarget)
2452 rc = chain->addTarget (last);
2453 else if (last == this)
2454 rc = chain->addSource (last);
2455 else
2456 rc = chain->addIntermediate (last);
2457 CheckComRCReturnRC (rc);
2458
2459 if (last == first)
2460 break;
2461
2462 last = last->mParent;
2463 }
2464 }
2465
2466 aChain = chain.release();
2467
2468 return S_OK;
2469}
2470
2471/**
2472 * Merges this hard disk to the specified hard disk which must be either its
2473 * direct ancestor or descendant.
2474 *
2475 * Given this hard disk is SOURCE and the specified hard disk is TARGET, we will
2476 * get two varians of the merge operation:
2477 *
2478 * forward merge
2479 * ------------------------->
2480 * [Extra] <- SOURCE <- Intermediate <- TARGET
2481 * Any Del Del LockWr
2482 *
2483 *
2484 * backward merge
2485 * <-------------------------
2486 * TARGET <- Intermediate <- SOURCE <- [Extra]
2487 * LockWr Del Del LockWr
2488 *
2489 * Each scheme shows the involved hard disks on the hard disk chain where
2490 * SOURCE and TARGET belong. Under each hard disk there is a state value which
2491 * the hard disk must have at a time of the mergeTo() call.
2492 *
2493 * The hard disks in the square braces may be absent (e.g. when the forward
2494 * operation takes place and SOURCE is the base hard disk, or when the backward
2495 * merge operation takes place and TARGET is the last child in the chain) but if
2496 * they present they are involved too as shown.
2497 *
2498 * Nor the source hard disk neither intermediate hard disks may be attached to
2499 * any VM directly or in the snapshot, otherwise this method will assert.
2500 *
2501 * The #prepareMergeTo() method must be called prior to this method to place all
2502 * involved to necessary states and perform other consistency checks.
2503 *
2504 * If @a aWait is @c true then this method will perform the operation on the
2505 * calling thread and will not return to the caller until the operation is
2506 * completed. When this method succeeds, all intermediate hard disk objects in
2507 * the chain will be uninitialized, the state of the target hard disk (and all
2508 * involved extra hard disks) will be restored and @a aChain will be deleted.
2509 * Note that this (source) hard disk is not uninitialized because of possible
2510 * AutoCaller instances held by the caller of this method on the current thread.
2511 * It's therefore the responsibility of the caller to call HardDisk::uninit()
2512 * after releasing all callers in this case!
2513 *
2514 * If @a aWait is @c false then this method will crea,te a thread to perform the
2515 * create operation asynchronously and will return immediately. If the operation
2516 * succeeds, the thread will uninitialize the source hard disk object and all
2517 * intermediate hard disk objects in the chain, reset the state of the target
2518 * hard disk (and all involved extra hard disks) and delete @a aChain. If the
2519 * operation fails, the thread will only reset the states of all involved hard
2520 * disks and delete @a aChain.
2521 *
2522 * When this method fails (regardless of the @a aWait mode), it is a caller's
2523 * responsiblity to undo state changes and delete @a aChain using
2524 * #cancelMergeTo().
2525 *
2526 * If @a aProgress is not NULL but the object it points to is @c null then a new
2527 * progress object will be created and assigned to @a *aProgress on success,
2528 * otherwise the existing progress object is used. If Progress is NULL, then no
2529 * progress object is created/used at all. Note that @a aProgress cannot be
2530 * NULL when @a aWait is @c false (this method will assert in this case).
2531 *
2532 * @param aChain Merge chain created by #prepareMergeTo().
2533 * @param aProgress Where to find/store a Progress object to track operation
2534 * completion.
2535 * @param aWait @c true if this method should block instead of creating
2536 * an asynchronous thread.
2537 *
2538 * @note Locks the branch lock for writing. Locks the hard disks from the chain
2539 * for writing.
2540 */
2541HRESULT HardDisk::mergeTo(MergeChain *aChain,
2542 ComObjPtr <Progress> *aProgress,
2543 bool aWait)
2544{
2545 AssertReturn (aChain != NULL, E_FAIL);
2546 AssertReturn (aProgress != NULL || aWait == true, E_FAIL);
2547
2548 AutoCaller autoCaller (this);
2549 CheckComRCReturnRC (autoCaller.rc());
2550
2551 HRESULT rc = S_OK;
2552
2553 ComObjPtr <Progress> progress;
2554
2555 if (aProgress != NULL)
2556 {
2557 /* use the existing progress object... */
2558 progress = *aProgress;
2559
2560 /* ...but create a new one if it is null */
2561 if (progress.isNull())
2562 {
2563 AutoReadLock alock (this);
2564
2565 progress.createObject();
2566 rc = progress->init (mVirtualBox, static_cast<IHardDisk*>(this),
2567 BstrFmt (tr ("Merging hard disk '%s' to '%s'"),
2568 name().raw(), aChain->target()->name().raw()),
2569 FALSE /* aCancelable */);
2570 CheckComRCReturnRC (rc);
2571 }
2572 }
2573
2574 /* setup task object and thread to carry out the operation
2575 * asynchronously */
2576
2577 std::auto_ptr <Task> task (new Task (this, progress, Task::Merge));
2578 AssertComRCReturnRC (task->autoCaller.rc());
2579
2580 task->setData (aChain);
2581
2582 /* Note: task owns aChain (will delete it when not needed) in all cases
2583 * except when @a aWait is @c true and runNow() fails -- in this case
2584 * aChain will be left away because cancelMergeTo() will be applied by the
2585 * caller on it as it is required in the documentation above */
2586
2587 if (aWait)
2588 {
2589 rc = task->runNow();
2590 }
2591 else
2592 {
2593 rc = task->startThread();
2594 CheckComRCReturnRC (rc);
2595 }
2596
2597 /* task is now owned (or already deleted) by taskThread() so release it */
2598 task.release();
2599
2600 if (aProgress != NULL)
2601 {
2602 /* return progress to the caller */
2603 *aProgress = progress;
2604 }
2605
2606 return rc;
2607}
2608
2609/**
2610 * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not called
2611 * or fails. Frees memory occupied by @a aChain.
2612 *
2613 * @param aChain Merge chain created by #prepareMergeTo().
2614 *
2615 * @note Locks the hard disks from the chain for writing.
2616 */
2617void HardDisk::cancelMergeTo (MergeChain *aChain)
2618{
2619 AutoCaller autoCaller (this);
2620 AssertComRCReturnVoid (autoCaller.rc());
2621
2622 AssertReturnVoid (aChain != NULL);
2623
2624 /* the destructor will do the thing */
2625 delete aChain;
2626}
2627
2628// private methods
2629////////////////////////////////////////////////////////////////////////////////
2630
2631/**
2632 * Sets the value of m.location and calculates the value of m.locationFull.
2633 *
2634 * Reimplements MediumBase::setLocation() to specially treat non-FS-path
2635 * locations and to prepend the default hard disk folder if the given location
2636 * string does not contain any path information at all.
2637 *
2638 * Also, if the specified location is a file path that ends with '/' then the
2639 * file name part will be generated by this method automatically in the format
2640 * '{<uuid>}.<ext>' where <uuid> is a fresh UUID that this method will generate
2641 * and assign to this medium, and <ext> is the default extension for this
2642 * medium's storage format. Note that this procedure requires the media state to
2643 * be NotCreated and will return a faiulre otherwise.
2644 *
2645 * @param aLocation Location of the storage unit. If the locaiton is a FS-path,
2646 * then it can be relative to the VirtualBox home directory.
2647 *
2648 * @note Must be called from under this object's write lock.
2649 */
2650HRESULT HardDisk::setLocation (CBSTR aLocation)
2651{
2652 /// @todo so far, we assert but later it makes sense to support null
2653 /// locations for hard disks that are not yet created fail to create a
2654 /// storage unit instead
2655 CheckComArgStrNotEmptyOrNull (aLocation);
2656
2657 AutoCaller autoCaller (this);
2658 AssertComRCReturnRC (autoCaller.rc());
2659
2660 /* formatObj may be null only when initializing from an existing path and
2661 * no format is known yet */
2662 AssertReturn ((!mm.format.isNull() && !mm.formatObj.isNull()) ||
2663 (autoCaller.state() == InInit &&
2664 m.state != MediaState_NotCreated && m.id.isEmpty() &&
2665 mm.format.isNull() && mm.formatObj.isNull()),
2666 E_FAIL);
2667
2668 /* are we dealing with a new hard disk constructed using the existing
2669 * location? */
2670 bool isImport = mm.format.isNull();
2671
2672 if (isImport ||
2673 (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File))
2674 {
2675 Guid id;
2676
2677 Utf8Str location (aLocation);
2678
2679 if (m.state == MediaState_NotCreated)
2680 {
2681 /* must be a file (formatObj must be already known) */
2682 Assert (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File);
2683
2684 if (RTPathFilename (location) == NULL)
2685 {
2686 /* no file name is given (either an empty string or ends with a
2687 * slash), generate a new UUID + file name if the state allows
2688 * this */
2689
2690 ComAssertMsgRet (!mm.formatObj->fileExtensions().empty(),
2691 ("Must be at least one extension if it is "
2692 "HardDiskFormatCapabilities_File\n"),
2693 E_FAIL);
2694
2695 Bstr ext = mm.formatObj->fileExtensions().front();
2696 ComAssertMsgRet (!ext.isEmpty(),
2697 ("Default extension must not be empty\n"),
2698 E_FAIL);
2699
2700 id.create();
2701
2702 location = Utf8StrFmt ("%s{%RTuuid}.%ls",
2703 location.raw(), id.raw(), ext.raw());
2704 }
2705 }
2706
2707 /* append the default folder if no path is given */
2708 if (!RTPathHavePath (location))
2709 {
2710 AutoReadLock propsLock (mVirtualBox->systemProperties());
2711 location = Utf8StrFmt ("%ls%c%s",
2712 mVirtualBox->systemProperties()->defaultHardDiskFolder().raw(),
2713 RTPATH_DELIMITER,
2714 location.raw());
2715 }
2716
2717 /* get the full file name */
2718 Utf8Str locationFull;
2719 int vrc = mVirtualBox->calculateFullPath (location, locationFull);
2720 if (RT_FAILURE (vrc))
2721 return setError (VBOX_E_FILE_ERROR,
2722 tr ("Invalid hard disk storage file location '%s' (%Rrc)"),
2723 location.raw(), vrc);
2724
2725 /* detect the backend from the storage unit if importing */
2726 if (isImport)
2727 {
2728 char *backendName = NULL;
2729
2730 /* is it a file? */
2731 {
2732 RTFILE file;
2733 vrc = RTFileOpen (&file, locationFull, RTFILE_O_READ);
2734 if (RT_SUCCESS (vrc))
2735 RTFileClose (file);
2736 }
2737 if (RT_SUCCESS (vrc))
2738 {
2739 vrc = VDGetFormat (locationFull, &backendName);
2740 }
2741 else if (vrc != VERR_FILE_NOT_FOUND && vrc != VERR_PATH_NOT_FOUND)
2742 {
2743 /* assume it's not a file, restore the original location */
2744 location = locationFull = aLocation;
2745 vrc = VDGetFormat (locationFull, &backendName);
2746 }
2747
2748 if (RT_FAILURE (vrc))
2749 return setError (VBOX_E_IPRT_ERROR,
2750 tr ("Could not get the storage format of the hard disk "
2751 "'%s' (%Rrc)"), locationFull.raw(), vrc);
2752
2753 ComAssertRet (backendName != NULL && *backendName != '\0', E_FAIL);
2754
2755 HRESULT rc = setFormat (Bstr (backendName));
2756 RTStrFree (backendName);
2757
2758 /* setFormat() must not fail since we've just used the backend so
2759 * the format object must be there */
2760 AssertComRCReturnRC (rc);
2761 }
2762
2763 /* is it still a file? */
2764 if (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File)
2765 {
2766 m.location = location;
2767 m.locationFull = locationFull;
2768
2769 if (m.state == MediaState_NotCreated)
2770 {
2771 /* assign a new UUID (this UUID will be used when calling
2772 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
2773 * also do that if we didn't generate it to make sure it is
2774 * either generated by us or reset to null */
2775 unconst (m.id) = id;
2776 }
2777 }
2778 else
2779 {
2780 m.location = locationFull;
2781 m.locationFull = locationFull;
2782 }
2783 }
2784 else
2785 {
2786 m.location = aLocation;
2787 m.locationFull = aLocation;
2788 }
2789
2790 return S_OK;
2791}
2792
2793/**
2794 * Checks that the format ID is valid and sets it on success.
2795 *
2796 * Note that this method will caller-reference the format object on success!
2797 * This reference must be released somewhere to let the HardDiskFormat object be
2798 * uninitialized.
2799 *
2800 * @note Must be called from under this object's write lock.
2801 */
2802HRESULT HardDisk::setFormat (CBSTR aFormat)
2803{
2804 /* get the format object first */
2805 {
2806 AutoReadLock propsLock (mVirtualBox->systemProperties());
2807
2808 unconst (mm.formatObj)
2809 = mVirtualBox->systemProperties()->hardDiskFormat (aFormat);
2810 if (mm.formatObj.isNull())
2811 return setError (E_INVALIDARG,
2812 tr ("Invalid hard disk storage format '%ls'"), aFormat);
2813
2814 /* reference the format permanently to prevent its unexpected
2815 * uninitialization */
2816 HRESULT rc = mm.formatObj->addCaller();
2817 AssertComRCReturnRC (rc);
2818
2819 /* get properties (preinsert them as keys in the map). Note that the
2820 * map doesn't grow over the object life time since the set of
2821 * properties is meant to be constant. */
2822
2823 Assert (mm.properties.empty());
2824
2825 for (HardDiskFormat::PropertyList::const_iterator it =
2826 mm.formatObj->properties().begin();
2827 it != mm.formatObj->properties().end();
2828 ++ it)
2829 {
2830 mm.properties.insert (std::make_pair (it->name, Bstr::Null));
2831 }
2832 }
2833
2834 unconst (mm.format) = aFormat;
2835
2836 return S_OK;
2837}
2838
2839/**
2840 * Queries information from the image file.
2841 *
2842 * As a result of this call, the accessibility state and data members such as
2843 * size and description will be updated with the current information.
2844 *
2845 * Reimplements MediumBase::queryInfo() to query hard disk information using the
2846 * VD backend interface.
2847 *
2848 * @note This method may block during a system I/O call that checks storage
2849 * accessibility.
2850 *
2851 * @note Locks treeLock() for reading and writing (for new diff media checked
2852 * for the first time). Locks mParent for reading. Locks this object for
2853 * writing.
2854 */
2855HRESULT HardDisk::queryInfo()
2856{
2857 AutoWriteLock alock (this);
2858
2859 AssertReturn (m.state == MediaState_Created ||
2860 m.state == MediaState_Inaccessible ||
2861 m.state == MediaState_LockedRead ||
2862 m.state == MediaState_LockedWrite,
2863 E_FAIL);
2864
2865 HRESULT rc = S_OK;
2866
2867 int vrc = VINF_SUCCESS;
2868
2869 /* check if a blocking queryInfo() call is in progress on some other thread,
2870 * and wait for it to finish if so instead of querying data ourselves */
2871 if (m.queryInfoSem != NIL_RTSEMEVENTMULTI)
2872 {
2873 Assert (m.state == MediaState_LockedRead);
2874
2875 ++ m.queryInfoCallers;
2876 alock.leave();
2877
2878 vrc = RTSemEventMultiWait (m.queryInfoSem, RT_INDEFINITE_WAIT);
2879
2880 alock.enter();
2881 -- m.queryInfoCallers;
2882
2883 if (m.queryInfoCallers == 0)
2884 {
2885 /* last waiting caller deletes the semaphore */
2886 RTSemEventMultiDestroy (m.queryInfoSem);
2887 m.queryInfoSem = NIL_RTSEMEVENTMULTI;
2888 }
2889
2890 AssertRC (vrc);
2891
2892 return S_OK;
2893 }
2894
2895 /* lazily create a semaphore for possible callers */
2896 vrc = RTSemEventMultiCreate (&m.queryInfoSem);
2897 ComAssertRCRet (vrc, E_FAIL);
2898
2899 bool tempStateSet = false;
2900 if (m.state != MediaState_LockedRead &&
2901 m.state != MediaState_LockedWrite)
2902 {
2903 /* Cause other methods to prevent any modifications before leaving the
2904 * lock. Note that clients will never see this temporary state change
2905 * since any COMGETTER(State) is (or will be) blocked until we finish
2906 * and restore the actual state. */
2907 m.state = MediaState_LockedRead;
2908 tempStateSet = true;
2909 }
2910
2911 /* leave the lock before a blocking operation */
2912 alock.leave();
2913
2914 bool success = false;
2915 Utf8Str lastAccessError;
2916
2917 try
2918 {
2919 Utf8Str location (m.locationFull);
2920
2921 /* are we dealing with a new hard disk constructed using the existing
2922 * location? */
2923 bool isImport = m.id.isEmpty();
2924
2925 PVBOXHDD hdd;
2926 vrc = VDCreate (mm.vdDiskIfaces, &hdd);
2927 ComAssertRCThrow (vrc, E_FAIL);
2928
2929 try
2930 {
2931 unsigned flags = VD_OPEN_FLAGS_INFO;
2932
2933 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
2934 * hard disks because that would prevent necessary modifications
2935 * when opening hard disks of some third-party formats for the first
2936 * time in VirtualBox (such as VMDK for which VDOpen() needs to
2937 * generate an UUID if it is missing) */
2938 if (!isImport)
2939 flags |= VD_OPEN_FLAGS_READONLY;
2940
2941 vrc = VDOpen (hdd, Utf8Str (mm.format), location, flags,
2942 mm.vdDiskIfaces);
2943 if (RT_FAILURE (vrc))
2944 {
2945 lastAccessError = Utf8StrFmt (
2946 tr ("Could not open the hard disk '%ls'%s"),
2947 m.locationFull.raw(), vdError (vrc).raw());
2948 throw S_OK;
2949 }
2950
2951 if (mm.formatObj->capabilities() & HardDiskFormatCapabilities_Uuid)
2952 {
2953 /* check the UUID */
2954 RTUUID uuid;
2955 vrc = VDGetUuid (hdd, 0, &uuid);
2956 ComAssertRCThrow (vrc, E_FAIL);
2957
2958 if (isImport)
2959 {
2960 unconst (m.id) = uuid;
2961 }
2962 else
2963 {
2964 Assert (!m.id.isEmpty());
2965
2966 if (m.id != uuid)
2967 {
2968 lastAccessError = Utf8StrFmt (
2969 tr ("UUID {%RTuuid} of the hard disk '%ls' does "
2970 "not match the value {%RTuuid} stored in the "
2971 "media registry ('%ls')"),
2972 &uuid, m.locationFull.raw(), m.id.raw(),
2973 mVirtualBox->settingsFileName().raw());
2974 throw S_OK;
2975 }
2976 }
2977 }
2978 else
2979 {
2980 /* the backend does not support storing UUIDs within the
2981 * underlying storage so use what we store in XML */
2982
2983 /* generate an UUID for an imported UUID-less hard disk */
2984 if (isImport)
2985 unconst (m.id).create();
2986 }
2987
2988 /* check the type */
2989 VDIMAGETYPE type;
2990 vrc = VDGetImageType (hdd, 0, &type);
2991 ComAssertRCThrow (vrc, E_FAIL);
2992
2993 if (type == VD_IMAGE_TYPE_DIFF)
2994 {
2995 RTUUID parentId;
2996 vrc = VDGetParentUuid (hdd, 0, &parentId);
2997 ComAssertRCThrow (vrc, E_FAIL);
2998
2999 if (isImport)
3000 {
3001 /* the parent must be known to us. Note that we freely
3002 * call locking methods of mVirtualBox and parent from the
3003 * write lock (breaking the {parent,child} lock order)
3004 * because there may be no concurrent access to the just
3005 * opened hard disk on ther threads yet (and init() will
3006 * fail if this method reporst MediaState_Inaccessible) */
3007
3008 Guid id = parentId;
3009 ComObjPtr<HardDisk> parent;
3010 rc = mVirtualBox->findHardDisk(&id, NULL,
3011 false /* aSetError */,
3012 &parent);
3013 if (FAILED (rc))
3014 {
3015 lastAccessError = Utf8StrFmt (
3016 tr ("Parent hard disk with UUID {%RTuuid} of the "
3017 "hard disk '%ls' is not found in the media "
3018 "registry ('%ls')"),
3019 &parentId, m.locationFull.raw(),
3020 mVirtualBox->settingsFileName().raw());
3021 throw S_OK;
3022 }
3023
3024 /* deassociate from VirtualBox, associate with parent */
3025
3026 mVirtualBox->removeDependentChild (this);
3027
3028 /* we set mParent & children() */
3029 AutoWriteLock treeLock (this->treeLock());
3030
3031 Assert (mParent.isNull());
3032 mParent = parent;
3033 mParent->addDependentChild (this);
3034 }
3035 else
3036 {
3037 /* we access mParent */
3038 AutoReadLock treeLock (this->treeLock());
3039
3040 /* check that parent UUIDs match. Note that there's no need
3041 * for the parent's AutoCaller (our lifetime is bound to
3042 * it) */
3043
3044 if (mParent.isNull())
3045 {
3046 lastAccessError = Utf8StrFmt (
3047 tr ("Hard disk '%ls' is differencing but it is not "
3048 "associated with any parent hard disk in the "
3049 "media registry ('%ls')"),
3050 m.locationFull.raw(),
3051 mVirtualBox->settingsFileName().raw());
3052 throw S_OK;
3053 }
3054
3055 AutoReadLock parentLock (mParent);
3056 if (mParent->state() != MediaState_Inaccessible &&
3057 mParent->id() != parentId)
3058 {
3059 lastAccessError = Utf8StrFmt (
3060 tr ("Parent UUID {%RTuuid} of the hard disk '%ls' "
3061 "does not match UUID {%RTuuid} of its parent "
3062 "hard disk stored in the media registry ('%ls')"),
3063 &parentId, m.locationFull.raw(),
3064 mParent->id().raw(),
3065 mVirtualBox->settingsFileName().raw());
3066 throw S_OK;
3067 }
3068
3069 /// @todo NEWMEDIA what to do if the parent is not
3070 /// accessible while the diff is? Probably, nothing. The
3071 /// real code will detect the mismatch anyway.
3072 }
3073 }
3074
3075 m.size = VDGetFileSize (hdd, 0);
3076 mm.logicalSize = VDGetSize (hdd, 0) / _1M;
3077
3078 success = true;
3079 }
3080 catch (HRESULT aRC)
3081 {
3082 rc = aRC;
3083 }
3084
3085 VDDestroy (hdd);
3086
3087 }
3088 catch (HRESULT aRC)
3089 {
3090 rc = aRC;
3091 }
3092
3093 alock.enter();
3094
3095 if (success)
3096 m.lastAccessError.setNull();
3097 else
3098 {
3099 m.lastAccessError = lastAccessError;
3100 LogWarningFunc (("'%ls' is not accessible (error='%ls', "
3101 "rc=%Rhrc, vrc=%Rrc)\n",
3102 m.locationFull.raw(), m.lastAccessError.raw(),
3103 rc, vrc));
3104 }
3105
3106 /* inform other callers if there are any */
3107 if (m.queryInfoCallers > 0)
3108 {
3109 RTSemEventMultiSignal (m.queryInfoSem);
3110 }
3111 else
3112 {
3113 /* delete the semaphore ourselves */
3114 RTSemEventMultiDestroy (m.queryInfoSem);
3115 m.queryInfoSem = NIL_RTSEMEVENTMULTI;
3116 }
3117
3118 if (tempStateSet)
3119 {
3120 /* Set the proper state according to the result of the check */
3121 if (success)
3122 m.state = MediaState_Created;
3123 else
3124 m.state = MediaState_Inaccessible;
3125 }
3126 else
3127 {
3128 /* we're locked, use a special field to store the result */
3129 m.accessibleInLock = success;
3130 }
3131
3132 return rc;
3133}
3134
3135/**
3136 * @note Called from this object's AutoMayUninitSpan and from under mVirtualBox
3137 * write lock.
3138 *
3139 * @note Also reused by HardDisk::Reset().
3140 *
3141 * @note Locks treeLock() for reading.
3142 */
3143HRESULT HardDisk::canClose()
3144{
3145 /* we access children */
3146 AutoReadLock treeLock (this->treeLock());
3147
3148 if (children().size() != 0)
3149 return setError (E_FAIL,
3150 tr ("Hard disk '%ls' has %d child hard disks"),
3151 children().size());
3152
3153 return S_OK;
3154}
3155
3156/**
3157 * @note Called from within this object's AutoWriteLock.
3158 */
3159HRESULT HardDisk::canAttach(const Guid & /* aMachineId */,
3160 const Guid & /* aSnapshotId */)
3161{
3162 if (mm.numCreateDiffTasks > 0)
3163 return setError (E_FAIL,
3164 tr ("One or more differencing child hard disks are "
3165 "being created for the hard disk '%ls' (%u)"),
3166 m.locationFull.raw(), mm.numCreateDiffTasks);
3167
3168 return S_OK;
3169}
3170
3171/**
3172 * @note Called from within this object's AutoMayUninitSpan (or AutoCaller) and
3173 * from under mVirtualBox write lock.
3174 *
3175 * @note Locks treeLock() for writing.
3176 */
3177HRESULT HardDisk::unregisterWithVirtualBox()
3178{
3179 /* Note that we need to de-associate ourselves from the parent to let
3180 * unregisterHardDisk() properly save the registry */
3181
3182 /* we modify mParent and access children */
3183 AutoWriteLock treeLock (this->treeLock());
3184
3185 const ComObjPtr<HardDisk, ComWeakRef> parent = mParent;
3186
3187 AssertReturn (children().size() == 0, E_FAIL);
3188
3189 if (!mParent.isNull())
3190 {
3191 /* deassociate from the parent, associate with VirtualBox */
3192 mVirtualBox->addDependentChild (this);
3193 mParent->removeDependentChild (this);
3194 mParent.setNull();
3195 }
3196
3197 HRESULT rc = mVirtualBox->unregisterHardDisk(this);
3198
3199 if (FAILED (rc))
3200 {
3201 if (!parent.isNull())
3202 {
3203 /* re-associate with the parent as we are still relatives in the
3204 * registry */
3205 mParent = parent;
3206 mParent->addDependentChild (this);
3207 mVirtualBox->removeDependentChild (this);
3208 }
3209 }
3210
3211 return rc;
3212}
3213
3214/**
3215 * Returns the last error message collected by the vdErrorCall callback and
3216 * resets it.
3217 *
3218 * The error message is returned prepended with a dot and a space, like this:
3219 * <code>
3220 * ". <error_text> (%Rrc)"
3221 * </code>
3222 * to make it easily appendable to a more general error message. The @c %Rrc
3223 * format string is given @a aVRC as an argument.
3224 *
3225 * If there is no last error message collected by vdErrorCall or if it is a
3226 * null or empty string, then this function returns the following text:
3227 * <code>
3228 * " (%Rrc)"
3229 * </code>
3230 *
3231 * @note Doesn't do any object locking; it is assumed that the caller makes sure
3232 * the callback isn't called by more than one thread at a time.
3233 *
3234 * @param aVRC VBox error code to use when no error message is provided.
3235 */
3236Utf8Str HardDisk::vdError (int aVRC)
3237{
3238 Utf8Str error;
3239
3240 if (mm.vdError.isEmpty())
3241 error = Utf8StrFmt (" (%Rrc)", aVRC);
3242 else
3243 error = Utf8StrFmt (".\n%s", mm.vdError.raw());
3244
3245 mm.vdError.setNull();
3246
3247 return error;
3248}
3249
3250/**
3251 * Error message callback.
3252 *
3253 * Puts the reported error message to the mm.vdError field.
3254 *
3255 * @note Doesn't do any object locking; it is assumed that the caller makes sure
3256 * the callback isn't called by more than one thread at a time.
3257 *
3258 * @param pvUser The opaque data passed on container creation.
3259 * @param rc The VBox error code.
3260 * @param RT_SRC_POS_DECL Use RT_SRC_POS.
3261 * @param pszFormat Error message format string.
3262 * @param va Error message arguments.
3263 */
3264/*static*/
3265DECLCALLBACK(void) HardDisk::vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
3266 const char *pszFormat, va_list va)
3267{
3268 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
3269
3270 HardDisk *that = static_cast<HardDisk*>(pvUser);
3271 AssertReturnVoid (that != NULL);
3272
3273 if (that->mm.vdError.isEmpty())
3274 that->mm.vdError =
3275 Utf8StrFmt ("%s (%Rrc)", Utf8StrFmtVA (pszFormat, va).raw(), rc);
3276 else
3277 that->mm.vdError =
3278 Utf8StrFmt ("%s.\n%s (%Rrc)", that->mm.vdError.raw(),
3279 Utf8StrFmtVA (pszFormat, va).raw(), rc);
3280}
3281
3282/**
3283 * PFNVMPROGRESS callback handler for Task operations.
3284 *
3285 * @param uPercent Completetion precentage (0-100).
3286 * @param pvUser Pointer to the Progress instance.
3287 */
3288/*static*/
3289DECLCALLBACK(int) HardDisk::vdProgressCall(PVM /* pVM */, unsigned uPercent,
3290 void *pvUser)
3291{
3292 HardDisk *that = static_cast<HardDisk*>(pvUser);
3293 AssertReturn (that != NULL, VERR_GENERAL_FAILURE);
3294
3295 if (that->mm.vdProgress != NULL)
3296 {
3297 /* update the progress object, capping it at 99% as the final percent
3298 * is used for additional operations like setting the UUIDs and similar. */
3299 that->mm.vdProgress->notifyProgress (RT_MIN (uPercent, 99));
3300 }
3301
3302 return VINF_SUCCESS;
3303}
3304
3305/* static */
3306DECLCALLBACK(bool) HardDisk::vdConfigAreKeysValid (void *pvUser,
3307 const char * /* pszzValid */)
3308{
3309 HardDisk *that = static_cast<HardDisk*>(pvUser);
3310 AssertReturn (that != NULL, false);
3311
3312 /* we always return true since the only keys we have are those found in
3313 * VDBACKENDINFO */
3314 return true;
3315}
3316
3317/* static */
3318DECLCALLBACK(int) HardDisk::vdConfigQuerySize(void *pvUser, const char *pszName,
3319 size_t *pcbValue)
3320{
3321 AssertReturn (VALID_PTR (pcbValue), VERR_INVALID_POINTER);
3322
3323 HardDisk *that = static_cast<HardDisk*>(pvUser);
3324 AssertReturn (that != NULL, VERR_GENERAL_FAILURE);
3325
3326 Data::PropertyMap::const_iterator it =
3327 that->mm.properties.find (Bstr (pszName));
3328 if (it == that->mm.properties.end())
3329 return VERR_CFGM_VALUE_NOT_FOUND;
3330
3331 /* we interpret null values as "no value" in HardDisk */
3332 if (it->second.isNull())
3333 return VERR_CFGM_VALUE_NOT_FOUND;
3334
3335 *pcbValue = it->second.length() + 1 /* include terminator */;
3336
3337 return VINF_SUCCESS;
3338}
3339
3340/* static */
3341DECLCALLBACK(int) HardDisk::vdConfigQuery (void *pvUser, const char *pszName,
3342 char *pszValue, size_t cchValue)
3343{
3344 AssertReturn (VALID_PTR (pszValue), VERR_INVALID_POINTER);
3345
3346 HardDisk *that = static_cast<HardDisk*>(pvUser);
3347 AssertReturn (that != NULL, VERR_GENERAL_FAILURE);
3348
3349 Data::PropertyMap::const_iterator it =
3350 that->mm.properties.find (Bstr (pszName));
3351 if (it == that->mm.properties.end())
3352 return VERR_CFGM_VALUE_NOT_FOUND;
3353
3354 Utf8Str value = it->second;
3355 if (value.length() >= cchValue)
3356 return VERR_CFGM_NOT_ENOUGH_SPACE;
3357
3358 /* we interpret null values as "no value" in HardDisk */
3359 if (it->second.isNull())
3360 return VERR_CFGM_VALUE_NOT_FOUND;
3361
3362 memcpy (pszValue, value, value.length() + 1);
3363
3364 return VINF_SUCCESS;
3365}
3366
3367/**
3368 * Thread function for time-consuming tasks.
3369 *
3370 * The Task structure passed to @a pvUser must be allocated using new and will
3371 * be freed by this method before it returns.
3372 *
3373 * @param pvUser Pointer to the Task instance.
3374 */
3375/* static */
3376DECLCALLBACK(int) HardDisk::taskThread (RTTHREAD thread, void *pvUser)
3377{
3378 std::auto_ptr <Task> task (static_cast <Task *> (pvUser));
3379 AssertReturn (task.get(), VERR_GENERAL_FAILURE);
3380
3381 bool isAsync = thread != NIL_RTTHREAD;
3382
3383 HardDisk *that = task->that;
3384
3385 /// @todo ugly hack, fix ComAssert... later
3386 #define setError that->setError
3387
3388 /* Note: no need in AutoCaller because Task does that */
3389
3390 LogFlowFuncEnter();
3391 LogFlowFunc (("{%p}: operation=%d\n", that, task->operation));
3392
3393 HRESULT rc = S_OK;
3394
3395 switch (task->operation)
3396 {
3397 ////////////////////////////////////////////////////////////////////////
3398
3399 case Task::CreateDynamic:
3400 case Task::CreateFixed:
3401 {
3402 /* The lock is also used as a signal from the task initiator (which
3403 * releases it only after RTThreadCreate()) that we can start the job */
3404 AutoWriteLock thatLock (that);
3405
3406 /* these parameters we need after creation */
3407 uint64_t size = 0, logicalSize = 0;
3408
3409 /* The object may request a specific UUID (through a special form of
3410 * the setLocation() argument). Otherwise we have to generate it */
3411 Guid id = that->m.id;
3412 bool generateUuid = id.isEmpty();
3413 if (generateUuid)
3414 {
3415 id.create();
3416 /* VirtualBox::registerHardDisk() will need UUID */
3417 unconst (that->m.id) = id;
3418 }
3419
3420 try
3421 {
3422 PVBOXHDD hdd;
3423 int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
3424 ComAssertRCThrow (vrc, E_FAIL);
3425
3426 Utf8Str format (that->mm.format);
3427 Utf8Str location (that->m.locationFull);
3428 /* uint64_t capabilities = */ that->mm.formatObj->capabilities();
3429
3430 /* unlock before the potentially lengthy operation */
3431 Assert (that->m.state == MediaState_Creating);
3432 thatLock.leave();
3433
3434 try
3435 {
3436 /* ensure the directory exists */
3437 rc = VirtualBox::ensureFilePathExists (location);
3438 CheckComRCThrowRC (rc);
3439
3440 PDMMEDIAGEOMETRY geo = { 0 }; /* auto-detect */
3441
3442 /* needed for vdProgressCallback */
3443 that->mm.vdProgress = task->progress;
3444
3445 vrc = VDCreateBase (hdd, format, location,
3446 task->operation == Task::CreateDynamic ?
3447 VD_IMAGE_TYPE_NORMAL :
3448 VD_IMAGE_TYPE_FIXED,
3449 task->d.size * _1M,
3450 task->d.variant,
3451 NULL, &geo, &geo, id.raw(),
3452 VD_OPEN_FLAGS_NORMAL,
3453 NULL, that->mm.vdDiskIfaces);
3454
3455 if (RT_FAILURE (vrc))
3456 {
3457 throw setError (E_FAIL,
3458 tr ("Could not create the hard disk storage "
3459 "unit '%s'%s"),
3460 location.raw(), that->vdError (vrc).raw());
3461 }
3462
3463 size = VDGetFileSize (hdd, 0);
3464 logicalSize = VDGetSize (hdd, 0) / _1M;
3465 }
3466 catch (HRESULT aRC) { rc = aRC; }
3467
3468 VDDestroy (hdd);
3469 }
3470 catch (HRESULT aRC) { rc = aRC; }
3471
3472 if (SUCCEEDED (rc))
3473 {
3474 /* register with mVirtualBox as the last step and move to
3475 * Created state only on success (leaving an orphan file is
3476 * better than breaking media registry consistency) */
3477 rc = that->mVirtualBox->registerHardDisk(that);
3478 }
3479
3480 thatLock.maybeEnter();
3481
3482 if (SUCCEEDED (rc))
3483 {
3484 that->m.state = MediaState_Created;
3485
3486 that->m.size = size;
3487 that->mm.logicalSize = logicalSize;
3488 }
3489 else
3490 {
3491 /* back to NotCreated on failiure */
3492 that->m.state = MediaState_NotCreated;
3493
3494 /* reset UUID to prevent it from being reused next time */
3495 if (generateUuid)
3496 unconst (that->m.id).clear();
3497 }
3498
3499 break;
3500 }
3501
3502 ////////////////////////////////////////////////////////////////////////
3503
3504 case Task::CreateDiff:
3505 {
3506 ComObjPtr<HardDisk> &target = task->d.target;
3507
3508 /* Lock both in {parent,child} order. The lock is also used as a
3509 * signal from the task initiator (which releases it only after
3510 * RTThreadCreate()) that we can start the job*/
3511 AutoMultiWriteLock2 thatLock (that, target);
3512
3513 uint64_t size = 0, logicalSize = 0;
3514
3515 /* The object may request a specific UUID (through a special form of
3516 * the setLocation() argument). Otherwise we have to generate it */
3517 Guid targetId = target->m.id;
3518 bool generateUuid = targetId.isEmpty();
3519 if (generateUuid)
3520 {
3521 targetId.create();
3522 /* VirtualBox::registerHardDisk() will need UUID */
3523 unconst (target->m.id) = targetId;
3524 }
3525
3526 try
3527 {
3528 PVBOXHDD hdd;
3529 int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
3530 ComAssertRCThrow (vrc, E_FAIL);
3531
3532 Guid id = that->m.id;
3533 Utf8Str format (that->mm.format);
3534 Utf8Str location (that->m.locationFull);
3535
3536 Utf8Str targetFormat (target->mm.format);
3537 Utf8Str targetLocation (target->m.locationFull);
3538
3539 Assert (target->m.state == MediaState_Creating);
3540
3541 /* Note: MediaState_LockedWrite is ok when taking an online
3542 * snapshot */
3543 Assert (that->m.state == MediaState_LockedRead ||
3544 that->m.state == MediaState_LockedWrite);
3545
3546 /* unlock before the potentially lengthy operation */
3547 thatLock.leave();
3548
3549 try
3550 {
3551 vrc = VDOpen (hdd, format, location,
3552 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
3553 that->mm.vdDiskIfaces);
3554 if (RT_FAILURE (vrc))
3555 {
3556 throw setError (E_FAIL,
3557 tr ("Could not open the hard disk storage "
3558 "unit '%s'%s"),
3559 location.raw(), that->vdError (vrc).raw());
3560 }
3561
3562 /* ensure the target directory exists */
3563 rc = VirtualBox::ensureFilePathExists (targetLocation);
3564 CheckComRCThrowRC (rc);
3565
3566 /* needed for vdProgressCallback */
3567 that->mm.vdProgress = task->progress;
3568
3569 vrc = VDCreateDiff (hdd, targetFormat, targetLocation,
3570 task->d.variant,
3571 NULL, targetId.raw(),
3572 id.raw(),
3573 VD_OPEN_FLAGS_NORMAL,
3574 target->mm.vdDiskIfaces,
3575 that->mm.vdDiskIfaces);
3576
3577 that->mm.vdProgress = NULL;
3578
3579 if (RT_FAILURE (vrc))
3580 {
3581 throw setError (E_FAIL,
3582 tr ("Could not create the differencing hard disk "
3583 "storage unit '%s'%s"),
3584 targetLocation.raw(), that->vdError (vrc).raw());
3585 }
3586
3587 size = VDGetFileSize (hdd, 1);
3588 logicalSize = VDGetSize (hdd, 1) / _1M;
3589 }
3590 catch (HRESULT aRC) { rc = aRC; }
3591
3592 VDDestroy (hdd);
3593 }
3594 catch (HRESULT aRC) { rc = aRC; }
3595
3596 if (SUCCEEDED (rc))
3597 {
3598 /* we set mParent & children() (note that thatLock is released
3599 * here), but lock VirtualBox first to follow the rule */
3600 AutoMultiWriteLock2 alock (that->mVirtualBox->lockHandle(),
3601 that->treeLock());
3602
3603 Assert (target->mParent.isNull());
3604
3605 /* associate the child with the parent and deassociate from
3606 * VirtualBox */
3607 target->mParent = that;
3608 that->addDependentChild (target);
3609 target->mVirtualBox->removeDependentChild (target);
3610
3611 /* diffs for immutable hard disks are auto-reset by default */
3612 target->mm.autoReset =
3613 that->root()->mm.type == HardDiskType_Immutable ?
3614 TRUE : FALSE;
3615
3616 /* register with mVirtualBox as the last step and move to
3617 * Created state only on success (leaving an orphan file is
3618 * better than breaking media registry consistency) */
3619 rc = that->mVirtualBox->registerHardDisk (target);
3620
3621 if (FAILED (rc))
3622 {
3623 /* break the parent association on failure to register */
3624 target->mVirtualBox->addDependentChild (target);
3625 that->removeDependentChild (target);
3626 target->mParent.setNull();
3627 }
3628 }
3629
3630 thatLock.maybeEnter();
3631
3632 if (SUCCEEDED (rc))
3633 {
3634 target->m.state = MediaState_Created;
3635
3636 target->m.size = size;
3637 target->mm.logicalSize = logicalSize;
3638 }
3639 else
3640 {
3641 /* back to NotCreated on failiure */
3642 target->m.state = MediaState_NotCreated;
3643
3644 target->mm.autoReset = FALSE;
3645
3646 /* reset UUID to prevent it from being reused next time */
3647 if (generateUuid)
3648 unconst (target->m.id).clear();
3649 }
3650
3651 if (isAsync)
3652 {
3653 /* unlock ourselves when done (unless in MediaState_LockedWrite
3654 * state because of taking the online snapshot*/
3655 if (that->m.state != MediaState_LockedWrite)
3656 {
3657 HRESULT rc2 = that->UnlockRead (NULL);
3658 AssertComRC (rc2);
3659 }
3660 }
3661
3662 /* deregister the task registered in createDiffStorage() */
3663 Assert (that->mm.numCreateDiffTasks != 0);
3664 -- that->mm.numCreateDiffTasks;
3665
3666 /* Note that in sync mode, it's the caller's responsibility to
3667 * unlock the hard disk */
3668
3669 break;
3670 }
3671
3672 ////////////////////////////////////////////////////////////////////////
3673
3674 case Task::Merge:
3675 {
3676 /* The lock is also used as a signal from the task initiator (which
3677 * releases it only after RTThreadCreate()) that we can start the
3678 * job. We don't actually need the lock for anything else since the
3679 * object is protected by MediaState_Deleting and we don't modify
3680 * its sensitive fields below */
3681 {
3682 AutoWriteLock thatLock (that);
3683 }
3684
3685 MergeChain *chain = task->d.chain.get();
3686
3687#if 0
3688 LogFlow (("*** MERGE forward = %RTbool\n", chain->isForward()));
3689#endif
3690
3691 try
3692 {
3693 PVBOXHDD hdd;
3694 int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
3695 ComAssertRCThrow (vrc, E_FAIL);
3696
3697 try
3698 {
3699 /* open all hard disks in the chain (they are in the
3700 * {parent,child} order in there. Note that we don't lock
3701 * objects in this chain since they must be in states
3702 * (Deleting and LockedWrite) that prevent from chaning
3703 * their format and location fields from outside. */
3704
3705 for (MergeChain::const_iterator it = chain->begin();
3706 it != chain->end(); ++ it)
3707 {
3708 /* complex sanity (sane complexity) */
3709 Assert ((chain->isForward() &&
3710 ((*it != chain->back() &&
3711 (*it)->m.state == MediaState_Deleting) ||
3712 (*it == chain->back() &&
3713 (*it)->m.state == MediaState_LockedWrite))) ||
3714 (!chain->isForward() &&
3715 ((*it != chain->front() &&
3716 (*it)->m.state == MediaState_Deleting) ||
3717 (*it == chain->front() &&
3718 (*it)->m.state == MediaState_LockedWrite))));
3719
3720 Assert (*it == chain->target() ||
3721 (*it)->m.backRefs.size() == 0);
3722
3723 /* open the first image with VDOPEN_FLAGS_INFO because
3724 * it's not necessarily the base one */
3725 vrc = VDOpen (hdd, Utf8Str ((*it)->mm.format),
3726 Utf8Str ((*it)->m.locationFull),
3727 it == chain->begin() ?
3728 VD_OPEN_FLAGS_INFO : 0,
3729 (*it)->mm.vdDiskIfaces);
3730 if (RT_FAILURE (vrc))
3731 throw vrc;
3732#if 0
3733 LogFlow (("*** MERGE disk = %ls\n",
3734 (*it)->m.locationFull.raw()));
3735#endif
3736 }
3737
3738 /* needed for vdProgressCallback */
3739 that->mm.vdProgress = task->progress;
3740
3741 unsigned start = chain->isForward() ?
3742 0 : chain->size() - 1;
3743 unsigned end = chain->isForward() ?
3744 chain->size() - 1 : 0;
3745#if 0
3746 LogFlow (("*** MERGE from %d to %d\n", start, end));
3747#endif
3748 vrc = VDMerge (hdd, start, end, that->mm.vdDiskIfaces);
3749
3750 that->mm.vdProgress = NULL;
3751
3752 if (RT_FAILURE (vrc))
3753 throw vrc;
3754
3755 /* update parent UUIDs */
3756 /// @todo VDMerge should be taught to do so, including the
3757 /// multiple children case
3758 if (chain->isForward())
3759 {
3760 /* target's UUID needs to be updated (note that target
3761 * is the only image in the container on success) */
3762 vrc = VDSetParentUuid (hdd, 0, chain->parent()->m.id);
3763 if (RT_FAILURE (vrc))
3764 throw vrc;
3765 }
3766 else
3767 {
3768 /* we need to update UUIDs of all source's children
3769 * which cannot be part of the container at once so
3770 * add each one in there individually */
3771 if (chain->children().size() > 0)
3772 {
3773 for (List::const_iterator it = chain->children().begin();
3774 it != chain->children().end(); ++ it)
3775 {
3776 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
3777 vrc = VDOpen (hdd, Utf8Str ((*it)->mm.format),
3778 Utf8Str ((*it)->m.locationFull),
3779 VD_OPEN_FLAGS_INFO,
3780 (*it)->mm.vdDiskIfaces);
3781 if (RT_FAILURE (vrc))
3782 throw vrc;
3783
3784 vrc = VDSetParentUuid (hdd, 1,
3785 chain->target()->m.id);
3786 if (RT_FAILURE (vrc))
3787 throw vrc;
3788
3789 vrc = VDClose (hdd, false /* fDelete */);
3790 if (RT_FAILURE (vrc))
3791 throw vrc;
3792 }
3793 }
3794 }
3795 }
3796 catch (HRESULT aRC) { rc = aRC; }
3797 catch (int aVRC)
3798 {
3799 throw setError (E_FAIL,
3800 tr ("Could not merge the hard disk '%ls' to '%ls'%s"),
3801 chain->source()->m.locationFull.raw(),
3802 chain->target()->m.locationFull.raw(),
3803 that->vdError (aVRC).raw());
3804 }
3805
3806 VDDestroy (hdd);
3807 }
3808 catch (HRESULT aRC) { rc = aRC; }
3809
3810 HRESULT rc2;
3811
3812 bool saveSettingsFailed = false;
3813
3814 if (SUCCEEDED (rc))
3815 {
3816 /* all hard disks but the target were successfully deleted by
3817 * VDMerge; reparent the last one and uninitialize deleted */
3818
3819 /* we set mParent & children() (note that thatLock is released
3820 * here), but lock VirtualBox first to follow the rule */
3821 AutoMultiWriteLock2 alock (that->mVirtualBox->lockHandle(),
3822 that->treeLock());
3823
3824 HardDisk *source = chain->source();
3825 HardDisk *target = chain->target();
3826
3827 if (chain->isForward())
3828 {
3829 /* first, unregister the target since it may become a base
3830 * hard disk which needs re-registration */
3831 rc2 = target->mVirtualBox->
3832 unregisterHardDisk (target, false /* aSaveSettings */);
3833 AssertComRC (rc2);
3834
3835 /* then, reparent it and disconnect the deleted branch at
3836 * both ends (chain->parent() is source's parent) */
3837 target->mParent->removeDependentChild (target);
3838 target->mParent = chain->parent();
3839 if (!target->mParent.isNull())
3840 {
3841 target->mParent->addDependentChild (target);
3842 target->mParent->removeDependentChild (source);
3843 source->mParent.setNull();
3844 }
3845 else
3846 {
3847 target->mVirtualBox->addDependentChild (target);
3848 target->mVirtualBox->removeDependentChild (source);
3849 }
3850
3851 /* then, register again */
3852 rc2 = target->mVirtualBox->
3853 registerHardDisk (target, false /* aSaveSettings */);
3854 AssertComRC (rc2);
3855 }
3856 else
3857 {
3858 Assert (target->children().size() == 1);
3859 HardDisk *targetChild = target->children().front();
3860
3861 /* disconnect the deleted branch at the elder end */
3862 target->removeDependentChild (targetChild);
3863 targetChild->mParent.setNull();
3864
3865 const List &children = chain->children();
3866
3867 /* reparent source's chidren and disconnect the deleted
3868 * branch at the younger end m*/
3869 if (children.size() > 0)
3870 {
3871 /* obey {parent,child} lock order */
3872 AutoWriteLock sourceLock (source);
3873
3874 for (List::const_iterator it = children.begin();
3875 it != children.end(); ++ it)
3876 {
3877 AutoWriteLock childLock (*it);
3878
3879 (*it)->mParent = target;
3880 (*it)->mParent->addDependentChild (*it);
3881 source->removeDependentChild (*it);
3882 }
3883 }
3884 }
3885
3886 /* try to save the hard disk registry */
3887 rc = that->mVirtualBox->saveSettings();
3888
3889 if (SUCCEEDED (rc))
3890 {
3891 /* unregister and uninitialize all hard disks in the chain
3892 * but the target */
3893
3894 for (MergeChain::iterator it = chain->begin();
3895 it != chain->end();)
3896 {
3897 if (*it == chain->target())
3898 {
3899 ++ it;
3900 continue;
3901 }
3902
3903 rc2 = (*it)->mVirtualBox->
3904 unregisterHardDisk(*it, false /* aSaveSettings */);
3905 AssertComRC (rc2);
3906
3907 /* now, uninitialize the deleted hard disk (note that
3908 * due to the Deleting state, uninit() will not touch
3909 * the parent-child relationship so we need to
3910 * uninitialize each disk individually) */
3911
3912 /* note that the operation initiator hard disk (which is
3913 * normally also the source hard disk) is a special case
3914 * -- there is one more caller added by Task to it which
3915 * we must release. Also, if we are in sync mode, the
3916 * caller may still hold an AutoCaller instance for it
3917 * and therefore we cannot uninit() it (it's therefore
3918 * the caller's responsibility) */
3919 if (*it == that)
3920 task->autoCaller.release();
3921
3922 /* release the caller added by MergeChain before
3923 * uninit() */
3924 (*it)->releaseCaller();
3925
3926 if (isAsync || *it != that)
3927 (*it)->uninit();
3928
3929 /* delete (to prevent uninitialization in MergeChain
3930 * dtor) and advance to the next item */
3931 it = chain->erase (it);
3932 }
3933
3934 /* Note that states of all other hard disks (target, parent,
3935 * children) will be restored by the MergeChain dtor */
3936 }
3937 else
3938 {
3939 /* too bad if we fail, but we'll need to rollback everything
3940 * we did above to at least keep the HD tree in sync with
3941 * the current registry on disk */
3942
3943 saveSettingsFailed = true;
3944
3945 /// @todo NEWMEDIA implement a proper undo
3946
3947 AssertFailed();
3948 }
3949 }
3950
3951 if (FAILED (rc))
3952 {
3953 /* Here we come if either VDMerge() failed (in which case we
3954 * assume that it tried to do everything to make a further
3955 * retry possible -- e.g. not deleted intermediate hard disks
3956 * and so on) or VirtualBox::saveSettings() failed (where we
3957 * should have the original tree but with intermediate storage
3958 * units deleted by VDMerge()). We have to only restore states
3959 * (through the MergeChain dtor) unless we are run synchronously
3960 * in which case it's the responsibility of the caller as stated
3961 * in the mergeTo() docs. The latter also implies that we
3962 * don't own the merge chain, so release it in this case. */
3963
3964 if (!isAsync)
3965 task->d.chain.release();
3966
3967 NOREF (saveSettingsFailed);
3968 }
3969
3970 break;
3971 }
3972
3973 ////////////////////////////////////////////////////////////////////////
3974
3975 case Task::Clone:
3976 {
3977 ComObjPtr<HardDisk> &target = task->d.target;
3978
3979 /* Lock both in {parent,child} order. The lock is also used as a
3980 * signal from the task initiator (which releases it only after
3981 * RTThreadCreate()) that we can start the job*/
3982 AutoMultiWriteLock2 thatLock (that, target);
3983
3984 uint64_t size = 0, logicalSize = 0;
3985
3986 /* The object may request a specific UUID (through a special form of
3987 * the setLocation() argument). Otherwise we have to generate it */
3988 Guid targetId = target->m.id;
3989 bool generateUuid = targetId.isEmpty();
3990 if (generateUuid)
3991 {
3992 targetId.create();
3993 /* VirtualBox::registerHardDisk() will need UUID */
3994 unconst (target->m.id) = targetId;
3995 }
3996
3997 try
3998 {
3999 PVBOXHDD hdd;
4000 int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
4001 ComAssertRCThrow (vrc, E_FAIL);
4002
4003 Utf8Str format (that->mm.format);
4004 Utf8Str location (that->m.locationFull);
4005
4006 Utf8Str targetFormat (target->mm.format);
4007 Utf8Str targetLocation (target->m.locationFull);
4008
4009 Assert (target->m.state == MediaState_Creating);
4010
4011 Assert (that->m.state == MediaState_LockedRead);
4012
4013 /* unlock before the potentially lengthy operation */
4014 thatLock.leave();
4015
4016 try
4017 {
4018 vrc = VDOpen (hdd, format, location,
4019 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
4020 that->mm.vdDiskIfaces);
4021 if (RT_FAILURE (vrc))
4022 {
4023 throw setError (E_FAIL,
4024 tr ("Could not open the hard disk storage "
4025 "unit '%s'%s"),
4026 location.raw(), that->vdError (vrc).raw());
4027 }
4028
4029 /* ensure the target directory exists */
4030 rc = VirtualBox::ensureFilePathExists (targetLocation);
4031 CheckComRCThrowRC (rc);
4032
4033 /* needed for vdProgressCallback */
4034 that->mm.vdProgress = task->progress;
4035
4036 PVBOXHDD targetHdd;
4037 int vrc = VDCreate (that->mm.vdDiskIfaces, &targetHdd);
4038 ComAssertRCThrow (vrc, E_FAIL);
4039
4040 vrc = VDCopy (hdd, 0, targetHdd, targetFormat,
4041 targetLocation, false, 0, task->d.variant,
4042 targetId.raw(), NULL,
4043 target->mm.vdDiskIfaces,
4044 that->mm.vdDiskIfaces);
4045
4046 that->mm.vdProgress = NULL;
4047
4048 if (RT_FAILURE (vrc))
4049 {
4050 VDDestroy (targetHdd);
4051
4052 throw setError (E_FAIL,
4053 tr ("Could not create the clone hard disk "
4054 "'%s'%s"),
4055 targetLocation.raw(), that->vdError (vrc).raw());
4056 }
4057
4058 size = VDGetFileSize (targetHdd, 0);
4059 logicalSize = VDGetSize (targetHdd, 0) / _1M;
4060
4061 VDDestroy (targetHdd);
4062 }
4063 catch (HRESULT aRC) { rc = aRC; }
4064
4065 VDDestroy (hdd);
4066 }
4067 catch (HRESULT aRC) { rc = aRC; }
4068
4069 if (SUCCEEDED (rc))
4070 {
4071 /* we set mParent & children() (note that thatLock is released
4072 * here), but lock VirtualBox first to follow the rule */
4073 AutoMultiWriteLock2 alock (that->mVirtualBox->lockHandle(),
4074 that->treeLock());
4075
4076 Assert (target->mParent.isNull());
4077
4078 if (!that->mParent.isNull())
4079 {
4080 /* associate the clone with the original's parent and
4081 * deassociate from VirtualBox */
4082 target->mParent = that->mParent;
4083 that->mParent->addDependentChild (target);
4084 target->mVirtualBox->removeDependentChild (target);
4085
4086 /* register with mVirtualBox as the last step and move to
4087 * Created state only on success (leaving an orphan file is
4088 * better than breaking media registry consistency) */
4089 rc = that->mVirtualBox->registerHardDisk(target);
4090
4091 if (FAILED (rc))
4092 {
4093 /* break the parent association on failure to register */
4094 target->mVirtualBox->addDependentChild (target);
4095 that->mParent->removeDependentChild (target);
4096 target->mParent.setNull();
4097 }
4098 }
4099 else
4100 {
4101 /* just register */
4102 rc = that->mVirtualBox->registerHardDisk(target);
4103 }
4104 }
4105
4106 thatLock.maybeEnter();
4107
4108 if (SUCCEEDED (rc))
4109 {
4110 target->m.state = MediaState_Created;
4111
4112 target->m.size = size;
4113 target->mm.logicalSize = logicalSize;
4114 }
4115 else
4116 {
4117 /* back to NotCreated on failiure */
4118 target->m.state = MediaState_NotCreated;
4119
4120 /* reset UUID to prevent it from being reused next time */
4121 if (generateUuid)
4122 unconst (target->m.id).clear();
4123 }
4124
4125 if (isAsync)
4126 {
4127 /* unlock ourselves when done (unless in MediaState_LockedWrite
4128 * state because of taking the online snapshot*/
4129 if (that->m.state != MediaState_LockedWrite)
4130 {
4131 HRESULT rc2 = that->UnlockRead (NULL);
4132 AssertComRC (rc2);
4133 }
4134 }
4135
4136 /* Note that in sync mode, it's the caller's responsibility to
4137 * unlock the hard disk */
4138
4139 break;
4140 }
4141
4142 ////////////////////////////////////////////////////////////////////////
4143
4144 case Task::Delete:
4145 {
4146 /* The lock is also used as a signal from the task initiator (which
4147 * releases it only after RTThreadCreate()) that we can start the job */
4148 AutoWriteLock thatLock (that);
4149
4150 try
4151 {
4152 PVBOXHDD hdd;
4153 int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
4154 ComAssertRCThrow (vrc, E_FAIL);
4155
4156 Utf8Str format (that->mm.format);
4157 Utf8Str location (that->m.locationFull);
4158
4159 /* unlock before the potentially lengthy operation */
4160 Assert (that->m.state == MediaState_Deleting);
4161 thatLock.leave();
4162
4163 try
4164 {
4165 vrc = VDOpen (hdd, format, location,
4166 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
4167 that->mm.vdDiskIfaces);
4168 if (RT_SUCCESS (vrc))
4169 vrc = VDClose (hdd, true /* fDelete */);
4170
4171 if (RT_FAILURE (vrc))
4172 {
4173 throw setError (E_FAIL,
4174 tr ("Could not delete the hard disk storage "
4175 "unit '%s'%s"),
4176 location.raw(), that->vdError (vrc).raw());
4177 }
4178
4179 }
4180 catch (HRESULT aRC) { rc = aRC; }
4181
4182 VDDestroy (hdd);
4183 }
4184 catch (HRESULT aRC) { rc = aRC; }
4185
4186 thatLock.maybeEnter();
4187
4188 /* go to the NotCreated state even on failure since the storage
4189 * may have been already partially deleted and cannot be used any
4190 * more. One will be able to manually re-open the storage if really
4191 * needed to re-register it. */
4192 that->m.state = MediaState_NotCreated;
4193
4194 /* Reset UUID to prevent Create* from reusing it again */
4195 unconst (that->m.id).clear();
4196
4197 break;
4198 }
4199
4200 case Task::Reset:
4201 {
4202 /* The lock is also used as a signal from the task initiator (which
4203 * releases it only after RTThreadCreate()) that we can start the job */
4204 AutoWriteLock thatLock (that);
4205
4206 /// @todo Below we use a pair of delete/create operations to reset
4207 /// the diff contents but the most efficient way will of course be
4208 /// to add a VDResetDiff() API call
4209
4210 uint64_t size = 0, logicalSize = 0;
4211
4212 try
4213 {
4214 PVBOXHDD hdd;
4215 int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
4216 ComAssertRCThrow (vrc, E_FAIL);
4217
4218 Guid id = that->m.id;
4219 Utf8Str format (that->mm.format);
4220 Utf8Str location (that->m.locationFull);
4221
4222 Guid parentId = that->mParent->m.id;
4223 Utf8Str parentFormat (that->mParent->mm.format);
4224 Utf8Str parentLocation (that->mParent->m.locationFull);
4225
4226 Assert (that->m.state == MediaState_LockedWrite);
4227
4228 /* unlock before the potentially lengthy operation */
4229 thatLock.leave();
4230
4231 try
4232 {
4233 /* first, delete the storage unit */
4234 vrc = VDOpen (hdd, format, location,
4235 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
4236 that->mm.vdDiskIfaces);
4237 if (RT_SUCCESS (vrc))
4238 vrc = VDClose (hdd, true /* fDelete */);
4239
4240 if (RT_FAILURE (vrc))
4241 {
4242 throw setError (E_FAIL,
4243 tr ("Could not delete the hard disk storage "
4244 "unit '%s'%s"),
4245 location.raw(), that->vdError (vrc).raw());
4246 }
4247
4248 /* next, create it again */
4249 vrc = VDOpen (hdd, parentFormat, parentLocation,
4250 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
4251 that->mm.vdDiskIfaces);
4252 if (RT_FAILURE (vrc))
4253 {
4254 throw setError (E_FAIL,
4255 tr ("Could not open the hard disk storage "
4256 "unit '%s'%s"),
4257 parentLocation.raw(), that->vdError (vrc).raw());
4258 }
4259
4260 /* needed for vdProgressCallback */
4261 that->mm.vdProgress = task->progress;
4262
4263 vrc = VDCreateDiff (hdd, format, location,
4264 /// @todo use the same image variant as before
4265 VD_IMAGE_FLAGS_NONE,
4266 NULL, id.raw(),
4267 parentId.raw(),
4268 VD_OPEN_FLAGS_NORMAL,
4269 that->mm.vdDiskIfaces,
4270 that->mm.vdDiskIfaces);
4271
4272 that->mm.vdProgress = NULL;
4273
4274 if (RT_FAILURE (vrc))
4275 {
4276 throw setError (E_FAIL,
4277 tr ("Could not create the differencing hard disk "
4278 "storage unit '%s'%s"),
4279 location.raw(), that->vdError (vrc).raw());
4280 }
4281
4282 size = VDGetFileSize (hdd, 1);
4283 logicalSize = VDGetSize (hdd, 1) / _1M;
4284 }
4285 catch (HRESULT aRC) { rc = aRC; }
4286
4287 VDDestroy (hdd);
4288 }
4289 catch (HRESULT aRC) { rc = aRC; }
4290
4291 thatLock.enter();
4292
4293 that->m.size = size;
4294 that->mm.logicalSize = logicalSize;
4295
4296 if (isAsync)
4297 {
4298 /* unlock ourselves when done */
4299 HRESULT rc2 = that->UnlockWrite (NULL);
4300 AssertComRC (rc2);
4301 }
4302
4303 /* Note that in sync mode, it's the caller's responsibility to
4304 * unlock the hard disk */
4305
4306 break;
4307 }
4308
4309 default:
4310 AssertFailedReturn (VERR_GENERAL_FAILURE);
4311 }
4312
4313 /* complete the progress if run asynchronously */
4314 if (isAsync)
4315 {
4316 if (!task->progress.isNull())
4317 task->progress->notifyComplete (rc);
4318 }
4319 else
4320 {
4321 task->rc = rc;
4322 }
4323
4324 LogFlowFunc (("rc=%Rhrc\n", rc));
4325 LogFlowFuncLeave();
4326
4327 return VINF_SUCCESS;
4328
4329 /// @todo ugly hack, fix ComAssert... later
4330 #undef setError
4331}
4332/* 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