VirtualBox

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

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

Modify HCustomImage to accept images which do not have a UUID

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