VirtualBox

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

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

Main: Removed incorrect assertion.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 167.9 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 /* initialize the container */
3406 int vrc = VDCreate (VDError, this, &mContainer);
3407 ComAssertRCRet (vrc, E_FAIL);
3408
3409 return S_OK;
3410}
3411
3412void HVMDKImage::FinalRelease()
3413{
3414 if (mContainer != NULL)
3415 VDDestroy (mContainer);
3416
3417 HardDisk::FinalRelease();
3418}
3419
3420// public initializer/uninitializer for internal purposes only
3421////////////////////////////////////////////////////////////////////////////////
3422
3423// public methods for internal purposes only
3424/////////////////////////////////////////////////////////////////////////////
3425
3426/**
3427 * Initializes the VMDK hard disk object by reading its properties from
3428 * the given configuration node. The created hard disk will be marked as
3429 * registered on success.
3430 *
3431 * @param aHDNode <HardDisk> node.
3432 * @param aVMDKNode <VirtualDiskImage> node.
3433 */
3434HRESULT HVMDKImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
3435 const settings::Key &aHDNode,
3436 const settings::Key &aVMDKNode)
3437{
3438 using namespace settings;
3439
3440 LogFlowThisFunc (("\n"));
3441
3442 AssertReturn (!aHDNode.isNull() && !aVMDKNode.isNull(), E_FAIL);
3443
3444 AutoWriteLock alock (this);
3445 ComAssertRet (!isReady(), E_UNEXPECTED);
3446
3447 mStorageType = HardDiskStorageType_VMDKImage;
3448
3449 HRESULT rc = S_OK;
3450
3451 do
3452 {
3453 rc = protectedInit (aVirtualBox, aParent);
3454 CheckComRCBreakRC (rc);
3455
3456 /* set ready to let protectedUninit() be called on failure */
3457 setReady (true);
3458
3459 /* filePath (required) */
3460 Bstr filePath = aVMDKNode.stringValue ("filePath");
3461 rc = setFilePath (filePath);
3462 CheckComRCBreakRC (rc);
3463
3464 LogFlowThisFunc (("'%ls'\n", mFilePathFull.raw()));
3465
3466 /* load basic settings and children */
3467 rc = loadSettings (aHDNode);
3468 CheckComRCBreakRC (rc);
3469
3470 if (mType != HardDiskType_Writethrough)
3471 {
3472 rc = setError (E_FAIL,
3473 tr ("Currently, non-Writethrough VMDK images are not "
3474 "allowed ('%ls')"),
3475 toString().raw());
3476 break;
3477 }
3478
3479 mState = Created;
3480 mRegistered = TRUE;
3481
3482 /* Don't call queryInformation() for registered hard disks to
3483 * prevent the calling thread (i.e. the VirtualBox server startup
3484 * thread) from an unexpected freeze. The vital mId property (UUID)
3485 * is read from the registry file in loadSettings(). To get the rest,
3486 * the user will have to call COMGETTER(Accessible) manually. */
3487 }
3488 while (0);
3489
3490 if (FAILED (rc))
3491 uninit();
3492
3493 return rc;
3494}
3495
3496/**
3497 * Initializes the VMDK hard disk object using the given image file name.
3498 *
3499 * @param aVirtualBox VirtualBox parent.
3500 * @param aParent Currently, must always be @c NULL.
3501 * @param aFilePath Path to the image file, or @c NULL to create an
3502 * image-less object.
3503 * @param aRegistered Whether to mark this disk as registered or not
3504 * (ignored when @a aFilePath is @c NULL, assuming @c FALSE)
3505 */
3506HRESULT HVMDKImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
3507 const BSTR aFilePath, BOOL aRegistered /* = FALSE */)
3508{
3509 LogFlowThisFunc (("aFilePath='%ls', aRegistered=%d\n", aFilePath, aRegistered));
3510
3511 AssertReturn (aParent == NULL, E_FAIL);
3512
3513 AutoWriteLock alock (this);
3514 ComAssertRet (!isReady(), E_UNEXPECTED);
3515
3516 mStorageType = HardDiskStorageType_VMDKImage;
3517
3518 HRESULT rc = S_OK;
3519
3520 do
3521 {
3522 rc = protectedInit (aVirtualBox, aParent);
3523 CheckComRCBreakRC (rc);
3524
3525 /* set ready to let protectedUninit() be called on failure */
3526 setReady (true);
3527
3528 rc = setFilePath (aFilePath);
3529 CheckComRCBreakRC (rc);
3530
3531 /* currently, all VMDK hard disks are writethrough */
3532 mType = HardDiskType_Writethrough;
3533
3534 Assert (mId.isEmpty());
3535
3536 if (aFilePath && *aFilePath)
3537 {
3538 mRegistered = aRegistered;
3539 mState = Created;
3540
3541 /* Call queryInformation() anyway (even if it will block), because
3542 * it is the only way to get the UUID of the existing VDI and
3543 * initialize the vital mId property. */
3544 Bstr errMsg;
3545 rc = queryInformation (&errMsg);
3546 if (SUCCEEDED (rc))
3547 {
3548 /* We are constructing a new HVirtualDiskImage object. If there
3549 * is a fatal accessibility error (we cannot read image UUID),
3550 * we have to fail. We do so even on non-fatal errors as well,
3551 * because it's not worth to keep going with the inaccessible
3552 * image from the very beginning (when nothing else depends on
3553 * it yet). */
3554 if (!errMsg.isNull())
3555 rc = setErrorBstr (E_FAIL, errMsg);
3556 }
3557 }
3558 else
3559 {
3560 mRegistered = FALSE;
3561 mState = NotCreated;
3562 mId.create();
3563 }
3564 }
3565 while (0);
3566
3567 if (FAILED (rc))
3568 uninit();
3569
3570 return rc;
3571}
3572
3573/**
3574 * Uninitializes the instance and sets the ready flag to FALSE.
3575 * Called either from FinalRelease(), by the parent when it gets destroyed,
3576 * or by a third party when it decides this object is no more valid.
3577 */
3578void HVMDKImage::uninit()
3579{
3580 LogFlowThisFunc (("\n"));
3581
3582 AutoWriteLock alock (this);
3583 if (!isReady())
3584 return;
3585
3586 HardDisk::protectedUninit (alock);
3587}
3588
3589// IHardDisk properties
3590////////////////////////////////////////////////////////////////////////////////
3591
3592STDMETHODIMP HVMDKImage::COMGETTER(Description) (BSTR *aDescription)
3593{
3594 if (!aDescription)
3595 return E_POINTER;
3596
3597 AutoReadLock alock (this);
3598 CHECK_READY();
3599
3600 mDescription.cloneTo (aDescription);
3601 return S_OK;
3602}
3603
3604STDMETHODIMP HVMDKImage::COMSETTER(Description) (INPTR BSTR aDescription)
3605{
3606 AutoWriteLock alock (this);
3607 CHECK_READY();
3608
3609 CHECK_BUSY_AND_READERS();
3610
3611 return E_NOTIMPL;
3612
3613/// @todo (r=dmik) implement
3614//
3615// if (mState >= Created)
3616// {
3617// int vrc = VDISetImageComment (Utf8Str (mFilePathFull), Utf8Str (aDescription));
3618// if (VBOX_FAILURE (vrc))
3619// return setError (E_FAIL,
3620// tr ("Could not change the description of the VDI hard disk '%ls' "
3621// "(%Vrc)"),
3622// toString().raw(), vrc);
3623// }
3624//
3625// mDescription = aDescription;
3626// return S_OK;
3627}
3628
3629STDMETHODIMP HVMDKImage::COMGETTER(Size) (ULONG64 *aSize)
3630{
3631 if (!aSize)
3632 return E_POINTER;
3633
3634 AutoReadLock alock (this);
3635 CHECK_READY();
3636
3637/// @todo (r=dmik) will need this if we add support for differencing VMDKs
3638//
3639// /* only a non-differencing image knows the logical size */
3640// if (isDifferencing())
3641// return root()->COMGETTER(Size) (aSize);
3642
3643 *aSize = mSize;
3644 return S_OK;
3645}
3646
3647STDMETHODIMP HVMDKImage::COMGETTER(ActualSize) (ULONG64 *aActualSize)
3648{
3649 if (!aActualSize)
3650 return E_POINTER;
3651
3652 AutoReadLock alock (this);
3653 CHECK_READY();
3654
3655 *aActualSize = mActualSize;
3656 return S_OK;
3657}
3658
3659// IVirtualDiskImage properties
3660////////////////////////////////////////////////////////////////////////////////
3661
3662STDMETHODIMP HVMDKImage::COMGETTER(FilePath) (BSTR *aFilePath)
3663{
3664 if (!aFilePath)
3665 return E_POINTER;
3666
3667 AutoReadLock alock (this);
3668 CHECK_READY();
3669
3670 mFilePathFull.cloneTo (aFilePath);
3671 return S_OK;
3672}
3673
3674STDMETHODIMP HVMDKImage::COMSETTER(FilePath) (INPTR BSTR aFilePath)
3675{
3676 AutoWriteLock alock (this);
3677 CHECK_READY();
3678
3679 if (mState != NotCreated)
3680 return setError (E_ACCESSDENIED,
3681 tr ("Cannot change the file path of the existing hard disk '%ls'"),
3682 toString().raw());
3683
3684 CHECK_BUSY_AND_READERS();
3685
3686 /* append the default path if only a name is given */
3687 Bstr path = aFilePath;
3688 if (aFilePath && *aFilePath)
3689 {
3690 Utf8Str fp = aFilePath;
3691 if (!RTPathHavePath (fp))
3692 {
3693 AutoReadLock propsLock (mVirtualBox->systemProperties());
3694 path = Utf8StrFmt ("%ls%c%s",
3695 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
3696 RTPATH_DELIMITER,
3697 fp.raw());
3698 }
3699 }
3700
3701 return setFilePath (path);
3702}
3703
3704STDMETHODIMP HVMDKImage::COMGETTER(Created) (BOOL *aCreated)
3705{
3706 if (!aCreated)
3707 return E_POINTER;
3708
3709 AutoReadLock alock (this);
3710 CHECK_READY();
3711
3712 *aCreated = mState >= Created;
3713 return S_OK;
3714}
3715
3716// IVMDKImage methods
3717/////////////////////////////////////////////////////////////////////////////
3718
3719STDMETHODIMP HVMDKImage::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
3720{
3721 if (!aProgress)
3722 return E_POINTER;
3723
3724 AutoWriteLock alock (this);
3725 CHECK_READY();
3726
3727 return createImage (aSize, TRUE /* aDynamic */, aProgress);
3728}
3729
3730STDMETHODIMP HVMDKImage::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
3731{
3732 if (!aProgress)
3733 return E_POINTER;
3734
3735 AutoWriteLock alock (this);
3736 CHECK_READY();
3737
3738 return createImage (aSize, FALSE /* aDynamic */, aProgress);
3739}
3740
3741STDMETHODIMP HVMDKImage::DeleteImage()
3742{
3743 AutoWriteLock alock (this);
3744 CHECK_READY();
3745 CHECK_BUSY_AND_READERS();
3746
3747 return E_NOTIMPL;
3748
3749/// @todo (r=dmik) later
3750// We will need to parse the file in order to delete all related delta and
3751// sparse images etc. We may also want to obey the .vmdk.lck file
3752// which is (as far as I understood) created when the VMware VM is
3753// running or saved etc.
3754//
3755// if (mRegistered)
3756// return setError (E_ACCESSDENIED,
3757// tr ("Cannot delete an image of the registered hard disk image '%ls"),
3758// mFilePathFull.raw());
3759// if (mState == NotCreated)
3760// return setError (E_FAIL,
3761// tr ("Hard disk image has been already deleted or never created"));
3762//
3763// int vrc = RTFileDelete (Utf8Str (mFilePathFull));
3764// if (VBOX_FAILURE (vrc))
3765// return setError (E_FAIL, tr ("Could not delete the image file '%ls' (%Vrc)"),
3766// mFilePathFull.raw(), vrc);
3767//
3768// mState = NotCreated;
3769//
3770// /* reset the fields */
3771// mSize = 0;
3772// mActualSize = 0;
3773//
3774// return S_OK;
3775}
3776
3777// public/protected methods for internal purposes only
3778/////////////////////////////////////////////////////////////////////////////
3779
3780/**
3781 * Attempts to mark the hard disk as registered.
3782 * Only VirtualBox can call this method.
3783 */
3784HRESULT HVMDKImage::trySetRegistered (BOOL aRegistered)
3785{
3786 AutoWriteLock alock (this);
3787 CHECK_READY();
3788
3789 if (aRegistered)
3790 {
3791 if (mState == NotCreated)
3792 return setError (E_FAIL,
3793 tr ("Image file '%ls' is not yet created for this hard disk"),
3794 mFilePathFull.raw());
3795
3796/// @todo (r=dmik) will need this if we add support for differencing VMDKs
3797// if (isDifferencing())
3798// return setError (E_FAIL,
3799// tr ("Hard disk '%ls' is differencing and cannot be unregistered "
3800// "explicitly"),
3801// mFilePathFull.raw());
3802 }
3803 else
3804 {
3805 ComAssertRet (mState >= Created, E_FAIL);
3806 }
3807
3808 return HardDisk::trySetRegistered (aRegistered);
3809}
3810
3811/**
3812 * Checks accessibility of this hard disk image only (w/o parents).
3813 *
3814 * @param aAccessError on output, a null string indicates the hard disk is
3815 * accessible, otherwise contains a message describing
3816 * the reason of inaccessibility.
3817 *
3818 * @note Locks this object for writing.
3819 */
3820HRESULT HVMDKImage::getAccessible (Bstr &aAccessError)
3821{
3822 /* queryInformation() needs a write lock */
3823 AutoWriteLock alock (this);
3824 CHECK_READY();
3825
3826 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
3827 {
3828 /* An accessibility check in progress on some other thread,
3829 * wait for it to finish. */
3830
3831 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL);
3832 ++ mStateCheckWaiters;
3833 alock.leave();
3834
3835 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
3836
3837 alock.enter();
3838 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
3839 -- mStateCheckWaiters;
3840 if (mStateCheckWaiters == 0)
3841 {
3842 RTSemEventMultiDestroy (mStateCheckSem);
3843 mStateCheckSem = NIL_RTSEMEVENTMULTI;
3844 }
3845
3846 AssertRCReturn (vrc, E_FAIL);
3847
3848 /* don't touch aAccessError, it has been already set */
3849 return S_OK;
3850 }
3851
3852 /* check the basic accessibility */
3853 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */);
3854 if (FAILED (rc) || !aAccessError.isNull())
3855 return rc;
3856
3857 if (mState >= Created)
3858 {
3859 return queryInformation (&aAccessError);
3860 }
3861
3862 aAccessError = Utf8StrFmt ("Hard disk image '%ls' is not yet created",
3863 mFilePathFull.raw());
3864 return S_OK;
3865}
3866
3867/**
3868 * Saves hard disk settings to the specified storage node and saves
3869 * all children to the specified hard disk node
3870 *
3871 * @param aHDNode <HardDisk> or <DiffHardDisk> node.
3872 * @param aStorageNode <VirtualDiskImage> node.
3873 */
3874HRESULT HVMDKImage::saveSettings (settings::Key &aHDNode,
3875 settings::Key &aStorageNode)
3876{
3877 AssertReturn (!aHDNode.isNull() && !aStorageNode.isNull(), E_FAIL);
3878
3879 AutoReadLock alock (this);
3880 CHECK_READY();
3881
3882 /* filePath (required) */
3883 aStorageNode.setValue <Bstr> ("filePath", mFilePath);
3884
3885 /* save basic settings and children */
3886 return HardDisk::saveSettings (aHDNode);
3887}
3888
3889/**
3890 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
3891 * of this hard disk and updates it if necessary to reflect the new location.
3892 * Intended to be from HardDisk::updatePaths().
3893 *
3894 * @param aOldPath old path (full)
3895 * @param aNewPath new path (full)
3896 *
3897 * @note Locks this object for writing.
3898 */
3899void HVMDKImage::updatePath (const char *aOldPath, const char *aNewPath)
3900{
3901 AssertReturnVoid (aOldPath);
3902 AssertReturnVoid (aNewPath);
3903
3904 AutoWriteLock alock (this);
3905 AssertReturnVoid (isReady());
3906
3907 size_t oldPathLen = strlen (aOldPath);
3908
3909 Utf8Str path = mFilePathFull;
3910 LogFlowThisFunc (("VMDK.fullPath={%s}\n", path.raw()));
3911
3912 if (RTPathStartsWith (path, aOldPath))
3913 {
3914 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
3915 path.raw() + oldPathLen);
3916 path = newPath;
3917
3918 mVirtualBox->calculateRelativePath (path, path);
3919
3920 unconst (mFilePathFull) = newPath;
3921 unconst (mFilePath) = path;
3922
3923 LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
3924 newPath.raw(), path.raw()));
3925 }
3926}
3927
3928/**
3929 * Returns the string representation of this hard disk.
3930 * When \a aShort is false, returns the full image file path.
3931 * Otherwise, returns the image file name only.
3932 *
3933 * @param aShort if true, a short representation is returned
3934 *
3935 * @note Locks this object for reading.
3936 */
3937Bstr HVMDKImage::toString (bool aShort /* = false */)
3938{
3939 AutoReadLock alock (this);
3940
3941 if (!aShort)
3942 return mFilePathFull;
3943 else
3944 {
3945 Utf8Str fname = mFilePathFull;
3946 return RTPathFilename (fname.mutableRaw());
3947 }
3948}
3949
3950/**
3951 * Creates a clone of this hard disk by storing hard disk data in the given
3952 * VDI file.
3953 *
3954 * If the operation fails, @a aDeleteTarget will be set to @c true unless the
3955 * failure happened because the target file already existed.
3956 *
3957 * @param aId UUID to assign to the created image.
3958 * @param aTargetPath VDI file where the cloned image is to be to stored.
3959 * @param aProgress progress object to run during operation.
3960 * @param aDeleteTarget Whether it is recommended to delete target on
3961 * failure or not.
3962 */
3963HRESULT
3964HVMDKImage::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
3965 Progress *aProgress, bool &aDeleteTarget)
3966{
3967 ComAssertMsgFailed (("Not implemented"));
3968 return E_NOTIMPL;
3969
3970/// @todo (r=dmik) will need this if we add support for differencing VMDKs
3971// Use code from HVirtualDiskImage::cloneToImage as an example.
3972}
3973
3974/**
3975 * Creates a new differencing image for this hard disk with the given
3976 * VDI file name.
3977 *
3978 * @param aId UUID to assign to the created image
3979 * @param aTargetPath VDI file where to store the created differencing image
3980 * @param aProgress progress object to run during operation
3981 * (can be NULL)
3982 */
3983HRESULT
3984HVMDKImage::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
3985 Progress *aProgress)
3986{
3987 ComAssertMsgFailed (("Not implemented"));
3988 return E_NOTIMPL;
3989
3990/// @todo (r=dmik) will need this if we add support for differencing VMDKs
3991// Use code from HVirtualDiskImage::createDiffImage as an example.
3992}
3993
3994// private methods
3995/////////////////////////////////////////////////////////////////////////////
3996
3997/**
3998 * Helper to set a new file path.
3999 * Resolves a path relatively to the Virtual Box home directory.
4000 *
4001 * @note
4002 * Must be called from under the object's lock!
4003 */
4004HRESULT HVMDKImage::setFilePath (const BSTR aFilePath)
4005{
4006 if (aFilePath && *aFilePath)
4007 {
4008 /* get the full file name */
4009 char filePathFull [RTPATH_MAX];
4010 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aFilePath),
4011 filePathFull, sizeof (filePathFull));
4012 if (VBOX_FAILURE (vrc))
4013 return setError (E_FAIL,
4014 tr ("Invalid image file path '%ls' (%Vrc)"), aFilePath, vrc);
4015
4016 mFilePath = aFilePath;
4017 mFilePathFull = filePathFull;
4018 }
4019 else
4020 {
4021 mFilePath.setNull();
4022 mFilePathFull.setNull();
4023 }
4024
4025 return S_OK;
4026}
4027
4028/**
4029 * Helper to query information about the VDI hard disk.
4030 *
4031 * @param aAccessError not used when NULL, otherwise see #getAccessible()
4032 *
4033 * @note Must be called from under the object's lock, only after
4034 * CHECK_BUSY_AND_READERS() succeeds.
4035 */
4036HRESULT HVMDKImage::queryInformation (Bstr *aAccessError)
4037{
4038 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
4039
4040 /* create a lock object to completely release it later */
4041 AutoWriteLock alock (this);
4042
4043 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
4044
4045 ComAssertRet (mState >= Created, E_FAIL);
4046
4047 HRESULT rc = S_OK;
4048 int vrc = VINF_SUCCESS;
4049
4050 /* lazily create a semaphore */
4051 vrc = RTSemEventMultiCreate (&mStateCheckSem);
4052 ComAssertRCRet (vrc, E_FAIL);
4053
4054 /* Reference the disk to prevent any concurrent modifications
4055 * after releasing the lock below (to unblock getters before
4056 * a lengthy operation). */
4057 addReader();
4058
4059 alock.leave();
4060
4061 /* VBoxVHDD management interface needs to be optimized: we're opening a
4062 * file three times in a raw to get three bits of information. */
4063
4064 Utf8Str filePath = mFilePathFull;
4065 Bstr errMsg;
4066
4067 /* reset any previous error report from VDError() */
4068 mLastVDError.setNull();
4069
4070 do
4071 {
4072 Guid id, parentId;
4073
4074 /// @todo changed from VD_OPEN_FLAGS_READONLY to VD_OPEN_FLAGS_NORMAL,
4075 /// because otherwise registering a VMDK which so far has no UUID will
4076 /// yield a null UUID. It cannot be added to a VMDK opened readonly,
4077 /// obviously. This of course changes locking behavior, but for now
4078 /// this is acceptable. A better solution needs to be found later.
4079 vrc = VDOpen (mContainer, "VMDK", filePath, VD_OPEN_FLAGS_NORMAL);
4080 if (VBOX_FAILURE (vrc))
4081 break;
4082
4083 vrc = VDGetUuid (mContainer, 0, id.ptr());
4084 if (VBOX_FAILURE (vrc))
4085 break;
4086 vrc = VDGetParentUuid (mContainer, 0, parentId.ptr());
4087 if (VBOX_FAILURE (vrc))
4088 break;
4089
4090 if (!mId.isEmpty())
4091 {
4092 /* check that the actual UUID of the image matches the stored UUID */
4093 if (mId != id)
4094 {
4095 errMsg = Utf8StrFmt (
4096 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
4097 "match UUID {%Vuuid} stored in the registry"),
4098 id.ptr(), filePath.raw(), mId.ptr());
4099 break;
4100 }
4101 }
4102 else
4103 {
4104 /* assgn an UUID read from the image file */
4105 mId = id;
4106 }
4107
4108 if (mParent)
4109 {
4110 /* check parent UUID */
4111 AutoWriteLock parentLock (mParent);
4112 if (mParent->id() != parentId)
4113 {
4114 errMsg = Utf8StrFmt (
4115 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
4116 "the hard disk image file '%s' doesn't match "
4117 "UUID {%Vuuid} stored in the registry"),
4118 parentId.raw(), mParent->toString().raw(),
4119 filePath.raw(), mParent->id().raw());
4120 break;
4121 }
4122 }
4123 else if (!parentId.isEmpty())
4124 {
4125 errMsg = Utf8StrFmt (
4126 tr ("Hard disk image '%s' is a differencing image that is linked "
4127 "to a hard disk with UUID {%Vuuid} and cannot be used "
4128 "directly as a base hard disk"),
4129 filePath.raw(), parentId.raw());
4130 break;
4131 }
4132
4133 /* get actual file size */
4134 /// @todo is there a direct method in RT?
4135 {
4136 RTFILE file = NIL_RTFILE;
4137 vrc = RTFileOpen (&file, filePath, RTFILE_O_READ);
4138 if (VBOX_SUCCESS (vrc))
4139 {
4140 uint64_t size = 0;
4141 vrc = RTFileGetSize (file, &size);
4142 if (VBOX_SUCCESS (vrc))
4143 mActualSize = size;
4144 RTFileClose (file);
4145 }
4146 if (VBOX_FAILURE (vrc))
4147 break;
4148 }
4149
4150 /* query logical size only for non-differencing images */
4151 if (!mParent)
4152 {
4153 uint64_t size = VDGetSize (mContainer, 0);
4154 /* convert to MBytes */
4155 mSize = size / 1024 / 1024;
4156 }
4157 }
4158 while (0);
4159
4160 VDCloseAll (mContainer);
4161
4162 /* enter the lock again */
4163 alock.enter();
4164
4165 /* remove the reference */
4166 releaseReader();
4167
4168 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
4169 {
4170 LogWarningFunc (("'%ls' is not accessible "
4171 "(rc=%08X, vrc=%Vrc, errMsg='%ls', mLastVDError='%s')\n",
4172 mFilePathFull.raw(), rc, vrc, errMsg.raw(), mLastVDError.raw()));
4173
4174 if (aAccessError)
4175 {
4176 if (!errMsg.isNull())
4177 *aAccessError = errMsg;
4178 else if (!mLastVDError.isNull())
4179 *aAccessError = mLastVDError;
4180 else if (VBOX_FAILURE (vrc))
4181 *aAccessError = Utf8StrFmt (
4182 tr ("Could not access hard disk image '%ls' (%Vrc)"),
4183 mFilePathFull.raw(), vrc);
4184 }
4185
4186 /* downgrade to not accessible */
4187 mState = Created;
4188 }
4189 else
4190 {
4191 if (aAccessError)
4192 aAccessError->setNull();
4193
4194 mState = Accessible;
4195 }
4196
4197 /* inform waiters if there are any */
4198 if (mStateCheckWaiters > 0)
4199 {
4200 RTSemEventMultiSignal (mStateCheckSem);
4201 }
4202 else
4203 {
4204 /* delete the semaphore ourselves */
4205 RTSemEventMultiDestroy (mStateCheckSem);
4206 mStateCheckSem = NIL_RTSEMEVENTMULTI;
4207 }
4208
4209 /* cleanup the last error report from VDError() */
4210 mLastVDError.setNull();
4211
4212 return rc;
4213}
4214
4215/**
4216 * Helper to create hard disk images.
4217 *
4218 * @param aSize size in MB
4219 * @param aDynamic dynamic or fixed image
4220 * @param aProgress address of IProgress pointer to return
4221 */
4222HRESULT HVMDKImage::createImage (ULONG64 aSize, BOOL aDynamic,
4223 IProgress **aProgress)
4224{
4225 ComAssertMsgFailed (("Not implemented"));
4226 return E_NOTIMPL;
4227
4228/// @todo (r=dmik) later
4229// Use code from HVirtualDiskImage::createImage as an example.
4230}
4231
4232/* static */
4233DECLCALLBACK(int) HVMDKImage::VDITaskThread (RTTHREAD thread, void *pvUser)
4234{
4235 AssertMsgFailed (("Not implemented"));
4236 return VERR_GENERAL_FAILURE;
4237
4238/// @todo (r=dmik) later
4239// Use code from HVirtualDiskImage::VDITaskThread as an example.
4240}
4241
4242/* static */
4243DECLCALLBACK(void) HVMDKImage::VDError (void *pvUser, int rc, RT_SRC_POS_DECL,
4244 const char *pszFormat, va_list va)
4245{
4246 HVMDKImage *that = static_cast <HVMDKImage *> (pvUser);
4247 AssertReturnVoid (that != NULL);
4248
4249 /// @todo pass the error message to the operation initiator
4250 Utf8Str err = Utf8StrFmtVA (pszFormat, va);
4251 if (VBOX_FAILURE (rc))
4252 err = Utf8StrFmt ("%s (%Vrc)", err.raw(), rc);
4253
4254 if (that->mLastVDError.isNull())
4255 that->mLastVDError = err;
4256 else
4257 that->mLastVDError = Utf8StrFmt
4258 ("%s.\n%s", that->mLastVDError.raw(), err.raw());
4259}
4260
4261////////////////////////////////////////////////////////////////////////////////
4262// HCustomHardDisk class
4263////////////////////////////////////////////////////////////////////////////////
4264
4265// constructor / destructor
4266////////////////////////////////////////////////////////////////////////////////
4267
4268HRESULT HCustomHardDisk::FinalConstruct()
4269{
4270 HRESULT rc = HardDisk::FinalConstruct();
4271 if (FAILED (rc))
4272 return rc;
4273
4274 mState = NotCreated;
4275
4276 mStateCheckSem = NIL_RTSEMEVENTMULTI;
4277 mStateCheckWaiters = 0;
4278
4279 mSize = 0;
4280 mActualSize = 0;
4281 mContainer = NULL;
4282
4283 ComAssertRCRet (rc, E_FAIL);
4284
4285 return S_OK;
4286}
4287
4288void HCustomHardDisk::FinalRelease()
4289{
4290 if (mContainer != NULL)
4291 VDDestroy (mContainer);
4292
4293 HardDisk::FinalRelease();
4294}
4295
4296// public initializer/uninitializer for internal purposes only
4297////////////////////////////////////////////////////////////////////////////////
4298
4299// public methods for internal purposes only
4300/////////////////////////////////////////////////////////////////////////////
4301
4302/**
4303 * Initializes the custom hard disk object by reading its properties from
4304 * the given configuration node. The created hard disk will be marked as
4305 * registered on success.
4306 *
4307 * @param aHDNode <HardDisk> node.
4308 * @param aCustomNode <VirtualDiskImage> node.
4309 */
4310HRESULT HCustomHardDisk::init (VirtualBox *aVirtualBox, HardDisk *aParent,
4311 const settings::Key &aHDNode,
4312 const settings::Key &aCustomNode)
4313{
4314 using namespace settings;
4315
4316 LogFlowThisFunc (("\n"));
4317
4318 AssertReturn (!aHDNode.isNull() && !aCustomNode.isNull(), E_FAIL);
4319
4320 AutoWriteLock alock (this);
4321 ComAssertRet (!isReady(), E_UNEXPECTED);
4322
4323 mStorageType = HardDiskStorageType_CustomHardDisk;
4324
4325 HRESULT rc = S_OK;
4326 int vrc = VINF_SUCCESS;
4327 do
4328 {
4329 rc = protectedInit (aVirtualBox, aParent);
4330 CheckComRCBreakRC (rc);
4331
4332 /* set ready to let protectedUninit() be called on failure */
4333 setReady (true);
4334
4335 /* location (required) */
4336 Bstr location = aCustomNode.stringValue ("location");
4337 rc = setLocation (location);
4338 CheckComRCBreakRC (rc);
4339
4340 LogFlowThisFunc (("'%ls'\n", mLocationFull.raw()));
4341
4342 /* format (required) */
4343 mFormat = aCustomNode.stringValue ("format");
4344
4345 /* initialize the container */
4346 vrc = VDCreate (VDError, this, &mContainer);
4347 if (VBOX_FAILURE (vrc))
4348 {
4349 AssertRC (vrc);
4350 if (mLastVDError.isEmpty())
4351 rc = setError (E_FAIL,
4352 tr ("Unknown format '%ls' of the custom "
4353 "hard disk '%ls' (%Vrc)"),
4354 mFormat.raw(), toString().raw(), vrc);
4355 else
4356 rc = setErrorBstr (E_FAIL, mLastVDError);
4357 break;
4358 }
4359
4360 /* load basic settings and children */
4361 rc = loadSettings (aHDNode);
4362 CheckComRCBreakRC (rc);
4363
4364 if (mType != HardDiskType_Writethrough)
4365 {
4366 rc = setError (E_FAIL,
4367 tr ("Currently, non-Writethrough custom hard disks "
4368 "are not allowed ('%ls')"),
4369 toString().raw());
4370 break;
4371 }
4372
4373 mState = Created;
4374 mRegistered = TRUE;
4375
4376 /* Don't call queryInformation() for registered hard disks to
4377 * prevent the calling thread (i.e. the VirtualBox server startup
4378 * thread) from an unexpected freeze. The vital mId property (UUID)
4379 * is read from the registry file in loadSettings(). To get the rest,
4380 * the user will have to call COMGETTER(Accessible) manually. */
4381 }
4382 while (0);
4383
4384 if (FAILED (rc))
4385 uninit();
4386
4387 return rc;
4388}
4389
4390/**
4391 * Initializes the custom hard disk object using the given image file name.
4392 *
4393 * @param aVirtualBox VirtualBox parent.
4394 * @param aParent Currently, must always be @c NULL.
4395 * @param aLocation Location of the virtual disk, or @c NULL to create an
4396 * image-less object.
4397 * @param aRegistered Whether to mark this disk as registered or not
4398 * (ignored when @a aLocation is @c NULL, assuming @c FALSE)
4399 */
4400HRESULT HCustomHardDisk::init (VirtualBox *aVirtualBox, HardDisk *aParent,
4401 const BSTR aLocation, BOOL aRegistered /* = FALSE */)
4402{
4403 LogFlowThisFunc (("aLocation='%ls', aRegistered=%d\n", aLocation, aRegistered));
4404
4405 AssertReturn (aParent == NULL, E_FAIL);
4406
4407 AutoWriteLock alock (this);
4408 ComAssertRet (!isReady(), E_UNEXPECTED);
4409
4410 mStorageType = HardDiskStorageType_CustomHardDisk;
4411
4412 HRESULT rc = S_OK;
4413
4414 do
4415 {
4416 rc = protectedInit (aVirtualBox, aParent);
4417 CheckComRCBreakRC (rc);
4418
4419 /* set ready to let protectedUninit() be called on failure */
4420 setReady (true);
4421
4422 rc = setLocation (aLocation);
4423 CheckComRCBreakRC (rc);
4424
4425 /* currently, all custom hard disks are writethrough */
4426 mType = HardDiskType_Writethrough;
4427
4428 Assert (mId.isEmpty());
4429
4430 if (aLocation && *aLocation)
4431 {
4432 mRegistered = aRegistered;
4433 mState = Created;
4434
4435 char *pszFormat = NULL;
4436
4437 int vrc = VDGetFormat (Utf8Str (mLocation), &pszFormat);
4438 if (VBOX_FAILURE(vrc))
4439 {
4440 rc = setError (E_FAIL,
4441 tr ("Cannot recognize the format of the custom "
4442 "hard disk '%ls' (%Vrc)"),
4443 toString().raw(), vrc);
4444 break;
4445 }
4446 mFormat = Bstr (pszFormat);
4447 RTStrFree (pszFormat);
4448
4449 /* Create the corresponding container. */
4450 vrc = VDCreate (VDError, this, &mContainer);
4451
4452 /* the format has been already checked for presence at this point */
4453 ComAssertRCBreak (vrc, rc = E_FAIL);
4454
4455 /* Call queryInformation() anyway (even if it will block), because
4456 * it is the only way to get the UUID of the existing VDI and
4457 * initialize the vital mId property. */
4458 Bstr errMsg;
4459 rc = queryInformation (&errMsg);
4460 if (SUCCEEDED (rc))
4461 {
4462 /* We are constructing a new HVirtualDiskImage object. If there
4463 * is a fatal accessibility error (we cannot read image UUID),
4464 * we have to fail. We do so even on non-fatal errors as well,
4465 * because it's not worth to keep going with the inaccessible
4466 * image from the very beginning (when nothing else depends on
4467 * it yet). */
4468 if (!errMsg.isNull())
4469 rc = setErrorBstr (E_FAIL, errMsg);
4470 }
4471 }
4472 else
4473 {
4474 mRegistered = FALSE;
4475 mState = NotCreated;
4476 mId.create();
4477 }
4478 }
4479 while (0);
4480
4481 if (FAILED (rc))
4482 uninit();
4483
4484 return rc;
4485}
4486
4487/**
4488 * Uninitializes the instance and sets the ready flag to FALSE.
4489 * Called either from FinalRelease(), by the parent when it gets destroyed,
4490 * or by a third party when it decides this object is no more valid.
4491 */
4492void HCustomHardDisk::uninit()
4493{
4494 LogFlowThisFunc (("\n"));
4495
4496 AutoWriteLock alock (this);
4497 if (!isReady())
4498 return;
4499
4500 HardDisk::protectedUninit (alock);
4501}
4502
4503// IHardDisk properties
4504////////////////////////////////////////////////////////////////////////////////
4505
4506STDMETHODIMP HCustomHardDisk::COMGETTER(Description) (BSTR *aDescription)
4507{
4508 if (!aDescription)
4509 return E_POINTER;
4510
4511 AutoReadLock alock (this);
4512 CHECK_READY();
4513
4514 mDescription.cloneTo (aDescription);
4515 return S_OK;
4516}
4517
4518STDMETHODIMP HCustomHardDisk::COMSETTER(Description) (INPTR BSTR aDescription)
4519{
4520 AutoWriteLock alock (this);
4521 CHECK_READY();
4522
4523 CHECK_BUSY_AND_READERS();
4524
4525 return E_NOTIMPL;
4526}
4527
4528STDMETHODIMP HCustomHardDisk::COMGETTER(Size) (ULONG64 *aSize)
4529{
4530 if (!aSize)
4531 return E_POINTER;
4532
4533 AutoReadLock alock (this);
4534 CHECK_READY();
4535
4536 *aSize = mSize;
4537 return S_OK;
4538}
4539
4540STDMETHODIMP HCustomHardDisk::COMGETTER(ActualSize) (ULONG64 *aActualSize)
4541{
4542 if (!aActualSize)
4543 return E_POINTER;
4544
4545 AutoReadLock alock (this);
4546 CHECK_READY();
4547
4548 *aActualSize = mActualSize;
4549 return S_OK;
4550}
4551
4552// ICustomHardDisk properties
4553////////////////////////////////////////////////////////////////////////////////
4554
4555STDMETHODIMP HCustomHardDisk::COMGETTER(Location) (BSTR *aLocation)
4556{
4557 if (!aLocation)
4558 return E_POINTER;
4559
4560 AutoReadLock alock (this);
4561 CHECK_READY();
4562
4563 mLocationFull.cloneTo (aLocation);
4564 return S_OK;
4565}
4566
4567STDMETHODIMP HCustomHardDisk::COMSETTER(Location) (INPTR BSTR aLocation)
4568{
4569 AutoWriteLock alock (this);
4570 CHECK_READY();
4571
4572 if (mState != NotCreated)
4573 return setError (E_ACCESSDENIED,
4574 tr ("Cannot change the file path of the existing hard disk '%ls'"),
4575 toString().raw());
4576
4577 CHECK_BUSY_AND_READERS();
4578
4579 /// @todo currently, we assume that location is always a file path for
4580 /// all custom hard disks. This is not generally correct, and needs to be
4581 /// parametrized in the VD plugin interface.
4582
4583 /* append the default path if only a name is given */
4584 Bstr path = aLocation;
4585 if (aLocation && *aLocation)
4586 {
4587 Utf8Str fp = aLocation;
4588 if (!RTPathHavePath (fp))
4589 {
4590 AutoReadLock propsLock (mVirtualBox->systemProperties());
4591 path = Utf8StrFmt ("%ls%c%s",
4592 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
4593 RTPATH_DELIMITER,
4594 fp.raw());
4595 }
4596 }
4597
4598 return setLocation (path);
4599}
4600
4601STDMETHODIMP HCustomHardDisk::COMGETTER(Created) (BOOL *aCreated)
4602{
4603 if (!aCreated)
4604 return E_POINTER;
4605
4606 AutoReadLock alock (this);
4607 CHECK_READY();
4608
4609 *aCreated = mState >= Created;
4610 return S_OK;
4611}
4612
4613STDMETHODIMP HCustomHardDisk::COMGETTER(Format) (BSTR *aFormat)
4614{
4615 if (!aFormat)
4616 return E_POINTER;
4617
4618 AutoReadLock alock (this);
4619 CHECK_READY();
4620
4621 mFormat.cloneTo (aFormat);
4622 return S_OK;
4623}
4624
4625// ICustomHardDisk methods
4626/////////////////////////////////////////////////////////////////////////////
4627
4628STDMETHODIMP HCustomHardDisk::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
4629{
4630 if (!aProgress)
4631 return E_POINTER;
4632
4633 AutoWriteLock alock (this);
4634 CHECK_READY();
4635
4636 return createImage (aSize, TRUE /* aDynamic */, aProgress);
4637}
4638
4639STDMETHODIMP HCustomHardDisk::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
4640{
4641 if (!aProgress)
4642 return E_POINTER;
4643
4644 AutoWriteLock alock (this);
4645 CHECK_READY();
4646
4647 return createImage (aSize, FALSE /* aDynamic */, aProgress);
4648}
4649
4650STDMETHODIMP HCustomHardDisk::DeleteImage()
4651{
4652 AutoWriteLock alock (this);
4653 CHECK_READY();
4654 CHECK_BUSY_AND_READERS();
4655
4656 return E_NOTIMPL;
4657
4658/// @todo later
4659}
4660
4661// public/protected methods for internal purposes only
4662/////////////////////////////////////////////////////////////////////////////
4663
4664/**
4665 * Attempts to mark the hard disk as registered.
4666 * Only VirtualBox can call this method.
4667 */
4668HRESULT HCustomHardDisk::trySetRegistered (BOOL aRegistered)
4669{
4670 AutoWriteLock alock (this);
4671 CHECK_READY();
4672
4673 if (aRegistered)
4674 {
4675 if (mState == NotCreated)
4676 return setError (E_FAIL,
4677 tr ("Storage location '%ls' is not yet created for this hard disk"),
4678 mLocationFull.raw());
4679 }
4680 else
4681 {
4682 ComAssertRet (mState >= Created, E_FAIL);
4683 }
4684
4685 return HardDisk::trySetRegistered (aRegistered);
4686}
4687
4688/**
4689 * Checks accessibility of this hard disk image only (w/o parents).
4690 *
4691 * @param aAccessError on output, a null string indicates the hard disk is
4692 * accessible, otherwise contains a message describing
4693 * the reason of inaccessibility.
4694 *
4695 * @note Locks this object for writing.
4696 */
4697HRESULT HCustomHardDisk::getAccessible (Bstr &aAccessError)
4698{
4699 /* queryInformation() needs a write lock */
4700 AutoWriteLock alock (this);
4701 CHECK_READY();
4702
4703 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
4704 {
4705 /* An accessibility check in progress on some other thread,
4706 * wait for it to finish. */
4707
4708 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL);
4709 ++ mStateCheckWaiters;
4710 alock.leave();
4711
4712 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
4713
4714 alock.enter();
4715 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
4716 -- mStateCheckWaiters;
4717 if (mStateCheckWaiters == 0)
4718 {
4719 RTSemEventMultiDestroy (mStateCheckSem);
4720 mStateCheckSem = NIL_RTSEMEVENTMULTI;
4721 }
4722
4723 AssertRCReturn (vrc, E_FAIL);
4724
4725 /* don't touch aAccessError, it has been already set */
4726 return S_OK;
4727 }
4728
4729 /* check the basic accessibility */
4730 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */);
4731 if (FAILED (rc) || !aAccessError.isNull())
4732 return rc;
4733
4734 if (mState >= Created)
4735 {
4736 return queryInformation (&aAccessError);
4737 }
4738
4739 aAccessError = Utf8StrFmt ("Hard disk '%ls' is not yet created",
4740 mLocationFull.raw());
4741 return S_OK;
4742}
4743
4744/**
4745 * Saves hard disk settings to the specified storage node and saves
4746 * all children to the specified hard disk node
4747 *
4748 * @param aHDNode <HardDisk> or <DiffHardDisk> node.
4749 * @param aStorageNode <VirtualDiskImage> node.
4750 */
4751HRESULT HCustomHardDisk::saveSettings (settings::Key &aHDNode,
4752 settings::Key &aStorageNode)
4753{
4754 AssertReturn (!aHDNode.isNull() && !aStorageNode.isNull(), E_FAIL);
4755
4756 AutoReadLock alock (this);
4757 CHECK_READY();
4758
4759 /* location (required) */
4760 aStorageNode.setValue <Bstr> ("location", mLocationFull);
4761
4762 /* format (required) */
4763 aStorageNode.setValue <Bstr> ("format", mFormat);
4764
4765 /* save basic settings and children */
4766 return HardDisk::saveSettings (aHDNode);
4767}
4768
4769/**
4770 * Returns the string representation of this hard disk.
4771 * When \a aShort is false, returns the full image file path.
4772 * Otherwise, returns the image file name only.
4773 *
4774 * @param aShort if true, a short representation is returned
4775 *
4776 * @note Locks this object for reading.
4777 */
4778Bstr HCustomHardDisk::toString (bool aShort /* = false */)
4779{
4780 AutoReadLock alock (this);
4781
4782 /// @todo currently, we assume that location is always a file path for
4783 /// all custom hard disks. This is not generally correct, and needs to be
4784 /// parametrized in the VD plugin interface.
4785
4786 if (!aShort)
4787 return mLocationFull;
4788 else
4789 {
4790 Utf8Str fname = mLocationFull;
4791 return RTPathFilename (fname.mutableRaw());
4792 }
4793}
4794
4795/**
4796 * Creates a clone of this hard disk by storing hard disk data in the given
4797 * VDI file.
4798 *
4799 * If the operation fails, @a aDeleteTarget will be set to @c true unless the
4800 * failure happened because the target file already existed.
4801 *
4802 * @param aId UUID to assign to the created image.
4803 * @param aTargetPath VDI file where the cloned image is to be to stored.
4804 * @param aProgress progress object to run during operation.
4805 * @param aDeleteTarget Whether it is recommended to delete target on
4806 * failure or not.
4807 */
4808HRESULT
4809HCustomHardDisk::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
4810 Progress *aProgress, bool &aDeleteTarget)
4811{
4812 ComAssertMsgFailed (("Not implemented"));
4813 return E_NOTIMPL;
4814}
4815
4816/**
4817 * Creates a new differencing image for this hard disk with the given
4818 * VDI file name.
4819 *
4820 * @param aId UUID to assign to the created image
4821 * @param aTargetPath VDI file where to store the created differencing image
4822 * @param aProgress progress object to run during operation
4823 * (can be NULL)
4824 */
4825HRESULT
4826HCustomHardDisk::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
4827 Progress *aProgress)
4828{
4829 ComAssertMsgFailed (("Not implemented"));
4830 return E_NOTIMPL;
4831}
4832
4833// private methods
4834/////////////////////////////////////////////////////////////////////////////
4835
4836/**
4837 * Helper to set a new location.
4838 *
4839 * @note
4840 * Must be called from under the object's lock!
4841 */
4842HRESULT HCustomHardDisk::setLocation (const BSTR aLocation)
4843{
4844 /// @todo currently, we assume that location is always a file path for
4845 /// all custom hard disks. This is not generally correct, and needs to be
4846 /// parametrized in the VD plugin interface.
4847
4848 if (aLocation && *aLocation)
4849 {
4850 /* get the full file name */
4851 char locationFull [RTPATH_MAX];
4852 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aLocation),
4853 locationFull, sizeof (locationFull));
4854 if (VBOX_FAILURE (vrc))
4855 return setError (E_FAIL,
4856 tr ("Invalid hard disk location '%ls' (%Vrc)"), aLocation, vrc);
4857
4858 mLocation = aLocation;
4859 mLocationFull = locationFull;
4860 }
4861 else
4862 {
4863 mLocation.setNull();
4864 mLocationFull.setNull();
4865 }
4866
4867 return S_OK;
4868}
4869
4870/**
4871 * Helper to query information about the custom hard disk.
4872 *
4873 * @param aAccessError not used when NULL, otherwise see #getAccessible()
4874 *
4875 * @note Must be called from under the object's lock, only after
4876 * CHECK_BUSY_AND_READERS() succeeds.
4877 */
4878HRESULT HCustomHardDisk::queryInformation (Bstr *aAccessError)
4879{
4880 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
4881
4882 /* create a lock object to completely release it later */
4883 AutoWriteLock alock (this);
4884
4885 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
4886
4887 ComAssertRet (mState >= Created, E_FAIL);
4888
4889 HRESULT rc = S_OK;
4890 int vrc = VINF_SUCCESS;
4891
4892 /* lazily create a semaphore */
4893 vrc = RTSemEventMultiCreate (&mStateCheckSem);
4894 ComAssertRCRet (vrc, E_FAIL);
4895
4896 /* Reference the disk to prevent any concurrent modifications
4897 * after releasing the lock below (to unblock getters before
4898 * a lengthy operation). */
4899 addReader();
4900
4901 alock.leave();
4902
4903 /* VBoxVHDD management interface needs to be optimized: we're opening a
4904 * file three times in a raw to get three bits of information. */
4905
4906 Utf8Str location = mLocationFull;
4907 Bstr errMsg;
4908
4909 /* reset any previous error report from VDError() */
4910 mLastVDError.setNull();
4911
4912 do
4913 {
4914 Guid id, parentId;
4915
4916 vrc = VDOpen (mContainer, Utf8Str (mFormat), location, VD_OPEN_FLAGS_INFO);
4917 if (VBOX_FAILURE (vrc))
4918 break;
4919
4920 vrc = VDGetUuid (mContainer, 0, id.ptr());
4921 if (VBOX_FAILURE (vrc))
4922 break;
4923 vrc = VDGetParentUuid (mContainer, 0, parentId.ptr());
4924 if (VBOX_FAILURE (vrc))
4925 break;
4926
4927 if (!mId.isEmpty())
4928 {
4929 /* check that the actual UUID of the image matches the stored UUID */
4930 if (mId != id)
4931 {
4932 errMsg = Utf8StrFmt (
4933 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
4934 "match UUID {%Vuuid} stored in the registry"),
4935 id.ptr(), location.raw(), mId.ptr());
4936 break;
4937 }
4938 }
4939 else
4940 {
4941 /* assgn an UUID read from the image file */
4942 mId = id;
4943 }
4944
4945 if (mParent)
4946 {
4947 /* check parent UUID */
4948 AutoWriteLock parentLock (mParent);
4949 if (mParent->id() != parentId)
4950 {
4951 errMsg = Utf8StrFmt (
4952 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
4953 "the hard disk image file '%s' doesn't match "
4954 "UUID {%Vuuid} stored in the registry"),
4955 parentId.raw(), mParent->toString().raw(),
4956 location.raw(), mParent->id().raw());
4957 break;
4958 }
4959 }
4960 else if (!parentId.isEmpty())
4961 {
4962 errMsg = Utf8StrFmt (
4963 tr ("Hard disk image '%s' is a differencing image that is linked "
4964 "to a hard disk with UUID {%Vuuid} and cannot be used "
4965 "directly as a base hard disk"),
4966 location.raw(), parentId.raw());
4967 break;
4968 }
4969
4970 /* get actual file size */
4971 /// @todo is there a direct method in RT?
4972 {
4973 RTFILE file = NIL_RTFILE;
4974 vrc = RTFileOpen (&file, location, RTFILE_O_READ);
4975 if (VBOX_SUCCESS (vrc))
4976 {
4977 uint64_t size = 0;
4978 vrc = RTFileGetSize (file, &size);
4979 if (VBOX_SUCCESS (vrc))
4980 mActualSize = size;
4981 RTFileClose (file);
4982 }
4983 if (VBOX_FAILURE (vrc))
4984 break;
4985 }
4986
4987 /* query logical size only for non-differencing images */
4988 if (!mParent)
4989 {
4990 uint64_t size = VDGetSize (mContainer, 0);
4991 /* convert to MBytes */
4992 mSize = size / 1024 / 1024;
4993 }
4994 }
4995 while (0);
4996
4997 VDCloseAll (mContainer);
4998
4999 /* enter the lock again */
5000 alock.enter();
5001
5002 /* remove the reference */
5003 releaseReader();
5004
5005 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
5006 {
5007 LogWarningFunc (("'%ls' is not accessible "
5008 "(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
5009 mLocationFull.raw(), rc, vrc, errMsg.raw()));
5010
5011 if (aAccessError)
5012 {
5013 if (!errMsg.isNull())
5014 *aAccessError = errMsg;
5015 else if (!mLastVDError.isNull())
5016 *aAccessError = mLastVDError;
5017 else if (VBOX_FAILURE (vrc))
5018 *aAccessError = Utf8StrFmt (
5019 tr ("Could not access hard disk '%ls' (%Vrc)"),
5020 mLocationFull.raw(), vrc);
5021 }
5022
5023 /* downgrade to not accessible */
5024 mState = Created;
5025 }
5026 else
5027 {
5028 if (aAccessError)
5029 aAccessError->setNull();
5030
5031 mState = Accessible;
5032 }
5033
5034 /* inform waiters if there are any */
5035 if (mStateCheckWaiters > 0)
5036 {
5037 RTSemEventMultiSignal (mStateCheckSem);
5038 }
5039 else
5040 {
5041 /* delete the semaphore ourselves */
5042 RTSemEventMultiDestroy (mStateCheckSem);
5043 mStateCheckSem = NIL_RTSEMEVENTMULTI;
5044 }
5045
5046 /* cleanup the last error report from VDError() */
5047 mLastVDError.setNull();
5048
5049 return rc;
5050}
5051
5052/**
5053 * Helper to create hard disk images.
5054 *
5055 * @param aSize size in MB
5056 * @param aDynamic dynamic or fixed image
5057 * @param aProgress address of IProgress pointer to return
5058 */
5059HRESULT HCustomHardDisk::createImage (ULONG64 aSize, BOOL aDynamic,
5060 IProgress **aProgress)
5061{
5062 ComAssertMsgFailed (("Not implemented"));
5063 return E_NOTIMPL;
5064}
5065
5066/* static */
5067DECLCALLBACK(int) HCustomHardDisk::VDITaskThread (RTTHREAD thread, void *pvUser)
5068{
5069 AssertMsgFailed (("Not implemented"));
5070 return VERR_GENERAL_FAILURE;
5071}
5072
5073/* static */
5074DECLCALLBACK(void) HCustomHardDisk::VDError (void *pvUser, int rc, RT_SRC_POS_DECL,
5075 const char *pszFormat, va_list va)
5076{
5077 HCustomHardDisk *that = static_cast <HCustomHardDisk *> (pvUser);
5078 AssertReturnVoid (that != NULL);
5079
5080 /// @todo pass the error message to the operation initiator
5081 Utf8Str err = Utf8StrFmtVA (pszFormat, va);
5082 if (VBOX_FAILURE (rc))
5083 err = Utf8StrFmt ("%s (%Vrc)", err.raw(), rc);
5084
5085 if (that->mLastVDError.isNull())
5086 that->mLastVDError = err;
5087 else
5088 that->mLastVDError = Utf8StrFmt
5089 ("%s.\n%s", that->mLastVDError.raw(), err.raw());
5090}
5091
5092////////////////////////////////////////////////////////////////////////////////
5093// HVHDImage class
5094////////////////////////////////////////////////////////////////////////////////
5095
5096// constructor / destructor
5097////////////////////////////////////////////////////////////////////////////////
5098
5099HRESULT HVHDImage::FinalConstruct()
5100{
5101 HRESULT rc = HardDisk::FinalConstruct();
5102 if (FAILED (rc))
5103 return rc;
5104
5105 mState = NotCreated;
5106
5107 mStateCheckSem = NIL_RTSEMEVENTMULTI;
5108 mStateCheckWaiters = 0;
5109
5110 mSize = 0;
5111 mActualSize = 0;
5112
5113 /* initialize the container */
5114 int vrc = VDCreate (VDError, this, &mContainer);
5115 ComAssertRCRet (vrc, E_FAIL);
5116
5117 return S_OK;
5118}
5119
5120void HVHDImage::FinalRelease()
5121{
5122 if (mContainer != NULL)
5123 VDDestroy (mContainer);
5124
5125 HardDisk::FinalRelease();
5126}
5127
5128// public initializer/uninitializer for internal purposes only
5129////////////////////////////////////////////////////////////////////////////////
5130
5131// public methods for internal purposes only
5132/////////////////////////////////////////////////////////////////////////////
5133
5134/**
5135 * Initializes the VHD hard disk object by reading its properties from
5136 * the given configuration node. The created hard disk will be marked as
5137 * registered on success.
5138 *
5139 * @param aHDNode <HardDisk> node
5140 * @param aVHDNode <VirtualDiskImage> node
5141 */
5142HRESULT HVHDImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
5143 const settings::Key &aHDNode,
5144 const settings::Key &aVHDNode)
5145{
5146 LogFlowThisFunc (("\n"));
5147
5148 AssertReturn (!aHDNode.isNull() && !aVHDNode.isNull(), E_FAIL);
5149
5150 AutoWriteLock alock (this);
5151 ComAssertRet (!isReady(), E_UNEXPECTED);
5152
5153 mStorageType = HardDiskStorageType_VHDImage;
5154
5155 HRESULT rc = S_OK;
5156
5157 do
5158 {
5159 rc = protectedInit (aVirtualBox, aParent);
5160 CheckComRCBreakRC (rc);
5161
5162 /* set ready to let protectedUninit() be called on failure */
5163 setReady (true);
5164
5165 /* filePath (required) */
5166 Bstr filePath = aVHDNode.stringValue("filePath");
5167
5168 rc = setFilePath (filePath);
5169 CheckComRCBreakRC (rc);
5170
5171 LogFlowThisFunc (("'%ls'\n", mFilePathFull.raw()));
5172
5173 /* load basic settings and children */
5174 rc = loadSettings (aHDNode);
5175 CheckComRCBreakRC (rc);
5176
5177 if (mType != HardDiskType_Writethrough)
5178 {
5179 rc = setError (E_FAIL,
5180 tr ("Currently, non-Writethrough VHD images are not "
5181 "allowed ('%ls')"),
5182 toString().raw());
5183 break;
5184 }
5185
5186 mState = Created;
5187 mRegistered = TRUE;
5188
5189 /* Don't call queryInformation() for registered hard disks to
5190 * prevent the calling thread (i.e. the VirtualBox server startup
5191 * thread) from an unexpected freeze. The vital mId property (UUID)
5192 * is read from the registry file in loadSettings(). To get the rest,
5193 * the user will have to call COMGETTER(Accessible) manually. */
5194 }
5195 while (0);
5196
5197 if (FAILED (rc))
5198 uninit();
5199
5200 return rc;
5201}
5202
5203/**
5204 * Initializes the VHD hard disk object using the given image file name.
5205 *
5206 * @param aVirtualBox VirtualBox parent.
5207 * @param aParent Currently, must always be @c NULL.
5208 * @param aFilePath Path to the image file, or @c NULL to create an
5209 * image-less object.
5210 * @param aRegistered Whether to mark this disk as registered or not
5211 * (ignored when @a aFilePath is @c NULL, assuming @c FALSE)
5212 */
5213HRESULT HVHDImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
5214 const BSTR aFilePath, BOOL aRegistered /* = FALSE */)
5215{
5216 LogFlowThisFunc (("aFilePath='%ls', aRegistered=%d\n", aFilePath, aRegistered));
5217
5218 AssertReturn (aParent == NULL, E_FAIL);
5219
5220 AutoWriteLock alock (this);
5221 ComAssertRet (!isReady(), E_UNEXPECTED);
5222
5223 mStorageType = HardDiskStorageType_VHDImage;
5224
5225 HRESULT rc = S_OK;
5226
5227 do
5228 {
5229 rc = protectedInit (aVirtualBox, aParent);
5230 CheckComRCBreakRC (rc);
5231
5232 /* set ready to let protectedUninit() be called on failure */
5233 setReady (true);
5234
5235 rc = setFilePath (aFilePath);
5236 CheckComRCBreakRC (rc);
5237
5238 /* currently, all VHD hard disks are writethrough */
5239 mType = HardDiskType_Writethrough;
5240
5241 Assert (mId.isEmpty());
5242
5243 if (aFilePath && *aFilePath)
5244 {
5245 mRegistered = aRegistered;
5246 mState = Created;
5247
5248 /* Call queryInformation() anyway (even if it will block), because
5249 * it is the only way to get the UUID of the existing VDI and
5250 * initialize the vital mId property. */
5251 Bstr errMsg;
5252 rc = queryInformation (&errMsg);
5253 if (SUCCEEDED (rc))
5254 {
5255 /* We are constructing a new HVirtualDiskImage object. If there
5256 * is a fatal accessibility error (we cannot read image UUID),
5257 * we have to fail. We do so even on non-fatal errors as well,
5258 * because it's not worth to keep going with the inaccessible
5259 * image from the very beginning (when nothing else depends on
5260 * it yet). */
5261 if (!errMsg.isNull())
5262 rc = setErrorBstr (E_FAIL, errMsg);
5263 }
5264 }
5265 else
5266 {
5267 mRegistered = FALSE;
5268 mState = NotCreated;
5269 mId.create();
5270 }
5271 }
5272 while (0);
5273
5274 if (FAILED (rc))
5275 uninit();
5276
5277 return rc;
5278}
5279
5280/**
5281 * Uninitializes the instance and sets the ready flag to FALSE.
5282 * Called either from FinalRelease(), by the parent when it gets destroyed,
5283 * or by a third party when it decides this object is no more valid.
5284 */
5285void HVHDImage::uninit()
5286{
5287 LogFlowThisFunc (("\n"));
5288
5289 AutoWriteLock alock (this);
5290 if (!isReady())
5291 return;
5292
5293 HardDisk::protectedUninit (alock);
5294}
5295
5296// IHardDisk properties
5297////////////////////////////////////////////////////////////////////////////////
5298
5299STDMETHODIMP HVHDImage::COMGETTER(Description) (BSTR *aDescription)
5300{
5301 if (!aDescription)
5302 return E_POINTER;
5303
5304 AutoReadLock alock (this);
5305 CHECK_READY();
5306
5307 mDescription.cloneTo (aDescription);
5308 return S_OK;
5309}
5310
5311STDMETHODIMP HVHDImage::COMSETTER(Description) (INPTR BSTR aDescription)
5312{
5313 AutoWriteLock alock (this);
5314 CHECK_READY();
5315
5316 CHECK_BUSY_AND_READERS();
5317
5318 return E_NOTIMPL;
5319
5320/// @todo implement
5321//
5322// if (mState >= Created)
5323// {
5324// int vrc = VDISetImageComment (Utf8Str (mFilePathFull), Utf8Str (aDescription));
5325// if (VBOX_FAILURE (vrc))
5326// return setError (E_FAIL,
5327// tr ("Could not change the description of the VDI hard disk '%ls' "
5328// "(%Vrc)"),
5329// toString().raw(), vrc);
5330// }
5331//
5332// mDescription = aDescription;
5333// return S_OK;
5334}
5335
5336STDMETHODIMP HVHDImage::COMGETTER(Size) (ULONG64 *aSize)
5337{
5338 if (!aSize)
5339 return E_POINTER;
5340
5341 AutoReadLock alock (this);
5342 CHECK_READY();
5343
5344/// @todo will need this if we add suppord for differencing VMDKs
5345//
5346// /* only a non-differencing image knows the logical size */
5347// if (isDifferencing())
5348// return root()->COMGETTER(Size) (aSize);
5349
5350 *aSize = mSize;
5351 return S_OK;
5352}
5353
5354STDMETHODIMP HVHDImage::COMGETTER(ActualSize) (ULONG64 *aActualSize)
5355{
5356 if (!aActualSize)
5357 return E_POINTER;
5358
5359 AutoReadLock alock (this);
5360 CHECK_READY();
5361
5362 *aActualSize = mActualSize;
5363 return S_OK;
5364}
5365
5366// IVirtualDiskImage properties
5367////////////////////////////////////////////////////////////////////////////////
5368
5369STDMETHODIMP HVHDImage::COMGETTER(FilePath) (BSTR *aFilePath)
5370{
5371 if (!aFilePath)
5372 return E_POINTER;
5373
5374 AutoReadLock alock (this);
5375 CHECK_READY();
5376
5377 mFilePathFull.cloneTo (aFilePath);
5378 return S_OK;
5379}
5380
5381STDMETHODIMP HVHDImage::COMSETTER(FilePath) (INPTR BSTR aFilePath)
5382{
5383 AutoWriteLock alock (this);
5384 CHECK_READY();
5385
5386 if (mState != NotCreated)
5387 return setError (E_ACCESSDENIED,
5388 tr ("Cannot change the file path of the existing hard disk '%ls'"),
5389 toString().raw());
5390
5391 CHECK_BUSY_AND_READERS();
5392
5393 /* append the default path if only a name is given */
5394 Bstr path = aFilePath;
5395 if (aFilePath && *aFilePath)
5396 {
5397 Utf8Str fp = aFilePath;
5398 if (!RTPathHavePath (fp))
5399 {
5400 AutoReadLock propsLock (mVirtualBox->systemProperties());
5401 path = Utf8StrFmt ("%ls%c%s",
5402 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
5403 RTPATH_DELIMITER,
5404 fp.raw());
5405 }
5406 }
5407
5408 return setFilePath (path);
5409}
5410
5411STDMETHODIMP HVHDImage::COMGETTER(Created) (BOOL *aCreated)
5412{
5413 if (!aCreated)
5414 return E_POINTER;
5415
5416 AutoReadLock alock (this);
5417 CHECK_READY();
5418
5419 *aCreated = mState >= Created;
5420 return S_OK;
5421}
5422
5423// IVHDImage methods
5424/////////////////////////////////////////////////////////////////////////////
5425
5426STDMETHODIMP HVHDImage::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
5427{
5428 if (!aProgress)
5429 return E_POINTER;
5430
5431 AutoWriteLock alock (this);
5432 CHECK_READY();
5433
5434 return createImage (aSize, TRUE /* aDynamic */, aProgress);
5435}
5436
5437STDMETHODIMP HVHDImage::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
5438{
5439 if (!aProgress)
5440 return E_POINTER;
5441
5442 AutoWriteLock alock (this);
5443 CHECK_READY();
5444
5445 return createImage (aSize, FALSE /* aDynamic */, aProgress);
5446}
5447
5448STDMETHODIMP HVHDImage::DeleteImage()
5449{
5450 AutoWriteLock alock (this);
5451 CHECK_READY();
5452 CHECK_BUSY_AND_READERS();
5453
5454 return E_NOTIMPL;
5455
5456/// @todo later
5457// We will need to parse the file in order to delete all related delta and
5458// sparse images etc. We may also want to obey the .vmdk.lck file
5459// which is (as far as I understood) created when the VMware VM is
5460// running or saved etc.
5461//
5462// if (mRegistered)
5463// return setError (E_ACCESSDENIED,
5464// tr ("Cannot delete an image of the registered hard disk image '%ls"),
5465// mFilePathFull.raw());
5466// if (mState == NotCreated)
5467// return setError (E_FAIL,
5468// tr ("Hard disk image has been already deleted or never created"));
5469//
5470// int vrc = RTFileDelete (Utf8Str (mFilePathFull));
5471// if (VBOX_FAILURE (vrc))
5472// return setError (E_FAIL, tr ("Could not delete the image file '%ls' (%Vrc)"),
5473// mFilePathFull.raw(), vrc);
5474//
5475// mState = NotCreated;
5476//
5477// /* reset the fields */
5478// mSize = 0;
5479// mActualSize = 0;
5480//
5481// return S_OK;
5482}
5483
5484// public/protected methods for internal purposes only
5485/////////////////////////////////////////////////////////////////////////////
5486
5487/**
5488 * Attempts to mark the hard disk as registered.
5489 * Only VirtualBox can call this method.
5490 */
5491HRESULT HVHDImage::trySetRegistered (BOOL aRegistered)
5492{
5493 AutoWriteLock alock (this);
5494 CHECK_READY();
5495
5496 if (aRegistered)
5497 {
5498 if (mState == NotCreated)
5499 return setError (E_FAIL,
5500 tr ("Image file '%ls' is not yet created for this hard disk"),
5501 mFilePathFull.raw());
5502
5503/// @todo will need this if we add suppord for differencing VHDs
5504// if (isDifferencing())
5505// return setError (E_FAIL,
5506// tr ("Hard disk '%ls' is differencing and cannot be unregistered "
5507// "explicitly"),
5508// mFilePathFull.raw());
5509 }
5510 else
5511 {
5512 ComAssertRet (mState >= Created, E_FAIL);
5513 }
5514
5515 return HardDisk::trySetRegistered (aRegistered);
5516}
5517
5518/**
5519 * Checks accessibility of this hard disk image only (w/o parents).
5520 *
5521 * @param aAccessError on output, a null string indicates the hard disk is
5522 * accessible, otherwise contains a message describing
5523 * the reason of inaccessibility.
5524 *
5525 * @note Locks this object for writing.
5526 */
5527HRESULT HVHDImage::getAccessible (Bstr &aAccessError)
5528{
5529 /* queryInformation() needs a write lock */
5530 AutoWriteLock alock (this);
5531 CHECK_READY();
5532
5533 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
5534 {
5535 /* An accessibility check in progress on some other thread,
5536 * wait for it to finish. */
5537
5538 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL);
5539 ++ mStateCheckWaiters;
5540 alock.leave();
5541
5542 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
5543
5544 alock.enter();
5545 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
5546 -- mStateCheckWaiters;
5547 if (mStateCheckWaiters == 0)
5548 {
5549 RTSemEventMultiDestroy (mStateCheckSem);
5550 mStateCheckSem = NIL_RTSEMEVENTMULTI;
5551 }
5552
5553 AssertRCReturn (vrc, E_FAIL);
5554
5555 /* don't touch aAccessError, it has been already set */
5556 return S_OK;
5557 }
5558
5559 /* check the basic accessibility */
5560 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */);
5561 if (FAILED (rc) || !aAccessError.isNull())
5562 return rc;
5563
5564 if (mState >= Created)
5565 {
5566 return queryInformation (&aAccessError);
5567 }
5568
5569 aAccessError = Utf8StrFmt ("Hard disk image '%ls' is not yet created",
5570 mFilePathFull.raw());
5571 return S_OK;
5572}
5573
5574/**
5575 * Saves hard disk settings to the specified storage node and saves
5576 * all children to the specified hard disk node
5577 *
5578 * @param aHDNode <HardDisk> or <DiffHardDisk> node
5579 * @param aStorageNode <VirtualDiskImage> node
5580 */
5581HRESULT HVHDImage::saveSettings (settings::Key &aHDNode, settings::Key &aStorageNode)
5582{
5583 AssertReturn (!aHDNode.isNull() && !aStorageNode.isNull(), E_FAIL);
5584
5585 AutoReadLock alock (this);
5586 CHECK_READY();
5587
5588 /* filePath (required) */
5589 aStorageNode.setValue <Bstr> ("filePath", mFilePath);
5590
5591 /* save basic settings and children */
5592 return HardDisk::saveSettings (aHDNode);
5593}
5594
5595/**
5596 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
5597 * of this hard disk and updates it if necessary to reflect the new location.
5598 * Intended to be from HardDisk::updatePaths().
5599 *
5600 * @param aOldPath old path (full)
5601 * @param aNewPath new path (full)
5602 *
5603 * @note Locks this object for writing.
5604 */
5605void HVHDImage::updatePath (const char *aOldPath, const char *aNewPath)
5606{
5607 AssertReturnVoid (aOldPath);
5608 AssertReturnVoid (aNewPath);
5609
5610 AutoWriteLock alock (this);
5611 AssertReturnVoid (isReady());
5612
5613 size_t oldPathLen = strlen (aOldPath);
5614
5615 Utf8Str path = mFilePathFull;
5616 LogFlowThisFunc (("VHD.fullPath={%s}\n", path.raw()));
5617
5618 if (RTPathStartsWith (path, aOldPath))
5619 {
5620 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
5621 path.raw() + oldPathLen);
5622 path = newPath;
5623
5624 mVirtualBox->calculateRelativePath (path, path);
5625
5626 unconst (mFilePathFull) = newPath;
5627 unconst (mFilePath) = path;
5628
5629 LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
5630 newPath.raw(), path.raw()));
5631 }
5632}
5633
5634/**
5635 * Returns the string representation of this hard disk.
5636 * When \a aShort is false, returns the full image file path.
5637 * Otherwise, returns the image file name only.
5638 *
5639 * @param aShort if true, a short representation is returned
5640 *
5641 * @note Locks this object for reading.
5642 */
5643Bstr HVHDImage::toString (bool aShort /* = false */)
5644{
5645 AutoReadLock alock (this);
5646
5647 if (!aShort)
5648 return mFilePathFull;
5649 else
5650 {
5651 Utf8Str fname = mFilePathFull;
5652 return RTPathFilename (fname.mutableRaw());
5653 }
5654}
5655
5656/**
5657 * Creates a clone of this hard disk by storing hard disk data in the given
5658 * VDI file.
5659 *
5660 * If the operation fails, @a aDeleteTarget will be set to @c true unless the
5661 * failure happened because the target file already existed.
5662 *
5663 * @param aId UUID to assign to the created image.
5664 * @param aTargetPath VDI file where the cloned image is to be to stored.
5665 * @param aProgress progress object to run during operation.
5666 * @param aDeleteTarget Whether it is recommended to delete target on
5667 * failure or not.
5668 */
5669HRESULT
5670HVHDImage::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
5671 Progress *aProgress, bool &aDeleteTarget)
5672{
5673 ComAssertMsgFailed (("Not implemented"));
5674 return E_NOTIMPL;
5675
5676/// @todo will need this if we add suppord for differencing VHDs
5677// Use code from HVirtualDiskImage::cloneToImage as an example.
5678}
5679
5680/**
5681 * Creates a new differencing image for this hard disk with the given
5682 * VDI file name.
5683 *
5684 * @param aId UUID to assign to the created image
5685 * @param aTargetPath VDI file where to store the created differencing image
5686 * @param aProgress progress object to run during operation
5687 * (can be NULL)
5688 */
5689HRESULT
5690HVHDImage::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
5691 Progress *aProgress)
5692{
5693 ComAssertMsgFailed (("Not implemented"));
5694 return E_NOTIMPL;
5695
5696/// @todo will need this if we add suppord for differencing VHDs
5697// Use code from HVirtualDiskImage::createDiffImage as an example.
5698}
5699
5700// private methods
5701/////////////////////////////////////////////////////////////////////////////
5702
5703/**
5704 * Helper to set a new file path.
5705 * Resolves a path relatively to the Virtual Box home directory.
5706 *
5707 * @note
5708 * Must be called from under the object's lock!
5709 */
5710HRESULT HVHDImage::setFilePath (const BSTR aFilePath)
5711{
5712 if (aFilePath && *aFilePath)
5713 {
5714 /* get the full file name */
5715 char filePathFull [RTPATH_MAX];
5716 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aFilePath),
5717 filePathFull, sizeof (filePathFull));
5718 if (VBOX_FAILURE (vrc))
5719 return setError (E_FAIL,
5720 tr ("Invalid image file path '%ls' (%Vrc)"), aFilePath, vrc);
5721
5722 mFilePath = aFilePath;
5723 mFilePathFull = filePathFull;
5724 }
5725 else
5726 {
5727 mFilePath.setNull();
5728 mFilePathFull.setNull();
5729 }
5730
5731 return S_OK;
5732}
5733
5734/**
5735 * Helper to query information about the VDI hard disk.
5736 *
5737 * @param aAccessError not used when NULL, otherwise see #getAccessible()
5738 *
5739 * @note Must be called from under the object's lock, only after
5740 * CHECK_BUSY_AND_READERS() succeeds.
5741 */
5742HRESULT HVHDImage::queryInformation (Bstr *aAccessError)
5743{
5744 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
5745
5746 /* create a lock object to completely release it later */
5747 AutoWriteLock alock (this);
5748
5749 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
5750
5751 ComAssertRet (mState >= Created, E_FAIL);
5752
5753 HRESULT rc = S_OK;
5754 int vrc = VINF_SUCCESS;
5755
5756 /* lazily create a semaphore */
5757 vrc = RTSemEventMultiCreate (&mStateCheckSem);
5758 ComAssertRCRet (vrc, E_FAIL);
5759
5760 /* Reference the disk to prevent any concurrent modifications
5761 * after releasing the lock below (to unblock getters before
5762 * a lengthy operation). */
5763 addReader();
5764
5765 alock.leave();
5766
5767 /* VBoxVHDD management interface needs to be optimized: we're opening a
5768 * file three times in a raw to get three bits of information. */
5769
5770 Utf8Str filePath = mFilePathFull;
5771 Bstr errMsg;
5772
5773 /* reset any previous error report from VDError() */
5774 mLastVDError.setNull();
5775
5776 do
5777 {
5778 Guid id, parentId;
5779
5780 /// @todo changed from VD_OPEN_FLAGS_READONLY to VD_OPEN_FLAGS_NORMAL,
5781 /// because otherwise registering a VHD which so far has no UUID will
5782 /// yield a null UUID. It cannot be added to a VHD opened readonly,
5783 /// obviously. This of course changes locking behavior, but for now
5784 /// this is acceptable. A better solution needs to be found later.
5785 vrc = VDOpen (mContainer, "VHD", filePath, VD_OPEN_FLAGS_NORMAL);
5786 if (VBOX_FAILURE (vrc))
5787 break;
5788
5789 vrc = VDGetUuid (mContainer, 0, id.ptr());
5790 if (VBOX_FAILURE (vrc))
5791 break;
5792 vrc = VDGetParentUuid (mContainer, 0, parentId.ptr());
5793 if (VBOX_FAILURE (vrc))
5794 break;
5795
5796 if (!mId.isEmpty())
5797 {
5798 /* check that the actual UUID of the image matches the stored UUID */
5799 if (mId != id)
5800 {
5801 errMsg = Utf8StrFmt (
5802 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
5803 "match UUID {%Vuuid} stored in the registry"),
5804 id.ptr(), filePath.raw(), mId.ptr());
5805 break;
5806 }
5807 }
5808 else
5809 {
5810 /* assgn an UUID read from the image file */
5811 mId = id;
5812 }
5813
5814 if (mParent)
5815 {
5816 /* check parent UUID */
5817 AutoWriteLock parentLock (mParent);
5818 if (mParent->id() != parentId)
5819 {
5820 errMsg = Utf8StrFmt (
5821 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
5822 "the hard disk image file '%s' doesn't match "
5823 "UUID {%Vuuid} stored in the registry"),
5824 parentId.raw(), mParent->toString().raw(),
5825 filePath.raw(), mParent->id().raw());
5826 break;
5827 }
5828 }
5829 else if (!parentId.isEmpty())
5830 {
5831 errMsg = Utf8StrFmt (
5832 tr ("Hard disk image '%s' is a differencing image that is linked "
5833 "to a hard disk with UUID {%Vuuid} and cannot be used "
5834 "directly as a base hard disk"),
5835 filePath.raw(), parentId.raw());
5836 break;
5837 }
5838
5839 /* get actual file size */
5840 /// @todo is there a direct method in RT?
5841 {
5842 RTFILE file = NIL_RTFILE;
5843 vrc = RTFileOpen (&file, filePath, RTFILE_O_READ);
5844 if (VBOX_SUCCESS (vrc))
5845 {
5846 uint64_t size = 0;
5847 vrc = RTFileGetSize (file, &size);
5848 if (VBOX_SUCCESS (vrc))
5849 mActualSize = size;
5850 RTFileClose (file);
5851 }
5852 if (VBOX_FAILURE (vrc))
5853 break;
5854 }
5855
5856 /* query logical size only for non-differencing images */
5857 if (!mParent)
5858 {
5859 uint64_t size = VDGetSize (mContainer, 0);
5860 /* convert to MBytes */
5861 mSize = size / 1024 / 1024;
5862 }
5863 }
5864 while (0);
5865
5866 VDCloseAll (mContainer);
5867
5868 /* enter the lock again */
5869 alock.enter();
5870
5871 /* remove the reference */
5872 releaseReader();
5873
5874 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
5875 {
5876 LogWarningFunc (("'%ls' is not accessible "
5877 "(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
5878 mFilePathFull.raw(), rc, vrc, errMsg.raw()));
5879
5880 if (aAccessError)
5881 {
5882 if (!errMsg.isNull())
5883 *aAccessError = errMsg;
5884 else if (!mLastVDError.isNull())
5885 *aAccessError = mLastVDError;
5886 else if (VBOX_FAILURE (vrc))
5887 *aAccessError = Utf8StrFmt (
5888 tr ("Could not access hard disk image '%ls' (%Vrc)"),
5889 mFilePathFull.raw(), vrc);
5890 }
5891
5892 /* downgrade to not accessible */
5893 mState = Created;
5894 }
5895 else
5896 {
5897 if (aAccessError)
5898 aAccessError->setNull();
5899
5900 mState = Accessible;
5901 }
5902
5903 /* inform waiters if there are any */
5904 if (mStateCheckWaiters > 0)
5905 {
5906 RTSemEventMultiSignal (mStateCheckSem);
5907 }
5908 else
5909 {
5910 /* delete the semaphore ourselves */
5911 RTSemEventMultiDestroy (mStateCheckSem);
5912 mStateCheckSem = NIL_RTSEMEVENTMULTI;
5913 }
5914
5915 /* cleanup the last error report from VDError() */
5916 mLastVDError.setNull();
5917
5918 return rc;
5919}
5920
5921/**
5922 * Helper to create hard disk images.
5923 *
5924 * @param aSize size in MB
5925 * @param aDynamic dynamic or fixed image
5926 * @param aProgress address of IProgress pointer to return
5927 */
5928HRESULT HVHDImage::createImage (ULONG64 aSize, BOOL aDynamic,
5929 IProgress **aProgress)
5930{
5931 ComAssertMsgFailed (("Not implemented"));
5932 return E_NOTIMPL;
5933
5934/// @todo later
5935// Use code from HVirtualDiskImage::createImage as an example.
5936}
5937
5938/* static */
5939DECLCALLBACK(int) HVHDImage::VDITaskThread (RTTHREAD thread, void *pvUser)
5940{
5941 AssertMsgFailed (("Not implemented"));
5942 return VERR_GENERAL_FAILURE;
5943
5944/// @todo later
5945// Use code from HVirtualDiskImage::VDITaskThread as an example.
5946}
5947
5948/* static */
5949DECLCALLBACK(void) HVHDImage::VDError (void *pvUser, int rc, RT_SRC_POS_DECL,
5950 const char *pszFormat, va_list va)
5951{
5952 HVHDImage *that = static_cast <HVHDImage *> (pvUser);
5953 AssertReturnVoid (that != NULL);
5954
5955 /// @todo pass the error message to the operation initiator
5956 Utf8Str err = Utf8StrFmtVA (pszFormat, va);
5957 if (VBOX_FAILURE (rc))
5958 err = Utf8StrFmt ("%s (%Vrc)", err.raw(), rc);
5959
5960 if (that->mLastVDError.isNull())
5961 that->mLastVDError = err;
5962 else
5963 that->mLastVDError = Utf8StrFmt
5964 ("%s.\n%s", that->mLastVDError.raw(), err.raw());
5965}
5966
Note: See TracBrowser for help on using the repository browser.

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