VirtualBox

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

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

Storage/VBoxHDD-new: introduced VD interfaces per image and per operation, completely unifying callback handling.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 169.6 KB
Line 
1/** @file
2 *
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#include "HardDiskImpl.h"
23#include "ProgressImpl.h"
24#include "VirtualBoxImpl.h"
25#include "SystemPropertiesImpl.h"
26#include "Logging.h"
27
28#include <iprt/string.h>
29#include <iprt/thread.h>
30#include <iprt/file.h>
31#include <iprt/path.h>
32#include <iprt/dir.h>
33#include <iprt/cpputils.h>
34#include <VBox/VBoxHDD.h>
35#include <VBox/err.h>
36
37#include <algorithm>
38
39#define CHECK_BUSY() \
40 do { \
41 if (isBusy()) \
42 return setError (E_UNEXPECTED, \
43 tr ("Hard disk '%ls' is being used by another task"), \
44 toString().raw()); \
45 } while (0)
46
47#define CHECK_BUSY_AND_READERS() \
48do { \
49 if (readers() > 0 || isBusy()) \
50 return setError (E_UNEXPECTED, \
51 tr ("Hard disk '%ls' is being used by another task"), \
52 toString().raw()); \
53} while (0)
54
55/** Task structure for asynchronous VDI operations */
56struct VDITask
57{
58 enum Op { CreateDynamic, CreateStatic, CloneToImage };
59
60 VDITask (Op op, HVirtualDiskImage *i, Progress *p)
61 : operation (op)
62 , vdi (i)
63 , progress (p)
64 {}
65
66 Op operation;
67 ComObjPtr <HVirtualDiskImage> vdi;
68 ComObjPtr <Progress> progress;
69
70 /* for CreateDynamic, CreateStatic */
71 uint64_t size;
72
73 /* for CloneToImage */
74 ComObjPtr <HardDisk> source;
75};
76
77/**
78 * Progress callback handler for VDI operations.
79 *
80 * @param uPercent Completetion precentage (0-100).
81 * @param pvUser Pointer to the Progress instance.
82 */
83static DECLCALLBACK(int) progressCallback (PVM /* pVM */, unsigned uPercent, void *pvUser)
84{
85 Progress *progress = static_cast <Progress *> (pvUser);
86
87 /* update the progress object, capping it at 99% as the final percent
88 * is used for additional operations like setting the UUIDs and similar. */
89 if (progress)
90 progress->notifyProgress (RT_MIN (uPercent, 99));
91
92 return VINF_SUCCESS;
93}
94
95////////////////////////////////////////////////////////////////////////////////
96// HardDisk class
97////////////////////////////////////////////////////////////////////////////////
98
99// constructor / destructor
100////////////////////////////////////////////////////////////////////////////////
101
102/** Shold be called by subclasses from #FinalConstruct() */
103HRESULT HardDisk::FinalConstruct()
104{
105 mRegistered = FALSE;
106
107 mStorageType = HardDiskStorageType_VirtualDiskImage;
108 mType = HardDiskType_Normal;
109
110 mBusy = false;
111 mReaders = 0;
112
113 return S_OK;
114}
115
116/**
117 * Shold be called by subclasses from #FinalRelease().
118 * Uninitializes this object by calling #uninit() if it's not yet done.
119 */
120void HardDisk::FinalRelease()
121{
122 uninit();
123}
124
125// protected initializer/uninitializer for internal purposes only
126////////////////////////////////////////////////////////////////////////////////
127
128/**
129 * Initializes the hard disk object.
130 *
131 * Subclasses should call this or any other #init() method from their
132 * init() implementations.
133 *
134 * @note
135 * This method doesn't do |isReady()| check and doesn't call
136 * |setReady (true)| on success!
137 * @note
138 * This method must be called from under the object's lock!
139 */
140HRESULT HardDisk::protectedInit (VirtualBox *aVirtualBox, HardDisk *aParent)
141{
142 LogFlowThisFunc (("aParent=%p\n", aParent));
143
144 ComAssertRet (aVirtualBox, E_INVALIDARG);
145
146 mVirtualBox = aVirtualBox;
147 mParent = aParent;
148
149 if (!aParent)
150 aVirtualBox->addDependentChild (this);
151 else
152 aParent->addDependentChild (this);
153
154 return S_OK;
155}
156
157/**
158 * Uninitializes the instance.
159 * Subclasses should call this from their uninit() implementations.
160 * The readiness flag must be true on input and will be set to false
161 * on output.
162 *
163 * @param alock this object's autolock
164 *
165 * @note
166 * Using mParent and mVirtualBox members after this method returns
167 * is forbidden.
168 */
169void HardDisk::protectedUninit (AutoWriteLock &alock)
170{
171 LogFlowThisFunc (("\n"));
172
173 Assert (alock.belongsTo (this));
174 Assert (isReady());
175
176 /* uninit all children */
177 uninitDependentChildren();
178
179 setReady (false);
180
181 if (mParent)
182 mParent->removeDependentChild (this);
183 else
184 {
185 alock.leave();
186 mVirtualBox->removeDependentChild (this);
187 alock.enter();
188 }
189
190 mParent.setNull();
191 mVirtualBox.setNull();
192}
193
194// IHardDisk properties
195/////////////////////////////////////////////////////////////////////////////
196
197STDMETHODIMP HardDisk::COMGETTER(Id) (GUIDPARAMOUT aId)
198{
199 if (!aId)
200 return E_POINTER;
201
202 AutoReadLock alock (this);
203 CHECK_READY();
204
205 mId.cloneTo (aId);
206 return S_OK;
207}
208
209STDMETHODIMP HardDisk::COMGETTER(StorageType) (HardDiskStorageType_T *aStorageType)
210{
211 if (!aStorageType)
212 return E_POINTER;
213
214 AutoReadLock alock (this);
215 CHECK_READY();
216
217 *aStorageType = mStorageType;
218 return S_OK;
219}
220
221STDMETHODIMP HardDisk::COMGETTER(Location) (BSTR *aLocation)
222{
223 if (!aLocation)
224 return E_POINTER;
225
226 AutoReadLock alock (this);
227 CHECK_READY();
228
229 toString (false /* aShort */).cloneTo (aLocation);
230 return S_OK;
231}
232
233STDMETHODIMP HardDisk::COMGETTER(Type) (HardDiskType_T *aType)
234{
235 if (!aType)
236 return E_POINTER;
237
238 AutoReadLock alock (this);
239 CHECK_READY();
240
241 *aType = mType;
242 return S_OK;
243}
244
245STDMETHODIMP HardDisk::COMSETTER(Type) (HardDiskType_T aType)
246{
247 AutoWriteLock alock (this);
248 CHECK_READY();
249
250 if (mRegistered)
251 return setError (E_FAIL,
252 tr ("You cannot change the type of the registered hard disk '%ls'"),
253 toString().raw());
254
255 /* return silently if nothing to do */
256 if (mType == aType)
257 return S_OK;
258
259 if (mStorageType == HardDiskStorageType_VMDKImage)
260 return setError (E_FAIL,
261 tr ("Currently, changing the type of VMDK hard disks is not allowed"));
262
263 if (mStorageType == HardDiskStorageType_ISCSIHardDisk)
264 return setError (E_FAIL,
265 tr ("Currently, changing the type of iSCSI hard disks is not allowed"));
266
267 /// @todo (dmik) later: allow to change the type on any registered hard disk
268 // depending on whether it is attached or not, has children etc.
269 // Don't forget to save hdd configuration afterwards.
270
271 mType = aType;
272 return S_OK;
273}
274
275STDMETHODIMP HardDisk::COMGETTER(Parent) (IHardDisk **aParent)
276{
277 if (!aParent)
278 return E_POINTER;
279
280 AutoReadLock alock (this);
281 CHECK_READY();
282
283 mParent.queryInterfaceTo (aParent);
284 return S_OK;
285}
286
287STDMETHODIMP HardDisk::COMGETTER(Children) (IHardDiskCollection **aChildren)
288{
289 if (!aChildren)
290 return E_POINTER;
291
292 AutoReadLock lock(this);
293 CHECK_READY();
294
295 AutoReadLock chLock (childrenLock());
296
297 ComObjPtr <HardDiskCollection> collection;
298 collection.createObject();
299 collection->init (children());
300 collection.queryInterfaceTo (aChildren);
301 return S_OK;
302}
303
304STDMETHODIMP HardDisk::COMGETTER(Root) (IHardDisk **aRoot)
305{
306 if (!aRoot)
307 return E_POINTER;
308
309 AutoReadLock lock(this);
310 CHECK_READY();
311
312 root().queryInterfaceTo (aRoot);
313 return S_OK;
314}
315
316STDMETHODIMP HardDisk::COMGETTER(Accessible) (BOOL *aAccessible)
317{
318 if (!aAccessible)
319 return E_POINTER;
320
321 AutoWriteLock alock (this);
322 CHECK_READY();
323
324 HRESULT rc = getAccessible (mLastAccessError);
325 if (FAILED (rc))
326 return rc;
327
328 *aAccessible = mLastAccessError.isNull();
329 return S_OK;
330}
331
332STDMETHODIMP HardDisk::COMGETTER(AllAccessible) (BOOL *aAllAccessible)
333{
334 if (!aAllAccessible)
335 return E_POINTER;
336
337 AutoWriteLock alock (this);
338 CHECK_READY();
339
340 if (mParent)
341 {
342 HRESULT rc = S_OK;
343
344 /* check the accessibility state of all ancestors */
345 ComObjPtr <HardDisk> parent = (HardDisk *) mParent;
346 while (parent)
347 {
348 AutoWriteLock parentLock (parent);
349 HRESULT rc = parent->getAccessible (mLastAccessError);
350 if (FAILED (rc))
351 break;
352 *aAllAccessible = mLastAccessError.isNull();
353 if (!*aAllAccessible)
354 break;
355 parent = parent->mParent;
356 }
357
358 return rc;
359 }
360
361 HRESULT rc = getAccessible (mLastAccessError);
362 if (FAILED (rc))
363 return rc;
364
365 *aAllAccessible = mLastAccessError.isNull();
366 return S_OK;
367}
368
369STDMETHODIMP HardDisk::COMGETTER(LastAccessError) (BSTR *aLastAccessError)
370{
371 if (!aLastAccessError)
372 return E_POINTER;
373
374 AutoReadLock alock (this);
375 CHECK_READY();
376
377 mLastAccessError.cloneTo (aLastAccessError);
378 return S_OK;
379}
380
381STDMETHODIMP HardDisk::COMGETTER(MachineId) (GUIDPARAMOUT aMachineId)
382{
383 if (!aMachineId)
384 return E_POINTER;
385
386 AutoReadLock alock (this);
387 CHECK_READY();
388
389 mMachineId.cloneTo (aMachineId);
390 return S_OK;
391}
392
393STDMETHODIMP HardDisk::COMGETTER(SnapshotId) (GUIDPARAMOUT aSnapshotId)
394{
395 if (!aSnapshotId)
396 return E_POINTER;
397
398 AutoReadLock alock (this);
399 CHECK_READY();
400
401 mSnapshotId.cloneTo (aSnapshotId);
402 return S_OK;
403}
404
405STDMETHODIMP HardDisk::CloneToImage (INPTR BSTR aFilePath,
406 IVirtualDiskImage **aImage,
407 IProgress **aProgress)
408{
409 if (!aFilePath || !(*aFilePath))
410 return E_INVALIDARG;
411 if (!aImage || !aProgress)
412 return E_POINTER;
413
414 AutoWriteLock alock (this);
415 CHECK_READY();
416 CHECK_BUSY();
417
418 if (!mParent.isNull())
419 return setError (E_FAIL,
420 tr ("Cloning differencing VDI images is not yet supported ('%ls')"),
421 toString().raw());
422
423 HRESULT rc = S_OK;
424
425 /* create a project object */
426 ComObjPtr <Progress> progress;
427 progress.createObject();
428 rc = progress->init (mVirtualBox, static_cast <IHardDisk *> (this),
429 Bstr (tr ("Creating a hard disk clone")),
430 FALSE /* aCancelable */);
431 CheckComRCReturnRC (rc);
432
433 /* create an imageless resulting object */
434 ComObjPtr <HVirtualDiskImage> image;
435 image.createObject();
436 rc = image->init (mVirtualBox, NULL, NULL);
437 CheckComRCReturnRC (rc);
438
439 /* append the default path if only a name is given */
440 Bstr path = aFilePath;
441 {
442 Utf8Str fp = aFilePath;
443 if (!RTPathHavePath (fp))
444 {
445 AutoReadLock propsLock (mVirtualBox->systemProperties());
446 path = Utf8StrFmt ("%ls%c%s",
447 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
448 RTPATH_DELIMITER,
449 fp.raw());
450 }
451 }
452
453 /* set the desired path */
454 rc = image->setFilePath (path);
455 CheckComRCReturnRC (rc);
456
457 /* ensure the directory exists */
458 {
459 Utf8Str imageDir = image->filePath();
460 RTPathStripFilename (imageDir.mutableRaw());
461 if (!RTDirExists (imageDir))
462 {
463 int vrc = RTDirCreateFullPath (imageDir, 0777);
464 if (VBOX_FAILURE (vrc))
465 {
466 return setError (E_FAIL,
467 tr ("Could not create a directory '%s' "
468 "to store the image file (%Vrc)"),
469 imageDir.raw(), vrc);
470 }
471 }
472 }
473
474 /* mark as busy (being created)
475 * (VDI task thread will unmark it) */
476 image->setBusy();
477
478 /* fill in a VDI task data */
479 VDITask *task = new VDITask (VDITask::CloneToImage, image, progress);
480 task->source = this;
481
482 /* increase readers until finished
483 * (VDI task thread will decrease them) */
484 addReader();
485
486 /* create the hard disk creation thread, pass operation data */
487 int vrc = RTThreadCreate (NULL, HVirtualDiskImage::VDITaskThread,
488 (void *) task, 0, RTTHREADTYPE_MAIN_HEAVY_WORKER,
489 0, "VDITask");
490 ComAssertMsgRC (vrc, ("Could not create a thread (%Vrc)", vrc));
491 if (VBOX_FAILURE (vrc))
492 {
493 releaseReader();
494 image->clearBusy();
495 delete task;
496 return E_FAIL;
497 }
498
499 /* return interfaces to the caller */
500 image.queryInterfaceTo (aImage);
501 progress.queryInterfaceTo (aProgress);
502
503 return S_OK;
504}
505
506// public methods for internal purposes only
507/////////////////////////////////////////////////////////////////////////////
508
509/**
510 * Returns the very first (grand-) parent of this hard disk or the hard
511 * disk itself, if it doesn't have a parent.
512 *
513 * @note
514 * Must be called from under the object's lock
515 */
516ComObjPtr <HardDisk> HardDisk::root() const
517{
518 ComObjPtr <HardDisk> root = const_cast <HardDisk *> (this);
519 ComObjPtr <HardDisk> parent;
520 while ((parent = root->parent()))
521 root = parent;
522
523 return root;
524}
525
526/**
527 * Attempts to mark the hard disk as registered.
528 * Must be always called by every reimplementation.
529 * Only VirtualBox can call this method.
530 *
531 * @param aRegistered true to set registered and false to set unregistered
532 */
533HRESULT HardDisk::trySetRegistered (BOOL aRegistered)
534{
535 AutoWriteLock alock (this);
536 CHECK_READY();
537
538 if (aRegistered)
539 {
540 ComAssertRet (mMachineId.isEmpty(), E_FAIL);
541 ComAssertRet (mId && children().size() == 0, E_FAIL);
542
543 if (mRegistered)
544 return setError (E_FAIL,
545 tr ("Hard disk '%ls' is already registered"),
546 toString().raw());
547
548 CHECK_BUSY();
549 }
550 else
551 {
552 if (!mRegistered)
553 return setError (E_FAIL,
554 tr ("Hard disk '%ls' is already unregistered"),
555 toString().raw());
556
557 if (!mMachineId.isEmpty())
558 return setError (E_FAIL,
559 tr ("Hard disk '%ls' is attached to a virtual machine with UUID {%s}"),
560 toString().raw(), mMachineId.toString().raw());
561
562 if (children().size() > 0)
563 return setError (E_FAIL,
564 tr ("Hard disk '%ls' has %d differencing hard disks based on it"),
565 toString().raw(), children().size());
566
567 CHECK_BUSY_AND_READERS();
568 }
569
570 mRegistered = aRegistered;
571 return S_OK;
572}
573
574/**
575 * Checks basic accessibility of this hard disk only (w/o parents).
576 * Must be always called by every HardDisk::getAccessible() reimplementation
577 * in the first place.
578 *
579 * When @a aCheckBusy is true, this method checks that mBusy = false (and
580 * returns an appropriate error if not). This lets reimplementations
581 * successfully call addReader() after getBaseAccessible() succeeds to
582 * reference the disk and protect it from being modified or deleted before
583 * the remaining check steps are done. Note that in this case, the
584 * reimplementation must enter the object lock before calling this method and
585 * must not leave it before calling addReader() to avoid race condition.
586 *
587 * When @a aCheckReaders is true, this method checks that mReaders = 0 (and
588 * returns an appropriate error if not). When set to true together with
589 * @a aCheckBusy, this lets reimplementations successfully call setBusy() after
590 * getBaseAccessible() succeeds to lock the disk and make sure nobody is
591 * referencing it until the remaining check steps are done. Note that in this
592 * case, the reimplementation must enter the object lock before calling this
593 * method and must not leave it before calling setBusy() to avoid race
594 * condition.
595 *
596 * @param aAccessError On output, a null string indicates the hard disk is
597 * accessible, otherwise contains a message describing
598 * the reason of inaccessibility.
599 * @param aCheckBusy Whether to do the busy check or not.
600 * @param aCheckReaders Whether to do readers check or not.
601 */
602HRESULT HardDisk::getBaseAccessible (Bstr &aAccessError,
603 bool aCheckBusy /* = false */,
604 bool aCheckReaders /* = false */)
605{
606 AutoReadLock alock (this);
607 CHECK_READY();
608
609 aAccessError.setNull();
610
611 if (aCheckBusy)
612 {
613 if (mBusy)
614 {
615 aAccessError = Utf8StrFmt (
616 tr ("Hard disk '%ls' is being exclusively used by another task"),
617 toString().raw());
618 return S_OK;
619 }
620 }
621
622 if (aCheckReaders)
623 {
624 if (mReaders > 0)
625 {
626 aAccessError = Utf8StrFmt (
627 tr ("Hard disk '%ls' is being used by another task (%d readers)"),
628 toString().raw(), mReaders);
629 return S_OK;
630 }
631 }
632
633 return S_OK;
634}
635
636/**
637 * Returns true if the set of properties that makes this object unique
638 * is equal to the same set of properties in the given object.
639 */
640bool HardDisk::sameAs (HardDisk *that)
641{
642 AutoReadLock alock (this);
643 if (!isReady())
644 return false;
645
646 /// @todo (dmik) besides UUID, we temporarily use toString() to uniquely
647 // identify objects. This is ok for VDIs but may be not good for iSCSI,
648 // so it will need a reimp of this method.
649
650 return that->mId == mId ||
651 toString (false /* aShort */) == that->toString (false /* aShort */);
652}
653
654/**
655 * Marks this hard disk as busy.
656 * A busy hard disk cannot have readers and its properties (UUID, description)
657 * cannot be externally modified.
658 */
659void HardDisk::setBusy()
660{
661 AutoWriteLock alock (this);
662 AssertReturnVoid (isReady());
663
664 AssertMsgReturnVoid (mBusy == false, ("%ls", toString().raw()));
665 AssertMsgReturnVoid (mReaders == 0, ("%ls", toString().raw()));
666
667 mBusy = true;
668}
669
670/**
671 * Clears the busy flag previously set by #setBusy().
672 */
673void HardDisk::clearBusy()
674{
675 AutoWriteLock alock (this);
676 AssertReturnVoid (isReady());
677
678 AssertMsgReturnVoid (mBusy == true, ("%ls", toString().raw()));
679
680 mBusy = false;
681}
682
683/**
684 * Increases the number of readers of this hard disk.
685 * A hard disk that have readers cannot be marked as busy (and vice versa)
686 * and its properties (UUID, description) cannot be externally modified.
687 */
688void HardDisk::addReader()
689{
690 AutoWriteLock alock (this);
691 AssertReturnVoid (isReady());
692
693 AssertMsgReturnVoid (mBusy == false, ("%ls", toString().raw()));
694
695 ++ mReaders;
696}
697
698/**
699 * Decreases the number of readers of this hard disk.
700 */
701void HardDisk::releaseReader()
702{
703 AutoWriteLock alock (this);
704 AssertReturnVoid (isReady());
705
706 AssertMsgReturnVoid (mBusy == false, ("%ls", toString().raw()));
707 AssertMsgReturnVoid (mReaders > 0, ("%ls", toString().raw()));
708
709 -- mReaders;
710}
711
712/**
713 * Increases the number of readers on all ancestors of this hard disk.
714 */
715void HardDisk::addReaderOnAncestors()
716{
717 AutoWriteLock alock (this);
718 AssertReturnVoid (isReady());
719
720 if (mParent)
721 {
722 AutoWriteLock alock (mParent);
723 mParent->addReader();
724 mParent->addReaderOnAncestors();
725 }
726}
727
728/**
729 * Decreases the number of readers on all ancestors of this hard disk.
730 */
731void HardDisk::releaseReaderOnAncestors()
732{
733 AutoWriteLock alock (this);
734 AssertReturnVoid (isReady());
735
736 if (mParent)
737 {
738 AutoWriteLock alock (mParent);
739 mParent->releaseReaderOnAncestors();
740 mParent->releaseReader();
741 }
742}
743
744/**
745 * Returns true if this hard disk has children not belonging to the same
746 * machine.
747 */
748bool HardDisk::hasForeignChildren()
749{
750 AutoReadLock alock (this);
751 AssertReturn (isReady(), false);
752
753 AssertReturn (!mMachineId.isEmpty(), false);
754
755 /* check all children */
756 AutoReadLock chLock (childrenLock());
757 for (HardDiskList::const_iterator it = children().begin();
758 it != children().end();
759 ++ it)
760 {
761 ComObjPtr <HardDisk> child = *it;
762 AutoReadLock childLock (child);
763 if (child->mMachineId != mMachineId)
764 return true;
765 }
766
767 return false;
768}
769
770/**
771 * Marks this hard disk and all its children as busy.
772 * Used for merge operations.
773 * Returns a meaningful error info on failure.
774 */
775HRESULT HardDisk::setBusyWithChildren()
776{
777 AutoWriteLock alock (this);
778 AssertReturn (isReady(), E_FAIL);
779
780 const char *errMsg = tr ("Hard disk '%ls' is being used by another task");
781
782 if (mReaders > 0 || mBusy)
783 return setError (E_FAIL, errMsg, toString().raw());
784
785 AutoReadLock chLock (childrenLock());
786
787 for (HardDiskList::const_iterator it = children().begin();
788 it != children().end();
789 ++ it)
790 {
791 ComObjPtr <HardDisk> child = *it;
792 AutoWriteLock childLock (child);
793 if (child->mReaders > 0 || child->mBusy)
794 {
795 /* reset the busy flag of all previous children */
796 while (it != children().begin())
797 (*(-- it))->clearBusy();
798 return setError (E_FAIL, errMsg, child->toString().raw());
799 }
800 else
801 child->mBusy = true;
802 }
803
804 mBusy = true;
805
806 return S_OK;
807}
808
809/**
810 * Clears the busy flag of this hard disk and all its children.
811 * An opposite to #setBusyWithChildren.
812 */
813void HardDisk::clearBusyWithChildren()
814{
815 AutoWriteLock alock (this);
816 AssertReturn (isReady(), (void) 0);
817
818 AssertReturn (mBusy == true, (void) 0);
819
820 AutoReadLock chLock (childrenLock());
821
822 for (HardDiskList::const_iterator it = children().begin();
823 it != children().end();
824 ++ it)
825 {
826 ComObjPtr <HardDisk> child = *it;
827 AutoWriteLock childLock (child);
828 Assert (child->mBusy == true);
829 child->mBusy = false;
830 }
831
832 mBusy = false;
833}
834
835/**
836 * Checks that this hard disk and all its direct children are accessible.
837 *
838 * @note Locks this object for writing.
839 */
840HRESULT HardDisk::getAccessibleWithChildren (Bstr &aAccessError)
841{
842 /* getAccessible() needs a write lock */
843 AutoWriteLock alock (this);
844 AssertReturn (isReady(), E_FAIL);
845
846 HRESULT rc = getAccessible (aAccessError);
847 if (FAILED (rc) || !aAccessError.isNull())
848 return rc;
849
850 AutoReadLock chLock (childrenLock());
851
852 for (HardDiskList::const_iterator it = children().begin();
853 it != children().end();
854 ++ it)
855 {
856 ComObjPtr <HardDisk> child = *it;
857 rc = child->getAccessible (aAccessError);
858 if (FAILED (rc) || !aAccessError.isNull())
859 return rc;
860 }
861
862 return rc;
863}
864
865/**
866 * Checks that this hard disk and all its descendants are consistent.
867 * For now, the consistency means that:
868 *
869 * 1) every differencing image is associated with a registered machine
870 * 2) every root image that has differencing children is associated with
871 * a registered machine.
872 *
873 * This method is used by the VirtualBox constructor after loading all hard
874 * disks and all machines.
875 */
876HRESULT HardDisk::checkConsistency()
877{
878 AutoReadLock alock (this);
879 AssertReturn (isReady(), E_FAIL);
880
881 if (isDifferencing())
882 {
883 Assert (mVirtualBox->isMachineIdValid (mMachineId) ||
884 mMachineId.isEmpty());
885
886 if (mMachineId.isEmpty())
887 return setError (E_FAIL,
888 tr ("Differencing hard disk '%ls' is not associated with "
889 "any registered virtual machine or snapshot"),
890 toString().raw());
891 }
892
893 HRESULT rc = S_OK;
894
895 AutoReadLock chLock (childrenLock());
896
897 if (mParent.isNull() && mType == HardDiskType_Normal &&
898 children().size() != 0)
899 {
900 if (mMachineId.isEmpty())
901 return setError (E_FAIL,
902 tr ("Hard disk '%ls' is not associated with any registered "
903 "virtual machine or snapshot, but has differencing child "
904 "hard disks based on it"),
905 toString().raw());
906 }
907
908 for (HardDiskList::const_iterator it = children().begin();
909 it != children().end() && SUCCEEDED (rc);
910 ++ it)
911 {
912 rc = (*it)->checkConsistency();
913 }
914
915 return rc;
916}
917
918/**
919 * Creates a differencing hard disk for this hard disk and returns the
920 * created hard disk object to the caller.
921 *
922 * The created differencing hard disk is automatically added to the list of
923 * children of this hard disk object and registered within VirtualBox.
924
925 * The specified progress object (if not NULL) receives the percentage
926 * of the operation completion. However, it is responsibility of the caller to
927 * call Progress::notifyComplete() after this method returns.
928 *
929 * @param aFolder folder where to create the differencing disk
930 * (must be a full path)
931 * @param aMachineId machine ID the new hard disk will belong to
932 * @param aHardDisk resulting hard disk object
933 * @param aProgress progress object to run during copy operation
934 * (may be NULL)
935 *
936 * @note
937 * Must be NOT called from under locks of other objects that need external
938 * access dirung this method execurion!
939 */
940HRESULT HardDisk::createDiffHardDisk (const Bstr &aFolder, const Guid &aMachineId,
941 ComObjPtr <HVirtualDiskImage> &aHardDisk,
942 Progress *aProgress)
943{
944 AssertReturn (!aFolder.isEmpty() && !aMachineId.isEmpty(),
945 E_FAIL);
946
947 AutoWriteLock alock (this);
948 CHECK_READY();
949
950 ComAssertRet (isBusy() == false, E_FAIL);
951
952 Guid id;
953 id.create();
954
955 Utf8Str filePathTo = Utf8StrFmt ("%ls%c{%Vuuid}.vdi",
956 aFolder.raw(), RTPATH_DELIMITER, id.ptr());
957
958 /* try to make the path relative to the vbox home dir */
959 const char *filePathToRel = filePathTo;
960 {
961 const Utf8Str &homeDir = mVirtualBox->homeDir();
962 if (!strncmp (filePathTo, homeDir, homeDir.length()))
963 filePathToRel = (filePathToRel + homeDir.length() + 1);
964 }
965
966 /* first ensure the directory exists */
967 {
968 Utf8Str dir = aFolder;
969 if (!RTDirExists (dir))
970 {
971 int vrc = RTDirCreateFullPath (dir, 0777);
972 if (VBOX_FAILURE (vrc))
973 {
974 return setError (E_FAIL,
975 tr ("Could not create a directory '%s' "
976 "to store the image file (%Vrc)"),
977 dir.raw(), vrc);
978 }
979 }
980 }
981
982 alock.leave();
983
984 /* call storage type specific diff creation method */
985 HRESULT rc = createDiffImage (id, filePathTo, aProgress);
986
987 alock.enter();
988
989 CheckComRCReturnRC (rc);
990
991 ComObjPtr <HVirtualDiskImage> vdi;
992 vdi.createObject();
993 rc = vdi->init (mVirtualBox, this, Bstr (filePathToRel),
994 TRUE /* aRegistered */);
995 CheckComRCReturnRC (rc);
996
997 /* associate the created hard disk with the given machine */
998 vdi->setMachineId (aMachineId);
999
1000 rc = mVirtualBox->registerHardDisk (vdi, VirtualBox::RHD_Internal);
1001 CheckComRCReturnRC (rc);
1002
1003 aHardDisk = vdi;
1004
1005 return S_OK;
1006}
1007
1008/**
1009 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
1010 * of this hard disk or any of its children and updates it if necessary (by
1011 * calling #updatePath()). Intended to be called only by
1012 * VirtualBox::updateSettings() if a machine's name change causes directory
1013 * renaming that affects this image.
1014 *
1015 * @param aOldPath old path (full)
1016 * @param aNewPath new path (full)
1017 *
1018 * @note Locks this object and all children for writing.
1019 */
1020void HardDisk::updatePaths (const char *aOldPath, const char *aNewPath)
1021{
1022 AssertReturnVoid (aOldPath);
1023 AssertReturnVoid (aNewPath);
1024
1025 AutoWriteLock alock (this);
1026 AssertReturnVoid (isReady());
1027
1028 updatePath (aOldPath, aNewPath);
1029
1030 /* update paths of all children */
1031 AutoReadLock chLock (childrenLock());
1032 for (HardDiskList::const_iterator it = children().begin();
1033 it != children().end();
1034 ++ it)
1035 {
1036 (*it)->updatePaths (aOldPath, aNewPath);
1037 }
1038}
1039
1040/**
1041 * Helper method that deduces a hard disk object type to create from
1042 * the location string format and from the contents of the resource
1043 * pointed to by the location string.
1044 *
1045 * Currently, the location string must be a file path which is
1046 * passed to the HVirtualDiskImage or HVMDKImage initializer in
1047 * attempt to create a hard disk object.
1048 *
1049 * @param aVirtualBox
1050 * @param aLocation
1051 * @param hardDisk
1052 *
1053 * @return
1054 */
1055/* static */
1056HRESULT HardDisk::openHardDisk (VirtualBox *aVirtualBox, INPTR BSTR aLocation,
1057 ComObjPtr <HardDisk> &hardDisk)
1058{
1059 LogFlowFunc (("aLocation=\"%ls\"\n", aLocation));
1060
1061 AssertReturn (aVirtualBox, E_POINTER);
1062
1063 /* null and empty strings are not allowed locations */
1064 AssertReturn (aLocation, E_INVALIDARG);
1065 AssertReturn (*aLocation, E_INVALIDARG);
1066
1067 static const struct
1068 {
1069 HardDiskStorageType_T type;
1070 const char *ext;
1071 }
1072 storageTypes[] =
1073 {
1074 /* try the plugin format first if there is no extension match */
1075 { HardDiskStorageType_CustomHardDisk, NULL },
1076 /* then try the rest */
1077 { HardDiskStorageType_VMDKImage, ".vmdk" },
1078 { HardDiskStorageType_VirtualDiskImage, ".vdi" },
1079 { HardDiskStorageType_VHDImage, ".vhd" },
1080 };
1081
1082 /* try to guess the probe order by extension */
1083 size_t first = 0;
1084 bool haveFirst = false;
1085 Utf8Str loc = aLocation;
1086 char *ext = RTPathExt (loc);
1087
1088 for (size_t i = 0; i < ELEMENTS (storageTypes); ++ i)
1089 {
1090 if (storageTypes [i].ext &&
1091 RTPathCompare (ext, storageTypes [i].ext) == 0)
1092 {
1093 first = i;
1094 haveFirst = true;
1095 break;
1096 }
1097 }
1098
1099 HRESULT rc = S_OK;
1100
1101 HRESULT firstRC = S_OK;
1102 com::ErrorInfoKeeper firstErr (true /* aIsNull */);
1103
1104 for (size_t i = 0; i < ELEMENTS (storageTypes); ++ i)
1105 {
1106 size_t j = !haveFirst ? i : i == 0 ? first : i == first ? 0 : i;
1107 switch (storageTypes [j].type)
1108 {
1109 case HardDiskStorageType_VirtualDiskImage:
1110 {
1111 ComObjPtr <HVirtualDiskImage> obj;
1112 obj.createObject();
1113 rc = obj->init (aVirtualBox, NULL, aLocation,
1114 FALSE /* aRegistered */);
1115 if (SUCCEEDED (rc))
1116 {
1117 hardDisk = obj;
1118 return rc;
1119 }
1120 break;
1121 }
1122 case HardDiskStorageType_VMDKImage:
1123 {
1124 ComObjPtr <HVMDKImage> obj;
1125 obj.createObject();
1126 rc = obj->init (aVirtualBox, NULL, aLocation,
1127 FALSE /* aRegistered */);
1128 if (SUCCEEDED (rc))
1129 {
1130 hardDisk = obj;
1131 return rc;
1132 }
1133 break;
1134 }
1135 case HardDiskStorageType_CustomHardDisk:
1136 {
1137 ComObjPtr <HCustomHardDisk> obj;
1138 obj.createObject();
1139 rc = obj->init (aVirtualBox, NULL, aLocation,
1140 FALSE /* aRegistered */);
1141 if (SUCCEEDED (rc))
1142 {
1143 hardDisk = obj;
1144 return rc;
1145 }
1146 break;
1147 }
1148 case HardDiskStorageType_VHDImage:
1149 {
1150 ComObjPtr <HVHDImage> obj;
1151 obj.createObject();
1152 rc = obj->init (aVirtualBox, NULL, aLocation,
1153 FALSE /* aRegistered */);
1154 if (SUCCEEDED (rc))
1155 {
1156 hardDisk = obj;
1157 return rc;
1158 }
1159 break;
1160 }
1161 default:
1162 {
1163 AssertComRCReturnRC (E_FAIL);
1164 }
1165 }
1166
1167 Assert (FAILED (rc));
1168
1169 /* remember the error of the matching class */
1170 if (haveFirst && j == first)
1171 {
1172 firstRC = rc;
1173 firstErr.fetch();
1174 }
1175 }
1176
1177 if (haveFirst)
1178 {
1179 Assert (FAILED (firstRC));
1180 /* firstErr will restore the error info upon destruction */
1181 return firstRC;
1182 }
1183
1184 /* There was no exact extension match; chances are high that an error we
1185 * got after probing is useless. Use a generic error message instead. */
1186
1187 firstErr.forget();
1188
1189 return setError (E_FAIL,
1190 tr ("Could not recognize the format of the hard disk '%ls'. "
1191 "Either the given format is not supported or hard disk data "
1192 "is corrupt"),
1193 aLocation);
1194}
1195
1196// protected methods
1197/////////////////////////////////////////////////////////////////////////////
1198
1199/**
1200 * Loads the base settings of the hard disk from the given node, registers
1201 * it and loads and registers all child hard disks as HVirtualDiskImage
1202 * instances.
1203 *
1204 * Subclasses must call this method in their init() or loadSettings() methods
1205 * *after* they load specific parts of data (at least, necessary to let
1206 * toString() function correctly), in order to be properly loaded from the
1207 * settings file and registered.
1208 *
1209 * @param aHDNode <HardDisk> node when #isDifferencing() = false, or
1210 * <DiffHardDisk> node otherwise.
1211 *
1212 * @note
1213 * Must be called from under the object's lock
1214 */
1215HRESULT HardDisk::loadSettings (const settings::Key &aHDNode)
1216{
1217 using namespace settings;
1218
1219 AssertReturn (!aHDNode.isNull(), E_FAIL);
1220
1221 /* required */
1222 mId = aHDNode.value <Guid> ("uuid");
1223
1224 if (!isDifferencing())
1225 {
1226 /* type required for <HardDisk> nodes only */
1227 const char *type = aHDNode.stringValue ("type");
1228 if (strcmp (type, "normal") == 0)
1229 mType = HardDiskType_Normal;
1230 else if (strcmp (type, "immutable") == 0)
1231 mType = HardDiskType_Immutable;
1232 else if (strcmp (type, "writethrough") == 0)
1233 mType = HardDiskType_Writethrough;
1234 else
1235 ComAssertMsgFailedRet (("Invalid hard disk type '%s'\n", type),
1236 E_FAIL);
1237 }
1238 else
1239 mType = HardDiskType_Normal;
1240
1241 HRESULT rc = mVirtualBox->registerHardDisk (this, VirtualBox::RHD_OnStartUp);
1242 CheckComRCReturnRC (rc);
1243
1244 /* load all children */
1245 Key::List children = aHDNode.keys ("DiffHardDisk");
1246 for (Key::List::const_iterator it = children.begin();
1247 it != children.end(); ++ it)
1248 {
1249 Key vdiNode = (*it).key ("VirtualDiskImage");
1250
1251 ComObjPtr <HVirtualDiskImage> vdi;
1252 vdi.createObject();
1253 rc = vdi->init (mVirtualBox, this, (*it), vdiNode);
1254 CheckComRCBreakRC (rc);
1255 }
1256
1257 return rc;
1258}
1259
1260/**
1261 * Saves the base settings of the hard disk to the given node
1262 * and saves all child hard disks as <DiffHardDisk> nodes.
1263 *
1264 * Subclasses must call this method in their saveSettings() methods
1265 * in order to be properly saved to the settings file.
1266 *
1267 * @param aHDNode <HardDisk> node when #isDifferencing() = false, or
1268 * <DiffHardDisk> node otherwise.
1269 *
1270 * @note
1271 * Must be called from under the object's read lock
1272 */
1273HRESULT HardDisk::saveSettings (settings::Key &aHDNode)
1274{
1275 using namespace settings;
1276
1277 AssertReturn (!aHDNode.isNull(), E_FAIL);
1278
1279 /* uuid (required) */
1280 aHDNode.setValue <Guid> ("uuid", mId);
1281
1282 if (!isDifferencing())
1283 {
1284 /* type (required) */
1285 const char *type = NULL;
1286 switch (mType)
1287 {
1288 case HardDiskType_Normal:
1289 type = "normal";
1290 break;
1291 case HardDiskType_Immutable:
1292 type = "immutable";
1293 break;
1294 case HardDiskType_Writethrough:
1295 type = "writethrough";
1296 break;
1297 }
1298 aHDNode.setStringValue ("type", type);
1299 }
1300
1301 /* save all children */
1302 AutoReadLock chLock (childrenLock());
1303 for (HardDiskList::const_iterator it = children().begin();
1304 it != children().end();
1305 ++ it)
1306 {
1307 ComObjPtr <HardDisk> child = *it;
1308 AutoReadLock childLock (child);
1309
1310 Key hdNode = aHDNode.appendKey ("DiffHardDisk");
1311
1312 {
1313 Key vdiNode = hdNode.createKey ("VirtualDiskImage");
1314 HRESULT rc = child->saveSettings (hdNode, vdiNode);
1315 CheckComRCReturnRC (rc);
1316 }
1317 }
1318
1319 return S_OK;
1320}
1321
1322////////////////////////////////////////////////////////////////////////////////
1323// HVirtualDiskImage class
1324////////////////////////////////////////////////////////////////////////////////
1325
1326// constructor / destructor
1327////////////////////////////////////////////////////////////////////////////////
1328
1329HRESULT HVirtualDiskImage::FinalConstruct()
1330{
1331 HRESULT rc = HardDisk::FinalConstruct();
1332 if (FAILED (rc))
1333 return rc;
1334
1335 mState = NotCreated;
1336
1337 mStateCheckSem = NIL_RTSEMEVENTMULTI;
1338 mStateCheckWaiters = 0;
1339
1340 mSize = 0;
1341 mActualSize = 0;
1342
1343 return S_OK;
1344}
1345
1346void HVirtualDiskImage::FinalRelease()
1347{
1348 HardDisk::FinalRelease();
1349}
1350
1351// public initializer/uninitializer for internal purposes only
1352////////////////////////////////////////////////////////////////////////////////
1353
1354// public methods for internal purposes only
1355/////////////////////////////////////////////////////////////////////////////
1356
1357/**
1358 * Initializes the VDI hard disk object by reading its properties from
1359 * the given configuration node. The created hard disk will be marked as
1360 * registered on success.
1361 *
1362 * @param aHDNode <HardDisk> or <DiffHardDisk> node.
1363 * @param aVDINode <VirtualDiskImage> node.
1364 */
1365HRESULT HVirtualDiskImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
1366 const settings::Key &aHDNode,
1367 const settings::Key &aVDINode)
1368{
1369 using namespace settings;
1370
1371 LogFlowThisFunc (("\n"));
1372
1373 AssertReturn (!aHDNode.isNull() && !aVDINode.isNull(), E_FAIL);
1374
1375 AutoWriteLock alock (this);
1376 ComAssertRet (!isReady(), E_UNEXPECTED);
1377
1378 mStorageType = HardDiskStorageType_VirtualDiskImage;
1379
1380 HRESULT rc = S_OK;
1381
1382 do
1383 {
1384 rc = protectedInit (aVirtualBox, aParent);
1385 CheckComRCBreakRC (rc);
1386
1387 /* set ready to let protectedUninit() be called on failure */
1388 setReady (true);
1389
1390 /* filePath (required) */
1391 Bstr filePath = aVDINode.stringValue ("filePath");
1392 rc = setFilePath (filePath);
1393 CheckComRCBreakRC (rc);
1394
1395 LogFlowThisFunc (("'%ls'\n", mFilePathFull.raw()));
1396
1397 /* load basic settings and children */
1398 rc = loadSettings (aHDNode);
1399 CheckComRCBreakRC (rc);
1400
1401 mState = Created;
1402 mRegistered = TRUE;
1403
1404 /* Don't call queryInformation() for registered hard disks to
1405 * prevent the calling thread (i.e. the VirtualBox server startup
1406 * thread) from an unexpected freeze. The vital mId property (UUID)
1407 * is read from the registry file in loadSettings(). To get the rest,
1408 * the user will have to call COMGETTER(Accessible) manually. */
1409 }
1410 while (0);
1411
1412 if (FAILED (rc))
1413 uninit();
1414
1415 return rc;
1416}
1417
1418/**
1419 * Initializes the VDI hard disk object using the given image file name.
1420 *
1421 * @param aVirtualBox VirtualBox parent.
1422 * @param aParent Parent hard disk.
1423 * @param aFilePath Path to the image file, or @c NULL to create an
1424 * image-less object.
1425 * @param aRegistered Whether to mark this disk as registered or not
1426 * (ignored when @a aFilePath is @c NULL, assuming @c FALSE)
1427 */
1428HRESULT HVirtualDiskImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
1429 const BSTR aFilePath, BOOL aRegistered /* = FALSE */)
1430{
1431 LogFlowThisFunc (("aFilePath='%ls', aRegistered=%d\n",
1432 aFilePath, aRegistered));
1433
1434 AutoWriteLock alock (this);
1435 ComAssertRet (!isReady(), E_UNEXPECTED);
1436
1437 mStorageType = HardDiskStorageType_VirtualDiskImage;
1438
1439 HRESULT rc = S_OK;
1440
1441 do
1442 {
1443 rc = protectedInit (aVirtualBox, aParent);
1444 CheckComRCBreakRC (rc);
1445
1446 /* set ready to let protectedUninit() be called on failure */
1447 setReady (true);
1448
1449 rc = setFilePath (aFilePath);
1450 CheckComRCBreakRC (rc);
1451
1452 Assert (mId.isEmpty());
1453
1454 if (aFilePath && *aFilePath)
1455 {
1456 mRegistered = aRegistered;
1457 mState = Created;
1458
1459 /* Call queryInformation() anyway (even if it will block), because
1460 * it is the only way to get the UUID of the existing VDI and
1461 * initialize the vital mId property. */
1462 Bstr errMsg;
1463 rc = queryInformation (&errMsg);
1464 if (SUCCEEDED (rc))
1465 {
1466 /* We are constructing a new HVirtualDiskImage object. If there
1467 * is a fatal accessibility error (we cannot read image UUID),
1468 * we have to fail. We do so even on non-fatal errors as well,
1469 * because it's not worth to keep going with the inaccessible
1470 * image from the very beginning (when nothing else depends on
1471 * it yet). */
1472 if (!errMsg.isNull())
1473 rc = setErrorBstr (E_FAIL, errMsg);
1474 }
1475 }
1476 else
1477 {
1478 mRegistered = FALSE;
1479 mState = NotCreated;
1480 mId.create();
1481 }
1482 }
1483 while (0);
1484
1485 if (FAILED (rc))
1486 uninit();
1487
1488 return rc;
1489}
1490
1491/**
1492 * Uninitializes the instance and sets the ready flag to FALSE.
1493 * Called either from FinalRelease(), by the parent when it gets destroyed,
1494 * or by a third party when it decides this object is no more valid.
1495 */
1496void HVirtualDiskImage::uninit()
1497{
1498 LogFlowThisFunc (("\n"));
1499
1500 AutoWriteLock alock (this);
1501 if (!isReady())
1502 return;
1503
1504 HardDisk::protectedUninit (alock);
1505}
1506
1507// IHardDisk properties
1508////////////////////////////////////////////////////////////////////////////////
1509
1510STDMETHODIMP HVirtualDiskImage::COMGETTER(Description) (BSTR *aDescription)
1511{
1512 if (!aDescription)
1513 return E_POINTER;
1514
1515 AutoReadLock alock (this);
1516 CHECK_READY();
1517
1518 mDescription.cloneTo (aDescription);
1519 return S_OK;
1520}
1521
1522STDMETHODIMP HVirtualDiskImage::COMSETTER(Description) (INPTR BSTR aDescription)
1523{
1524 AutoWriteLock alock (this);
1525 CHECK_READY();
1526
1527 CHECK_BUSY_AND_READERS();
1528
1529 if (mState >= Created)
1530 {
1531 int vrc = VDISetImageComment (Utf8Str (mFilePathFull), Utf8Str (aDescription));
1532 if (VBOX_FAILURE (vrc))
1533 return setError (E_FAIL,
1534 tr ("Could not change the description of the VDI hard disk '%ls' "
1535 "(%Vrc)"),
1536 toString().raw(), vrc);
1537 }
1538
1539 mDescription = aDescription;
1540 return S_OK;
1541}
1542
1543STDMETHODIMP HVirtualDiskImage::COMGETTER(Size) (ULONG64 *aSize)
1544{
1545 if (!aSize)
1546 return E_POINTER;
1547
1548 AutoReadLock alock (this);
1549 CHECK_READY();
1550
1551 /* only a non-differencing image knows the logical size */
1552 if (isDifferencing())
1553 return root()->COMGETTER(Size) (aSize);
1554
1555 *aSize = mSize;
1556 return S_OK;
1557}
1558
1559STDMETHODIMP HVirtualDiskImage::COMGETTER(ActualSize) (ULONG64 *aActualSize)
1560{
1561 if (!aActualSize)
1562 return E_POINTER;
1563
1564 AutoReadLock alock (this);
1565 CHECK_READY();
1566
1567 *aActualSize = mActualSize;
1568 return S_OK;
1569}
1570
1571// IVirtualDiskImage properties
1572////////////////////////////////////////////////////////////////////////////////
1573
1574STDMETHODIMP HVirtualDiskImage::COMGETTER(FilePath) (BSTR *aFilePath)
1575{
1576 if (!aFilePath)
1577 return E_POINTER;
1578
1579 AutoReadLock alock (this);
1580 CHECK_READY();
1581
1582 mFilePathFull.cloneTo (aFilePath);
1583 return S_OK;
1584}
1585
1586STDMETHODIMP HVirtualDiskImage::COMSETTER(FilePath) (INPTR BSTR aFilePath)
1587{
1588 AutoWriteLock alock (this);
1589 CHECK_READY();
1590
1591 if (mState != NotCreated)
1592 return setError (E_ACCESSDENIED,
1593 tr ("Cannot change the file path of the existing hard disk '%ls'"),
1594 toString().raw());
1595
1596 CHECK_BUSY_AND_READERS();
1597
1598 /* append the default path if only a name is given */
1599 Bstr path = aFilePath;
1600 if (aFilePath && *aFilePath)
1601 {
1602 Utf8Str fp = aFilePath;
1603 if (!RTPathHavePath (fp))
1604 {
1605 AutoReadLock propsLock (mVirtualBox->systemProperties());
1606 path = Utf8StrFmt ("%ls%c%s",
1607 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
1608 RTPATH_DELIMITER,
1609 fp.raw());
1610 }
1611 }
1612
1613 return setFilePath (path);
1614}
1615
1616STDMETHODIMP HVirtualDiskImage::COMGETTER(Created) (BOOL *aCreated)
1617{
1618 if (!aCreated)
1619 return E_POINTER;
1620
1621 AutoReadLock alock (this);
1622 CHECK_READY();
1623
1624 *aCreated = mState >= Created;
1625 return S_OK;
1626}
1627
1628// IVirtualDiskImage methods
1629/////////////////////////////////////////////////////////////////////////////
1630
1631STDMETHODIMP HVirtualDiskImage::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
1632{
1633 if (!aProgress)
1634 return E_POINTER;
1635
1636 AutoWriteLock alock (this);
1637 CHECK_READY();
1638
1639 return createImage (aSize, TRUE /* aDynamic */, aProgress);
1640}
1641
1642STDMETHODIMP HVirtualDiskImage::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
1643{
1644 if (!aProgress)
1645 return E_POINTER;
1646
1647 AutoWriteLock alock (this);
1648 CHECK_READY();
1649
1650 return createImage (aSize, FALSE /* aDynamic */, aProgress);
1651}
1652
1653STDMETHODIMP HVirtualDiskImage::DeleteImage()
1654{
1655 AutoWriteLock alock (this);
1656 CHECK_READY();
1657 CHECK_BUSY_AND_READERS();
1658
1659 if (mRegistered)
1660 return setError (E_ACCESSDENIED,
1661 tr ("Cannot delete an image of the registered hard disk image '%ls"),
1662 mFilePathFull.raw());
1663 if (mState == NotCreated)
1664 return setError (E_FAIL,
1665 tr ("Hard disk image has been already deleted or never created"));
1666
1667 HRESULT rc = deleteImage();
1668 CheckComRCReturnRC (rc);
1669
1670 mState = NotCreated;
1671
1672 /* reset the fields */
1673 mSize = 0;
1674 mActualSize = 0;
1675
1676 return S_OK;
1677}
1678
1679// public/protected methods for internal purposes only
1680/////////////////////////////////////////////////////////////////////////////
1681
1682/**
1683 * Attempts to mark the hard disk as registered.
1684 * Only VirtualBox can call this method.
1685 */
1686HRESULT HVirtualDiskImage::trySetRegistered (BOOL aRegistered)
1687{
1688 AutoWriteLock alock (this);
1689 CHECK_READY();
1690
1691 if (aRegistered)
1692 {
1693 if (mState == NotCreated)
1694 return setError (E_FAIL,
1695 tr ("Image file '%ls' is not yet created for this hard disk"),
1696 mFilePathFull.raw());
1697 if (isDifferencing())
1698 return setError (E_FAIL,
1699 tr ("Hard disk '%ls' is differencing and cannot be unregistered "
1700 "explicitly"),
1701 mFilePathFull.raw());
1702 }
1703 else
1704 {
1705 ComAssertRet (mState >= Created, E_FAIL);
1706 }
1707
1708 return HardDisk::trySetRegistered (aRegistered);
1709}
1710
1711/**
1712 * Checks accessibility of this hard disk image only (w/o parents).
1713 *
1714 * @param aAccessError on output, a null string indicates the hard disk is
1715 * accessible, otherwise contains a message describing
1716 * the reason of inaccessibility.
1717 *
1718 * @note Locks this object for writing.
1719 */
1720HRESULT HVirtualDiskImage::getAccessible (Bstr &aAccessError)
1721{
1722 /* queryInformation() needs a write lock */
1723 AutoWriteLock alock (this);
1724 CHECK_READY();
1725
1726 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
1727 {
1728 /* An accessibility check in progress on some other thread,
1729 * wait for it to finish. */
1730
1731 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL);
1732 ++ mStateCheckWaiters;
1733 alock.leave();
1734
1735 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
1736
1737 alock.enter();
1738 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
1739 -- mStateCheckWaiters;
1740 if (mStateCheckWaiters == 0)
1741 {
1742 RTSemEventMultiDestroy (mStateCheckSem);
1743 mStateCheckSem = NIL_RTSEMEVENTMULTI;
1744 }
1745
1746 AssertRCReturn (vrc, E_FAIL);
1747
1748 /* don't touch aAccessError, it has been already set */
1749 return S_OK;
1750 }
1751
1752 /* check the basic accessibility */
1753 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */);
1754 if (FAILED (rc) || !aAccessError.isNull())
1755 return rc;
1756
1757 if (mState >= Created)
1758 {
1759 return queryInformation (&aAccessError);
1760 }
1761
1762 aAccessError = Utf8StrFmt ("Hard disk image '%ls' is not yet created",
1763 mFilePathFull.raw());
1764 return S_OK;
1765}
1766
1767/**
1768 * Saves hard disk settings to the specified storage node and saves
1769 * all children to the specified hard disk node
1770 *
1771 * @param aHDNode <HardDisk> or <DiffHardDisk> node.
1772 * @param aStorageNode <VirtualDiskImage> node.
1773 */
1774HRESULT HVirtualDiskImage::saveSettings (settings::Key &aHDNode,
1775 settings::Key &aStorageNode)
1776{
1777 AssertReturn (!aHDNode.isNull() && !aStorageNode.isNull(), E_FAIL);
1778
1779 AutoReadLock alock (this);
1780 CHECK_READY();
1781
1782 /* filePath (required) */
1783 aStorageNode.setValue <Bstr> ("filePath", mFilePath);
1784
1785 /* save basic settings and children */
1786 return HardDisk::saveSettings (aHDNode);
1787}
1788
1789/**
1790 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
1791 * of this hard disk and updates it if necessary to reflect the new location.
1792 * Intended to be from HardDisk::updatePaths().
1793 *
1794 * @param aOldPath old path (full)
1795 * @param aNewPath new path (full)
1796 *
1797 * @note Locks this object for writing.
1798 */
1799void HVirtualDiskImage::updatePath (const char *aOldPath, const char *aNewPath)
1800{
1801 AssertReturnVoid (aOldPath);
1802 AssertReturnVoid (aNewPath);
1803
1804 AutoWriteLock alock (this);
1805 AssertReturnVoid (isReady());
1806
1807 size_t oldPathLen = strlen (aOldPath);
1808
1809 Utf8Str path = mFilePathFull;
1810 LogFlowThisFunc (("VDI.fullPath={%s}\n", path.raw()));
1811
1812 if (RTPathStartsWith (path, aOldPath))
1813 {
1814 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
1815 path.raw() + oldPathLen);
1816 path = newPath;
1817
1818 mVirtualBox->calculateRelativePath (path, path);
1819
1820 unconst (mFilePathFull) = newPath;
1821 unconst (mFilePath) = path;
1822
1823 LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
1824 newPath.raw(), path.raw()));
1825 }
1826}
1827
1828/**
1829 * Returns the string representation of this hard disk.
1830 * When \a aShort is false, returns the full image file path.
1831 * Otherwise, returns the image file name only.
1832 *
1833 * @param aShort if true, a short representation is returned
1834 *
1835 * @note Locks this object for reading.
1836 */
1837Bstr HVirtualDiskImage::toString (bool aShort /* = false */)
1838{
1839 AutoReadLock alock (this);
1840
1841 if (!aShort)
1842 return mFilePathFull;
1843 else
1844 {
1845 Utf8Str fname = mFilePathFull;
1846 return RTPathFilename (fname.mutableRaw());
1847 }
1848}
1849
1850/**
1851 * Creates a clone of this hard disk by storing hard disk data in the given
1852 * VDI file.
1853 *
1854 * If the operation fails, @a aDeleteTarget will be set to @c true unless the
1855 * failure happened because the target file already existed.
1856 *
1857 * @param aId UUID to assign to the created image.
1858 * @param aTargetPath VDI file where the cloned image is to be to stored.
1859 * @param aProgress progress object to run during operation.
1860 * @param aDeleteTarget Whether it is recommended to delete target on
1861 * failure or not.
1862 */
1863HRESULT
1864HVirtualDiskImage::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
1865 Progress *aProgress, bool &aDeleteTarget)
1866{
1867 /* normally, the target file should be deleted on error */
1868 aDeleteTarget = true;
1869
1870 AssertReturn (!aId.isEmpty(), E_FAIL);
1871 AssertReturn (!aTargetPath.isNull(), E_FAIL);
1872 AssertReturn (aProgress, E_FAIL);
1873
1874 AutoWriteLock alock (this);
1875 AssertReturn (isReady(), E_FAIL);
1876
1877 AssertReturn (isBusy() == false, E_FAIL);
1878
1879 /// @todo (dmik) cloning of differencing images is not yet supported
1880 AssertReturn (mParent.isNull(), E_FAIL);
1881
1882 Utf8Str filePathFull = mFilePathFull;
1883
1884 if (mState == NotCreated)
1885 return setError (E_FAIL,
1886 tr ("Source hard disk image '%s' is not yet created"),
1887 filePathFull.raw());
1888
1889 addReader();
1890 alock.leave();
1891
1892 int vrc = VDICopyImage (aTargetPath, filePathFull, NULL,
1893 progressCallback,
1894 static_cast <Progress *> (aProgress));
1895
1896 alock.enter();
1897 releaseReader();
1898
1899 /* We don't want to delete existing user files */
1900 if (vrc == VERR_ALREADY_EXISTS)
1901 aDeleteTarget = false;
1902
1903 if (VBOX_SUCCESS (vrc))
1904 vrc = VDISetImageUUIDs (aTargetPath, aId, NULL, NULL, NULL);
1905
1906 if (VBOX_FAILURE (vrc))
1907 return setError (E_FAIL,
1908 tr ("Could not copy the hard disk image '%s' to '%s' (%Vrc)"),
1909 filePathFull.raw(), aTargetPath.raw(), vrc);
1910
1911 return S_OK;
1912}
1913
1914/**
1915 * Creates a new differencing image for this hard disk with the given
1916 * VDI file name.
1917 *
1918 * @param aId UUID to assign to the created image
1919 * @param aTargetPath VDI file where to store the created differencing image
1920 * @param aProgress progress object to run during operation
1921 * (can be NULL)
1922 */
1923HRESULT
1924HVirtualDiskImage::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
1925 Progress *aProgress)
1926{
1927 AssertReturn (!aId.isEmpty(), E_FAIL);
1928 AssertReturn (!aTargetPath.isNull(), E_FAIL);
1929
1930 AutoWriteLock alock (this);
1931 AssertReturn (isReady(), E_FAIL);
1932
1933 AssertReturn (isBusy() == false, E_FAIL);
1934 AssertReturn (mState >= Created, E_FAIL);
1935
1936 addReader();
1937 alock.leave();
1938
1939 int vrc = VDICreateDifferenceImage (aTargetPath, Utf8Str (mFilePathFull),
1940 NULL, aProgress ? progressCallback : NULL,
1941 static_cast <Progress *> (aProgress));
1942 alock.enter();
1943 releaseReader();
1944
1945 /* update the UUID to correspond to the file name */
1946 if (VBOX_SUCCESS (vrc))
1947 vrc = VDISetImageUUIDs (aTargetPath, aId, NULL, NULL, NULL);
1948
1949 if (VBOX_FAILURE (vrc))
1950 return setError (E_FAIL,
1951 tr ("Could not create a differencing hard disk '%s' (%Vrc)"),
1952 aTargetPath.raw(), vrc);
1953
1954 return S_OK;
1955}
1956
1957/**
1958 * Copies the image file of this hard disk to a separate VDI file (with an
1959 * unique creation UUID) and creates a new hard disk object for the copied
1960 * image. The copy will be created as a child of this hard disk's parent
1961 * (so that this hard disk must be a differencing one).
1962 *
1963 * The specified progress object (if not NULL) receives the percentage
1964 * of the operation completion. However, it is responsibility of the caller to
1965 * call Progress::notifyComplete() after this method returns.
1966 *
1967 * @param aFolder folder where to create a copy (must be a full path)
1968 * @param aMachineId machine ID the new hard disk will belong to
1969 * @param aHardDisk resulting hard disk object
1970 * @param aProgress progress object to run during copy operation
1971 * (may be NULL)
1972 *
1973 * @note
1974 * Must be NOT called from under locks of other objects that need external
1975 * access dirung this method execurion!
1976 */
1977HRESULT
1978HVirtualDiskImage::cloneDiffImage (const Bstr &aFolder, const Guid &aMachineId,
1979 ComObjPtr <HVirtualDiskImage> &aHardDisk,
1980 Progress *aProgress)
1981{
1982 AssertReturn (!aFolder.isEmpty() && !aMachineId.isEmpty(),
1983 E_FAIL);
1984
1985 AutoWriteLock alock (this);
1986 CHECK_READY();
1987
1988 AssertReturn (!mParent.isNull(), E_FAIL);
1989
1990 ComAssertRet (isBusy() == false, E_FAIL);
1991 ComAssertRet (mState >= Created, E_FAIL);
1992
1993 Guid id;
1994 id.create();
1995
1996 Utf8Str filePathTo = Utf8StrFmt ("%ls%c{%Vuuid}.vdi",
1997 aFolder.raw(), RTPATH_DELIMITER, id.ptr());
1998
1999 /* try to make the path relative to the vbox home dir */
2000 const char *filePathToRel = filePathTo;
2001 {
2002 const Utf8Str &homeDir = mVirtualBox->homeDir();
2003 if (!strncmp (filePathTo, homeDir, homeDir.length()))
2004 filePathToRel = (filePathToRel + homeDir.length() + 1);
2005 }
2006
2007 /* first ensure the directory exists */
2008 {
2009 Utf8Str dir = aFolder;
2010 if (!RTDirExists (dir))
2011 {
2012 int vrc = RTDirCreateFullPath (dir, 0777);
2013 if (VBOX_FAILURE (vrc))
2014 {
2015 return setError (E_FAIL,
2016 tr ("Could not create a directory '%s' "
2017 "to store the image file (%Vrc)"),
2018 dir.raw(), vrc);
2019 }
2020 }
2021 }
2022
2023 Utf8Str filePathFull = mFilePathFull;
2024
2025 alock.leave();
2026
2027 int vrc = VDICopyImage (filePathTo, filePathFull, NULL,
2028 progressCallback,
2029 static_cast <Progress *> (aProgress));
2030
2031 alock.enter();
2032
2033 /* get modification and parent UUIDs of this image */
2034 RTUUID modUuid, parentUuid, parentModUuid;
2035 if (VBOX_SUCCESS (vrc))
2036 vrc = VDIGetImageUUIDs (filePathFull, NULL, &modUuid,
2037 &parentUuid, &parentModUuid);
2038
2039 // update the UUID of the copy to correspond to the file name
2040 // and copy all other UUIDs from this image
2041 if (VBOX_SUCCESS (vrc))
2042 vrc = VDISetImageUUIDs (filePathTo, id, &modUuid,
2043 &parentUuid, &parentModUuid);
2044
2045 if (VBOX_FAILURE (vrc))
2046 return setError (E_FAIL,
2047 tr ("Could not copy the hard disk image '%s' to '%s' (%Vrc)"),
2048 filePathFull.raw(), filePathTo.raw(), vrc);
2049
2050 ComObjPtr <HVirtualDiskImage> vdi;
2051 vdi.createObject();
2052 HRESULT rc = vdi->init (mVirtualBox, mParent, Bstr (filePathToRel),
2053 TRUE /* aRegistered */);
2054 if (FAILED (rc))
2055 return rc;
2056
2057 /* associate the created hard disk with the given machine */
2058 vdi->setMachineId (aMachineId);
2059
2060 rc = mVirtualBox->registerHardDisk (vdi, VirtualBox::RHD_Internal);
2061 if (FAILED (rc))
2062 return rc;
2063
2064 aHardDisk = vdi;
2065
2066 return S_OK;
2067}
2068
2069/**
2070 * Merges this child image to its parent image and updates the parent UUID
2071 * of all children of this image (to point to this image's parent).
2072 * It's a responsibility of the caller to unregister and uninitialize
2073 * the merged image on success.
2074 *
2075 * This method is intended to be called on a worker thread (the operation
2076 * can be time consuming).
2077 *
2078 * The specified progress object (if not NULL) receives the percentage
2079 * of the operation completion. However, it is responsibility of the caller to
2080 * call Progress::notifyComplete() after this method returns.
2081 *
2082 * @param aProgress progress object to run during copy operation
2083 * (may be NULL)
2084 *
2085 * @note
2086 * This method expects that both this hard disk and the paret hard disk
2087 * are marked as busy using #setBusyWithChildren() prior to calling it!
2088 * Busy flags of both hard disks will be cleared by this method
2089 * on a successful return. In case of failure, #clearBusyWithChildren()
2090 * must be called on a parent.
2091 *
2092 * @note
2093 * Must be NOT called from under locks of other objects that need external
2094 * access dirung this method execurion!
2095 */
2096HRESULT HVirtualDiskImage::mergeImageToParent (Progress *aProgress)
2097{
2098 LogFlowThisFunc (("mFilePathFull='%ls'\n", mFilePathFull.raw()));
2099
2100 AutoWriteLock alock (this);
2101 CHECK_READY();
2102
2103 AssertReturn (!mParent.isNull(), E_FAIL);
2104 AutoWriteLock parentLock (mParent);
2105
2106 ComAssertRet (isBusy() == true, E_FAIL);
2107 ComAssertRet (mParent->isBusy() == true, E_FAIL);
2108
2109 ComAssertRet (mState >= Created && mParent->asVDI()->mState >= Created, E_FAIL);
2110
2111 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2112 ("non VDI storage types are not yet supported!"), E_FAIL);
2113
2114 parentLock.leave();
2115 alock.leave();
2116
2117 int vrc = VDIMergeImage (Utf8Str (mFilePathFull),
2118 Utf8Str (mParent->asVDI()->mFilePathFull),
2119 progressCallback,
2120 static_cast <Progress *> (aProgress));
2121 alock.enter();
2122 parentLock.enter();
2123
2124 if (VBOX_FAILURE (vrc))
2125 return setError (E_FAIL,
2126 tr ("Could not merge the hard disk image '%ls' to "
2127 "its parent image '%ls' (%Vrc)"),
2128 mFilePathFull.raw(), mParent->asVDI()->mFilePathFull.raw(), vrc);
2129
2130 {
2131 HRESULT rc = S_OK;
2132
2133 AutoReadLock chLock (childrenLock());
2134
2135 for (HardDiskList::const_iterator it = children().begin();
2136 it != children().end(); ++ it)
2137 {
2138 ComObjPtr <HVirtualDiskImage> child = (*it)->asVDI();
2139 AutoWriteLock childLock (child);
2140
2141 /* reparent the child */
2142 child->mParent = mParent;
2143 if (mParent)
2144 mParent->addDependentChild (child);
2145
2146 /* change the parent UUID in the image as well */
2147 RTUUID parentUuid, parentModUuid;
2148 vrc = VDIGetImageUUIDs (Utf8Str (mParent->asVDI()->mFilePathFull),
2149 &parentUuid, &parentModUuid, NULL, NULL);
2150 if (VBOX_FAILURE (vrc))
2151 {
2152 rc = setError (E_FAIL,
2153 tr ("Could not access the hard disk image '%ls' (%Vrc)"),
2154 mParent->asVDI()->mFilePathFull.raw(), vrc);
2155 break;
2156 }
2157 ComAssertBreak (mParent->id() == Guid (parentUuid), rc = E_FAIL);
2158 vrc = VDISetImageUUIDs (Utf8Str (child->mFilePathFull),
2159 NULL, NULL, &parentUuid, &parentModUuid);
2160 if (VBOX_FAILURE (vrc))
2161 {
2162 rc = setError (E_FAIL,
2163 tr ("Could not update parent UUID of the hard disk image "
2164 "'%ls' (%Vrc)"),
2165 child->mFilePathFull.raw(), vrc);
2166 break;
2167 }
2168 }
2169
2170 if (FAILED (rc))
2171 return rc;
2172 }
2173
2174 /* detach all our children to avoid their uninit in #uninit() */
2175 removeDependentChildren();
2176
2177 mParent->clearBusy();
2178 clearBusy();
2179
2180 return S_OK;
2181}
2182
2183/**
2184 * Merges this image to all its child images, updates the parent UUID
2185 * of all children of this image (to point to this image's parent).
2186 * It's a responsibility of the caller to unregister and uninitialize
2187 * the merged image on success.
2188 *
2189 * This method is intended to be called on a worker thread (the operation
2190 * can be time consuming).
2191 *
2192 * The specified progress object (if not NULL) receives the percentage
2193 * of the operation completion. However, it is responsibility of the caller to
2194 * call Progress::notifyComplete() after this method returns.
2195 *
2196 * @param aProgress progress object to run during copy operation
2197 * (may be NULL)
2198 *
2199 * @note
2200 * This method expects that both this hard disk and all children
2201 * are marked as busy using setBusyWithChildren() prior to calling it!
2202 * Busy flags of all affected hard disks will be cleared by this method
2203 * on a successful return. In case of failure, #clearBusyWithChildren()
2204 * must be called for this hard disk.
2205 *
2206 * @note
2207 * Must be NOT called from under locks of other objects that need external
2208 * access dirung this method execurion!
2209 */
2210HRESULT HVirtualDiskImage::mergeImageToChildren (Progress *aProgress)
2211{
2212 LogFlowThisFunc (("mFilePathFull='%ls'\n", mFilePathFull.raw()));
2213
2214 AutoWriteLock alock (this);
2215 CHECK_READY();
2216
2217 /* this must be a diff image */
2218 AssertReturn (isDifferencing(), E_FAIL);
2219
2220 ComAssertRet (isBusy() == true, E_FAIL);
2221 ComAssertRet (mState >= Created, E_FAIL);
2222
2223 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2224 ("non VDI storage types are not yet supported!"), E_FAIL);
2225
2226 {
2227 HRESULT rc = S_OK;
2228
2229 AutoWriteLock chLock (childrenLock ());
2230
2231 /* iterate over a copy since we will modify the list */
2232 HardDiskList list = children();
2233
2234 for (HardDiskList::const_iterator it = list.begin();
2235 it != list.end(); ++ it)
2236 {
2237 ComObjPtr <HardDisk> hd = *it;
2238 ComObjPtr <HVirtualDiskImage> child = hd->asVDI();
2239 AutoWriteLock childLock (child);
2240
2241 ComAssertRet (child->isBusy() == true, E_FAIL);
2242 ComAssertBreak (child->mState >= Created, rc = E_FAIL);
2243
2244 childLock.leave();
2245 alock.leave();
2246
2247 int vrc = VDIMergeImage (Utf8Str (mFilePathFull),
2248 Utf8Str (child->mFilePathFull),
2249 progressCallback,
2250 static_cast <Progress *> (aProgress));
2251 alock.enter();
2252 childLock.enter();
2253
2254 if (VBOX_FAILURE (vrc))
2255 {
2256 rc = setError (E_FAIL,
2257 tr ("Could not merge the hard disk image '%ls' to "
2258 "its parent image '%ls' (%Vrc)"),
2259 mFilePathFull.raw(), child->mFilePathFull.raw(), vrc);
2260 break;
2261 }
2262
2263 /* reparent the child */
2264 child->mParent = mParent;
2265 if (mParent)
2266 mParent->addDependentChild (child);
2267
2268 /* change the parent UUID in the image as well */
2269 RTUUID parentUuid, parentModUuid;
2270 vrc = VDIGetImageUUIDs (Utf8Str (mParent->asVDI()->mFilePathFull),
2271 &parentUuid, &parentModUuid, NULL, NULL);
2272 if (VBOX_FAILURE (vrc))
2273 {
2274 rc = setError (E_FAIL,
2275 tr ("Could not access the hard disk image '%ls' (%Vrc)"),
2276 mParent->asVDI()->mFilePathFull.raw(), vrc);
2277 break;
2278 }
2279 ComAssertBreak (mParent->id() == Guid (parentUuid), rc = E_FAIL);
2280 vrc = VDISetImageUUIDs (Utf8Str (child->mFilePathFull),
2281 NULL, NULL, &parentUuid, &parentModUuid);
2282 if (VBOX_FAILURE (vrc))
2283 {
2284 rc = setError (E_FAIL,
2285 tr ("Could not update parent UUID of the hard disk image "
2286 "'%ls' (%Vrc)"),
2287 child->mFilePathFull.raw(), vrc);
2288 break;
2289 }
2290
2291 /* detach child to avoid its uninit in #uninit() */
2292 removeDependentChild (child);
2293
2294 /* remove the busy flag */
2295 child->clearBusy();
2296 }
2297
2298 if (FAILED (rc))
2299 return rc;
2300 }
2301
2302 clearBusy();
2303
2304 return S_OK;
2305}
2306
2307/**
2308 * Deletes and recreates the differencing hard disk image from scratch.
2309 * The file name and UUID remain the same.
2310 */
2311HRESULT HVirtualDiskImage::wipeOutImage()
2312{
2313 AutoWriteLock alock (this);
2314 CHECK_READY();
2315
2316 AssertReturn (isDifferencing(), E_FAIL);
2317 AssertReturn (mRegistered, E_FAIL);
2318 AssertReturn (mState >= Created, E_FAIL);
2319
2320 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2321 ("non-VDI storage types are not yet supported!"), E_FAIL);
2322
2323 Utf8Str filePathFull = mFilePathFull;
2324
2325 int vrc = RTFileDelete (filePathFull);
2326 if (VBOX_FAILURE (vrc))
2327 return setError (E_FAIL,
2328 tr ("Could not delete the image file '%s' (%Vrc)"),
2329 filePathFull.raw(), vrc);
2330
2331 vrc = VDICreateDifferenceImage (filePathFull,
2332 Utf8Str (mParent->asVDI()->mFilePathFull),
2333 NULL, NULL, NULL);
2334 /* update the UUID to correspond to the file name */
2335 if (VBOX_SUCCESS (vrc))
2336 vrc = VDISetImageUUIDs (filePathFull, mId, NULL, NULL, NULL);
2337
2338 if (VBOX_FAILURE (vrc))
2339 return setError (E_FAIL,
2340 tr ("Could not create a differencing hard disk '%s' (%Vrc)"),
2341 filePathFull.raw(), vrc);
2342
2343 return S_OK;
2344}
2345
2346HRESULT HVirtualDiskImage::deleteImage (bool aIgnoreErrors /* = false */)
2347{
2348 AutoWriteLock alock (this);
2349 CHECK_READY();
2350
2351 AssertReturn (!mRegistered, E_FAIL);
2352 AssertReturn (mState >= Created, E_FAIL);
2353
2354 int vrc = RTFileDelete (Utf8Str (mFilePathFull));
2355 if (VBOX_FAILURE (vrc) && !aIgnoreErrors)
2356 return setError (E_FAIL,
2357 tr ("Could not delete the image file '%ls' (%Vrc)"),
2358 mFilePathFull.raw(), vrc);
2359
2360 return S_OK;
2361}
2362
2363// private methods
2364/////////////////////////////////////////////////////////////////////////////
2365
2366/**
2367 * Helper to set a new file path.
2368 * Resolves a path relatively to the Virtual Box home directory.
2369 *
2370 * @note
2371 * Must be called from under the object's lock!
2372 */
2373HRESULT HVirtualDiskImage::setFilePath (const BSTR aFilePath)
2374{
2375 if (aFilePath && *aFilePath)
2376 {
2377 /* get the full file name */
2378 char filePathFull [RTPATH_MAX];
2379 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aFilePath),
2380 filePathFull, sizeof (filePathFull));
2381 if (VBOX_FAILURE (vrc))
2382 return setError (E_FAIL,
2383 tr ("Invalid image file path '%ls' (%Vrc)"), aFilePath, vrc);
2384
2385 mFilePath = aFilePath;
2386 mFilePathFull = filePathFull;
2387 }
2388 else
2389 {
2390 mFilePath.setNull();
2391 mFilePathFull.setNull();
2392 }
2393
2394 return S_OK;
2395}
2396
2397/**
2398 * Helper to query information about the VDI hard disk.
2399 *
2400 * @param aAccessError not used when NULL, otherwise see #getAccessible()
2401 *
2402 * @note Must be called from under the object's write lock, only after
2403 * CHECK_BUSY_AND_READERS() succeeds.
2404 */
2405HRESULT HVirtualDiskImage::queryInformation (Bstr *aAccessError)
2406{
2407 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
2408
2409 /* create a lock object to completely release it later */
2410 AutoWriteLock alock (this);
2411
2412 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
2413
2414 ComAssertRet (mState >= Created, E_FAIL);
2415
2416 HRESULT rc = S_OK;
2417 int vrc = VINF_SUCCESS;
2418
2419 /* lazily create a semaphore */
2420 vrc = RTSemEventMultiCreate (&mStateCheckSem);
2421 ComAssertRCRet (vrc, E_FAIL);
2422
2423 /* Reference the disk to prevent any concurrent modifications
2424 * after releasing the lock below (to unblock getters before
2425 * a lengthy operation). */
2426 addReader();
2427
2428 alock.leave();
2429
2430 /* VBoxVHDD management interface needs to be optimized: we're opening a
2431 * file three times in a raw to get three bits of information. */
2432
2433 Utf8Str filePath = mFilePathFull;
2434 Bstr errMsg;
2435
2436 do
2437 {
2438 /* check the image file */
2439 Guid id, parentId;
2440 vrc = VDICheckImage (filePath, NULL, NULL, NULL,
2441 id.ptr(), parentId.ptr(), NULL, 0);
2442
2443 if (VBOX_FAILURE (vrc))
2444 break;
2445
2446 if (!mId.isEmpty())
2447 {
2448 /* check that the actual UUID of the image matches the stored UUID */
2449 if (mId != id)
2450 {
2451 errMsg = Utf8StrFmt (
2452 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
2453 "match UUID {%Vuuid} stored in the registry"),
2454 id.ptr(), filePath.raw(), mId.ptr());
2455 break;
2456 }
2457 }
2458 else
2459 {
2460 /* assgn an UUID read from the image file */
2461 mId = id;
2462 }
2463
2464 if (mParent)
2465 {
2466 /* check parent UUID */
2467 AutoWriteLock parentLock (mParent);
2468 if (mParent->id() != parentId)
2469 {
2470 errMsg = Utf8StrFmt (
2471 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
2472 "the hard disk image file '%s' doesn't match "
2473 "UUID {%Vuuid} stored in the registry"),
2474 parentId.raw(), mParent->toString().raw(),
2475 filePath.raw(), mParent->id().raw());
2476 break;
2477 }
2478 }
2479 else if (!parentId.isEmpty())
2480 {
2481 errMsg = Utf8StrFmt (
2482 tr ("Hard disk image '%s' is a differencing image that is linked "
2483 "to a hard disk with UUID {%Vuuid} and cannot be used "
2484 "directly as a base hard disk"),
2485 filePath.raw(), parentId.raw());
2486 break;
2487 }
2488
2489 {
2490 RTFILE file = NIL_RTFILE;
2491 vrc = RTFileOpen (&file, filePath, RTFILE_O_READ);
2492 if (VBOX_SUCCESS (vrc))
2493 {
2494 uint64_t size = 0;
2495 vrc = RTFileGetSize (file, &size);
2496 if (VBOX_SUCCESS (vrc))
2497 mActualSize = size;
2498 RTFileClose (file);
2499 }
2500 if (VBOX_FAILURE (vrc))
2501 break;
2502 }
2503
2504 if (!mParent)
2505 {
2506 /* query logical size only for non-differencing images */
2507
2508 PVDIDISK disk = VDIDiskCreate();
2509 vrc = VDIDiskOpenImage (disk, Utf8Str (mFilePathFull),
2510 VDI_OPEN_FLAGS_READONLY);
2511 if (VBOX_SUCCESS (vrc))
2512 {
2513 uint64_t size = VDIDiskGetSize (disk);
2514 /* convert to MBytes */
2515 mSize = size / 1024 / 1024;
2516 }
2517
2518 VDIDiskDestroy (disk);
2519 if (VBOX_FAILURE (vrc))
2520 break;
2521 }
2522 }
2523 while (0);
2524
2525 /* enter the lock again */
2526 alock.enter();
2527
2528 /* remove the reference */
2529 releaseReader();
2530
2531 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
2532 {
2533 LogWarningFunc (("'%ls' is not accessible "
2534 "(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
2535 mFilePathFull.raw(), rc, vrc, errMsg.raw()));
2536
2537 if (aAccessError)
2538 {
2539 if (!errMsg.isNull())
2540 *aAccessError = errMsg;
2541 else if (VBOX_FAILURE (vrc))
2542 *aAccessError = Utf8StrFmt (
2543 tr ("Could not access hard disk image '%ls' (%Vrc)"),
2544 mFilePathFull.raw(), vrc);
2545 }
2546
2547 /* downgrade to not accessible */
2548 mState = Created;
2549 }
2550 else
2551 {
2552 if (aAccessError)
2553 aAccessError->setNull();
2554
2555 mState = Accessible;
2556 }
2557
2558 /* inform waiters if there are any */
2559 if (mStateCheckWaiters > 0)
2560 {
2561 RTSemEventMultiSignal (mStateCheckSem);
2562 }
2563 else
2564 {
2565 /* delete the semaphore ourselves */
2566 RTSemEventMultiDestroy (mStateCheckSem);
2567 mStateCheckSem = NIL_RTSEMEVENTMULTI;
2568 }
2569
2570 return rc;
2571}
2572
2573/**
2574 * Helper to create hard disk images.
2575 *
2576 * @param aSize size in MB
2577 * @param aDynamic dynamic or fixed image
2578 * @param aProgress address of IProgress pointer to return
2579 */
2580HRESULT HVirtualDiskImage::createImage (ULONG64 aSize, BOOL aDynamic,
2581 IProgress **aProgress)
2582{
2583 AutoWriteLock alock (this);
2584
2585 CHECK_BUSY_AND_READERS();
2586
2587 if (mState != NotCreated)
2588 return setError (E_ACCESSDENIED,
2589 tr ("Hard disk image '%ls' is already created"),
2590 mFilePathFull.raw());
2591
2592 if (!mFilePathFull)
2593 return setError (E_ACCESSDENIED,
2594 tr ("Cannot create a hard disk image using an empty (null) file path"),
2595 mFilePathFull.raw());
2596
2597 /* first ensure the directory exists */
2598 {
2599 Utf8Str imageDir = mFilePathFull;
2600 RTPathStripFilename (imageDir.mutableRaw());
2601 if (!RTDirExists (imageDir))
2602 {
2603 int vrc = RTDirCreateFullPath (imageDir, 0777);
2604 if (VBOX_FAILURE (vrc))
2605 {
2606 return setError (E_FAIL,
2607 tr ("Could not create a directory '%s' "
2608 "to store the image file (%Vrc)"),
2609 imageDir.raw(), vrc);
2610 }
2611 }
2612 }
2613
2614 /* check whether the given file exists or not */
2615 RTFILE file;
2616 int vrc = RTFileOpen (&file, Utf8Str (mFilePathFull),
2617 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
2618 if (vrc != VERR_FILE_NOT_FOUND)
2619 {
2620 if (VBOX_SUCCESS (vrc))
2621 RTFileClose (file);
2622 switch(vrc)
2623 {
2624 case VINF_SUCCESS:
2625 return setError (E_FAIL,
2626 tr ("Image file '%ls' already exists"),
2627 mFilePathFull.raw());
2628
2629 default:
2630 return setError(E_FAIL,
2631 tr ("Invalid image file path '%ls' (%Vrc)"),
2632 mFilePathFull.raw(), vrc);
2633 }
2634 }
2635
2636 /* check VDI size limits */
2637 {
2638 HRESULT rc;
2639 ULONG64 maxVDISize;
2640 ComPtr <ISystemProperties> props;
2641 rc = mVirtualBox->COMGETTER(SystemProperties) (props.asOutParam());
2642 ComAssertComRCRet (rc, E_FAIL);
2643 rc = props->COMGETTER(MaxVDISize) (&maxVDISize);
2644 ComAssertComRCRet (rc, E_FAIL);
2645
2646 if (aSize < 1 || aSize > maxVDISize)
2647 return setError (E_INVALIDARG,
2648 tr ("Invalid VDI size: %llu MB (must be in range [1, %llu] MB)"),
2649 aSize, maxVDISize);
2650 }
2651
2652 HRESULT rc;
2653
2654 /* create a project object */
2655 ComObjPtr <Progress> progress;
2656 progress.createObject();
2657 {
2658 Bstr desc = aDynamic ? tr ("Creating a dynamically expanding hard disk")
2659 : tr ("Creating a fixed-size hard disk");
2660 rc = progress->init (mVirtualBox, static_cast <IVirtualDiskImage *> (this),
2661 desc, FALSE /* aCancelable */);
2662 CheckComRCReturnRC (rc);
2663 }
2664
2665 /* mark as busy (being created)
2666 * (VDI task thread will unmark it) */
2667 setBusy();
2668
2669 /* fill in VDI task data */
2670 VDITask *task = new VDITask (aDynamic ? VDITask::CreateDynamic
2671 : VDITask::CreateStatic,
2672 this, progress);
2673 task->size = aSize;
2674 task->size *= 1024 * 1024; /* convert to bytes */
2675
2676 /* create the hard disk creation thread, pass operation data */
2677 vrc = RTThreadCreate (NULL, VDITaskThread, (void *) task, 0,
2678 RTTHREADTYPE_MAIN_HEAVY_WORKER, 0, "VDITask");
2679 ComAssertMsgRC (vrc, ("Could not create a thread (%Vrc)", vrc));
2680 if (VBOX_FAILURE (vrc))
2681 {
2682 clearBusy();
2683 delete task;
2684 rc = E_FAIL;
2685 }
2686 else
2687 {
2688 /* get one interface for the caller */
2689 progress.queryInterfaceTo (aProgress);
2690 }
2691
2692 return rc;
2693}
2694
2695/* static */
2696DECLCALLBACK(int) HVirtualDiskImage::VDITaskThread (RTTHREAD thread, void *pvUser)
2697{
2698 VDITask *task = static_cast <VDITask *> (pvUser);
2699 AssertReturn (task, VERR_GENERAL_FAILURE);
2700
2701 LogFlowFunc (("operation=%d, size=%llu\n", task->operation, task->size));
2702
2703 VDIIMAGETYPE type = (VDIIMAGETYPE) 0;
2704
2705 switch (task->operation)
2706 {
2707 case VDITask::CreateDynamic: type = VDI_IMAGE_TYPE_NORMAL; break;
2708 case VDITask::CreateStatic: type = VDI_IMAGE_TYPE_FIXED; break;
2709 case VDITask::CloneToImage: break;
2710 default: AssertFailedReturn (VERR_GENERAL_FAILURE); break;
2711 }
2712
2713 HRESULT rc = S_OK;
2714 Utf8Str errorMsg;
2715
2716 bool deleteTarget = true;
2717
2718 if (task->operation == VDITask::CloneToImage)
2719 {
2720 Assert (!task->vdi->id().isEmpty());
2721 /// @todo (dmik) check locks
2722 AutoWriteLock sourceLock (task->source);
2723 rc = task->source->cloneToImage (task->vdi->id(),
2724 Utf8Str (task->vdi->filePathFull()),
2725 task->progress, deleteTarget);
2726
2727 /* release reader added in HardDisk::CloneToImage() */
2728 task->source->releaseReader();
2729 }
2730 else
2731 {
2732 int vrc = VDICreateBaseImage (Utf8Str (task->vdi->filePathFull()),
2733 type, task->size,
2734 Utf8Str (task->vdi->mDescription),
2735 progressCallback,
2736 static_cast <Progress *> (task->progress));
2737
2738 /* We don't want to delete existing user files */
2739 if (vrc == VERR_ALREADY_EXISTS)
2740 deleteTarget = false;
2741
2742 if (VBOX_SUCCESS (vrc) && task->vdi->id())
2743 {
2744 /* we have a non-null UUID, update the created image */
2745 vrc = VDISetImageUUIDs (Utf8Str (task->vdi->filePathFull()),
2746 task->vdi->id().raw(), NULL, NULL, NULL);
2747 }
2748
2749 if (VBOX_FAILURE (vrc))
2750 {
2751 errorMsg = Utf8StrFmt (
2752 tr ("Falied to create a hard disk image '%ls' (%Vrc)"),
2753 task->vdi->filePathFull().raw(), vrc);
2754 rc = E_FAIL;
2755 }
2756 }
2757
2758 LogFlowFunc (("rc=%08X\n", rc));
2759
2760 AutoWriteLock alock (task->vdi);
2761
2762 /* clear busy set in in HardDisk::CloneToImage() or
2763 * in HVirtualDiskImage::createImage() */
2764 task->vdi->clearBusy();
2765
2766 if (SUCCEEDED (rc))
2767 {
2768 task->vdi->mState = HVirtualDiskImage::Created;
2769 /* update VDI data fields */
2770 Bstr errMsg;
2771 rc = task->vdi->queryInformation (&errMsg);
2772 /* we want to deliver the access check result to the caller
2773 * immediately, before he calls HardDisk::GetAccssible() himself. */
2774 if (SUCCEEDED (rc) && !errMsg.isNull())
2775 task->progress->notifyCompleteBstr (
2776 E_FAIL, COM_IIDOF (IVirtualDiskImage), getComponentName(),
2777 errMsg);
2778 else
2779 task->progress->notifyComplete (rc);
2780 }
2781 else
2782 {
2783 /* delete the target file so we don't have orphaned files */
2784 if (deleteTarget)
2785 RTFileDelete(Utf8Str (task->vdi->filePathFull()));
2786
2787 task->vdi->mState = HVirtualDiskImage::NotCreated;
2788 /* complete the progress object */
2789 if (errorMsg)
2790 task->progress->notifyComplete (
2791 E_FAIL, COM_IIDOF (IVirtualDiskImage), getComponentName(),
2792 errorMsg);
2793 else
2794 task->progress->notifyComplete (rc);
2795 }
2796
2797 delete task;
2798
2799 return VINF_SUCCESS;
2800}
2801
2802////////////////////////////////////////////////////////////////////////////////
2803// HISCSIHardDisk class
2804////////////////////////////////////////////////////////////////////////////////
2805
2806// constructor / destructor
2807////////////////////////////////////////////////////////////////////////////////
2808
2809HRESULT HISCSIHardDisk::FinalConstruct()
2810{
2811 HRESULT rc = HardDisk::FinalConstruct();
2812 if (FAILED (rc))
2813 return rc;
2814
2815 mSize = 0;
2816 mActualSize = 0;
2817
2818 mPort = 0;
2819 mLun = 0;
2820
2821 return S_OK;
2822}
2823
2824void HISCSIHardDisk::FinalRelease()
2825{
2826 HardDisk::FinalRelease();
2827}
2828
2829// public initializer/uninitializer for internal purposes only
2830////////////////////////////////////////////////////////////////////////////////
2831
2832// public methods for internal purposes only
2833/////////////////////////////////////////////////////////////////////////////
2834
2835/**
2836 * Initializes the iSCSI hard disk object by reading its properties from
2837 * the given configuration node. The created hard disk will be marked as
2838 * registered on success.
2839 *
2840 * @param aHDNode <HardDisk> node.
2841 * @param aVDINod <ISCSIHardDisk> node.
2842 */
2843HRESULT HISCSIHardDisk::init (VirtualBox *aVirtualBox,
2844 const settings::Key &aHDNode,
2845 const settings::Key &aISCSINode)
2846{
2847 using namespace settings;
2848
2849 LogFlowThisFunc (("\n"));
2850
2851 AssertReturn (!aHDNode.isNull() && !aISCSINode.isNull(), E_FAIL);
2852
2853 AutoWriteLock alock (this);
2854 ComAssertRet (!isReady(), E_UNEXPECTED);
2855
2856 mStorageType = HardDiskStorageType_ISCSIHardDisk;
2857
2858 HRESULT rc = S_OK;
2859
2860 do
2861 {
2862 rc = protectedInit (aVirtualBox, NULL);
2863 CheckComRCBreakRC (rc);
2864
2865 /* set ready to let protectedUninit() be called on failure */
2866 setReady (true);
2867
2868 /* server (required) */
2869 mServer = aISCSINode.stringValue ("server");
2870 /* target (required) */
2871 mTarget = aISCSINode.stringValue ("target");
2872
2873 /* port (optional) */
2874 mPort = aISCSINode.value <USHORT> ("port");
2875 /* lun (optional) */
2876 mLun = aISCSINode.value <ULONG64> ("lun");
2877 /* userName (optional) */
2878 mUserName = aISCSINode.stringValue ("userName");
2879 /* password (optional) */
2880 mPassword = aISCSINode.stringValue ("password");
2881
2882 LogFlowThisFunc (("'iscsi:%ls:%hu@%ls/%ls:%llu'\n",
2883 mServer.raw(), mPort, mUserName.raw(), mTarget.raw(),
2884 mLun));
2885
2886 /* load basic settings and children */
2887 rc = loadSettings (aHDNode);
2888 CheckComRCBreakRC (rc);
2889
2890 if (mType != HardDiskType_Writethrough)
2891 {
2892 rc = setError (E_FAIL,
2893 tr ("Currently, non-Writethrough iSCSI hard disks are not "
2894 "allowed ('%ls')"),
2895 toString().raw());
2896 break;
2897 }
2898
2899 mRegistered = TRUE;
2900 }
2901 while (0);
2902
2903 if (FAILED (rc))
2904 uninit();
2905
2906 return rc;
2907}
2908
2909/**
2910 * Initializes the iSCSI hard disk object using default values for all
2911 * properties. The created hard disk will NOT be marked as registered.
2912 */
2913HRESULT HISCSIHardDisk::init (VirtualBox *aVirtualBox)
2914{
2915 LogFlowThisFunc (("\n"));
2916
2917 AutoWriteLock alock (this);
2918 ComAssertRet (!isReady(), E_UNEXPECTED);
2919
2920 mStorageType = HardDiskStorageType_ISCSIHardDisk;
2921
2922 HRESULT rc = S_OK;
2923
2924 do
2925 {
2926 rc = protectedInit (aVirtualBox, NULL);
2927 CheckComRCBreakRC (rc);
2928
2929 /* set ready to let protectedUninit() be called on failure */
2930 setReady (true);
2931
2932 /* we have to generate a new UUID */
2933 mId.create();
2934 /* currently, all iSCSI hard disks are writethrough */
2935 mType = HardDiskType_Writethrough;
2936 mRegistered = FALSE;
2937 }
2938 while (0);
2939
2940 if (FAILED (rc))
2941 uninit();
2942
2943 return rc;
2944}
2945
2946/**
2947 * Uninitializes the instance and sets the ready flag to FALSE.
2948 * Called either from FinalRelease(), by the parent when it gets destroyed,
2949 * or by a third party when it decides this object is no more valid.
2950 */
2951void HISCSIHardDisk::uninit()
2952{
2953 LogFlowThisFunc (("\n"));
2954
2955 AutoWriteLock alock (this);
2956 if (!isReady())
2957 return;
2958
2959 HardDisk::protectedUninit (alock);
2960}
2961
2962// IHardDisk properties
2963////////////////////////////////////////////////////////////////////////////////
2964
2965STDMETHODIMP HISCSIHardDisk::COMGETTER(Description) (BSTR *aDescription)
2966{
2967 if (!aDescription)
2968 return E_POINTER;
2969
2970 AutoReadLock alock (this);
2971 CHECK_READY();
2972
2973 mDescription.cloneTo (aDescription);
2974 return S_OK;
2975}
2976
2977STDMETHODIMP HISCSIHardDisk::COMSETTER(Description) (INPTR BSTR aDescription)
2978{
2979 AutoWriteLock alock (this);
2980 CHECK_READY();
2981
2982 CHECK_BUSY_AND_READERS();
2983
2984 if (mDescription != aDescription)
2985 {
2986 mDescription = aDescription;
2987 if (mRegistered)
2988 return mVirtualBox->saveSettings();
2989 }
2990
2991 return S_OK;
2992}
2993
2994STDMETHODIMP HISCSIHardDisk::COMGETTER(Size) (ULONG64 *aSize)
2995{
2996 if (!aSize)
2997 return E_POINTER;
2998
2999 AutoReadLock alock (this);
3000 CHECK_READY();
3001
3002 *aSize = mSize;
3003 return S_OK;
3004}
3005
3006STDMETHODIMP HISCSIHardDisk::COMGETTER(ActualSize) (ULONG64 *aActualSize)
3007{
3008 if (!aActualSize)
3009 return E_POINTER;
3010
3011 AutoReadLock alock (this);
3012 CHECK_READY();
3013
3014 *aActualSize = mActualSize;
3015 return S_OK;
3016}
3017
3018// IISCSIHardDisk properties
3019////////////////////////////////////////////////////////////////////////////////
3020
3021STDMETHODIMP HISCSIHardDisk::COMGETTER(Server) (BSTR *aServer)
3022{
3023 if (!aServer)
3024 return E_POINTER;
3025
3026 AutoReadLock alock (this);
3027 CHECK_READY();
3028
3029 mServer.cloneTo (aServer);
3030 return S_OK;
3031}
3032
3033STDMETHODIMP HISCSIHardDisk::COMSETTER(Server) (INPTR BSTR aServer)
3034{
3035 if (!aServer || !*aServer)
3036 return E_INVALIDARG;
3037
3038 AutoWriteLock alock (this);
3039 CHECK_READY();
3040
3041 CHECK_BUSY_AND_READERS();
3042
3043 if (mServer != aServer)
3044 {
3045 mServer = aServer;
3046 if (mRegistered)
3047 return mVirtualBox->saveSettings();
3048 }
3049
3050 return S_OK;
3051}
3052
3053STDMETHODIMP HISCSIHardDisk::COMGETTER(Port) (USHORT *aPort)
3054{
3055 if (!aPort)
3056 return E_POINTER;
3057
3058 AutoReadLock alock (this);
3059 CHECK_READY();
3060
3061 *aPort = mPort;
3062 return S_OK;
3063}
3064
3065STDMETHODIMP HISCSIHardDisk::COMSETTER(Port) (USHORT aPort)
3066{
3067 AutoWriteLock alock (this);
3068 CHECK_READY();
3069
3070 CHECK_BUSY_AND_READERS();
3071
3072 if (mPort != aPort)
3073 {
3074 mPort = aPort;
3075 if (mRegistered)
3076 return mVirtualBox->saveSettings();
3077 }
3078
3079 return S_OK;
3080}
3081
3082STDMETHODIMP HISCSIHardDisk::COMGETTER(Target) (BSTR *aTarget)
3083{
3084 if (!aTarget)
3085 return E_POINTER;
3086
3087 AutoReadLock alock (this);
3088 CHECK_READY();
3089
3090 mTarget.cloneTo (aTarget);
3091 return S_OK;
3092}
3093
3094STDMETHODIMP HISCSIHardDisk::COMSETTER(Target) (INPTR BSTR aTarget)
3095{
3096 if (!aTarget || !*aTarget)
3097 return E_INVALIDARG;
3098
3099 AutoWriteLock alock (this);
3100 CHECK_READY();
3101
3102 CHECK_BUSY_AND_READERS();
3103
3104 if (mTarget != aTarget)
3105 {
3106 mTarget = aTarget;
3107 if (mRegistered)
3108 return mVirtualBox->saveSettings();
3109 }
3110
3111 return S_OK;
3112}
3113
3114STDMETHODIMP HISCSIHardDisk::COMGETTER(Lun) (ULONG64 *aLun)
3115{
3116 if (!aLun)
3117 return E_POINTER;
3118
3119 AutoReadLock alock (this);
3120 CHECK_READY();
3121
3122 *aLun = mLun;
3123 return S_OK;
3124}
3125
3126STDMETHODIMP HISCSIHardDisk::COMSETTER(Lun) (ULONG64 aLun)
3127{
3128 AutoWriteLock alock (this);
3129 CHECK_READY();
3130
3131 CHECK_BUSY_AND_READERS();
3132
3133 if (mLun != aLun)
3134 {
3135 mLun = aLun;
3136 if (mRegistered)
3137 return mVirtualBox->saveSettings();
3138 }
3139
3140 return S_OK;
3141}
3142
3143STDMETHODIMP HISCSIHardDisk::COMGETTER(UserName) (BSTR *aUserName)
3144{
3145 if (!aUserName)
3146 return E_POINTER;
3147
3148 AutoReadLock alock (this);
3149 CHECK_READY();
3150
3151 mUserName.cloneTo (aUserName);
3152 return S_OK;
3153}
3154
3155STDMETHODIMP HISCSIHardDisk::COMSETTER(UserName) (INPTR BSTR aUserName)
3156{
3157 AutoWriteLock alock (this);
3158 CHECK_READY();
3159
3160 CHECK_BUSY_AND_READERS();
3161
3162 if (mUserName != aUserName)
3163 {
3164 mUserName = aUserName;
3165 if (mRegistered)
3166 return mVirtualBox->saveSettings();
3167 }
3168
3169 return S_OK;
3170}
3171
3172STDMETHODIMP HISCSIHardDisk::COMGETTER(Password) (BSTR *aPassword)
3173{
3174 if (!aPassword)
3175 return E_POINTER;
3176
3177 AutoReadLock alock (this);
3178 CHECK_READY();
3179
3180 mPassword.cloneTo (aPassword);
3181 return S_OK;
3182}
3183
3184STDMETHODIMP HISCSIHardDisk::COMSETTER(Password) (INPTR BSTR aPassword)
3185{
3186 AutoWriteLock alock (this);
3187 CHECK_READY();
3188
3189 CHECK_BUSY_AND_READERS();
3190
3191 if (mPassword != aPassword)
3192 {
3193 mPassword = aPassword;
3194 if (mRegistered)
3195 return mVirtualBox->saveSettings();
3196 }
3197
3198 return S_OK;
3199}
3200
3201// public/protected methods for internal purposes only
3202/////////////////////////////////////////////////////////////////////////////
3203
3204/**
3205 * Attempts to mark the hard disk as registered.
3206 * Only VirtualBox can call this method.
3207 */
3208HRESULT HISCSIHardDisk::trySetRegistered (BOOL aRegistered)
3209{
3210 AutoWriteLock alock (this);
3211 CHECK_READY();
3212
3213 if (aRegistered)
3214 {
3215 if (mServer.isEmpty() || mTarget.isEmpty())
3216 return setError (E_FAIL,
3217 tr ("iSCSI Hard disk has no server or target defined"));
3218 }
3219 else
3220 {
3221 }
3222
3223 return HardDisk::trySetRegistered (aRegistered);
3224}
3225
3226/**
3227 * Checks accessibility of this iSCSI hard disk.
3228 *
3229 * @note Locks this object for writing.
3230 */
3231HRESULT HISCSIHardDisk::getAccessible (Bstr &aAccessError)
3232{
3233 /* queryInformation() needs a write lock */
3234 AutoWriteLock alock (this);
3235 CHECK_READY();
3236
3237 /* check the basic accessibility */
3238 HRESULT rc = getBaseAccessible (aAccessError);
3239 if (FAILED (rc) || !aAccessError.isNull())
3240 return rc;
3241
3242 return queryInformation (aAccessError);
3243}
3244
3245/**
3246 * Saves hard disk settings to the specified storage node and saves
3247 * all children to the specified hard disk node
3248 *
3249 * @param aHDNode <HardDisk>.
3250 * @param aStorageNode <ISCSIHardDisk> node.
3251 */
3252HRESULT HISCSIHardDisk::saveSettings (settings::Key &aHDNode,
3253 settings::Key &aStorageNode)
3254{
3255 AssertReturn (!aHDNode.isNull() && !aStorageNode.isNull(), E_FAIL);
3256
3257 AutoReadLock alock (this);
3258 CHECK_READY();
3259
3260 /* server (required) */
3261 aStorageNode.setValue <Bstr> ("server", mServer);
3262 /* target (required) */
3263 aStorageNode.setValue <Bstr> ("target", mTarget);
3264
3265 /* port (optional, defaults to 0) */
3266 aStorageNode.setValueOr <USHORT> ("port", mPort, 0);
3267 /* lun (optional, force 0x format to conform to XML Schema!) */
3268 aStorageNode.setValueOr <ULONG64> ("lun", mLun, 0, 16);
3269 /* userName (optional) */
3270 aStorageNode.setValueOr <Bstr> ("userName", mUserName, Bstr::Null);
3271 /* password (optional) */
3272 aStorageNode.setValueOr <Bstr> ("password", mPassword, Bstr::Null);
3273
3274 /* save basic settings and children */
3275 return HardDisk::saveSettings (aHDNode);
3276}
3277
3278/**
3279 * Returns the string representation of this hard disk.
3280 * When \a aShort is false, returns the full image file path.
3281 * Otherwise, returns the image file name only.
3282 *
3283 * @param aShort if true, a short representation is returned
3284 *
3285 * @note Locks this object for reading.
3286 */
3287Bstr HISCSIHardDisk::toString (bool aShort /* = false */)
3288{
3289 AutoReadLock alock (this);
3290
3291 Bstr str;
3292 if (!aShort)
3293 {
3294 /* the format is iscsi:[<user@>]<server>[:<port>]/<target>[:<lun>] */
3295 str = Utf8StrFmt ("iscsi:%s%ls%s/%ls%s",
3296 !mUserName.isNull() ? Utf8StrFmt ("%ls@", mUserName.raw()).raw() : "",
3297 mServer.raw(),
3298 mPort != 0 ? Utf8StrFmt (":%hu", mPort).raw() : "",
3299 mTarget.raw(),
3300 mLun != 0 ? Utf8StrFmt (":%llu", mLun).raw() : "");
3301 }
3302 else
3303 {
3304 str = Utf8StrFmt ("%ls%s",
3305 mTarget.raw(),
3306 mLun != 0 ? Utf8StrFmt (":%llu", mLun).raw() : "");
3307 }
3308
3309 return str;
3310}
3311
3312/**
3313 * Creates a clone of this hard disk by storing hard disk data in the given
3314 * VDI file.
3315 *
3316 * If the operation fails, @a aDeleteTarget will be set to @c true unless the
3317 * failure happened because the target file already existed.
3318 *
3319 * @param aId UUID to assign to the created image.
3320 * @param aTargetPath VDI file where the cloned image is to be to stored.
3321 * @param aProgress progress object to run during operation.
3322 * @param aDeleteTarget Whether it is recommended to delete target on
3323 * failure or not.
3324 */
3325HRESULT
3326HISCSIHardDisk::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
3327 Progress *aProgress, bool &aDeleteTarget)
3328{
3329 ComAssertMsgFailed (("Not implemented"));
3330 return E_NOTIMPL;
3331
3332// AssertReturn (isBusy() == false, E_FAIL);
3333// addReader();
3334// releaseReader();
3335}
3336
3337/**
3338 * Creates a new differencing image for this hard disk with the given
3339 * VDI file name.
3340 *
3341 * @param aId UUID to assign to the created image
3342 * @param aTargetPath VDI file where to store the created differencing image
3343 * @param aProgress progress object to run during operation
3344 * (can be NULL)
3345 */
3346HRESULT
3347HISCSIHardDisk::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
3348 Progress *aProgress)
3349{
3350 ComAssertMsgFailed (("Not implemented"));
3351 return E_NOTIMPL;
3352
3353// AssertReturn (isBusy() == false, E_FAIL);
3354// addReader();
3355// releaseReader();
3356}
3357
3358// private methods
3359/////////////////////////////////////////////////////////////////////////////
3360
3361/**
3362 * Helper to query information about the iSCSI hard disk.
3363 *
3364 * @param aAccessError see #getAccessible()
3365 *
3366 * @note Must be called from under the object's write lock, only after
3367 * CHECK_BUSY_AND_READERS() succeeds.
3368 */
3369HRESULT HISCSIHardDisk::queryInformation (Bstr &aAccessError)
3370{
3371 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
3372
3373 /* create a lock object to completely release it later */
3374 AutoWriteLock alock (this);
3375
3376 /// @todo (dmik) query info about this iSCSI disk,
3377 // set mSize and mActualSize,
3378 // or set aAccessError in case of failure
3379
3380 aAccessError.setNull();
3381 return S_OK;
3382}
3383
3384////////////////////////////////////////////////////////////////////////////////
3385// HVMDKImage class
3386////////////////////////////////////////////////////////////////////////////////
3387
3388// constructor / destructor
3389////////////////////////////////////////////////////////////////////////////////
3390
3391HRESULT HVMDKImage::FinalConstruct()
3392{
3393 HRESULT rc = HardDisk::FinalConstruct();
3394 if (FAILED (rc))
3395 return rc;
3396
3397 mState = NotCreated;
3398
3399 mStateCheckSem = NIL_RTSEMEVENTMULTI;
3400 mStateCheckWaiters = 0;
3401
3402 mSize = 0;
3403 mActualSize = 0;
3404
3405 /* Create supported error interface. */
3406 mVDInterfaces = NULL;
3407 mInterfaceErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
3408 mInterfaceErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
3409 mInterfaceErrorCallbacks.pfnError = VDError;
3410 int vrc = VDInterfaceAdd(&mInterfaceError, "VMDK_IError", VDINTERFACETYPE_ERROR,
3411 &mInterfaceErrorCallbacks, this, &mVDInterfaces);
3412 ComAssertRCRet (vrc, E_FAIL);
3413
3414 /* initialize the container */
3415 vrc = VDCreate (&mInterfaceError, &mContainer);
3416 ComAssertRCRet (vrc, E_FAIL);
3417
3418 return S_OK;
3419}
3420
3421void HVMDKImage::FinalRelease()
3422{
3423 if (mContainer != NULL)
3424 VDDestroy (mContainer);
3425
3426 HardDisk::FinalRelease();
3427}
3428
3429// public initializer/uninitializer for internal purposes only
3430////////////////////////////////////////////////////////////////////////////////
3431
3432// public methods for internal purposes only
3433/////////////////////////////////////////////////////////////////////////////
3434
3435/**
3436 * Initializes the VMDK hard disk object by reading its properties from
3437 * the given configuration node. The created hard disk will be marked as
3438 * registered on success.
3439 *
3440 * @param aHDNode <HardDisk> node.
3441 * @param aVMDKNode <VirtualDiskImage> node.
3442 */
3443HRESULT HVMDKImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
3444 const settings::Key &aHDNode,
3445 const settings::Key &aVMDKNode)
3446{
3447 using namespace settings;
3448
3449 LogFlowThisFunc (("\n"));
3450
3451 AssertReturn (!aHDNode.isNull() && !aVMDKNode.isNull(), E_FAIL);
3452
3453 AutoWriteLock alock (this);
3454 ComAssertRet (!isReady(), E_UNEXPECTED);
3455
3456 mStorageType = HardDiskStorageType_VMDKImage;
3457
3458 HRESULT rc = S_OK;
3459
3460 do
3461 {
3462 rc = protectedInit (aVirtualBox, aParent);
3463 CheckComRCBreakRC (rc);
3464
3465 /* set ready to let protectedUninit() be called on failure */
3466 setReady (true);
3467
3468 /* filePath (required) */
3469 Bstr filePath = aVMDKNode.stringValue ("filePath");
3470 rc = setFilePath (filePath);
3471 CheckComRCBreakRC (rc);
3472
3473 LogFlowThisFunc (("'%ls'\n", mFilePathFull.raw()));
3474
3475 /* load basic settings and children */
3476 rc = loadSettings (aHDNode);
3477 CheckComRCBreakRC (rc);
3478
3479 if (mType != HardDiskType_Writethrough)
3480 {
3481 rc = setError (E_FAIL,
3482 tr ("Currently, non-Writethrough VMDK images are not "
3483 "allowed ('%ls')"),
3484 toString().raw());
3485 break;
3486 }
3487
3488 mState = Created;
3489 mRegistered = TRUE;
3490
3491 /* Don't call queryInformation() for registered hard disks to
3492 * prevent the calling thread (i.e. the VirtualBox server startup
3493 * thread) from an unexpected freeze. The vital mId property (UUID)
3494 * is read from the registry file in loadSettings(). To get the rest,
3495 * the user will have to call COMGETTER(Accessible) manually. */
3496 }
3497 while (0);
3498
3499 if (FAILED (rc))
3500 uninit();
3501
3502 return rc;
3503}
3504
3505/**
3506 * Initializes the VMDK hard disk object using the given image file name.
3507 *
3508 * @param aVirtualBox VirtualBox parent.
3509 * @param aParent Currently, must always be @c NULL.
3510 * @param aFilePath Path to the image file, or @c NULL to create an
3511 * image-less object.
3512 * @param aRegistered Whether to mark this disk as registered or not
3513 * (ignored when @a aFilePath is @c NULL, assuming @c FALSE)
3514 */
3515HRESULT HVMDKImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
3516 const BSTR aFilePath, BOOL aRegistered /* = FALSE */)
3517{
3518 LogFlowThisFunc (("aFilePath='%ls', aRegistered=%d\n", aFilePath, aRegistered));
3519
3520 AssertReturn (aParent == NULL, E_FAIL);
3521
3522 AutoWriteLock alock (this);
3523 ComAssertRet (!isReady(), E_UNEXPECTED);
3524
3525 mStorageType = HardDiskStorageType_VMDKImage;
3526
3527 HRESULT rc = S_OK;
3528
3529 do
3530 {
3531 rc = protectedInit (aVirtualBox, aParent);
3532 CheckComRCBreakRC (rc);
3533
3534 /* set ready to let protectedUninit() be called on failure */
3535 setReady (true);
3536
3537 rc = setFilePath (aFilePath);
3538 CheckComRCBreakRC (rc);
3539
3540 /* currently, all VMDK hard disks are writethrough */
3541 mType = HardDiskType_Writethrough;
3542
3543 Assert (mId.isEmpty());
3544
3545 if (aFilePath && *aFilePath)
3546 {
3547 mRegistered = aRegistered;
3548 mState = Created;
3549
3550 /* Call queryInformation() anyway (even if it will block), because
3551 * it is the only way to get the UUID of the existing VDI and
3552 * initialize the vital mId property. */
3553 Bstr errMsg;
3554 rc = queryInformation (&errMsg);
3555 if (SUCCEEDED (rc))
3556 {
3557 /* We are constructing a new HVirtualDiskImage object. If there
3558 * is a fatal accessibility error (we cannot read image UUID),
3559 * we have to fail. We do so even on non-fatal errors as well,
3560 * because it's not worth to keep going with the inaccessible
3561 * image from the very beginning (when nothing else depends on
3562 * it yet). */
3563 if (!errMsg.isNull())
3564 rc = setErrorBstr (E_FAIL, errMsg);
3565 }
3566 }
3567 else
3568 {
3569 mRegistered = FALSE;
3570 mState = NotCreated;
3571 mId.create();
3572 }
3573 }
3574 while (0);
3575
3576 if (FAILED (rc))
3577 uninit();
3578
3579 return rc;
3580}
3581
3582/**
3583 * Uninitializes the instance and sets the ready flag to FALSE.
3584 * Called either from FinalRelease(), by the parent when it gets destroyed,
3585 * or by a third party when it decides this object is no more valid.
3586 */
3587void HVMDKImage::uninit()
3588{
3589 LogFlowThisFunc (("\n"));
3590
3591 AutoWriteLock alock (this);
3592 if (!isReady())
3593 return;
3594
3595 HardDisk::protectedUninit (alock);
3596}
3597
3598// IHardDisk properties
3599////////////////////////////////////////////////////////////////////////////////
3600
3601STDMETHODIMP HVMDKImage::COMGETTER(Description) (BSTR *aDescription)
3602{
3603 if (!aDescription)
3604 return E_POINTER;
3605
3606 AutoReadLock alock (this);
3607 CHECK_READY();
3608
3609 mDescription.cloneTo (aDescription);
3610 return S_OK;
3611}
3612
3613STDMETHODIMP HVMDKImage::COMSETTER(Description) (INPTR BSTR aDescription)
3614{
3615 AutoWriteLock alock (this);
3616 CHECK_READY();
3617
3618 CHECK_BUSY_AND_READERS();
3619
3620 return E_NOTIMPL;
3621
3622/// @todo (r=dmik) implement
3623//
3624// if (mState >= Created)
3625// {
3626// int vrc = VDISetImageComment (Utf8Str (mFilePathFull), Utf8Str (aDescription));
3627// if (VBOX_FAILURE (vrc))
3628// return setError (E_FAIL,
3629// tr ("Could not change the description of the VDI hard disk '%ls' "
3630// "(%Vrc)"),
3631// toString().raw(), vrc);
3632// }
3633//
3634// mDescription = aDescription;
3635// return S_OK;
3636}
3637
3638STDMETHODIMP HVMDKImage::COMGETTER(Size) (ULONG64 *aSize)
3639{
3640 if (!aSize)
3641 return E_POINTER;
3642
3643 AutoReadLock alock (this);
3644 CHECK_READY();
3645
3646/// @todo (r=dmik) will need this if we add support for differencing VMDKs
3647//
3648// /* only a non-differencing image knows the logical size */
3649// if (isDifferencing())
3650// return root()->COMGETTER(Size) (aSize);
3651
3652 *aSize = mSize;
3653 return S_OK;
3654}
3655
3656STDMETHODIMP HVMDKImage::COMGETTER(ActualSize) (ULONG64 *aActualSize)
3657{
3658 if (!aActualSize)
3659 return E_POINTER;
3660
3661 AutoReadLock alock (this);
3662 CHECK_READY();
3663
3664 *aActualSize = mActualSize;
3665 return S_OK;
3666}
3667
3668// IVirtualDiskImage properties
3669////////////////////////////////////////////////////////////////////////////////
3670
3671STDMETHODIMP HVMDKImage::COMGETTER(FilePath) (BSTR *aFilePath)
3672{
3673 if (!aFilePath)
3674 return E_POINTER;
3675
3676 AutoReadLock alock (this);
3677 CHECK_READY();
3678
3679 mFilePathFull.cloneTo (aFilePath);
3680 return S_OK;
3681}
3682
3683STDMETHODIMP HVMDKImage::COMSETTER(FilePath) (INPTR BSTR aFilePath)
3684{
3685 AutoWriteLock alock (this);
3686 CHECK_READY();
3687
3688 if (mState != NotCreated)
3689 return setError (E_ACCESSDENIED,
3690 tr ("Cannot change the file path of the existing hard disk '%ls'"),
3691 toString().raw());
3692
3693 CHECK_BUSY_AND_READERS();
3694
3695 /* append the default path if only a name is given */
3696 Bstr path = aFilePath;
3697 if (aFilePath && *aFilePath)
3698 {
3699 Utf8Str fp = aFilePath;
3700 if (!RTPathHavePath (fp))
3701 {
3702 AutoReadLock propsLock (mVirtualBox->systemProperties());
3703 path = Utf8StrFmt ("%ls%c%s",
3704 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
3705 RTPATH_DELIMITER,
3706 fp.raw());
3707 }
3708 }
3709
3710 return setFilePath (path);
3711}
3712
3713STDMETHODIMP HVMDKImage::COMGETTER(Created) (BOOL *aCreated)
3714{
3715 if (!aCreated)
3716 return E_POINTER;
3717
3718 AutoReadLock alock (this);
3719 CHECK_READY();
3720
3721 *aCreated = mState >= Created;
3722 return S_OK;
3723}
3724
3725// IVMDKImage methods
3726/////////////////////////////////////////////////////////////////////////////
3727
3728STDMETHODIMP HVMDKImage::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
3729{
3730 if (!aProgress)
3731 return E_POINTER;
3732
3733 AutoWriteLock alock (this);
3734 CHECK_READY();
3735
3736 return createImage (aSize, TRUE /* aDynamic */, aProgress);
3737}
3738
3739STDMETHODIMP HVMDKImage::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
3740{
3741 if (!aProgress)
3742 return E_POINTER;
3743
3744 AutoWriteLock alock (this);
3745 CHECK_READY();
3746
3747 return createImage (aSize, FALSE /* aDynamic */, aProgress);
3748}
3749
3750STDMETHODIMP HVMDKImage::DeleteImage()
3751{
3752 AutoWriteLock alock (this);
3753 CHECK_READY();
3754 CHECK_BUSY_AND_READERS();
3755
3756 return E_NOTIMPL;
3757
3758/// @todo (r=dmik) later
3759// We will need to parse the file in order to delete all related delta and
3760// sparse images etc. We may also want to obey the .vmdk.lck file
3761// which is (as far as I understood) created when the VMware VM is
3762// running or saved etc.
3763//
3764// if (mRegistered)
3765// return setError (E_ACCESSDENIED,
3766// tr ("Cannot delete an image of the registered hard disk image '%ls"),
3767// mFilePathFull.raw());
3768// if (mState == NotCreated)
3769// return setError (E_FAIL,
3770// tr ("Hard disk image has been already deleted or never created"));
3771//
3772// int vrc = RTFileDelete (Utf8Str (mFilePathFull));
3773// if (VBOX_FAILURE (vrc))
3774// return setError (E_FAIL, tr ("Could not delete the image file '%ls' (%Vrc)"),
3775// mFilePathFull.raw(), vrc);
3776//
3777// mState = NotCreated;
3778//
3779// /* reset the fields */
3780// mSize = 0;
3781// mActualSize = 0;
3782//
3783// return S_OK;
3784}
3785
3786// public/protected methods for internal purposes only
3787/////////////////////////////////////////////////////////////////////////////
3788
3789/**
3790 * Attempts to mark the hard disk as registered.
3791 * Only VirtualBox can call this method.
3792 */
3793HRESULT HVMDKImage::trySetRegistered (BOOL aRegistered)
3794{
3795 AutoWriteLock alock (this);
3796 CHECK_READY();
3797
3798 if (aRegistered)
3799 {
3800 if (mState == NotCreated)
3801 return setError (E_FAIL,
3802 tr ("Image file '%ls' is not yet created for this hard disk"),
3803 mFilePathFull.raw());
3804
3805/// @todo (r=dmik) will need this if we add support for differencing VMDKs
3806// if (isDifferencing())
3807// return setError (E_FAIL,
3808// tr ("Hard disk '%ls' is differencing and cannot be unregistered "
3809// "explicitly"),
3810// mFilePathFull.raw());
3811 }
3812 else
3813 {
3814 ComAssertRet (mState >= Created, E_FAIL);
3815 }
3816
3817 return HardDisk::trySetRegistered (aRegistered);
3818}
3819
3820/**
3821 * Checks accessibility of this hard disk image only (w/o parents).
3822 *
3823 * @param aAccessError on output, a null string indicates the hard disk is
3824 * accessible, otherwise contains a message describing
3825 * the reason of inaccessibility.
3826 *
3827 * @note Locks this object for writing.
3828 */
3829HRESULT HVMDKImage::getAccessible (Bstr &aAccessError)
3830{
3831 /* queryInformation() needs a write lock */
3832 AutoWriteLock alock (this);
3833 CHECK_READY();
3834
3835 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
3836 {
3837 /* An accessibility check in progress on some other thread,
3838 * wait for it to finish. */
3839
3840 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL);
3841 ++ mStateCheckWaiters;
3842 alock.leave();
3843
3844 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
3845
3846 alock.enter();
3847 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
3848 -- mStateCheckWaiters;
3849 if (mStateCheckWaiters == 0)
3850 {
3851 RTSemEventMultiDestroy (mStateCheckSem);
3852 mStateCheckSem = NIL_RTSEMEVENTMULTI;
3853 }
3854
3855 AssertRCReturn (vrc, E_FAIL);
3856
3857 /* don't touch aAccessError, it has been already set */
3858 return S_OK;
3859 }
3860
3861 /* check the basic accessibility */
3862 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */);
3863 if (FAILED (rc) || !aAccessError.isNull())
3864 return rc;
3865
3866 if (mState >= Created)
3867 {
3868 return queryInformation (&aAccessError);
3869 }
3870
3871 aAccessError = Utf8StrFmt ("Hard disk image '%ls' is not yet created",
3872 mFilePathFull.raw());
3873 return S_OK;
3874}
3875
3876/**
3877 * Saves hard disk settings to the specified storage node and saves
3878 * all children to the specified hard disk node
3879 *
3880 * @param aHDNode <HardDisk> or <DiffHardDisk> node.
3881 * @param aStorageNode <VirtualDiskImage> node.
3882 */
3883HRESULT HVMDKImage::saveSettings (settings::Key &aHDNode,
3884 settings::Key &aStorageNode)
3885{
3886 AssertReturn (!aHDNode.isNull() && !aStorageNode.isNull(), E_FAIL);
3887
3888 AutoReadLock alock (this);
3889 CHECK_READY();
3890
3891 /* filePath (required) */
3892 aStorageNode.setValue <Bstr> ("filePath", mFilePath);
3893
3894 /* save basic settings and children */
3895 return HardDisk::saveSettings (aHDNode);
3896}
3897
3898/**
3899 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
3900 * of this hard disk and updates it if necessary to reflect the new location.
3901 * Intended to be from HardDisk::updatePaths().
3902 *
3903 * @param aOldPath old path (full)
3904 * @param aNewPath new path (full)
3905 *
3906 * @note Locks this object for writing.
3907 */
3908void HVMDKImage::updatePath (const char *aOldPath, const char *aNewPath)
3909{
3910 AssertReturnVoid (aOldPath);
3911 AssertReturnVoid (aNewPath);
3912
3913 AutoWriteLock alock (this);
3914 AssertReturnVoid (isReady());
3915
3916 size_t oldPathLen = strlen (aOldPath);
3917
3918 Utf8Str path = mFilePathFull;
3919 LogFlowThisFunc (("VMDK.fullPath={%s}\n", path.raw()));
3920
3921 if (RTPathStartsWith (path, aOldPath))
3922 {
3923 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
3924 path.raw() + oldPathLen);
3925 path = newPath;
3926
3927 mVirtualBox->calculateRelativePath (path, path);
3928
3929 unconst (mFilePathFull) = newPath;
3930 unconst (mFilePath) = path;
3931
3932 LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
3933 newPath.raw(), path.raw()));
3934 }
3935}
3936
3937/**
3938 * Returns the string representation of this hard disk.
3939 * When \a aShort is false, returns the full image file path.
3940 * Otherwise, returns the image file name only.
3941 *
3942 * @param aShort if true, a short representation is returned
3943 *
3944 * @note Locks this object for reading.
3945 */
3946Bstr HVMDKImage::toString (bool aShort /* = false */)
3947{
3948 AutoReadLock alock (this);
3949
3950 if (!aShort)
3951 return mFilePathFull;
3952 else
3953 {
3954 Utf8Str fname = mFilePathFull;
3955 return RTPathFilename (fname.mutableRaw());
3956 }
3957}
3958
3959/**
3960 * Creates a clone of this hard disk by storing hard disk data in the given
3961 * VDI file.
3962 *
3963 * If the operation fails, @a aDeleteTarget will be set to @c true unless the
3964 * failure happened because the target file already existed.
3965 *
3966 * @param aId UUID to assign to the created image.
3967 * @param aTargetPath VDI file where the cloned image is to be to stored.
3968 * @param aProgress progress object to run during operation.
3969 * @param aDeleteTarget Whether it is recommended to delete target on
3970 * failure or not.
3971 */
3972HRESULT
3973HVMDKImage::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
3974 Progress *aProgress, bool &aDeleteTarget)
3975{
3976 ComAssertMsgFailed (("Not implemented"));
3977 return E_NOTIMPL;
3978
3979/// @todo (r=dmik) will need this if we add support for differencing VMDKs
3980// Use code from HVirtualDiskImage::cloneToImage as an example.
3981}
3982
3983/**
3984 * Creates a new differencing image for this hard disk with the given
3985 * VDI file name.
3986 *
3987 * @param aId UUID to assign to the created image
3988 * @param aTargetPath VDI file where to store the created differencing image
3989 * @param aProgress progress object to run during operation
3990 * (can be NULL)
3991 */
3992HRESULT
3993HVMDKImage::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
3994 Progress *aProgress)
3995{
3996 ComAssertMsgFailed (("Not implemented"));
3997 return E_NOTIMPL;
3998
3999/// @todo (r=dmik) will need this if we add support for differencing VMDKs
4000// Use code from HVirtualDiskImage::createDiffImage as an example.
4001}
4002
4003// private methods
4004/////////////////////////////////////////////////////////////////////////////
4005
4006/**
4007 * Helper to set a new file path.
4008 * Resolves a path relatively to the Virtual Box home directory.
4009 *
4010 * @note
4011 * Must be called from under the object's lock!
4012 */
4013HRESULT HVMDKImage::setFilePath (const BSTR aFilePath)
4014{
4015 if (aFilePath && *aFilePath)
4016 {
4017 /* get the full file name */
4018 char filePathFull [RTPATH_MAX];
4019 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aFilePath),
4020 filePathFull, sizeof (filePathFull));
4021 if (VBOX_FAILURE (vrc))
4022 return setError (E_FAIL,
4023 tr ("Invalid image file path '%ls' (%Vrc)"), aFilePath, vrc);
4024
4025 mFilePath = aFilePath;
4026 mFilePathFull = filePathFull;
4027 }
4028 else
4029 {
4030 mFilePath.setNull();
4031 mFilePathFull.setNull();
4032 }
4033
4034 return S_OK;
4035}
4036
4037/**
4038 * Helper to query information about the VDI hard disk.
4039 *
4040 * @param aAccessError not used when NULL, otherwise see #getAccessible()
4041 *
4042 * @note Must be called from under the object's lock, only after
4043 * CHECK_BUSY_AND_READERS() succeeds.
4044 */
4045HRESULT HVMDKImage::queryInformation (Bstr *aAccessError)
4046{
4047 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
4048
4049 /* create a lock object to completely release it later */
4050 AutoWriteLock alock (this);
4051
4052 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
4053
4054 ComAssertRet (mState >= Created, E_FAIL);
4055
4056 HRESULT rc = S_OK;
4057 int vrc = VINF_SUCCESS;
4058
4059 /* lazily create a semaphore */
4060 vrc = RTSemEventMultiCreate (&mStateCheckSem);
4061 ComAssertRCRet (vrc, E_FAIL);
4062
4063 /* Reference the disk to prevent any concurrent modifications
4064 * after releasing the lock below (to unblock getters before
4065 * a lengthy operation). */
4066 addReader();
4067
4068 alock.leave();
4069
4070 /* VBoxVHDD management interface needs to be optimized: we're opening a
4071 * file three times in a raw to get three bits of information. */
4072
4073 Utf8Str filePath = mFilePathFull;
4074 Bstr errMsg;
4075
4076 /* reset any previous error report from VDError() */
4077 mLastVDError.setNull();
4078
4079 do
4080 {
4081 Guid id, parentId;
4082
4083 /// @todo changed from VD_OPEN_FLAGS_READONLY to VD_OPEN_FLAGS_NORMAL,
4084 /// because otherwise registering a VMDK which so far has no UUID will
4085 /// yield a null UUID. It cannot be added to a VMDK opened readonly,
4086 /// obviously. This of course changes locking behavior, but for now
4087 /// this is acceptable. A better solution needs to be found later.
4088 vrc = VDOpen (mContainer, "VMDK", filePath, VD_OPEN_FLAGS_NORMAL, NULL);
4089 if (VBOX_FAILURE (vrc))
4090 break;
4091
4092 vrc = VDGetUuid (mContainer, 0, id.ptr());
4093 if (VBOX_FAILURE (vrc))
4094 break;
4095 vrc = VDGetParentUuid (mContainer, 0, parentId.ptr());
4096 if (VBOX_FAILURE (vrc))
4097 break;
4098
4099 if (!mId.isEmpty())
4100 {
4101 /* check that the actual UUID of the image matches the stored UUID */
4102 if (mId != id)
4103 {
4104 errMsg = Utf8StrFmt (
4105 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
4106 "match UUID {%Vuuid} stored in the registry"),
4107 id.ptr(), filePath.raw(), mId.ptr());
4108 break;
4109 }
4110 }
4111 else
4112 {
4113 /* assgn an UUID read from the image file */
4114 mId = id;
4115 }
4116
4117 if (mParent)
4118 {
4119 /* check parent UUID */
4120 AutoWriteLock parentLock (mParent);
4121 if (mParent->id() != parentId)
4122 {
4123 errMsg = Utf8StrFmt (
4124 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
4125 "the hard disk image file '%s' doesn't match "
4126 "UUID {%Vuuid} stored in the registry"),
4127 parentId.raw(), mParent->toString().raw(),
4128 filePath.raw(), mParent->id().raw());
4129 break;
4130 }
4131 }
4132 else if (!parentId.isEmpty())
4133 {
4134 errMsg = Utf8StrFmt (
4135 tr ("Hard disk image '%s' is a differencing image that is linked "
4136 "to a hard disk with UUID {%Vuuid} and cannot be used "
4137 "directly as a base hard disk"),
4138 filePath.raw(), parentId.raw());
4139 break;
4140 }
4141
4142 /* get actual file size */
4143 /// @todo is there a direct method in RT?
4144 {
4145 RTFILE file = NIL_RTFILE;
4146 vrc = RTFileOpen (&file, filePath, RTFILE_O_READ);
4147 if (VBOX_SUCCESS (vrc))
4148 {
4149 uint64_t size = 0;
4150 vrc = RTFileGetSize (file, &size);
4151 if (VBOX_SUCCESS (vrc))
4152 mActualSize = size;
4153 RTFileClose (file);
4154 }
4155 if (VBOX_FAILURE (vrc))
4156 break;
4157 }
4158
4159 /* query logical size only for non-differencing images */
4160 if (!mParent)
4161 {
4162 uint64_t size = VDGetSize (mContainer, 0);
4163 /* convert to MBytes */
4164 mSize = size / 1024 / 1024;
4165 }
4166 }
4167 while (0);
4168
4169 VDCloseAll (mContainer);
4170
4171 /* enter the lock again */
4172 alock.enter();
4173
4174 /* remove the reference */
4175 releaseReader();
4176
4177 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
4178 {
4179 LogWarningFunc (("'%ls' is not accessible "
4180 "(rc=%08X, vrc=%Vrc, errMsg='%ls', mLastVDError='%s')\n",
4181 mFilePathFull.raw(), rc, vrc, errMsg.raw(), mLastVDError.raw()));
4182
4183 if (aAccessError)
4184 {
4185 if (!errMsg.isNull())
4186 *aAccessError = errMsg;
4187 else if (!mLastVDError.isNull())
4188 *aAccessError = mLastVDError;
4189 else if (VBOX_FAILURE (vrc))
4190 *aAccessError = Utf8StrFmt (
4191 tr ("Could not access hard disk image '%ls' (%Vrc)"),
4192 mFilePathFull.raw(), vrc);
4193 }
4194
4195 /* downgrade to not accessible */
4196 mState = Created;
4197 }
4198 else
4199 {
4200 if (aAccessError)
4201 aAccessError->setNull();
4202
4203 mState = Accessible;
4204 }
4205
4206 /* inform waiters if there are any */
4207 if (mStateCheckWaiters > 0)
4208 {
4209 RTSemEventMultiSignal (mStateCheckSem);
4210 }
4211 else
4212 {
4213 /* delete the semaphore ourselves */
4214 RTSemEventMultiDestroy (mStateCheckSem);
4215 mStateCheckSem = NIL_RTSEMEVENTMULTI;
4216 }
4217
4218 /* cleanup the last error report from VDError() */
4219 mLastVDError.setNull();
4220
4221 return rc;
4222}
4223
4224/**
4225 * Helper to create hard disk images.
4226 *
4227 * @param aSize size in MB
4228 * @param aDynamic dynamic or fixed image
4229 * @param aProgress address of IProgress pointer to return
4230 */
4231HRESULT HVMDKImage::createImage (ULONG64 aSize, BOOL aDynamic,
4232 IProgress **aProgress)
4233{
4234 ComAssertMsgFailed (("Not implemented"));
4235 return E_NOTIMPL;
4236
4237/// @todo (r=dmik) later
4238// Use code from HVirtualDiskImage::createImage as an example.
4239}
4240
4241/* static */
4242DECLCALLBACK(int) HVMDKImage::VDITaskThread (RTTHREAD thread, void *pvUser)
4243{
4244 AssertMsgFailed (("Not implemented"));
4245 return VERR_GENERAL_FAILURE;
4246
4247/// @todo (r=dmik) later
4248// Use code from HVirtualDiskImage::VDITaskThread as an example.
4249}
4250
4251/* static */
4252DECLCALLBACK(void) HVMDKImage::VDError (void *pvUser, int rc, RT_SRC_POS_DECL,
4253 const char *pszFormat, va_list va)
4254{
4255 HVMDKImage *that = static_cast <HVMDKImage *> (pvUser);
4256 AssertReturnVoid (that != NULL);
4257
4258 /// @todo pass the error message to the operation initiator
4259 Utf8Str err = Utf8StrFmtVA (pszFormat, va);
4260 if (VBOX_FAILURE (rc))
4261 err = Utf8StrFmt ("%s (%Vrc)", err.raw(), rc);
4262
4263 if (that->mLastVDError.isNull())
4264 that->mLastVDError = err;
4265 else
4266 that->mLastVDError = Utf8StrFmt
4267 ("%s.\n%s", that->mLastVDError.raw(), err.raw());
4268}
4269
4270////////////////////////////////////////////////////////////////////////////////
4271// HCustomHardDisk class
4272////////////////////////////////////////////////////////////////////////////////
4273
4274// constructor / destructor
4275////////////////////////////////////////////////////////////////////////////////
4276
4277HRESULT HCustomHardDisk::FinalConstruct()
4278{
4279 HRESULT rc = HardDisk::FinalConstruct();
4280 if (FAILED (rc))
4281 return rc;
4282
4283 mState = NotCreated;
4284
4285 mStateCheckSem = NIL_RTSEMEVENTMULTI;
4286 mStateCheckWaiters = 0;
4287
4288 mSize = 0;
4289 mActualSize = 0;
4290 mContainer = NULL;
4291
4292 ComAssertRCRet (rc, E_FAIL);
4293
4294 /* Create supported error interface. */
4295 mVDInterfaces = NULL;
4296 mInterfaceErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
4297 mInterfaceErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
4298 mInterfaceErrorCallbacks.pfnError = VDError;
4299 int vrc = VDInterfaceAdd(&mInterfaceError, "Custom_IError", VDINTERFACETYPE_ERROR,
4300 &mInterfaceErrorCallbacks, this, &mVDInterfaces);
4301 ComAssertRCRet (vrc, E_FAIL);
4302
4303 return S_OK;
4304}
4305
4306void HCustomHardDisk::FinalRelease()
4307{
4308 if (mContainer != NULL)
4309 VDDestroy (mContainer);
4310
4311 HardDisk::FinalRelease();
4312}
4313
4314// public initializer/uninitializer for internal purposes only
4315////////////////////////////////////////////////////////////////////////////////
4316
4317// public methods for internal purposes only
4318/////////////////////////////////////////////////////////////////////////////
4319
4320/**
4321 * Initializes the custom hard disk object by reading its properties from
4322 * the given configuration node. The created hard disk will be marked as
4323 * registered on success.
4324 *
4325 * @param aHDNode <HardDisk> node.
4326 * @param aCustomNode <VirtualDiskImage> node.
4327 */
4328HRESULT HCustomHardDisk::init (VirtualBox *aVirtualBox, HardDisk *aParent,
4329 const settings::Key &aHDNode,
4330 const settings::Key &aCustomNode)
4331{
4332 using namespace settings;
4333
4334 LogFlowThisFunc (("\n"));
4335
4336 AssertReturn (!aHDNode.isNull() && !aCustomNode.isNull(), E_FAIL);
4337
4338 AutoWriteLock alock (this);
4339 ComAssertRet (!isReady(), E_UNEXPECTED);
4340
4341 mStorageType = HardDiskStorageType_CustomHardDisk;
4342
4343 HRESULT rc = S_OK;
4344 int vrc = VINF_SUCCESS;
4345 do
4346 {
4347 rc = protectedInit (aVirtualBox, aParent);
4348 CheckComRCBreakRC (rc);
4349
4350 /* set ready to let protectedUninit() be called on failure */
4351 setReady (true);
4352
4353 /* location (required) */
4354 Bstr location = aCustomNode.stringValue ("location");
4355 rc = setLocation (location);
4356 CheckComRCBreakRC (rc);
4357
4358 LogFlowThisFunc (("'%ls'\n", mLocationFull.raw()));
4359
4360 /* format (required) */
4361 mFormat = aCustomNode.stringValue ("format");
4362
4363 /* initialize the container */
4364 vrc = VDCreate (&mInterfaceError, &mContainer);
4365 if (VBOX_FAILURE (vrc))
4366 {
4367 AssertRC (vrc);
4368 if (mLastVDError.isEmpty())
4369 rc = setError (E_FAIL,
4370 tr ("Unknown format '%ls' of the custom "
4371 "hard disk '%ls' (%Vrc)"),
4372 mFormat.raw(), toString().raw(), vrc);
4373 else
4374 rc = setErrorBstr (E_FAIL, mLastVDError);
4375 break;
4376 }
4377
4378 /* load basic settings and children */
4379 rc = loadSettings (aHDNode);
4380 CheckComRCBreakRC (rc);
4381
4382 if (mType != HardDiskType_Writethrough)
4383 {
4384 rc = setError (E_FAIL,
4385 tr ("Currently, non-Writethrough custom hard disks "
4386 "are not allowed ('%ls')"),
4387 toString().raw());
4388 break;
4389 }
4390
4391 mState = Created;
4392 mRegistered = TRUE;
4393
4394 /* Don't call queryInformation() for registered hard disks to
4395 * prevent the calling thread (i.e. the VirtualBox server startup
4396 * thread) from an unexpected freeze. The vital mId property (UUID)
4397 * is read from the registry file in loadSettings(). To get the rest,
4398 * the user will have to call COMGETTER(Accessible) manually. */
4399 }
4400 while (0);
4401
4402 if (FAILED (rc))
4403 uninit();
4404
4405 return rc;
4406}
4407
4408/**
4409 * Initializes the custom hard disk object using the given image file name.
4410 *
4411 * @param aVirtualBox VirtualBox parent.
4412 * @param aParent Currently, must always be @c NULL.
4413 * @param aLocation Location of the virtual disk, or @c NULL to create an
4414 * image-less object.
4415 * @param aRegistered Whether to mark this disk as registered or not
4416 * (ignored when @a aLocation is @c NULL, assuming @c FALSE)
4417 */
4418HRESULT HCustomHardDisk::init (VirtualBox *aVirtualBox, HardDisk *aParent,
4419 const BSTR aLocation, BOOL aRegistered /* = FALSE */)
4420{
4421 LogFlowThisFunc (("aLocation='%ls', aRegistered=%d\n", aLocation, aRegistered));
4422
4423 AssertReturn (aParent == NULL, E_FAIL);
4424
4425 AutoWriteLock alock (this);
4426 ComAssertRet (!isReady(), E_UNEXPECTED);
4427
4428 mStorageType = HardDiskStorageType_CustomHardDisk;
4429
4430 HRESULT rc = S_OK;
4431
4432 do
4433 {
4434 rc = protectedInit (aVirtualBox, aParent);
4435 CheckComRCBreakRC (rc);
4436
4437 /* set ready to let protectedUninit() be called on failure */
4438 setReady (true);
4439
4440 rc = setLocation (aLocation);
4441 CheckComRCBreakRC (rc);
4442
4443 /* currently, all custom hard disks are writethrough */
4444 mType = HardDiskType_Writethrough;
4445
4446 Assert (mId.isEmpty());
4447
4448 if (aLocation && *aLocation)
4449 {
4450 mRegistered = aRegistered;
4451 mState = Created;
4452
4453 char *pszFormat = NULL;
4454
4455 int vrc = VDGetFormat (Utf8Str (mLocation), &pszFormat);
4456 if (VBOX_FAILURE(vrc))
4457 {
4458 rc = setError (E_FAIL,
4459 tr ("Cannot recognize the format of the custom "
4460 "hard disk '%ls' (%Vrc)"),
4461 toString().raw(), vrc);
4462 break;
4463 }
4464 mFormat = Bstr (pszFormat);
4465 RTStrFree (pszFormat);
4466
4467 /* initialize the container */
4468 vrc = VDCreate (&mInterfaceError, &mContainer);
4469
4470 /* the format has been already checked for presence at this point */
4471 ComAssertRCBreak (vrc, rc = E_FAIL);
4472
4473 /* Call queryInformation() anyway (even if it will block), because
4474 * it is the only way to get the UUID of the existing VDI and
4475 * initialize the vital mId property. */
4476 Bstr errMsg;
4477 rc = queryInformation (&errMsg);
4478 if (SUCCEEDED (rc))
4479 {
4480 /* We are constructing a new HVirtualDiskImage object. If there
4481 * is a fatal accessibility error (we cannot read image UUID),
4482 * we have to fail. We do so even on non-fatal errors as well,
4483 * because it's not worth to keep going with the inaccessible
4484 * image from the very beginning (when nothing else depends on
4485 * it yet). */
4486 if (!errMsg.isNull())
4487 rc = setErrorBstr (E_FAIL, errMsg);
4488 }
4489 }
4490 else
4491 {
4492 mRegistered = FALSE;
4493 mState = NotCreated;
4494 mId.create();
4495 }
4496 }
4497 while (0);
4498
4499 if (FAILED (rc))
4500 uninit();
4501
4502 return rc;
4503}
4504
4505/**
4506 * Uninitializes the instance and sets the ready flag to FALSE.
4507 * Called either from FinalRelease(), by the parent when it gets destroyed,
4508 * or by a third party when it decides this object is no more valid.
4509 */
4510void HCustomHardDisk::uninit()
4511{
4512 LogFlowThisFunc (("\n"));
4513
4514 AutoWriteLock alock (this);
4515 if (!isReady())
4516 return;
4517
4518 HardDisk::protectedUninit (alock);
4519}
4520
4521// IHardDisk properties
4522////////////////////////////////////////////////////////////////////////////////
4523
4524STDMETHODIMP HCustomHardDisk::COMGETTER(Description) (BSTR *aDescription)
4525{
4526 if (!aDescription)
4527 return E_POINTER;
4528
4529 AutoReadLock alock (this);
4530 CHECK_READY();
4531
4532 mDescription.cloneTo (aDescription);
4533 return S_OK;
4534}
4535
4536STDMETHODIMP HCustomHardDisk::COMSETTER(Description) (INPTR BSTR aDescription)
4537{
4538 AutoWriteLock alock (this);
4539 CHECK_READY();
4540
4541 CHECK_BUSY_AND_READERS();
4542
4543 return E_NOTIMPL;
4544}
4545
4546STDMETHODIMP HCustomHardDisk::COMGETTER(Size) (ULONG64 *aSize)
4547{
4548 if (!aSize)
4549 return E_POINTER;
4550
4551 AutoReadLock alock (this);
4552 CHECK_READY();
4553
4554 *aSize = mSize;
4555 return S_OK;
4556}
4557
4558STDMETHODIMP HCustomHardDisk::COMGETTER(ActualSize) (ULONG64 *aActualSize)
4559{
4560 if (!aActualSize)
4561 return E_POINTER;
4562
4563 AutoReadLock alock (this);
4564 CHECK_READY();
4565
4566 *aActualSize = mActualSize;
4567 return S_OK;
4568}
4569
4570// ICustomHardDisk properties
4571////////////////////////////////////////////////////////////////////////////////
4572
4573STDMETHODIMP HCustomHardDisk::COMGETTER(Location) (BSTR *aLocation)
4574{
4575 if (!aLocation)
4576 return E_POINTER;
4577
4578 AutoReadLock alock (this);
4579 CHECK_READY();
4580
4581 mLocationFull.cloneTo (aLocation);
4582 return S_OK;
4583}
4584
4585STDMETHODIMP HCustomHardDisk::COMSETTER(Location) (INPTR BSTR aLocation)
4586{
4587 AutoWriteLock alock (this);
4588 CHECK_READY();
4589
4590 if (mState != NotCreated)
4591 return setError (E_ACCESSDENIED,
4592 tr ("Cannot change the file path of the existing hard disk '%ls'"),
4593 toString().raw());
4594
4595 CHECK_BUSY_AND_READERS();
4596
4597 /// @todo currently, we assume that location is always a file path for
4598 /// all custom hard disks. This is not generally correct, and needs to be
4599 /// parametrized in the VD plugin interface.
4600
4601 /* append the default path if only a name is given */
4602 Bstr path = aLocation;
4603 if (aLocation && *aLocation)
4604 {
4605 Utf8Str fp = aLocation;
4606 if (!RTPathHavePath (fp))
4607 {
4608 AutoReadLock propsLock (mVirtualBox->systemProperties());
4609 path = Utf8StrFmt ("%ls%c%s",
4610 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
4611 RTPATH_DELIMITER,
4612 fp.raw());
4613 }
4614 }
4615
4616 return setLocation (path);
4617}
4618
4619STDMETHODIMP HCustomHardDisk::COMGETTER(Created) (BOOL *aCreated)
4620{
4621 if (!aCreated)
4622 return E_POINTER;
4623
4624 AutoReadLock alock (this);
4625 CHECK_READY();
4626
4627 *aCreated = mState >= Created;
4628 return S_OK;
4629}
4630
4631STDMETHODIMP HCustomHardDisk::COMGETTER(Format) (BSTR *aFormat)
4632{
4633 if (!aFormat)
4634 return E_POINTER;
4635
4636 AutoReadLock alock (this);
4637 CHECK_READY();
4638
4639 mFormat.cloneTo (aFormat);
4640 return S_OK;
4641}
4642
4643// ICustomHardDisk methods
4644/////////////////////////////////////////////////////////////////////////////
4645
4646STDMETHODIMP HCustomHardDisk::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
4647{
4648 if (!aProgress)
4649 return E_POINTER;
4650
4651 AutoWriteLock alock (this);
4652 CHECK_READY();
4653
4654 return createImage (aSize, TRUE /* aDynamic */, aProgress);
4655}
4656
4657STDMETHODIMP HCustomHardDisk::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
4658{
4659 if (!aProgress)
4660 return E_POINTER;
4661
4662 AutoWriteLock alock (this);
4663 CHECK_READY();
4664
4665 return createImage (aSize, FALSE /* aDynamic */, aProgress);
4666}
4667
4668STDMETHODIMP HCustomHardDisk::DeleteImage()
4669{
4670 AutoWriteLock alock (this);
4671 CHECK_READY();
4672 CHECK_BUSY_AND_READERS();
4673
4674 return E_NOTIMPL;
4675
4676/// @todo later
4677}
4678
4679// public/protected methods for internal purposes only
4680/////////////////////////////////////////////////////////////////////////////
4681
4682/**
4683 * Attempts to mark the hard disk as registered.
4684 * Only VirtualBox can call this method.
4685 */
4686HRESULT HCustomHardDisk::trySetRegistered (BOOL aRegistered)
4687{
4688 AutoWriteLock alock (this);
4689 CHECK_READY();
4690
4691 if (aRegistered)
4692 {
4693 if (mState == NotCreated)
4694 return setError (E_FAIL,
4695 tr ("Storage location '%ls' is not yet created for this hard disk"),
4696 mLocationFull.raw());
4697 }
4698 else
4699 {
4700 ComAssertRet (mState >= Created, E_FAIL);
4701 }
4702
4703 return HardDisk::trySetRegistered (aRegistered);
4704}
4705
4706/**
4707 * Checks accessibility of this hard disk image only (w/o parents).
4708 *
4709 * @param aAccessError on output, a null string indicates the hard disk is
4710 * accessible, otherwise contains a message describing
4711 * the reason of inaccessibility.
4712 *
4713 * @note Locks this object for writing.
4714 */
4715HRESULT HCustomHardDisk::getAccessible (Bstr &aAccessError)
4716{
4717 /* queryInformation() needs a write lock */
4718 AutoWriteLock alock (this);
4719 CHECK_READY();
4720
4721 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
4722 {
4723 /* An accessibility check in progress on some other thread,
4724 * wait for it to finish. */
4725
4726 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL);
4727 ++ mStateCheckWaiters;
4728 alock.leave();
4729
4730 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
4731
4732 alock.enter();
4733 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
4734 -- mStateCheckWaiters;
4735 if (mStateCheckWaiters == 0)
4736 {
4737 RTSemEventMultiDestroy (mStateCheckSem);
4738 mStateCheckSem = NIL_RTSEMEVENTMULTI;
4739 }
4740
4741 AssertRCReturn (vrc, E_FAIL);
4742
4743 /* don't touch aAccessError, it has been already set */
4744 return S_OK;
4745 }
4746
4747 /* check the basic accessibility */
4748 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */);
4749 if (FAILED (rc) || !aAccessError.isNull())
4750 return rc;
4751
4752 if (mState >= Created)
4753 {
4754 return queryInformation (&aAccessError);
4755 }
4756
4757 aAccessError = Utf8StrFmt ("Hard disk '%ls' is not yet created",
4758 mLocationFull.raw());
4759 return S_OK;
4760}
4761
4762/**
4763 * Saves hard disk settings to the specified storage node and saves
4764 * all children to the specified hard disk node
4765 *
4766 * @param aHDNode <HardDisk> or <DiffHardDisk> node.
4767 * @param aStorageNode <VirtualDiskImage> node.
4768 */
4769HRESULT HCustomHardDisk::saveSettings (settings::Key &aHDNode,
4770 settings::Key &aStorageNode)
4771{
4772 AssertReturn (!aHDNode.isNull() && !aStorageNode.isNull(), E_FAIL);
4773
4774 AutoReadLock alock (this);
4775 CHECK_READY();
4776
4777 /* location (required) */
4778 aStorageNode.setValue <Bstr> ("location", mLocationFull);
4779
4780 /* format (required) */
4781 aStorageNode.setValue <Bstr> ("format", mFormat);
4782
4783 /* save basic settings and children */
4784 return HardDisk::saveSettings (aHDNode);
4785}
4786
4787/**
4788 * Returns the string representation of this hard disk.
4789 * When \a aShort is false, returns the full image file path.
4790 * Otherwise, returns the image file name only.
4791 *
4792 * @param aShort if true, a short representation is returned
4793 *
4794 * @note Locks this object for reading.
4795 */
4796Bstr HCustomHardDisk::toString (bool aShort /* = false */)
4797{
4798 AutoReadLock alock (this);
4799
4800 /// @todo currently, we assume that location is always a file path for
4801 /// all custom hard disks. This is not generally correct, and needs to be
4802 /// parametrized in the VD plugin interface.
4803
4804 if (!aShort)
4805 return mLocationFull;
4806 else
4807 {
4808 Utf8Str fname = mLocationFull;
4809 return RTPathFilename (fname.mutableRaw());
4810 }
4811}
4812
4813/**
4814 * Creates a clone of this hard disk by storing hard disk data in the given
4815 * VDI file.
4816 *
4817 * If the operation fails, @a aDeleteTarget will be set to @c true unless the
4818 * failure happened because the target file already existed.
4819 *
4820 * @param aId UUID to assign to the created image.
4821 * @param aTargetPath VDI file where the cloned image is to be to stored.
4822 * @param aProgress progress object to run during operation.
4823 * @param aDeleteTarget Whether it is recommended to delete target on
4824 * failure or not.
4825 */
4826HRESULT
4827HCustomHardDisk::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
4828 Progress *aProgress, bool &aDeleteTarget)
4829{
4830 ComAssertMsgFailed (("Not implemented"));
4831 return E_NOTIMPL;
4832}
4833
4834/**
4835 * Creates a new differencing image for this hard disk with the given
4836 * VDI file name.
4837 *
4838 * @param aId UUID to assign to the created image
4839 * @param aTargetPath VDI file where to store the created differencing image
4840 * @param aProgress progress object to run during operation
4841 * (can be NULL)
4842 */
4843HRESULT
4844HCustomHardDisk::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
4845 Progress *aProgress)
4846{
4847 ComAssertMsgFailed (("Not implemented"));
4848 return E_NOTIMPL;
4849}
4850
4851// private methods
4852/////////////////////////////////////////////////////////////////////////////
4853
4854/**
4855 * Helper to set a new location.
4856 *
4857 * @note
4858 * Must be called from under the object's lock!
4859 */
4860HRESULT HCustomHardDisk::setLocation (const BSTR aLocation)
4861{
4862 /// @todo currently, we assume that location is always a file path for
4863 /// all custom hard disks. This is not generally correct, and needs to be
4864 /// parametrized in the VD plugin interface.
4865
4866 if (aLocation && *aLocation)
4867 {
4868 /* get the full file name */
4869 char locationFull [RTPATH_MAX];
4870 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aLocation),
4871 locationFull, sizeof (locationFull));
4872 if (VBOX_FAILURE (vrc))
4873 return setError (E_FAIL,
4874 tr ("Invalid hard disk location '%ls' (%Vrc)"), aLocation, vrc);
4875
4876 mLocation = aLocation;
4877 mLocationFull = locationFull;
4878 }
4879 else
4880 {
4881 mLocation.setNull();
4882 mLocationFull.setNull();
4883 }
4884
4885 return S_OK;
4886}
4887
4888/**
4889 * Helper to query information about the custom hard disk.
4890 *
4891 * @param aAccessError not used when NULL, otherwise see #getAccessible()
4892 *
4893 * @note Must be called from under the object's lock, only after
4894 * CHECK_BUSY_AND_READERS() succeeds.
4895 */
4896HRESULT HCustomHardDisk::queryInformation (Bstr *aAccessError)
4897{
4898 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
4899
4900 /* create a lock object to completely release it later */
4901 AutoWriteLock alock (this);
4902
4903 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
4904
4905 ComAssertRet (mState >= Created, E_FAIL);
4906
4907 HRESULT rc = S_OK;
4908 int vrc = VINF_SUCCESS;
4909
4910 /* lazily create a semaphore */
4911 vrc = RTSemEventMultiCreate (&mStateCheckSem);
4912 ComAssertRCRet (vrc, E_FAIL);
4913
4914 /* Reference the disk to prevent any concurrent modifications
4915 * after releasing the lock below (to unblock getters before
4916 * a lengthy operation). */
4917 addReader();
4918
4919 alock.leave();
4920
4921 /* VBoxVHDD management interface needs to be optimized: we're opening a
4922 * file three times in a raw to get three bits of information. */
4923
4924 Utf8Str location = mLocationFull;
4925 Bstr errMsg;
4926
4927 /* reset any previous error report from VDError() */
4928 mLastVDError.setNull();
4929
4930 do
4931 {
4932 Guid id, parentId;
4933
4934 vrc = VDOpen (mContainer, Utf8Str (mFormat), location, VD_OPEN_FLAGS_INFO, NULL);
4935 if (VBOX_FAILURE (vrc))
4936 break;
4937
4938 vrc = VDGetUuid (mContainer, 0, id.ptr());
4939 if (VBOX_FAILURE (vrc) && vrc != VERR_NOT_SUPPORTED)
4940 break;
4941
4942 if (!mId.isEmpty())
4943 {
4944 /* check that the actual UUID of the image matches the stored UUID */
4945 if (VBOX_SUCCESS(vrc) && (mId != id))
4946 {
4947 errMsg = Utf8StrFmt (
4948 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
4949 "match UUID {%Vuuid} stored in the registry"),
4950 id.ptr(), location.raw(), mId.ptr());
4951 break;
4952 }
4953 }
4954 else
4955 {
4956 /* assign an UUID read from the image file */
4957 if (VBOX_SUCCESS(vrc))
4958 mId = id;
4959 else
4960 {
4961 /* Create a UUID on our own. */
4962 vrc = RTUuidCreate(mId.ptr());
4963 if (VBOX_FAILURE(vrc))
4964 break;
4965 }
4966 }
4967
4968
4969 vrc = VDGetParentUuid (mContainer, 0, parentId.ptr());
4970 if (VBOX_FAILURE (vrc))
4971 break;
4972
4973 if (mParent)
4974 {
4975 /* check parent UUID */
4976 AutoWriteLock parentLock (mParent);
4977 if (mParent->id() != parentId)
4978 {
4979 errMsg = Utf8StrFmt (
4980 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
4981 "the hard disk image file '%s' doesn't match "
4982 "UUID {%Vuuid} stored in the registry"),
4983 parentId.raw(), mParent->toString().raw(),
4984 location.raw(), mParent->id().raw());
4985 break;
4986 }
4987 }
4988 else if (!parentId.isEmpty())
4989 {
4990 errMsg = Utf8StrFmt (
4991 tr ("Hard disk image '%s' is a differencing image that is linked "
4992 "to a hard disk with UUID {%Vuuid} and cannot be used "
4993 "directly as a base hard disk"),
4994 location.raw(), parentId.raw());
4995 break;
4996 }
4997
4998 /* get actual file size */
4999 /// @todo is there a direct method in RT?
5000 {
5001 RTFILE file = NIL_RTFILE;
5002 vrc = RTFileOpen (&file, location, RTFILE_O_READ);
5003 if (VBOX_SUCCESS (vrc))
5004 {
5005 uint64_t size = 0;
5006 vrc = RTFileGetSize (file, &size);
5007 if (VBOX_SUCCESS (vrc))
5008 mActualSize = size;
5009 RTFileClose (file);
5010 }
5011 if (VBOX_FAILURE (vrc))
5012 break;
5013 }
5014
5015 /* query logical size only for non-differencing images */
5016 if (!mParent)
5017 {
5018 uint64_t size = VDGetSize (mContainer, 0);
5019 /* convert to MBytes */
5020 mSize = size / 1024 / 1024;
5021 }
5022 }
5023 while (0);
5024
5025 VDCloseAll (mContainer);
5026
5027 /* enter the lock again */
5028 alock.enter();
5029
5030 /* remove the reference */
5031 releaseReader();
5032
5033 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
5034 {
5035 LogWarningFunc (("'%ls' is not accessible "
5036 "(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
5037 mLocationFull.raw(), rc, vrc, errMsg.raw()));
5038
5039 if (aAccessError)
5040 {
5041 if (!errMsg.isNull())
5042 *aAccessError = errMsg;
5043 else if (!mLastVDError.isNull())
5044 *aAccessError = mLastVDError;
5045 else if (VBOX_FAILURE (vrc))
5046 *aAccessError = Utf8StrFmt (
5047 tr ("Could not access hard disk '%ls' (%Vrc)"),
5048 mLocationFull.raw(), vrc);
5049 }
5050
5051 /* downgrade to not accessible */
5052 mState = Created;
5053 }
5054 else
5055 {
5056 if (aAccessError)
5057 aAccessError->setNull();
5058
5059 mState = Accessible;
5060 }
5061
5062 /* inform waiters if there are any */
5063 if (mStateCheckWaiters > 0)
5064 {
5065 RTSemEventMultiSignal (mStateCheckSem);
5066 }
5067 else
5068 {
5069 /* delete the semaphore ourselves */
5070 RTSemEventMultiDestroy (mStateCheckSem);
5071 mStateCheckSem = NIL_RTSEMEVENTMULTI;
5072 }
5073
5074 /* cleanup the last error report from VDError() */
5075 mLastVDError.setNull();
5076
5077 return rc;
5078}
5079
5080/**
5081 * Helper to create hard disk images.
5082 *
5083 * @param aSize size in MB
5084 * @param aDynamic dynamic or fixed image
5085 * @param aProgress address of IProgress pointer to return
5086 */
5087HRESULT HCustomHardDisk::createImage (ULONG64 aSize, BOOL aDynamic,
5088 IProgress **aProgress)
5089{
5090 ComAssertMsgFailed (("Not implemented"));
5091 return E_NOTIMPL;
5092}
5093
5094/* static */
5095DECLCALLBACK(int) HCustomHardDisk::VDITaskThread (RTTHREAD thread, void *pvUser)
5096{
5097 AssertMsgFailed (("Not implemented"));
5098 return VERR_GENERAL_FAILURE;
5099}
5100
5101/* static */
5102DECLCALLBACK(void) HCustomHardDisk::VDError (void *pvUser, int rc, RT_SRC_POS_DECL,
5103 const char *pszFormat, va_list va)
5104{
5105 HCustomHardDisk *that = static_cast <HCustomHardDisk *> (pvUser);
5106 AssertReturnVoid (that != NULL);
5107
5108 /// @todo pass the error message to the operation initiator
5109 Utf8Str err = Utf8StrFmtVA (pszFormat, va);
5110 if (VBOX_FAILURE (rc))
5111 err = Utf8StrFmt ("%s (%Vrc)", err.raw(), rc);
5112
5113 if (that->mLastVDError.isNull())
5114 that->mLastVDError = err;
5115 else
5116 that->mLastVDError = Utf8StrFmt
5117 ("%s.\n%s", that->mLastVDError.raw(), err.raw());
5118}
5119
5120////////////////////////////////////////////////////////////////////////////////
5121// HVHDImage class
5122////////////////////////////////////////////////////////////////////////////////
5123
5124// constructor / destructor
5125////////////////////////////////////////////////////////////////////////////////
5126
5127HRESULT HVHDImage::FinalConstruct()
5128{
5129 HRESULT rc = HardDisk::FinalConstruct();
5130 if (FAILED (rc))
5131 return rc;
5132
5133 mState = NotCreated;
5134
5135 mStateCheckSem = NIL_RTSEMEVENTMULTI;
5136 mStateCheckWaiters = 0;
5137
5138 mSize = 0;
5139 mActualSize = 0;
5140
5141 /* Create supported error interface. */
5142 mVDInterfaces = NULL;
5143 mInterfaceErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
5144 mInterfaceErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
5145 mInterfaceErrorCallbacks.pfnError = VDError;
5146 int vrc = VDInterfaceAdd(&mInterfaceError, "VHD_IError", VDINTERFACETYPE_ERROR,
5147 &mInterfaceErrorCallbacks, this, &mVDInterfaces);
5148 ComAssertRCRet (vrc, E_FAIL);
5149
5150 /* initialize the container */
5151 vrc = VDCreate (&mInterfaceError, &mContainer);
5152 ComAssertRCRet (vrc, E_FAIL);
5153
5154 return S_OK;
5155}
5156
5157void HVHDImage::FinalRelease()
5158{
5159 if (mContainer != NULL)
5160 VDDestroy (mContainer);
5161
5162 HardDisk::FinalRelease();
5163}
5164
5165// public initializer/uninitializer for internal purposes only
5166////////////////////////////////////////////////////////////////////////////////
5167
5168// public methods for internal purposes only
5169/////////////////////////////////////////////////////////////////////////////
5170
5171/**
5172 * Initializes the VHD hard disk object by reading its properties from
5173 * the given configuration node. The created hard disk will be marked as
5174 * registered on success.
5175 *
5176 * @param aHDNode <HardDisk> node
5177 * @param aVHDNode <VirtualDiskImage> node
5178 */
5179HRESULT HVHDImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
5180 const settings::Key &aHDNode,
5181 const settings::Key &aVHDNode)
5182{
5183 LogFlowThisFunc (("\n"));
5184
5185 AssertReturn (!aHDNode.isNull() && !aVHDNode.isNull(), E_FAIL);
5186
5187 AutoWriteLock alock (this);
5188 ComAssertRet (!isReady(), E_UNEXPECTED);
5189
5190 mStorageType = HardDiskStorageType_VHDImage;
5191
5192 HRESULT rc = S_OK;
5193
5194 do
5195 {
5196 rc = protectedInit (aVirtualBox, aParent);
5197 CheckComRCBreakRC (rc);
5198
5199 /* set ready to let protectedUninit() be called on failure */
5200 setReady (true);
5201
5202 /* filePath (required) */
5203 Bstr filePath = aVHDNode.stringValue("filePath");
5204
5205 rc = setFilePath (filePath);
5206 CheckComRCBreakRC (rc);
5207
5208 LogFlowThisFunc (("'%ls'\n", mFilePathFull.raw()));
5209
5210 /* load basic settings and children */
5211 rc = loadSettings (aHDNode);
5212 CheckComRCBreakRC (rc);
5213
5214 if (mType != HardDiskType_Writethrough)
5215 {
5216 rc = setError (E_FAIL,
5217 tr ("Currently, non-Writethrough VHD images are not "
5218 "allowed ('%ls')"),
5219 toString().raw());
5220 break;
5221 }
5222
5223 mState = Created;
5224 mRegistered = TRUE;
5225
5226 /* Don't call queryInformation() for registered hard disks to
5227 * prevent the calling thread (i.e. the VirtualBox server startup
5228 * thread) from an unexpected freeze. The vital mId property (UUID)
5229 * is read from the registry file in loadSettings(). To get the rest,
5230 * the user will have to call COMGETTER(Accessible) manually. */
5231 }
5232 while (0);
5233
5234 if (FAILED (rc))
5235 uninit();
5236
5237 return rc;
5238}
5239
5240/**
5241 * Initializes the VHD hard disk object using the given image file name.
5242 *
5243 * @param aVirtualBox VirtualBox parent.
5244 * @param aParent Currently, must always be @c NULL.
5245 * @param aFilePath Path to the image file, or @c NULL to create an
5246 * image-less object.
5247 * @param aRegistered Whether to mark this disk as registered or not
5248 * (ignored when @a aFilePath is @c NULL, assuming @c FALSE)
5249 */
5250HRESULT HVHDImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
5251 const BSTR aFilePath, BOOL aRegistered /* = FALSE */)
5252{
5253 LogFlowThisFunc (("aFilePath='%ls', aRegistered=%d\n", aFilePath, aRegistered));
5254
5255 AssertReturn (aParent == NULL, E_FAIL);
5256
5257 AutoWriteLock alock (this);
5258 ComAssertRet (!isReady(), E_UNEXPECTED);
5259
5260 mStorageType = HardDiskStorageType_VHDImage;
5261
5262 HRESULT rc = S_OK;
5263
5264 do
5265 {
5266 rc = protectedInit (aVirtualBox, aParent);
5267 CheckComRCBreakRC (rc);
5268
5269 /* set ready to let protectedUninit() be called on failure */
5270 setReady (true);
5271
5272 rc = setFilePath (aFilePath);
5273 CheckComRCBreakRC (rc);
5274
5275 /* currently, all VHD hard disks are writethrough */
5276 mType = HardDiskType_Writethrough;
5277
5278 Assert (mId.isEmpty());
5279
5280 if (aFilePath && *aFilePath)
5281 {
5282 mRegistered = aRegistered;
5283 mState = Created;
5284
5285 /* Call queryInformation() anyway (even if it will block), because
5286 * it is the only way to get the UUID of the existing VDI and
5287 * initialize the vital mId property. */
5288 Bstr errMsg;
5289 rc = queryInformation (&errMsg);
5290 if (SUCCEEDED (rc))
5291 {
5292 /* We are constructing a new HVirtualDiskImage object. If there
5293 * is a fatal accessibility error (we cannot read image UUID),
5294 * we have to fail. We do so even on non-fatal errors as well,
5295 * because it's not worth to keep going with the inaccessible
5296 * image from the very beginning (when nothing else depends on
5297 * it yet). */
5298 if (!errMsg.isNull())
5299 rc = setErrorBstr (E_FAIL, errMsg);
5300 }
5301 }
5302 else
5303 {
5304 mRegistered = FALSE;
5305 mState = NotCreated;
5306 mId.create();
5307 }
5308 }
5309 while (0);
5310
5311 if (FAILED (rc))
5312 uninit();
5313
5314 return rc;
5315}
5316
5317/**
5318 * Uninitializes the instance and sets the ready flag to FALSE.
5319 * Called either from FinalRelease(), by the parent when it gets destroyed,
5320 * or by a third party when it decides this object is no more valid.
5321 */
5322void HVHDImage::uninit()
5323{
5324 LogFlowThisFunc (("\n"));
5325
5326 AutoWriteLock alock (this);
5327 if (!isReady())
5328 return;
5329
5330 HardDisk::protectedUninit (alock);
5331}
5332
5333// IHardDisk properties
5334////////////////////////////////////////////////////////////////////////////////
5335
5336STDMETHODIMP HVHDImage::COMGETTER(Description) (BSTR *aDescription)
5337{
5338 if (!aDescription)
5339 return E_POINTER;
5340
5341 AutoReadLock alock (this);
5342 CHECK_READY();
5343
5344 mDescription.cloneTo (aDescription);
5345 return S_OK;
5346}
5347
5348STDMETHODIMP HVHDImage::COMSETTER(Description) (INPTR BSTR aDescription)
5349{
5350 AutoWriteLock alock (this);
5351 CHECK_READY();
5352
5353 CHECK_BUSY_AND_READERS();
5354
5355 return E_NOTIMPL;
5356
5357/// @todo implement
5358//
5359// if (mState >= Created)
5360// {
5361// int vrc = VDISetImageComment (Utf8Str (mFilePathFull), Utf8Str (aDescription));
5362// if (VBOX_FAILURE (vrc))
5363// return setError (E_FAIL,
5364// tr ("Could not change the description of the VDI hard disk '%ls' "
5365// "(%Vrc)"),
5366// toString().raw(), vrc);
5367// }
5368//
5369// mDescription = aDescription;
5370// return S_OK;
5371}
5372
5373STDMETHODIMP HVHDImage::COMGETTER(Size) (ULONG64 *aSize)
5374{
5375 if (!aSize)
5376 return E_POINTER;
5377
5378 AutoReadLock alock (this);
5379 CHECK_READY();
5380
5381/// @todo will need this if we add suppord for differencing VMDKs
5382//
5383// /* only a non-differencing image knows the logical size */
5384// if (isDifferencing())
5385// return root()->COMGETTER(Size) (aSize);
5386
5387 *aSize = mSize;
5388 return S_OK;
5389}
5390
5391STDMETHODIMP HVHDImage::COMGETTER(ActualSize) (ULONG64 *aActualSize)
5392{
5393 if (!aActualSize)
5394 return E_POINTER;
5395
5396 AutoReadLock alock (this);
5397 CHECK_READY();
5398
5399 *aActualSize = mActualSize;
5400 return S_OK;
5401}
5402
5403// IVirtualDiskImage properties
5404////////////////////////////////////////////////////////////////////////////////
5405
5406STDMETHODIMP HVHDImage::COMGETTER(FilePath) (BSTR *aFilePath)
5407{
5408 if (!aFilePath)
5409 return E_POINTER;
5410
5411 AutoReadLock alock (this);
5412 CHECK_READY();
5413
5414 mFilePathFull.cloneTo (aFilePath);
5415 return S_OK;
5416}
5417
5418STDMETHODIMP HVHDImage::COMSETTER(FilePath) (INPTR BSTR aFilePath)
5419{
5420 AutoWriteLock alock (this);
5421 CHECK_READY();
5422
5423 if (mState != NotCreated)
5424 return setError (E_ACCESSDENIED,
5425 tr ("Cannot change the file path of the existing hard disk '%ls'"),
5426 toString().raw());
5427
5428 CHECK_BUSY_AND_READERS();
5429
5430 /* append the default path if only a name is given */
5431 Bstr path = aFilePath;
5432 if (aFilePath && *aFilePath)
5433 {
5434 Utf8Str fp = aFilePath;
5435 if (!RTPathHavePath (fp))
5436 {
5437 AutoReadLock propsLock (mVirtualBox->systemProperties());
5438 path = Utf8StrFmt ("%ls%c%s",
5439 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
5440 RTPATH_DELIMITER,
5441 fp.raw());
5442 }
5443 }
5444
5445 return setFilePath (path);
5446}
5447
5448STDMETHODIMP HVHDImage::COMGETTER(Created) (BOOL *aCreated)
5449{
5450 if (!aCreated)
5451 return E_POINTER;
5452
5453 AutoReadLock alock (this);
5454 CHECK_READY();
5455
5456 *aCreated = mState >= Created;
5457 return S_OK;
5458}
5459
5460// IVHDImage methods
5461/////////////////////////////////////////////////////////////////////////////
5462
5463STDMETHODIMP HVHDImage::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
5464{
5465 if (!aProgress)
5466 return E_POINTER;
5467
5468 AutoWriteLock alock (this);
5469 CHECK_READY();
5470
5471 return createImage (aSize, TRUE /* aDynamic */, aProgress);
5472}
5473
5474STDMETHODIMP HVHDImage::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
5475{
5476 if (!aProgress)
5477 return E_POINTER;
5478
5479 AutoWriteLock alock (this);
5480 CHECK_READY();
5481
5482 return createImage (aSize, FALSE /* aDynamic */, aProgress);
5483}
5484
5485STDMETHODIMP HVHDImage::DeleteImage()
5486{
5487 AutoWriteLock alock (this);
5488 CHECK_READY();
5489 CHECK_BUSY_AND_READERS();
5490
5491 return E_NOTIMPL;
5492
5493/// @todo later
5494// We will need to parse the file in order to delete all related delta and
5495// sparse images etc. We may also want to obey the .vmdk.lck file
5496// which is (as far as I understood) created when the VMware VM is
5497// running or saved etc.
5498//
5499// if (mRegistered)
5500// return setError (E_ACCESSDENIED,
5501// tr ("Cannot delete an image of the registered hard disk image '%ls"),
5502// mFilePathFull.raw());
5503// if (mState == NotCreated)
5504// return setError (E_FAIL,
5505// tr ("Hard disk image has been already deleted or never created"));
5506//
5507// int vrc = RTFileDelete (Utf8Str (mFilePathFull));
5508// if (VBOX_FAILURE (vrc))
5509// return setError (E_FAIL, tr ("Could not delete the image file '%ls' (%Vrc)"),
5510// mFilePathFull.raw(), vrc);
5511//
5512// mState = NotCreated;
5513//
5514// /* reset the fields */
5515// mSize = 0;
5516// mActualSize = 0;
5517//
5518// return S_OK;
5519}
5520
5521// public/protected methods for internal purposes only
5522/////////////////////////////////////////////////////////////////////////////
5523
5524/**
5525 * Attempts to mark the hard disk as registered.
5526 * Only VirtualBox can call this method.
5527 */
5528HRESULT HVHDImage::trySetRegistered (BOOL aRegistered)
5529{
5530 AutoWriteLock alock (this);
5531 CHECK_READY();
5532
5533 if (aRegistered)
5534 {
5535 if (mState == NotCreated)
5536 return setError (E_FAIL,
5537 tr ("Image file '%ls' is not yet created for this hard disk"),
5538 mFilePathFull.raw());
5539
5540/// @todo will need this if we add suppord for differencing VHDs
5541// if (isDifferencing())
5542// return setError (E_FAIL,
5543// tr ("Hard disk '%ls' is differencing and cannot be unregistered "
5544// "explicitly"),
5545// mFilePathFull.raw());
5546 }
5547 else
5548 {
5549 ComAssertRet (mState >= Created, E_FAIL);
5550 }
5551
5552 return HardDisk::trySetRegistered (aRegistered);
5553}
5554
5555/**
5556 * Checks accessibility of this hard disk image only (w/o parents).
5557 *
5558 * @param aAccessError on output, a null string indicates the hard disk is
5559 * accessible, otherwise contains a message describing
5560 * the reason of inaccessibility.
5561 *
5562 * @note Locks this object for writing.
5563 */
5564HRESULT HVHDImage::getAccessible (Bstr &aAccessError)
5565{
5566 /* queryInformation() needs a write lock */
5567 AutoWriteLock alock (this);
5568 CHECK_READY();
5569
5570 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
5571 {
5572 /* An accessibility check in progress on some other thread,
5573 * wait for it to finish. */
5574
5575 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL);
5576 ++ mStateCheckWaiters;
5577 alock.leave();
5578
5579 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
5580
5581 alock.enter();
5582 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
5583 -- mStateCheckWaiters;
5584 if (mStateCheckWaiters == 0)
5585 {
5586 RTSemEventMultiDestroy (mStateCheckSem);
5587 mStateCheckSem = NIL_RTSEMEVENTMULTI;
5588 }
5589
5590 AssertRCReturn (vrc, E_FAIL);
5591
5592 /* don't touch aAccessError, it has been already set */
5593 return S_OK;
5594 }
5595
5596 /* check the basic accessibility */
5597 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */);
5598 if (FAILED (rc) || !aAccessError.isNull())
5599 return rc;
5600
5601 if (mState >= Created)
5602 {
5603 return queryInformation (&aAccessError);
5604 }
5605
5606 aAccessError = Utf8StrFmt ("Hard disk image '%ls' is not yet created",
5607 mFilePathFull.raw());
5608 return S_OK;
5609}
5610
5611/**
5612 * Saves hard disk settings to the specified storage node and saves
5613 * all children to the specified hard disk node
5614 *
5615 * @param aHDNode <HardDisk> or <DiffHardDisk> node
5616 * @param aStorageNode <VirtualDiskImage> node
5617 */
5618HRESULT HVHDImage::saveSettings (settings::Key &aHDNode, settings::Key &aStorageNode)
5619{
5620 AssertReturn (!aHDNode.isNull() && !aStorageNode.isNull(), E_FAIL);
5621
5622 AutoReadLock alock (this);
5623 CHECK_READY();
5624
5625 /* filePath (required) */
5626 aStorageNode.setValue <Bstr> ("filePath", mFilePath);
5627
5628 /* save basic settings and children */
5629 return HardDisk::saveSettings (aHDNode);
5630}
5631
5632/**
5633 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
5634 * of this hard disk and updates it if necessary to reflect the new location.
5635 * Intended to be from HardDisk::updatePaths().
5636 *
5637 * @param aOldPath old path (full)
5638 * @param aNewPath new path (full)
5639 *
5640 * @note Locks this object for writing.
5641 */
5642void HVHDImage::updatePath (const char *aOldPath, const char *aNewPath)
5643{
5644 AssertReturnVoid (aOldPath);
5645 AssertReturnVoid (aNewPath);
5646
5647 AutoWriteLock alock (this);
5648 AssertReturnVoid (isReady());
5649
5650 size_t oldPathLen = strlen (aOldPath);
5651
5652 Utf8Str path = mFilePathFull;
5653 LogFlowThisFunc (("VHD.fullPath={%s}\n", path.raw()));
5654
5655 if (RTPathStartsWith (path, aOldPath))
5656 {
5657 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
5658 path.raw() + oldPathLen);
5659 path = newPath;
5660
5661 mVirtualBox->calculateRelativePath (path, path);
5662
5663 unconst (mFilePathFull) = newPath;
5664 unconst (mFilePath) = path;
5665
5666 LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
5667 newPath.raw(), path.raw()));
5668 }
5669}
5670
5671/**
5672 * Returns the string representation of this hard disk.
5673 * When \a aShort is false, returns the full image file path.
5674 * Otherwise, returns the image file name only.
5675 *
5676 * @param aShort if true, a short representation is returned
5677 *
5678 * @note Locks this object for reading.
5679 */
5680Bstr HVHDImage::toString (bool aShort /* = false */)
5681{
5682 AutoReadLock alock (this);
5683
5684 if (!aShort)
5685 return mFilePathFull;
5686 else
5687 {
5688 Utf8Str fname = mFilePathFull;
5689 return RTPathFilename (fname.mutableRaw());
5690 }
5691}
5692
5693/**
5694 * Creates a clone of this hard disk by storing hard disk data in the given
5695 * VDI file.
5696 *
5697 * If the operation fails, @a aDeleteTarget will be set to @c true unless the
5698 * failure happened because the target file already existed.
5699 *
5700 * @param aId UUID to assign to the created image.
5701 * @param aTargetPath VDI file where the cloned image is to be to stored.
5702 * @param aProgress progress object to run during operation.
5703 * @param aDeleteTarget Whether it is recommended to delete target on
5704 * failure or not.
5705 */
5706HRESULT
5707HVHDImage::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
5708 Progress *aProgress, bool &aDeleteTarget)
5709{
5710 ComAssertMsgFailed (("Not implemented"));
5711 return E_NOTIMPL;
5712
5713/// @todo will need this if we add suppord for differencing VHDs
5714// Use code from HVirtualDiskImage::cloneToImage as an example.
5715}
5716
5717/**
5718 * Creates a new differencing image for this hard disk with the given
5719 * VDI file name.
5720 *
5721 * @param aId UUID to assign to the created image
5722 * @param aTargetPath VDI file where to store the created differencing image
5723 * @param aProgress progress object to run during operation
5724 * (can be NULL)
5725 */
5726HRESULT
5727HVHDImage::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
5728 Progress *aProgress)
5729{
5730 ComAssertMsgFailed (("Not implemented"));
5731 return E_NOTIMPL;
5732
5733/// @todo will need this if we add suppord for differencing VHDs
5734// Use code from HVirtualDiskImage::createDiffImage as an example.
5735}
5736
5737// private methods
5738/////////////////////////////////////////////////////////////////////////////
5739
5740/**
5741 * Helper to set a new file path.
5742 * Resolves a path relatively to the Virtual Box home directory.
5743 *
5744 * @note
5745 * Must be called from under the object's lock!
5746 */
5747HRESULT HVHDImage::setFilePath (const BSTR aFilePath)
5748{
5749 if (aFilePath && *aFilePath)
5750 {
5751 /* get the full file name */
5752 char filePathFull [RTPATH_MAX];
5753 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aFilePath),
5754 filePathFull, sizeof (filePathFull));
5755 if (VBOX_FAILURE (vrc))
5756 return setError (E_FAIL,
5757 tr ("Invalid image file path '%ls' (%Vrc)"), aFilePath, vrc);
5758
5759 mFilePath = aFilePath;
5760 mFilePathFull = filePathFull;
5761 }
5762 else
5763 {
5764 mFilePath.setNull();
5765 mFilePathFull.setNull();
5766 }
5767
5768 return S_OK;
5769}
5770
5771/**
5772 * Helper to query information about the VDI hard disk.
5773 *
5774 * @param aAccessError not used when NULL, otherwise see #getAccessible()
5775 *
5776 * @note Must be called from under the object's lock, only after
5777 * CHECK_BUSY_AND_READERS() succeeds.
5778 */
5779HRESULT HVHDImage::queryInformation (Bstr *aAccessError)
5780{
5781 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
5782
5783 /* create a lock object to completely release it later */
5784 AutoWriteLock alock (this);
5785
5786 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
5787
5788 ComAssertRet (mState >= Created, E_FAIL);
5789
5790 HRESULT rc = S_OK;
5791 int vrc = VINF_SUCCESS;
5792
5793 /* lazily create a semaphore */
5794 vrc = RTSemEventMultiCreate (&mStateCheckSem);
5795 ComAssertRCRet (vrc, E_FAIL);
5796
5797 /* Reference the disk to prevent any concurrent modifications
5798 * after releasing the lock below (to unblock getters before
5799 * a lengthy operation). */
5800 addReader();
5801
5802 alock.leave();
5803
5804 /* VBoxVHDD management interface needs to be optimized: we're opening a
5805 * file three times in a raw to get three bits of information. */
5806
5807 Utf8Str filePath = mFilePathFull;
5808 Bstr errMsg;
5809
5810 /* reset any previous error report from VDError() */
5811 mLastVDError.setNull();
5812
5813 do
5814 {
5815 Guid id, parentId;
5816
5817 /// @todo changed from VD_OPEN_FLAGS_READONLY to VD_OPEN_FLAGS_NORMAL,
5818 /// because otherwise registering a VHD which so far has no UUID will
5819 /// yield a null UUID. It cannot be added to a VHD opened readonly,
5820 /// obviously. This of course changes locking behavior, but for now
5821 /// this is acceptable. A better solution needs to be found later.
5822 vrc = VDOpen (mContainer, "VHD", filePath, VD_OPEN_FLAGS_NORMAL, NULL);
5823 if (VBOX_FAILURE (vrc))
5824 break;
5825
5826 vrc = VDGetUuid (mContainer, 0, id.ptr());
5827 if (VBOX_FAILURE (vrc))
5828 break;
5829 vrc = VDGetParentUuid (mContainer, 0, parentId.ptr());
5830 if (VBOX_FAILURE (vrc))
5831 break;
5832
5833 if (!mId.isEmpty())
5834 {
5835 /* check that the actual UUID of the image matches the stored UUID */
5836 if (mId != id)
5837 {
5838 errMsg = Utf8StrFmt (
5839 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
5840 "match UUID {%Vuuid} stored in the registry"),
5841 id.ptr(), filePath.raw(), mId.ptr());
5842 break;
5843 }
5844 }
5845 else
5846 {
5847 /* assgn an UUID read from the image file */
5848 mId = id;
5849 }
5850
5851 if (mParent)
5852 {
5853 /* check parent UUID */
5854 AutoWriteLock parentLock (mParent);
5855 if (mParent->id() != parentId)
5856 {
5857 errMsg = Utf8StrFmt (
5858 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
5859 "the hard disk image file '%s' doesn't match "
5860 "UUID {%Vuuid} stored in the registry"),
5861 parentId.raw(), mParent->toString().raw(),
5862 filePath.raw(), mParent->id().raw());
5863 break;
5864 }
5865 }
5866 else if (!parentId.isEmpty())
5867 {
5868 errMsg = Utf8StrFmt (
5869 tr ("Hard disk image '%s' is a differencing image that is linked "
5870 "to a hard disk with UUID {%Vuuid} and cannot be used "
5871 "directly as a base hard disk"),
5872 filePath.raw(), parentId.raw());
5873 break;
5874 }
5875
5876 /* get actual file size */
5877 /// @todo is there a direct method in RT?
5878 {
5879 RTFILE file = NIL_RTFILE;
5880 vrc = RTFileOpen (&file, filePath, RTFILE_O_READ);
5881 if (VBOX_SUCCESS (vrc))
5882 {
5883 uint64_t size = 0;
5884 vrc = RTFileGetSize (file, &size);
5885 if (VBOX_SUCCESS (vrc))
5886 mActualSize = size;
5887 RTFileClose (file);
5888 }
5889 if (VBOX_FAILURE (vrc))
5890 break;
5891 }
5892
5893 /* query logical size only for non-differencing images */
5894 if (!mParent)
5895 {
5896 uint64_t size = VDGetSize (mContainer, 0);
5897 /* convert to MBytes */
5898 mSize = size / 1024 / 1024;
5899 }
5900 }
5901 while (0);
5902
5903 VDCloseAll (mContainer);
5904
5905 /* enter the lock again */
5906 alock.enter();
5907
5908 /* remove the reference */
5909 releaseReader();
5910
5911 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
5912 {
5913 LogWarningFunc (("'%ls' is not accessible "
5914 "(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
5915 mFilePathFull.raw(), rc, vrc, errMsg.raw()));
5916
5917 if (aAccessError)
5918 {
5919 if (!errMsg.isNull())
5920 *aAccessError = errMsg;
5921 else if (!mLastVDError.isNull())
5922 *aAccessError = mLastVDError;
5923 else if (VBOX_FAILURE (vrc))
5924 *aAccessError = Utf8StrFmt (
5925 tr ("Could not access hard disk image '%ls' (%Vrc)"),
5926 mFilePathFull.raw(), vrc);
5927 }
5928
5929 /* downgrade to not accessible */
5930 mState = Created;
5931 }
5932 else
5933 {
5934 if (aAccessError)
5935 aAccessError->setNull();
5936
5937 mState = Accessible;
5938 }
5939
5940 /* inform waiters if there are any */
5941 if (mStateCheckWaiters > 0)
5942 {
5943 RTSemEventMultiSignal (mStateCheckSem);
5944 }
5945 else
5946 {
5947 /* delete the semaphore ourselves */
5948 RTSemEventMultiDestroy (mStateCheckSem);
5949 mStateCheckSem = NIL_RTSEMEVENTMULTI;
5950 }
5951
5952 /* cleanup the last error report from VDError() */
5953 mLastVDError.setNull();
5954
5955 return rc;
5956}
5957
5958/**
5959 * Helper to create hard disk images.
5960 *
5961 * @param aSize size in MB
5962 * @param aDynamic dynamic or fixed image
5963 * @param aProgress address of IProgress pointer to return
5964 */
5965HRESULT HVHDImage::createImage (ULONG64 aSize, BOOL aDynamic,
5966 IProgress **aProgress)
5967{
5968 ComAssertMsgFailed (("Not implemented"));
5969 return E_NOTIMPL;
5970
5971/// @todo later
5972// Use code from HVirtualDiskImage::createImage as an example.
5973}
5974
5975/* static */
5976DECLCALLBACK(int) HVHDImage::VDITaskThread (RTTHREAD thread, void *pvUser)
5977{
5978 AssertMsgFailed (("Not implemented"));
5979 return VERR_GENERAL_FAILURE;
5980
5981/// @todo later
5982// Use code from HVirtualDiskImage::VDITaskThread as an example.
5983}
5984
5985/* static */
5986DECLCALLBACK(void) HVHDImage::VDError (void *pvUser, int rc, RT_SRC_POS_DECL,
5987 const char *pszFormat, va_list va)
5988{
5989 HVHDImage *that = static_cast <HVHDImage *> (pvUser);
5990 AssertReturnVoid (that != NULL);
5991
5992 /// @todo pass the error message to the operation initiator
5993 Utf8Str err = Utf8StrFmtVA (pszFormat, va);
5994 if (VBOX_FAILURE (rc))
5995 err = Utf8StrFmt ("%s (%Vrc)", err.raw(), rc);
5996
5997 if (that->mLastVDError.isNull())
5998 that->mLastVDError = err;
5999 else
6000 that->mLastVDError = Utf8StrFmt
6001 ("%s.\n%s", that->mLastVDError.raw(), err.raw());
6002}
6003
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use