VirtualBox

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

Last change on this file since 8861 was 8155, checked in by vboxsync, 17 years ago

The Big Sun Rebranding Header Change

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 168.0 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 AssertRC (vrc);
4441 rc = setError (E_FAIL,
4442 tr ("Cannot recognize the format of the custom "
4443 "hard disk '%ls' (%Vrc)"),
4444 toString().raw(), vrc);
4445 break;
4446 }
4447 mFormat = Bstr (pszFormat);
4448 RTStrFree (pszFormat);
4449
4450 /* Create the corresponding container. */
4451 vrc = VDCreate (VDError, this, &mContainer);
4452
4453 /* the format has been already checked for presence at this point */
4454 ComAssertRCBreak (vrc, rc = E_FAIL);
4455
4456 /* Call queryInformation() anyway (even if it will block), because
4457 * it is the only way to get the UUID of the existing VDI and
4458 * initialize the vital mId property. */
4459 Bstr errMsg;
4460 rc = queryInformation (&errMsg);
4461 if (SUCCEEDED (rc))
4462 {
4463 /* We are constructing a new HVirtualDiskImage object. If there
4464 * is a fatal accessibility error (we cannot read image UUID),
4465 * we have to fail. We do so even on non-fatal errors as well,
4466 * because it's not worth to keep going with the inaccessible
4467 * image from the very beginning (when nothing else depends on
4468 * it yet). */
4469 if (!errMsg.isNull())
4470 rc = setErrorBstr (E_FAIL, errMsg);
4471 }
4472 }
4473 else
4474 {
4475 mRegistered = FALSE;
4476 mState = NotCreated;
4477 mId.create();
4478 }
4479 }
4480 while (0);
4481
4482 if (FAILED (rc))
4483 uninit();
4484
4485 return rc;
4486}
4487
4488/**
4489 * Uninitializes the instance and sets the ready flag to FALSE.
4490 * Called either from FinalRelease(), by the parent when it gets destroyed,
4491 * or by a third party when it decides this object is no more valid.
4492 */
4493void HCustomHardDisk::uninit()
4494{
4495 LogFlowThisFunc (("\n"));
4496
4497 AutoWriteLock alock (this);
4498 if (!isReady())
4499 return;
4500
4501 HardDisk::protectedUninit (alock);
4502}
4503
4504// IHardDisk properties
4505////////////////////////////////////////////////////////////////////////////////
4506
4507STDMETHODIMP HCustomHardDisk::COMGETTER(Description) (BSTR *aDescription)
4508{
4509 if (!aDescription)
4510 return E_POINTER;
4511
4512 AutoReadLock alock (this);
4513 CHECK_READY();
4514
4515 mDescription.cloneTo (aDescription);
4516 return S_OK;
4517}
4518
4519STDMETHODIMP HCustomHardDisk::COMSETTER(Description) (INPTR BSTR aDescription)
4520{
4521 AutoWriteLock alock (this);
4522 CHECK_READY();
4523
4524 CHECK_BUSY_AND_READERS();
4525
4526 return E_NOTIMPL;
4527}
4528
4529STDMETHODIMP HCustomHardDisk::COMGETTER(Size) (ULONG64 *aSize)
4530{
4531 if (!aSize)
4532 return E_POINTER;
4533
4534 AutoReadLock alock (this);
4535 CHECK_READY();
4536
4537 *aSize = mSize;
4538 return S_OK;
4539}
4540
4541STDMETHODIMP HCustomHardDisk::COMGETTER(ActualSize) (ULONG64 *aActualSize)
4542{
4543 if (!aActualSize)
4544 return E_POINTER;
4545
4546 AutoReadLock alock (this);
4547 CHECK_READY();
4548
4549 *aActualSize = mActualSize;
4550 return S_OK;
4551}
4552
4553// ICustomHardDisk properties
4554////////////////////////////////////////////////////////////////////////////////
4555
4556STDMETHODIMP HCustomHardDisk::COMGETTER(Location) (BSTR *aLocation)
4557{
4558 if (!aLocation)
4559 return E_POINTER;
4560
4561 AutoReadLock alock (this);
4562 CHECK_READY();
4563
4564 mLocationFull.cloneTo (aLocation);
4565 return S_OK;
4566}
4567
4568STDMETHODIMP HCustomHardDisk::COMSETTER(Location) (INPTR BSTR aLocation)
4569{
4570 AutoWriteLock alock (this);
4571 CHECK_READY();
4572
4573 if (mState != NotCreated)
4574 return setError (E_ACCESSDENIED,
4575 tr ("Cannot change the file path of the existing hard disk '%ls'"),
4576 toString().raw());
4577
4578 CHECK_BUSY_AND_READERS();
4579
4580 /// @todo currently, we assume that location is always a file path for
4581 /// all custom hard disks. This is not generally correct, and needs to be
4582 /// parametrized in the VD plugin interface.
4583
4584 /* append the default path if only a name is given */
4585 Bstr path = aLocation;
4586 if (aLocation && *aLocation)
4587 {
4588 Utf8Str fp = aLocation;
4589 if (!RTPathHavePath (fp))
4590 {
4591 AutoReadLock propsLock (mVirtualBox->systemProperties());
4592 path = Utf8StrFmt ("%ls%c%s",
4593 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
4594 RTPATH_DELIMITER,
4595 fp.raw());
4596 }
4597 }
4598
4599 return setLocation (path);
4600}
4601
4602STDMETHODIMP HCustomHardDisk::COMGETTER(Created) (BOOL *aCreated)
4603{
4604 if (!aCreated)
4605 return E_POINTER;
4606
4607 AutoReadLock alock (this);
4608 CHECK_READY();
4609
4610 *aCreated = mState >= Created;
4611 return S_OK;
4612}
4613
4614STDMETHODIMP HCustomHardDisk::COMGETTER(Format) (BSTR *aFormat)
4615{
4616 if (!aFormat)
4617 return E_POINTER;
4618
4619 AutoReadLock alock (this);
4620 CHECK_READY();
4621
4622 mFormat.cloneTo (aFormat);
4623 return S_OK;
4624}
4625
4626// ICustomHardDisk methods
4627/////////////////////////////////////////////////////////////////////////////
4628
4629STDMETHODIMP HCustomHardDisk::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
4630{
4631 if (!aProgress)
4632 return E_POINTER;
4633
4634 AutoWriteLock alock (this);
4635 CHECK_READY();
4636
4637 return createImage (aSize, TRUE /* aDynamic */, aProgress);
4638}
4639
4640STDMETHODIMP HCustomHardDisk::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
4641{
4642 if (!aProgress)
4643 return E_POINTER;
4644
4645 AutoWriteLock alock (this);
4646 CHECK_READY();
4647
4648 return createImage (aSize, FALSE /* aDynamic */, aProgress);
4649}
4650
4651STDMETHODIMP HCustomHardDisk::DeleteImage()
4652{
4653 AutoWriteLock alock (this);
4654 CHECK_READY();
4655 CHECK_BUSY_AND_READERS();
4656
4657 return E_NOTIMPL;
4658
4659/// @todo later
4660}
4661
4662// public/protected methods for internal purposes only
4663/////////////////////////////////////////////////////////////////////////////
4664
4665/**
4666 * Attempts to mark the hard disk as registered.
4667 * Only VirtualBox can call this method.
4668 */
4669HRESULT HCustomHardDisk::trySetRegistered (BOOL aRegistered)
4670{
4671 AutoWriteLock alock (this);
4672 CHECK_READY();
4673
4674 if (aRegistered)
4675 {
4676 if (mState == NotCreated)
4677 return setError (E_FAIL,
4678 tr ("Storage location '%ls' is not yet created for this hard disk"),
4679 mLocationFull.raw());
4680 }
4681 else
4682 {
4683 ComAssertRet (mState >= Created, E_FAIL);
4684 }
4685
4686 return HardDisk::trySetRegistered (aRegistered);
4687}
4688
4689/**
4690 * Checks accessibility of this hard disk image only (w/o parents).
4691 *
4692 * @param aAccessError on output, a null string indicates the hard disk is
4693 * accessible, otherwise contains a message describing
4694 * the reason of inaccessibility.
4695 *
4696 * @note Locks this object for writing.
4697 */
4698HRESULT HCustomHardDisk::getAccessible (Bstr &aAccessError)
4699{
4700 /* queryInformation() needs a write lock */
4701 AutoWriteLock alock (this);
4702 CHECK_READY();
4703
4704 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
4705 {
4706 /* An accessibility check in progress on some other thread,
4707 * wait for it to finish. */
4708
4709 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL);
4710 ++ mStateCheckWaiters;
4711 alock.leave();
4712
4713 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
4714
4715 alock.enter();
4716 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
4717 -- mStateCheckWaiters;
4718 if (mStateCheckWaiters == 0)
4719 {
4720 RTSemEventMultiDestroy (mStateCheckSem);
4721 mStateCheckSem = NIL_RTSEMEVENTMULTI;
4722 }
4723
4724 AssertRCReturn (vrc, E_FAIL);
4725
4726 /* don't touch aAccessError, it has been already set */
4727 return S_OK;
4728 }
4729
4730 /* check the basic accessibility */
4731 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */);
4732 if (FAILED (rc) || !aAccessError.isNull())
4733 return rc;
4734
4735 if (mState >= Created)
4736 {
4737 return queryInformation (&aAccessError);
4738 }
4739
4740 aAccessError = Utf8StrFmt ("Hard disk '%ls' is not yet created",
4741 mLocationFull.raw());
4742 return S_OK;
4743}
4744
4745/**
4746 * Saves hard disk settings to the specified storage node and saves
4747 * all children to the specified hard disk node
4748 *
4749 * @param aHDNode <HardDisk> or <DiffHardDisk> node.
4750 * @param aStorageNode <VirtualDiskImage> node.
4751 */
4752HRESULT HCustomHardDisk::saveSettings (settings::Key &aHDNode,
4753 settings::Key &aStorageNode)
4754{
4755 AssertReturn (!aHDNode.isNull() && !aStorageNode.isNull(), E_FAIL);
4756
4757 AutoReadLock alock (this);
4758 CHECK_READY();
4759
4760 /* location (required) */
4761 aStorageNode.setValue <Bstr> ("location", mLocationFull);
4762
4763 /* format (required) */
4764 aStorageNode.setValue <Bstr> ("format", mFormat);
4765
4766 /* save basic settings and children */
4767 return HardDisk::saveSettings (aHDNode);
4768}
4769
4770/**
4771 * Returns the string representation of this hard disk.
4772 * When \a aShort is false, returns the full image file path.
4773 * Otherwise, returns the image file name only.
4774 *
4775 * @param aShort if true, a short representation is returned
4776 *
4777 * @note Locks this object for reading.
4778 */
4779Bstr HCustomHardDisk::toString (bool aShort /* = false */)
4780{
4781 AutoReadLock alock (this);
4782
4783 /// @todo currently, we assume that location is always a file path for
4784 /// all custom hard disks. This is not generally correct, and needs to be
4785 /// parametrized in the VD plugin interface.
4786
4787 if (!aShort)
4788 return mLocationFull;
4789 else
4790 {
4791 Utf8Str fname = mLocationFull;
4792 return RTPathFilename (fname.mutableRaw());
4793 }
4794}
4795
4796/**
4797 * Creates a clone of this hard disk by storing hard disk data in the given
4798 * VDI file.
4799 *
4800 * If the operation fails, @a aDeleteTarget will be set to @c true unless the
4801 * failure happened because the target file already existed.
4802 *
4803 * @param aId UUID to assign to the created image.
4804 * @param aTargetPath VDI file where the cloned image is to be to stored.
4805 * @param aProgress progress object to run during operation.
4806 * @param aDeleteTarget Whether it is recommended to delete target on
4807 * failure or not.
4808 */
4809HRESULT
4810HCustomHardDisk::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
4811 Progress *aProgress, bool &aDeleteTarget)
4812{
4813 ComAssertMsgFailed (("Not implemented"));
4814 return E_NOTIMPL;
4815}
4816
4817/**
4818 * Creates a new differencing image for this hard disk with the given
4819 * VDI file name.
4820 *
4821 * @param aId UUID to assign to the created image
4822 * @param aTargetPath VDI file where to store the created differencing image
4823 * @param aProgress progress object to run during operation
4824 * (can be NULL)
4825 */
4826HRESULT
4827HCustomHardDisk::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
4828 Progress *aProgress)
4829{
4830 ComAssertMsgFailed (("Not implemented"));
4831 return E_NOTIMPL;
4832}
4833
4834// private methods
4835/////////////////////////////////////////////////////////////////////////////
4836
4837/**
4838 * Helper to set a new location.
4839 *
4840 * @note
4841 * Must be called from under the object's lock!
4842 */
4843HRESULT HCustomHardDisk::setLocation (const BSTR aLocation)
4844{
4845 /// @todo currently, we assume that location is always a file path for
4846 /// all custom hard disks. This is not generally correct, and needs to be
4847 /// parametrized in the VD plugin interface.
4848
4849 if (aLocation && *aLocation)
4850 {
4851 /* get the full file name */
4852 char locationFull [RTPATH_MAX];
4853 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aLocation),
4854 locationFull, sizeof (locationFull));
4855 if (VBOX_FAILURE (vrc))
4856 return setError (E_FAIL,
4857 tr ("Invalid hard disk location '%ls' (%Vrc)"), aLocation, vrc);
4858
4859 mLocation = aLocation;
4860 mLocationFull = locationFull;
4861 }
4862 else
4863 {
4864 mLocation.setNull();
4865 mLocationFull.setNull();
4866 }
4867
4868 return S_OK;
4869}
4870
4871/**
4872 * Helper to query information about the custom hard disk.
4873 *
4874 * @param aAccessError not used when NULL, otherwise see #getAccessible()
4875 *
4876 * @note Must be called from under the object's lock, only after
4877 * CHECK_BUSY_AND_READERS() succeeds.
4878 */
4879HRESULT HCustomHardDisk::queryInformation (Bstr *aAccessError)
4880{
4881 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
4882
4883 /* create a lock object to completely release it later */
4884 AutoWriteLock alock (this);
4885
4886 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
4887
4888 ComAssertRet (mState >= Created, E_FAIL);
4889
4890 HRESULT rc = S_OK;
4891 int vrc = VINF_SUCCESS;
4892
4893 /* lazily create a semaphore */
4894 vrc = RTSemEventMultiCreate (&mStateCheckSem);
4895 ComAssertRCRet (vrc, E_FAIL);
4896
4897 /* Reference the disk to prevent any concurrent modifications
4898 * after releasing the lock below (to unblock getters before
4899 * a lengthy operation). */
4900 addReader();
4901
4902 alock.leave();
4903
4904 /* VBoxVHDD management interface needs to be optimized: we're opening a
4905 * file three times in a raw to get three bits of information. */
4906
4907 Utf8Str location = mLocationFull;
4908 Bstr errMsg;
4909
4910 /* reset any previous error report from VDError() */
4911 mLastVDError.setNull();
4912
4913 do
4914 {
4915 Guid id, parentId;
4916
4917 vrc = VDOpen (mContainer, Utf8Str (mFormat), location, VD_OPEN_FLAGS_INFO);
4918 if (VBOX_FAILURE (vrc))
4919 break;
4920
4921 vrc = VDGetUuid (mContainer, 0, id.ptr());
4922 if (VBOX_FAILURE (vrc))
4923 break;
4924 vrc = VDGetParentUuid (mContainer, 0, parentId.ptr());
4925 if (VBOX_FAILURE (vrc))
4926 break;
4927
4928 if (!mId.isEmpty())
4929 {
4930 /* check that the actual UUID of the image matches the stored UUID */
4931 if (mId != id)
4932 {
4933 errMsg = Utf8StrFmt (
4934 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
4935 "match UUID {%Vuuid} stored in the registry"),
4936 id.ptr(), location.raw(), mId.ptr());
4937 break;
4938 }
4939 }
4940 else
4941 {
4942 /* assgn an UUID read from the image file */
4943 mId = id;
4944 }
4945
4946 if (mParent)
4947 {
4948 /* check parent UUID */
4949 AutoWriteLock parentLock (mParent);
4950 if (mParent->id() != parentId)
4951 {
4952 errMsg = Utf8StrFmt (
4953 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
4954 "the hard disk image file '%s' doesn't match "
4955 "UUID {%Vuuid} stored in the registry"),
4956 parentId.raw(), mParent->toString().raw(),
4957 location.raw(), mParent->id().raw());
4958 break;
4959 }
4960 }
4961 else if (!parentId.isEmpty())
4962 {
4963 errMsg = Utf8StrFmt (
4964 tr ("Hard disk image '%s' is a differencing image that is linked "
4965 "to a hard disk with UUID {%Vuuid} and cannot be used "
4966 "directly as a base hard disk"),
4967 location.raw(), parentId.raw());
4968 break;
4969 }
4970
4971 /* get actual file size */
4972 /// @todo is there a direct method in RT?
4973 {
4974 RTFILE file = NIL_RTFILE;
4975 vrc = RTFileOpen (&file, location, RTFILE_O_READ);
4976 if (VBOX_SUCCESS (vrc))
4977 {
4978 uint64_t size = 0;
4979 vrc = RTFileGetSize (file, &size);
4980 if (VBOX_SUCCESS (vrc))
4981 mActualSize = size;
4982 RTFileClose (file);
4983 }
4984 if (VBOX_FAILURE (vrc))
4985 break;
4986 }
4987
4988 /* query logical size only for non-differencing images */
4989 if (!mParent)
4990 {
4991 uint64_t size = VDGetSize (mContainer, 0);
4992 /* convert to MBytes */
4993 mSize = size / 1024 / 1024;
4994 }
4995 }
4996 while (0);
4997
4998 VDCloseAll (mContainer);
4999
5000 /* enter the lock again */
5001 alock.enter();
5002
5003 /* remove the reference */
5004 releaseReader();
5005
5006 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
5007 {
5008 LogWarningFunc (("'%ls' is not accessible "
5009 "(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
5010 mLocationFull.raw(), rc, vrc, errMsg.raw()));
5011
5012 if (aAccessError)
5013 {
5014 if (!errMsg.isNull())
5015 *aAccessError = errMsg;
5016 else if (!mLastVDError.isNull())
5017 *aAccessError = mLastVDError;
5018 else if (VBOX_FAILURE (vrc))
5019 *aAccessError = Utf8StrFmt (
5020 tr ("Could not access hard disk '%ls' (%Vrc)"),
5021 mLocationFull.raw(), vrc);
5022 }
5023
5024 /* downgrade to not accessible */
5025 mState = Created;
5026 }
5027 else
5028 {
5029 if (aAccessError)
5030 aAccessError->setNull();
5031
5032 mState = Accessible;
5033 }
5034
5035 /* inform waiters if there are any */
5036 if (mStateCheckWaiters > 0)
5037 {
5038 RTSemEventMultiSignal (mStateCheckSem);
5039 }
5040 else
5041 {
5042 /* delete the semaphore ourselves */
5043 RTSemEventMultiDestroy (mStateCheckSem);
5044 mStateCheckSem = NIL_RTSEMEVENTMULTI;
5045 }
5046
5047 /* cleanup the last error report from VDError() */
5048 mLastVDError.setNull();
5049
5050 return rc;
5051}
5052
5053/**
5054 * Helper to create hard disk images.
5055 *
5056 * @param aSize size in MB
5057 * @param aDynamic dynamic or fixed image
5058 * @param aProgress address of IProgress pointer to return
5059 */
5060HRESULT HCustomHardDisk::createImage (ULONG64 aSize, BOOL aDynamic,
5061 IProgress **aProgress)
5062{
5063 ComAssertMsgFailed (("Not implemented"));
5064 return E_NOTIMPL;
5065}
5066
5067/* static */
5068DECLCALLBACK(int) HCustomHardDisk::VDITaskThread (RTTHREAD thread, void *pvUser)
5069{
5070 AssertMsgFailed (("Not implemented"));
5071 return VERR_GENERAL_FAILURE;
5072}
5073
5074/* static */
5075DECLCALLBACK(void) HCustomHardDisk::VDError (void *pvUser, int rc, RT_SRC_POS_DECL,
5076 const char *pszFormat, va_list va)
5077{
5078 HCustomHardDisk *that = static_cast <HCustomHardDisk *> (pvUser);
5079 AssertReturnVoid (that != NULL);
5080
5081 /// @todo pass the error message to the operation initiator
5082 Utf8Str err = Utf8StrFmtVA (pszFormat, va);
5083 if (VBOX_FAILURE (rc))
5084 err = Utf8StrFmt ("%s (%Vrc)", err.raw(), rc);
5085
5086 if (that->mLastVDError.isNull())
5087 that->mLastVDError = err;
5088 else
5089 that->mLastVDError = Utf8StrFmt
5090 ("%s.\n%s", that->mLastVDError.raw(), err.raw());
5091}
5092
5093////////////////////////////////////////////////////////////////////////////////
5094// HVHDImage class
5095////////////////////////////////////////////////////////////////////////////////
5096
5097// constructor / destructor
5098////////////////////////////////////////////////////////////////////////////////
5099
5100HRESULT HVHDImage::FinalConstruct()
5101{
5102 HRESULT rc = HardDisk::FinalConstruct();
5103 if (FAILED (rc))
5104 return rc;
5105
5106 mState = NotCreated;
5107
5108 mStateCheckSem = NIL_RTSEMEVENTMULTI;
5109 mStateCheckWaiters = 0;
5110
5111 mSize = 0;
5112 mActualSize = 0;
5113
5114 /* initialize the container */
5115 int vrc = VDCreate (VDError, this, &mContainer);
5116 ComAssertRCRet (vrc, E_FAIL);
5117
5118 return S_OK;
5119}
5120
5121void HVHDImage::FinalRelease()
5122{
5123 if (mContainer != NULL)
5124 VDDestroy (mContainer);
5125
5126 HardDisk::FinalRelease();
5127}
5128
5129// public initializer/uninitializer for internal purposes only
5130////////////////////////////////////////////////////////////////////////////////
5131
5132// public methods for internal purposes only
5133/////////////////////////////////////////////////////////////////////////////
5134
5135/**
5136 * Initializes the VHD hard disk object by reading its properties from
5137 * the given configuration node. The created hard disk will be marked as
5138 * registered on success.
5139 *
5140 * @param aHDNode <HardDisk> node
5141 * @param aVHDNode <VirtualDiskImage> node
5142 */
5143HRESULT HVHDImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
5144 const settings::Key &aHDNode,
5145 const settings::Key &aVHDNode)
5146{
5147 LogFlowThisFunc (("\n"));
5148
5149 AssertReturn (!aHDNode.isNull() && !aVHDNode.isNull(), E_FAIL);
5150
5151 AutoWriteLock alock (this);
5152 ComAssertRet (!isReady(), E_UNEXPECTED);
5153
5154 mStorageType = HardDiskStorageType_VHDImage;
5155
5156 HRESULT rc = S_OK;
5157
5158 do
5159 {
5160 rc = protectedInit (aVirtualBox, aParent);
5161 CheckComRCBreakRC (rc);
5162
5163 /* set ready to let protectedUninit() be called on failure */
5164 setReady (true);
5165
5166 /* filePath (required) */
5167 Bstr filePath = aVHDNode.stringValue("filePath");
5168
5169 rc = setFilePath (filePath);
5170 CheckComRCBreakRC (rc);
5171
5172 LogFlowThisFunc (("'%ls'\n", mFilePathFull.raw()));
5173
5174 /* load basic settings and children */
5175 rc = loadSettings (aHDNode);
5176 CheckComRCBreakRC (rc);
5177
5178 if (mType != HardDiskType_Writethrough)
5179 {
5180 rc = setError (E_FAIL,
5181 tr ("Currently, non-Writethrough VHD images are not "
5182 "allowed ('%ls')"),
5183 toString().raw());
5184 break;
5185 }
5186
5187 mState = Created;
5188 mRegistered = TRUE;
5189
5190 /* Don't call queryInformation() for registered hard disks to
5191 * prevent the calling thread (i.e. the VirtualBox server startup
5192 * thread) from an unexpected freeze. The vital mId property (UUID)
5193 * is read from the registry file in loadSettings(). To get the rest,
5194 * the user will have to call COMGETTER(Accessible) manually. */
5195 }
5196 while (0);
5197
5198 if (FAILED (rc))
5199 uninit();
5200
5201 return rc;
5202}
5203
5204/**
5205 * Initializes the VHD hard disk object using the given image file name.
5206 *
5207 * @param aVirtualBox VirtualBox parent.
5208 * @param aParent Currently, must always be @c NULL.
5209 * @param aFilePath Path to the image file, or @c NULL to create an
5210 * image-less object.
5211 * @param aRegistered Whether to mark this disk as registered or not
5212 * (ignored when @a aFilePath is @c NULL, assuming @c FALSE)
5213 */
5214HRESULT HVHDImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
5215 const BSTR aFilePath, BOOL aRegistered /* = FALSE */)
5216{
5217 LogFlowThisFunc (("aFilePath='%ls', aRegistered=%d\n", aFilePath, aRegistered));
5218
5219 AssertReturn (aParent == NULL, E_FAIL);
5220
5221 AutoWriteLock alock (this);
5222 ComAssertRet (!isReady(), E_UNEXPECTED);
5223
5224 mStorageType = HardDiskStorageType_VHDImage;
5225
5226 HRESULT rc = S_OK;
5227
5228 do
5229 {
5230 rc = protectedInit (aVirtualBox, aParent);
5231 CheckComRCBreakRC (rc);
5232
5233 /* set ready to let protectedUninit() be called on failure */
5234 setReady (true);
5235
5236 rc = setFilePath (aFilePath);
5237 CheckComRCBreakRC (rc);
5238
5239 /* currently, all VHD hard disks are writethrough */
5240 mType = HardDiskType_Writethrough;
5241
5242 Assert (mId.isEmpty());
5243
5244 if (aFilePath && *aFilePath)
5245 {
5246 mRegistered = aRegistered;
5247 mState = Created;
5248
5249 /* Call queryInformation() anyway (even if it will block), because
5250 * it is the only way to get the UUID of the existing VDI and
5251 * initialize the vital mId property. */
5252 Bstr errMsg;
5253 rc = queryInformation (&errMsg);
5254 if (SUCCEEDED (rc))
5255 {
5256 /* We are constructing a new HVirtualDiskImage object. If there
5257 * is a fatal accessibility error (we cannot read image UUID),
5258 * we have to fail. We do so even on non-fatal errors as well,
5259 * because it's not worth to keep going with the inaccessible
5260 * image from the very beginning (when nothing else depends on
5261 * it yet). */
5262 if (!errMsg.isNull())
5263 rc = setErrorBstr (E_FAIL, errMsg);
5264 }
5265 }
5266 else
5267 {
5268 mRegistered = FALSE;
5269 mState = NotCreated;
5270 mId.create();
5271 }
5272 }
5273 while (0);
5274
5275 if (FAILED (rc))
5276 uninit();
5277
5278 return rc;
5279}
5280
5281/**
5282 * Uninitializes the instance and sets the ready flag to FALSE.
5283 * Called either from FinalRelease(), by the parent when it gets destroyed,
5284 * or by a third party when it decides this object is no more valid.
5285 */
5286void HVHDImage::uninit()
5287{
5288 LogFlowThisFunc (("\n"));
5289
5290 AutoWriteLock alock (this);
5291 if (!isReady())
5292 return;
5293
5294 HardDisk::protectedUninit (alock);
5295}
5296
5297// IHardDisk properties
5298////////////////////////////////////////////////////////////////////////////////
5299
5300STDMETHODIMP HVHDImage::COMGETTER(Description) (BSTR *aDescription)
5301{
5302 if (!aDescription)
5303 return E_POINTER;
5304
5305 AutoReadLock alock (this);
5306 CHECK_READY();
5307
5308 mDescription.cloneTo (aDescription);
5309 return S_OK;
5310}
5311
5312STDMETHODIMP HVHDImage::COMSETTER(Description) (INPTR BSTR aDescription)
5313{
5314 AutoWriteLock alock (this);
5315 CHECK_READY();
5316
5317 CHECK_BUSY_AND_READERS();
5318
5319 return E_NOTIMPL;
5320
5321/// @todo implement
5322//
5323// if (mState >= Created)
5324// {
5325// int vrc = VDISetImageComment (Utf8Str (mFilePathFull), Utf8Str (aDescription));
5326// if (VBOX_FAILURE (vrc))
5327// return setError (E_FAIL,
5328// tr ("Could not change the description of the VDI hard disk '%ls' "
5329// "(%Vrc)"),
5330// toString().raw(), vrc);
5331// }
5332//
5333// mDescription = aDescription;
5334// return S_OK;
5335}
5336
5337STDMETHODIMP HVHDImage::COMGETTER(Size) (ULONG64 *aSize)
5338{
5339 if (!aSize)
5340 return E_POINTER;
5341
5342 AutoReadLock alock (this);
5343 CHECK_READY();
5344
5345/// @todo will need this if we add suppord for differencing VMDKs
5346//
5347// /* only a non-differencing image knows the logical size */
5348// if (isDifferencing())
5349// return root()->COMGETTER(Size) (aSize);
5350
5351 *aSize = mSize;
5352 return S_OK;
5353}
5354
5355STDMETHODIMP HVHDImage::COMGETTER(ActualSize) (ULONG64 *aActualSize)
5356{
5357 if (!aActualSize)
5358 return E_POINTER;
5359
5360 AutoReadLock alock (this);
5361 CHECK_READY();
5362
5363 *aActualSize = mActualSize;
5364 return S_OK;
5365}
5366
5367// IVirtualDiskImage properties
5368////////////////////////////////////////////////////////////////////////////////
5369
5370STDMETHODIMP HVHDImage::COMGETTER(FilePath) (BSTR *aFilePath)
5371{
5372 if (!aFilePath)
5373 return E_POINTER;
5374
5375 AutoReadLock alock (this);
5376 CHECK_READY();
5377
5378 mFilePathFull.cloneTo (aFilePath);
5379 return S_OK;
5380}
5381
5382STDMETHODIMP HVHDImage::COMSETTER(FilePath) (INPTR BSTR aFilePath)
5383{
5384 AutoWriteLock alock (this);
5385 CHECK_READY();
5386
5387 if (mState != NotCreated)
5388 return setError (E_ACCESSDENIED,
5389 tr ("Cannot change the file path of the existing hard disk '%ls'"),
5390 toString().raw());
5391
5392 CHECK_BUSY_AND_READERS();
5393
5394 /* append the default path if only a name is given */
5395 Bstr path = aFilePath;
5396 if (aFilePath && *aFilePath)
5397 {
5398 Utf8Str fp = aFilePath;
5399 if (!RTPathHavePath (fp))
5400 {
5401 AutoReadLock propsLock (mVirtualBox->systemProperties());
5402 path = Utf8StrFmt ("%ls%c%s",
5403 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
5404 RTPATH_DELIMITER,
5405 fp.raw());
5406 }
5407 }
5408
5409 return setFilePath (path);
5410}
5411
5412STDMETHODIMP HVHDImage::COMGETTER(Created) (BOOL *aCreated)
5413{
5414 if (!aCreated)
5415 return E_POINTER;
5416
5417 AutoReadLock alock (this);
5418 CHECK_READY();
5419
5420 *aCreated = mState >= Created;
5421 return S_OK;
5422}
5423
5424// IVHDImage methods
5425/////////////////////////////////////////////////////////////////////////////
5426
5427STDMETHODIMP HVHDImage::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
5428{
5429 if (!aProgress)
5430 return E_POINTER;
5431
5432 AutoWriteLock alock (this);
5433 CHECK_READY();
5434
5435 return createImage (aSize, TRUE /* aDynamic */, aProgress);
5436}
5437
5438STDMETHODIMP HVHDImage::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
5439{
5440 if (!aProgress)
5441 return E_POINTER;
5442
5443 AutoWriteLock alock (this);
5444 CHECK_READY();
5445
5446 return createImage (aSize, FALSE /* aDynamic */, aProgress);
5447}
5448
5449STDMETHODIMP HVHDImage::DeleteImage()
5450{
5451 AutoWriteLock alock (this);
5452 CHECK_READY();
5453 CHECK_BUSY_AND_READERS();
5454
5455 return E_NOTIMPL;
5456
5457/// @todo later
5458// We will need to parse the file in order to delete all related delta and
5459// sparse images etc. We may also want to obey the .vmdk.lck file
5460// which is (as far as I understood) created when the VMware VM is
5461// running or saved etc.
5462//
5463// if (mRegistered)
5464// return setError (E_ACCESSDENIED,
5465// tr ("Cannot delete an image of the registered hard disk image '%ls"),
5466// mFilePathFull.raw());
5467// if (mState == NotCreated)
5468// return setError (E_FAIL,
5469// tr ("Hard disk image has been already deleted or never created"));
5470//
5471// int vrc = RTFileDelete (Utf8Str (mFilePathFull));
5472// if (VBOX_FAILURE (vrc))
5473// return setError (E_FAIL, tr ("Could not delete the image file '%ls' (%Vrc)"),
5474// mFilePathFull.raw(), vrc);
5475//
5476// mState = NotCreated;
5477//
5478// /* reset the fields */
5479// mSize = 0;
5480// mActualSize = 0;
5481//
5482// return S_OK;
5483}
5484
5485// public/protected methods for internal purposes only
5486/////////////////////////////////////////////////////////////////////////////
5487
5488/**
5489 * Attempts to mark the hard disk as registered.
5490 * Only VirtualBox can call this method.
5491 */
5492HRESULT HVHDImage::trySetRegistered (BOOL aRegistered)
5493{
5494 AutoWriteLock alock (this);
5495 CHECK_READY();
5496
5497 if (aRegistered)
5498 {
5499 if (mState == NotCreated)
5500 return setError (E_FAIL,
5501 tr ("Image file '%ls' is not yet created for this hard disk"),
5502 mFilePathFull.raw());
5503
5504/// @todo will need this if we add suppord for differencing VHDs
5505// if (isDifferencing())
5506// return setError (E_FAIL,
5507// tr ("Hard disk '%ls' is differencing and cannot be unregistered "
5508// "explicitly"),
5509// mFilePathFull.raw());
5510 }
5511 else
5512 {
5513 ComAssertRet (mState >= Created, E_FAIL);
5514 }
5515
5516 return HardDisk::trySetRegistered (aRegistered);
5517}
5518
5519/**
5520 * Checks accessibility of this hard disk image only (w/o parents).
5521 *
5522 * @param aAccessError on output, a null string indicates the hard disk is
5523 * accessible, otherwise contains a message describing
5524 * the reason of inaccessibility.
5525 *
5526 * @note Locks this object for writing.
5527 */
5528HRESULT HVHDImage::getAccessible (Bstr &aAccessError)
5529{
5530 /* queryInformation() needs a write lock */
5531 AutoWriteLock alock (this);
5532 CHECK_READY();
5533
5534 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
5535 {
5536 /* An accessibility check in progress on some other thread,
5537 * wait for it to finish. */
5538
5539 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL);
5540 ++ mStateCheckWaiters;
5541 alock.leave();
5542
5543 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
5544
5545 alock.enter();
5546 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
5547 -- mStateCheckWaiters;
5548 if (mStateCheckWaiters == 0)
5549 {
5550 RTSemEventMultiDestroy (mStateCheckSem);
5551 mStateCheckSem = NIL_RTSEMEVENTMULTI;
5552 }
5553
5554 AssertRCReturn (vrc, E_FAIL);
5555
5556 /* don't touch aAccessError, it has been already set */
5557 return S_OK;
5558 }
5559
5560 /* check the basic accessibility */
5561 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */);
5562 if (FAILED (rc) || !aAccessError.isNull())
5563 return rc;
5564
5565 if (mState >= Created)
5566 {
5567 return queryInformation (&aAccessError);
5568 }
5569
5570 aAccessError = Utf8StrFmt ("Hard disk image '%ls' is not yet created",
5571 mFilePathFull.raw());
5572 return S_OK;
5573}
5574
5575/**
5576 * Saves hard disk settings to the specified storage node and saves
5577 * all children to the specified hard disk node
5578 *
5579 * @param aHDNode <HardDisk> or <DiffHardDisk> node
5580 * @param aStorageNode <VirtualDiskImage> node
5581 */
5582HRESULT HVHDImage::saveSettings (settings::Key &aHDNode, settings::Key &aStorageNode)
5583{
5584 AssertReturn (!aHDNode.isNull() && !aStorageNode.isNull(), E_FAIL);
5585
5586 AutoReadLock alock (this);
5587 CHECK_READY();
5588
5589 /* filePath (required) */
5590 aStorageNode.setValue <Bstr> ("filePath", mFilePath);
5591
5592 /* save basic settings and children */
5593 return HardDisk::saveSettings (aHDNode);
5594}
5595
5596/**
5597 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
5598 * of this hard disk and updates it if necessary to reflect the new location.
5599 * Intended to be from HardDisk::updatePaths().
5600 *
5601 * @param aOldPath old path (full)
5602 * @param aNewPath new path (full)
5603 *
5604 * @note Locks this object for writing.
5605 */
5606void HVHDImage::updatePath (const char *aOldPath, const char *aNewPath)
5607{
5608 AssertReturnVoid (aOldPath);
5609 AssertReturnVoid (aNewPath);
5610
5611 AutoWriteLock alock (this);
5612 AssertReturnVoid (isReady());
5613
5614 size_t oldPathLen = strlen (aOldPath);
5615
5616 Utf8Str path = mFilePathFull;
5617 LogFlowThisFunc (("VHD.fullPath={%s}\n", path.raw()));
5618
5619 if (RTPathStartsWith (path, aOldPath))
5620 {
5621 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
5622 path.raw() + oldPathLen);
5623 path = newPath;
5624
5625 mVirtualBox->calculateRelativePath (path, path);
5626
5627 unconst (mFilePathFull) = newPath;
5628 unconst (mFilePath) = path;
5629
5630 LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
5631 newPath.raw(), path.raw()));
5632 }
5633}
5634
5635/**
5636 * Returns the string representation of this hard disk.
5637 * When \a aShort is false, returns the full image file path.
5638 * Otherwise, returns the image file name only.
5639 *
5640 * @param aShort if true, a short representation is returned
5641 *
5642 * @note Locks this object for reading.
5643 */
5644Bstr HVHDImage::toString (bool aShort /* = false */)
5645{
5646 AutoReadLock alock (this);
5647
5648 if (!aShort)
5649 return mFilePathFull;
5650 else
5651 {
5652 Utf8Str fname = mFilePathFull;
5653 return RTPathFilename (fname.mutableRaw());
5654 }
5655}
5656
5657/**
5658 * Creates a clone of this hard disk by storing hard disk data in the given
5659 * VDI file.
5660 *
5661 * If the operation fails, @a aDeleteTarget will be set to @c true unless the
5662 * failure happened because the target file already existed.
5663 *
5664 * @param aId UUID to assign to the created image.
5665 * @param aTargetPath VDI file where the cloned image is to be to stored.
5666 * @param aProgress progress object to run during operation.
5667 * @param aDeleteTarget Whether it is recommended to delete target on
5668 * failure or not.
5669 */
5670HRESULT
5671HVHDImage::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
5672 Progress *aProgress, bool &aDeleteTarget)
5673{
5674 ComAssertMsgFailed (("Not implemented"));
5675 return E_NOTIMPL;
5676
5677/// @todo will need this if we add suppord for differencing VHDs
5678// Use code from HVirtualDiskImage::cloneToImage as an example.
5679}
5680
5681/**
5682 * Creates a new differencing image for this hard disk with the given
5683 * VDI file name.
5684 *
5685 * @param aId UUID to assign to the created image
5686 * @param aTargetPath VDI file where to store the created differencing image
5687 * @param aProgress progress object to run during operation
5688 * (can be NULL)
5689 */
5690HRESULT
5691HVHDImage::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
5692 Progress *aProgress)
5693{
5694 ComAssertMsgFailed (("Not implemented"));
5695 return E_NOTIMPL;
5696
5697/// @todo will need this if we add suppord for differencing VHDs
5698// Use code from HVirtualDiskImage::createDiffImage as an example.
5699}
5700
5701// private methods
5702/////////////////////////////////////////////////////////////////////////////
5703
5704/**
5705 * Helper to set a new file path.
5706 * Resolves a path relatively to the Virtual Box home directory.
5707 *
5708 * @note
5709 * Must be called from under the object's lock!
5710 */
5711HRESULT HVHDImage::setFilePath (const BSTR aFilePath)
5712{
5713 if (aFilePath && *aFilePath)
5714 {
5715 /* get the full file name */
5716 char filePathFull [RTPATH_MAX];
5717 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aFilePath),
5718 filePathFull, sizeof (filePathFull));
5719 if (VBOX_FAILURE (vrc))
5720 return setError (E_FAIL,
5721 tr ("Invalid image file path '%ls' (%Vrc)"), aFilePath, vrc);
5722
5723 mFilePath = aFilePath;
5724 mFilePathFull = filePathFull;
5725 }
5726 else
5727 {
5728 mFilePath.setNull();
5729 mFilePathFull.setNull();
5730 }
5731
5732 return S_OK;
5733}
5734
5735/**
5736 * Helper to query information about the VDI hard disk.
5737 *
5738 * @param aAccessError not used when NULL, otherwise see #getAccessible()
5739 *
5740 * @note Must be called from under the object's lock, only after
5741 * CHECK_BUSY_AND_READERS() succeeds.
5742 */
5743HRESULT HVHDImage::queryInformation (Bstr *aAccessError)
5744{
5745 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
5746
5747 /* create a lock object to completely release it later */
5748 AutoWriteLock alock (this);
5749
5750 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
5751
5752 ComAssertRet (mState >= Created, E_FAIL);
5753
5754 HRESULT rc = S_OK;
5755 int vrc = VINF_SUCCESS;
5756
5757 /* lazily create a semaphore */
5758 vrc = RTSemEventMultiCreate (&mStateCheckSem);
5759 ComAssertRCRet (vrc, E_FAIL);
5760
5761 /* Reference the disk to prevent any concurrent modifications
5762 * after releasing the lock below (to unblock getters before
5763 * a lengthy operation). */
5764 addReader();
5765
5766 alock.leave();
5767
5768 /* VBoxVHDD management interface needs to be optimized: we're opening a
5769 * file three times in a raw to get three bits of information. */
5770
5771 Utf8Str filePath = mFilePathFull;
5772 Bstr errMsg;
5773
5774 /* reset any previous error report from VDError() */
5775 mLastVDError.setNull();
5776
5777 do
5778 {
5779 Guid id, parentId;
5780
5781 /// @todo changed from VD_OPEN_FLAGS_READONLY to VD_OPEN_FLAGS_NORMAL,
5782 /// because otherwise registering a VHD which so far has no UUID will
5783 /// yield a null UUID. It cannot be added to a VHD opened readonly,
5784 /// obviously. This of course changes locking behavior, but for now
5785 /// this is acceptable. A better solution needs to be found later.
5786 vrc = VDOpen (mContainer, "VHD", filePath, VD_OPEN_FLAGS_NORMAL);
5787 if (VBOX_FAILURE (vrc))
5788 break;
5789
5790 vrc = VDGetUuid (mContainer, 0, id.ptr());
5791 if (VBOX_FAILURE (vrc))
5792 break;
5793 vrc = VDGetParentUuid (mContainer, 0, parentId.ptr());
5794 if (VBOX_FAILURE (vrc))
5795 break;
5796
5797 if (!mId.isEmpty())
5798 {
5799 /* check that the actual UUID of the image matches the stored UUID */
5800 if (mId != id)
5801 {
5802 errMsg = Utf8StrFmt (
5803 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
5804 "match UUID {%Vuuid} stored in the registry"),
5805 id.ptr(), filePath.raw(), mId.ptr());
5806 break;
5807 }
5808 }
5809 else
5810 {
5811 /* assgn an UUID read from the image file */
5812 mId = id;
5813 }
5814
5815 if (mParent)
5816 {
5817 /* check parent UUID */
5818 AutoWriteLock parentLock (mParent);
5819 if (mParent->id() != parentId)
5820 {
5821 errMsg = Utf8StrFmt (
5822 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
5823 "the hard disk image file '%s' doesn't match "
5824 "UUID {%Vuuid} stored in the registry"),
5825 parentId.raw(), mParent->toString().raw(),
5826 filePath.raw(), mParent->id().raw());
5827 break;
5828 }
5829 }
5830 else if (!parentId.isEmpty())
5831 {
5832 errMsg = Utf8StrFmt (
5833 tr ("Hard disk image '%s' is a differencing image that is linked "
5834 "to a hard disk with UUID {%Vuuid} and cannot be used "
5835 "directly as a base hard disk"),
5836 filePath.raw(), parentId.raw());
5837 break;
5838 }
5839
5840 /* get actual file size */
5841 /// @todo is there a direct method in RT?
5842 {
5843 RTFILE file = NIL_RTFILE;
5844 vrc = RTFileOpen (&file, filePath, RTFILE_O_READ);
5845 if (VBOX_SUCCESS (vrc))
5846 {
5847 uint64_t size = 0;
5848 vrc = RTFileGetSize (file, &size);
5849 if (VBOX_SUCCESS (vrc))
5850 mActualSize = size;
5851 RTFileClose (file);
5852 }
5853 if (VBOX_FAILURE (vrc))
5854 break;
5855 }
5856
5857 /* query logical size only for non-differencing images */
5858 if (!mParent)
5859 {
5860 uint64_t size = VDGetSize (mContainer, 0);
5861 /* convert to MBytes */
5862 mSize = size / 1024 / 1024;
5863 }
5864 }
5865 while (0);
5866
5867 VDCloseAll (mContainer);
5868
5869 /* enter the lock again */
5870 alock.enter();
5871
5872 /* remove the reference */
5873 releaseReader();
5874
5875 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
5876 {
5877 LogWarningFunc (("'%ls' is not accessible "
5878 "(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
5879 mFilePathFull.raw(), rc, vrc, errMsg.raw()));
5880
5881 if (aAccessError)
5882 {
5883 if (!errMsg.isNull())
5884 *aAccessError = errMsg;
5885 else if (!mLastVDError.isNull())
5886 *aAccessError = mLastVDError;
5887 else if (VBOX_FAILURE (vrc))
5888 *aAccessError = Utf8StrFmt (
5889 tr ("Could not access hard disk image '%ls' (%Vrc)"),
5890 mFilePathFull.raw(), vrc);
5891 }
5892
5893 /* downgrade to not accessible */
5894 mState = Created;
5895 }
5896 else
5897 {
5898 if (aAccessError)
5899 aAccessError->setNull();
5900
5901 mState = Accessible;
5902 }
5903
5904 /* inform waiters if there are any */
5905 if (mStateCheckWaiters > 0)
5906 {
5907 RTSemEventMultiSignal (mStateCheckSem);
5908 }
5909 else
5910 {
5911 /* delete the semaphore ourselves */
5912 RTSemEventMultiDestroy (mStateCheckSem);
5913 mStateCheckSem = NIL_RTSEMEVENTMULTI;
5914 }
5915
5916 /* cleanup the last error report from VDError() */
5917 mLastVDError.setNull();
5918
5919 return rc;
5920}
5921
5922/**
5923 * Helper to create hard disk images.
5924 *
5925 * @param aSize size in MB
5926 * @param aDynamic dynamic or fixed image
5927 * @param aProgress address of IProgress pointer to return
5928 */
5929HRESULT HVHDImage::createImage (ULONG64 aSize, BOOL aDynamic,
5930 IProgress **aProgress)
5931{
5932 ComAssertMsgFailed (("Not implemented"));
5933 return E_NOTIMPL;
5934
5935/// @todo later
5936// Use code from HVirtualDiskImage::createImage as an example.
5937}
5938
5939/* static */
5940DECLCALLBACK(int) HVHDImage::VDITaskThread (RTTHREAD thread, void *pvUser)
5941{
5942 AssertMsgFailed (("Not implemented"));
5943 return VERR_GENERAL_FAILURE;
5944
5945/// @todo later
5946// Use code from HVirtualDiskImage::VDITaskThread as an example.
5947}
5948
5949/* static */
5950DECLCALLBACK(void) HVHDImage::VDError (void *pvUser, int rc, RT_SRC_POS_DECL,
5951 const char *pszFormat, va_list va)
5952{
5953 HVHDImage *that = static_cast <HVHDImage *> (pvUser);
5954 AssertReturnVoid (that != NULL);
5955
5956 /// @todo pass the error message to the operation initiator
5957 Utf8Str err = Utf8StrFmtVA (pszFormat, va);
5958 if (VBOX_FAILURE (rc))
5959 err = Utf8StrFmt ("%s (%Vrc)", err.raw(), rc);
5960
5961 if (that->mLastVDError.isNull())
5962 that->mLastVDError = err;
5963 else
5964 that->mLastVDError = Utf8StrFmt
5965 ("%s.\n%s", that->mLastVDError.raw(), err.raw());
5966}
5967
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