VirtualBox

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

Last change on this file since 3072 was 3043, checked in by vboxsync, 18 years ago

Hack for getting VMDK file registration work when there is no UUID in the file yet.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 115.0 KB
Line 
1/** @file
2 *
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
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/VBoxHDD-new.h>
36#include <VBox/err.h>
37
38#include <algorithm>
39
40#define CHECK_BUSY() \
41 do { \
42 if (isBusy()) \
43 return setError (E_UNEXPECTED, \
44 tr ("Hard disk '%ls' is being used by another task"), \
45 toString().raw()); \
46 } while (0)
47
48#define CHECK_BUSY_AND_READERS() \
49do { \
50 if (readers() > 0 || isBusy()) \
51 return setError (E_UNEXPECTED, \
52 tr ("Hard disk '%ls' is being used by another task"), \
53 toString().raw()); \
54} while (0)
55
56/** Task structure for asynchronous VDI operations */
57struct VDITask
58{
59 enum Op { CreateDynamic, CreateStatic, CloneToImage };
60
61 VDITask (Op op, HVirtualDiskImage *i, Progress *p)
62 : operation (op)
63 , vdi (i)
64 , progress (p)
65 {}
66
67 Op operation;
68 ComObjPtr <HVirtualDiskImage> vdi;
69 ComObjPtr <Progress> progress;
70
71 /* for CreateDynamic, CreateStatic */
72 uint64_t size;
73
74 /* for CloneToImage */
75 ComObjPtr <HardDisk> source;
76};
77
78/**
79 * Progress callback handler for VDI operations.
80 *
81 * @param uPercent Completetion precentage (0-100).
82 * @param pvUser Pointer to the Progress instance.
83 */
84static DECLCALLBACK(int) progressCallback (PVM /* pVM */, unsigned uPercent, void *pvUser)
85{
86 Progress *progress = static_cast <Progress *> (pvUser);
87
88 /* update the progress object */
89 if (progress)
90 progress->notifyProgress (uPercent);
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_NormalHardDisk;
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 LogFlowMember (("HardDisk::protectedInit (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 (AutoLock &alock)
170{
171 LogFlowMember (("HardDisk::protectedUninit()\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 AutoLock 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 AutoLock 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 AutoLock 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 AutoLock alock (this);
239 CHECK_READY();
240
241 *aType = mType;
242 return S_OK;
243}
244
245STDMETHODIMP HardDisk::COMSETTER(Type) (HardDiskType_T aType)
246{
247 AutoLock 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 AutoLock 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 AutoLock lock(this);
293 CHECK_READY();
294
295 AutoLock 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 AutoLock 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 AutoLock 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 AutoLock 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 AutoLock 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 AutoLock 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 AutoLock 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 AutoLock 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 AutoLock 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, (IVirtualDiskImage *) 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 AutoReaderLock 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 AutoLock 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 AutoLock 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 AutoLock 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 AutoLock 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 AutoLock 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 AutoLock 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 AutoLock 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 AutoLock alock (this);
718 AssertReturnVoid (isReady());
719
720 if (mParent)
721 {
722 AutoLock 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 AutoLock alock (this);
734 AssertReturnVoid (isReady());
735
736 if (mParent)
737 {
738 AutoLock 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 AutoLock alock (this);
751 AssertReturn (isReady(), false);
752
753 AssertReturn (!mMachineId.isEmpty(), false);
754
755 /* check all children */
756 AutoLock chLock (childrenLock());
757 for (HardDiskList::const_iterator it = children().begin();
758 it != children().end();
759 ++ it)
760 {
761 ComObjPtr <HardDisk> child = *it;
762 AutoLock 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 AutoLock 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 AutoLock chLock (childrenLock());
786
787 for (HardDiskList::const_iterator it = children().begin();
788 it != children().end();
789 ++ it)
790 {
791 ComObjPtr <HardDisk> child = *it;
792 AutoLock 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 AutoLock alock (this);
816 AssertReturn (isReady(), (void) 0);
817
818 AssertReturn (mBusy == true, (void) 0);
819
820 AutoLock chLock (childrenLock());
821
822 for (HardDiskList::const_iterator it = children().begin();
823 it != children().end();
824 ++ it)
825 {
826 ComObjPtr <HardDisk> child = *it;
827 AutoLock 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 */
838HRESULT HardDisk::getAccessibleWithChildren (Bstr &aAccessError)
839{
840 AutoLock alock (this);
841 AssertReturn (isReady(), E_FAIL);
842
843 HRESULT rc = getAccessible (aAccessError);
844 if (FAILED (rc) || !aAccessError.isNull())
845 return rc;
846
847 AutoLock chLock (childrenLock());
848
849 for (HardDiskList::const_iterator it = children().begin();
850 it != children().end();
851 ++ it)
852 {
853 ComObjPtr <HardDisk> child = *it;
854 rc = child->getAccessible (aAccessError);
855 if (FAILED (rc) || !aAccessError.isNull())
856 return rc;
857 }
858
859 return rc;
860}
861
862/**
863 * Checks that this hard disk and all its descendants are consistent.
864 * For now, the consistency means that:
865 *
866 * 1) every differencing image is associated with a registered machine
867 * 2) every root image that has differencing children is associated with
868 * a registered machine.
869 *
870 * This method is used by the VirtualBox constructor after loading all hard
871 * disks and all machines.
872 */
873HRESULT HardDisk::checkConsistency()
874{
875 AutoLock alock (this);
876 AssertReturn (isReady(), E_FAIL);
877
878 if (isDifferencing())
879 {
880 Assert (mVirtualBox->isMachineIdValid (mMachineId) ||
881 mMachineId.isEmpty());
882
883 if (mMachineId.isEmpty())
884 return setError (E_FAIL,
885 tr ("Differencing hard disk '%ls' is not associated with "
886 "any registered virtual machine or snapshot"),
887 toString().raw());
888 }
889
890 HRESULT rc = S_OK;
891
892 AutoLock chLock (childrenLock());
893
894 if (mParent.isNull() && mType == HardDiskType_NormalHardDisk &&
895 children().size() != 0)
896 {
897 if (mMachineId.isEmpty())
898 return setError (E_FAIL,
899 tr ("Hard disk '%ls' is not associated with any registered "
900 "virtual machine or snapshot, but has differencing child "
901 "hard disks based on it"),
902 toString().raw());
903 }
904
905 for (HardDiskList::const_iterator it = children().begin();
906 it != children().end() && SUCCEEDED (rc);
907 ++ it)
908 {
909 rc = (*it)->checkConsistency();
910 }
911
912 return rc;
913}
914
915/**
916 * Creates a differencing hard disk for this hard disk and returns the
917 * created hard disk object to the caller.
918 *
919 * The created differencing hard disk is automatically added to the list of
920 * children of this hard disk object and registered within VirtualBox.
921
922 * The specified progress object (if not NULL) receives the percentage
923 * of the operation completion. However, it is responsibility of the caller to
924 * call Progress::notifyComplete() after this method returns.
925 *
926 * @param aFolder folder where to create the differencing disk
927 * (must be a full path)
928 * @param aMachineId machine ID the new hard disk will belong to
929 * @param aHardDisk resulting hard disk object
930 * @param aProgress progress object to run during copy operation
931 * (may be NULL)
932 *
933 * @note
934 * Must be NOT called from under locks of other objects that need external
935 * access dirung this method execurion!
936 */
937HRESULT HardDisk::createDiffHardDisk (const Bstr &aFolder, const Guid &aMachineId,
938 ComObjPtr <HVirtualDiskImage> &aHardDisk,
939 Progress *aProgress)
940{
941 AssertReturn (!aFolder.isEmpty() && !aMachineId.isEmpty(),
942 E_FAIL);
943
944 AutoLock alock (this);
945 CHECK_READY();
946
947 ComAssertRet (isBusy() == false, E_FAIL);
948
949 Guid id;
950 id.create();
951
952 Utf8Str filePathTo = Utf8StrFmt ("%ls%c{%Vuuid}.vdi",
953 aFolder.raw(), RTPATH_DELIMITER, id.ptr());
954
955 /* try to make the path relative to the vbox home dir */
956 const char *filePathToRel = filePathTo;
957 {
958 const Utf8Str &homeDir = mVirtualBox->homeDir();
959 if (!strncmp (filePathTo, homeDir, homeDir.length()))
960 filePathToRel = (filePathToRel + homeDir.length() + 1);
961 }
962
963 /* first ensure the directory exists */
964 {
965 Utf8Str dir = aFolder;
966 if (!RTDirExists (dir))
967 {
968 int vrc = RTDirCreateFullPath (dir, 0777);
969 if (VBOX_FAILURE (vrc))
970 {
971 return setError (E_FAIL,
972 tr ("Could not create a directory '%s' "
973 "to store the image file (%Vrc)"),
974 dir.raw(), vrc);
975 }
976 }
977 }
978
979 alock.leave();
980
981 /* call storage type specific diff creation method */
982 HRESULT rc = createDiffImage (id, filePathTo, aProgress);
983
984 alock.enter();
985
986 CheckComRCReturnRC (rc);
987
988 ComObjPtr <HVirtualDiskImage> vdi;
989 vdi.createObject();
990 rc = vdi->init (mVirtualBox, this, Bstr (filePathToRel),
991 TRUE /* aRegistered */);
992 CheckComRCReturnRC (rc);
993
994 /* associate the created hard disk with the given machine */
995 vdi->setMachineId (aMachineId);
996
997 rc = mVirtualBox->registerHardDisk (vdi, VirtualBox::RHD_Internal);
998 CheckComRCReturnRC (rc);
999
1000 aHardDisk = vdi;
1001
1002 return S_OK;
1003}
1004
1005/**
1006 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
1007 * of this hard disk or any of its children and updates it if necessary (by
1008 * calling #updatePath()). Intended to be called only by
1009 * VirtualBox::updateSettings() if a machine's name change causes directory
1010 * renaming that affects this image.
1011 *
1012 * @param aOldPath old path (full)
1013 * @param aNewPath new path (full)
1014 *
1015 * @note Locks this object and all children for writing.
1016 */
1017void HardDisk::updatePaths (const char *aOldPath, const char *aNewPath)
1018{
1019 AssertReturnVoid (aOldPath);
1020 AssertReturnVoid (aNewPath);
1021
1022 AutoLock alock (this);
1023 AssertReturnVoid (isReady());
1024
1025 updatePath (aOldPath, aNewPath);
1026
1027 /* update paths of all children */
1028 AutoLock chLock (childrenLock());
1029 for (HardDiskList::const_iterator it = children().begin();
1030 it != children().end();
1031 ++ it)
1032 {
1033 (*it)->updatePaths (aOldPath, aNewPath);
1034 }
1035}
1036
1037/**
1038 * Helper method that deduces a hard disk object type to create from
1039 * the location string format and from the contents of the resource
1040 * pointed to by the location string.
1041 *
1042 * Currently, the location string must be a file path which is
1043 * passed to the HVirtualDiskImage or HVMDKImage initializer in
1044 * attempt to create a hard disk object.
1045 *
1046 * @param aVirtualBox
1047 * @param aLocation
1048 * @param hardDisk
1049 *
1050 * @return
1051 */
1052/* static */
1053HRESULT HardDisk::openHardDisk (VirtualBox *aVirtualBox, INPTR BSTR aLocation,
1054 ComObjPtr <HardDisk> &hardDisk)
1055{
1056 LogFlowFunc (("aLocation=\"%ls\"\n", aLocation));
1057
1058 AssertReturn (aVirtualBox, E_POINTER);
1059
1060 /* null and empty strings are not allowed locations */
1061 AssertReturn (aLocation, E_INVALIDARG);
1062 AssertReturn (*aLocation, E_INVALIDARG);
1063
1064 HRESULT rc = S_OK;
1065
1066 /* try to guess the probe order by extension */
1067 Utf8Str loc = aLocation;
1068 char *ext = RTPathExt (loc);
1069
1070 HardDiskStorageType_T order [2];
1071
1072 if (RTPathCompare (ext, ".vmdk") == 0)
1073 {
1074 order [0] = HardDiskStorageType_VMDKImage;
1075 order [1] = HardDiskStorageType_VirtualDiskImage;
1076 }
1077 else
1078 {
1079 order [0] = HardDiskStorageType_VirtualDiskImage;
1080 order [1] = HardDiskStorageType_VMDKImage;
1081 }
1082
1083 for (size_t i = 0; i < ELEMENTS (order); ++ i)
1084 {
1085 switch (order [i])
1086 {
1087 case HardDiskStorageType_VirtualDiskImage:
1088 {
1089 ComObjPtr <HVirtualDiskImage> obj;
1090 obj.createObject();
1091 rc = obj->init (aVirtualBox, NULL, aLocation,
1092 FALSE /* aRegistered */);
1093 if (SUCCEEDED (rc))
1094 {
1095 hardDisk = obj;
1096 return rc;
1097 }
1098 break;
1099 }
1100 case HardDiskStorageType_VMDKImage:
1101 {
1102 ComObjPtr <HVMDKImage> obj;
1103 obj.createObject();
1104 rc = obj->init (aVirtualBox, NULL, aLocation,
1105 FALSE /* aRegistered */);
1106 if (SUCCEEDED (rc))
1107 {
1108 hardDisk = obj;
1109 return rc;
1110 }
1111 break;
1112 }
1113 default:
1114 {
1115 ComAssertComRCRetRC (E_FAIL);
1116 }
1117 }
1118 }
1119
1120 return rc;
1121}
1122
1123// protected methods
1124/////////////////////////////////////////////////////////////////////////////
1125
1126/**
1127 * Loads the base settings of the hard disk from the given node, registers
1128 * it and loads and registers all child hard disks as HVirtualDiskImage
1129 * instances.
1130 *
1131 * Subclasses must call this method in their init() or loadSettings() methods
1132 * *after* they load specific parts of data (at least, necessary to let
1133 * toString() function correctly), in order to be properly loaded from the
1134 * settings file and registered.
1135 *
1136 * @param aHDNode <HardDisk> node when #isDifferencing() = false, or
1137 * <DiffHardDisk> node otherwise
1138 *
1139 * @note
1140 * Must be called from under the object's lock
1141 */
1142HRESULT HardDisk::loadSettings (CFGNODE aHDNode)
1143{
1144 AssertReturn (aHDNode, E_FAIL);
1145
1146 Guid uuid; /* uuid (required) */
1147 CFGLDRQueryUUID (aHDNode, "uuid", uuid.ptr());
1148 mId = uuid;
1149
1150 if (!isDifferencing())
1151 {
1152 Bstr type; /* type (required for <HardDisk> nodes only) */
1153 CFGLDRQueryBSTR (aHDNode, "type", type.asOutParam());
1154 if (type == L"normal")
1155 mType = HardDiskType_NormalHardDisk;
1156 else if (type == L"immutable")
1157 mType = HardDiskType_ImmutableHardDisk;
1158 else if (type == L"writethrough")
1159 mType = HardDiskType_WritethroughHardDisk;
1160 else
1161 ComAssertMsgFailedRet (("Invalid hard disk type '%ls'\n", type.raw()),
1162 E_FAIL);
1163 }
1164 else
1165 mType = HardDiskType_NormalHardDisk;
1166
1167 HRESULT rc = mVirtualBox->registerHardDisk (this, VirtualBox::RHD_OnStartUp);
1168 if (FAILED (rc))
1169 return rc;
1170
1171 /* load all children */
1172 unsigned count = 0;
1173 CFGLDRCountChildren (aHDNode, "DiffHardDisk", &count);
1174 for (unsigned i = 0; i < count && SUCCEEDED (rc); ++ i)
1175 {
1176 CFGNODE hdNode = 0;
1177
1178 CFGLDRGetChildNode (aHDNode, "DiffHardDisk", i, &hdNode);
1179 ComAssertBreak (hdNode, rc = E_FAIL);
1180
1181 do
1182 {
1183 CFGNODE vdiNode = 0;
1184 CFGLDRGetChildNode (hdNode, "VirtualDiskImage", 0, &vdiNode);
1185 ComAssertBreak (vdiNode, rc = E_FAIL);
1186
1187 ComObjPtr <HVirtualDiskImage> vdi;
1188 vdi.createObject();
1189 rc = vdi->init (mVirtualBox, this, hdNode, vdiNode);
1190
1191 CFGLDRReleaseNode (vdiNode);
1192 }
1193 while (0);
1194
1195 CFGLDRReleaseNode (hdNode);
1196 }
1197
1198 return rc;
1199}
1200
1201/**
1202 * Saves the base settings of the hard disk to the given node
1203 * and saves all child hard disks as <DiffHardDisk> nodes.
1204 *
1205 * Subclasses must call this method in their saveSettings() methods
1206 * in order to be properly saved to the settings file.
1207 *
1208 * @param aHDNode <HardDisk> node when #isDifferencing() = false, or
1209 * <DiffHardDisk> node otherwise
1210 *
1211 * @note
1212 * Must be called from under the object's lock
1213 */
1214HRESULT HardDisk::saveSettings (CFGNODE aHDNode)
1215{
1216 AssertReturn (aHDNode, E_FAIL);
1217
1218 /* uuid (required) */
1219 CFGLDRSetUUID (aHDNode, "uuid", mId.ptr());
1220
1221 if (!isDifferencing())
1222 {
1223 /* type (required) */
1224 const char *type = NULL;
1225 switch (mType)
1226 {
1227 case HardDiskType_NormalHardDisk:
1228 type = "normal";
1229 break;
1230 case HardDiskType_ImmutableHardDisk:
1231 type = "immutable";
1232 break;
1233 case HardDiskType_WritethroughHardDisk:
1234 type = "writethrough";
1235 break;
1236 }
1237 CFGLDRSetString (aHDNode, "type", type);
1238 }
1239
1240 HRESULT rc = S_OK;
1241
1242 /* save all children */
1243 AutoLock chLock (childrenLock());
1244 for (HardDiskList::const_iterator it = children().begin();
1245 it != children().end() && SUCCEEDED (rc);
1246 ++ it)
1247 {
1248 ComObjPtr <HardDisk> child = *it;
1249 AutoLock childLock (child);
1250
1251 CFGNODE hdNode = 0;
1252 CFGLDRAppendChildNode (aHDNode, "DiffHardDisk", &hdNode);
1253 ComAssertBreak (hdNode, rc = E_FAIL);
1254
1255 do
1256 {
1257 CFGNODE vdiNode = 0;
1258 CFGLDRAppendChildNode (hdNode, "VirtualDiskImage", &vdiNode);
1259 ComAssertBreak (vdiNode, rc = E_FAIL);
1260
1261 rc = child->saveSettings (hdNode, vdiNode);
1262
1263 CFGLDRReleaseNode (vdiNode);
1264 }
1265 while (0);
1266
1267 CFGLDRReleaseNode (hdNode);
1268 }
1269
1270 return rc;
1271}
1272
1273////////////////////////////////////////////////////////////////////////////////
1274// HVirtualDiskImage class
1275////////////////////////////////////////////////////////////////////////////////
1276
1277// constructor / destructor
1278////////////////////////////////////////////////////////////////////////////////
1279
1280HRESULT HVirtualDiskImage::FinalConstruct()
1281{
1282 HRESULT rc = HardDisk::FinalConstruct();
1283 if (FAILED (rc))
1284 return rc;
1285
1286 mState = NotCreated;
1287
1288 mStateCheckSem = NIL_RTSEMEVENTMULTI;
1289 mStateCheckWaiters = 0;
1290
1291 mSize = 0;
1292 mActualSize = 0;
1293
1294 return S_OK;
1295}
1296
1297void HVirtualDiskImage::FinalRelease()
1298{
1299 HardDisk::FinalRelease();
1300}
1301
1302// public initializer/uninitializer for internal purposes only
1303////////////////////////////////////////////////////////////////////////////////
1304
1305// public methods for internal purposes only
1306/////////////////////////////////////////////////////////////////////////////
1307
1308/**
1309 * Initializes the VDI hard disk object by reading its properties from
1310 * the given configuration node. The created hard disk will be marked as
1311 * registered on success.
1312 *
1313 * @param aHDNode <HardDisk> node
1314 * @param aVDINode <VirtualDiskImage> node
1315 */
1316HRESULT HVirtualDiskImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
1317 CFGNODE aHDNode, CFGNODE aVDINode)
1318{
1319 LogFlowMember (("HVirtualDiskImage::init (load)\n"));
1320
1321 AssertReturn (aHDNode && aVDINode, E_FAIL);
1322
1323 AutoLock alock (this);
1324 ComAssertRet (!isReady(), E_UNEXPECTED);
1325
1326 mStorageType = HardDiskStorageType_VirtualDiskImage;
1327
1328 HRESULT rc = S_OK;
1329
1330 do
1331 {
1332 rc = protectedInit (aVirtualBox, aParent);
1333 CheckComRCBreakRC (rc);
1334
1335 /* set ready to let protectedUninit() be called on failure */
1336 setReady (true);
1337
1338 /* filePath (required) */
1339 Bstr filePath;
1340 CFGLDRQueryBSTR (aVDINode, "filePath", filePath.asOutParam());
1341
1342 rc = setFilePath (filePath);
1343 CheckComRCBreakRC (rc);
1344
1345 LogFlowMember ((" '%ls'\n", mFilePathFull.raw()));
1346
1347 /* load basic settings and children */
1348 rc = loadSettings (aHDNode);
1349 CheckComRCBreakRC (rc);
1350
1351 mState = Created;
1352 mRegistered = TRUE;
1353
1354 /* Don't call queryInformation() for registered hard disks to
1355 * prevent the calling thread (i.e. the VirtualBox server startup
1356 * thread) from an unexpected freeze. The vital mId property (UUID)
1357 * is read from the registry file in loadSettings(). To get the rest,
1358 * the user will have to call COMGETTER(Accessible) manually. */
1359 }
1360 while (0);
1361
1362 if (FAILED (rc))
1363 uninit();
1364
1365 return rc;
1366}
1367
1368/**
1369 * Initializes the VDI hard disk object using the given image file name.
1370 *
1371 * @param aVirtualBox VirtualBox parent.
1372 * @param aParent Parent hard disk.
1373 * @param aFilePath Path to the image file, or @c NULL to create an
1374 * image-less object.
1375 * @param aRegistered Whether to mark this disk as registered or not
1376 * (ignored when @a aFilePath is @c NULL, assuming @c FALSE)
1377 */
1378HRESULT HVirtualDiskImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
1379 const BSTR aFilePath, BOOL aRegistered /* = FALSE */)
1380{
1381 LogFlowMember (("HVirtualDiskImage::init (aFilePath='%ls', aRegistered=%d)\n",
1382 aFilePath, aRegistered));
1383
1384 AutoLock alock (this);
1385 ComAssertRet (!isReady(), E_UNEXPECTED);
1386
1387 mStorageType = HardDiskStorageType_VirtualDiskImage;
1388
1389 HRESULT rc = S_OK;
1390
1391 do
1392 {
1393 rc = protectedInit (aVirtualBox, aParent);
1394 CheckComRCBreakRC (rc);
1395
1396 /* set ready to let protectedUninit() be called on failure */
1397 setReady (true);
1398
1399 rc = setFilePath (aFilePath);
1400 CheckComRCBreakRC (rc);
1401
1402 Assert (mId.isEmpty());
1403
1404 if (aFilePath && *aFilePath)
1405 {
1406 mRegistered = aRegistered;
1407 mState = Created;
1408
1409 /* Call queryInformation() anyway (even if it will block), because
1410 * it is the only way to get the UUID of the existing VDI and
1411 * initialize the vital mId property. */
1412 Bstr errMsg;
1413 rc = queryInformation (&errMsg);
1414 if (SUCCEEDED (rc))
1415 {
1416 /* We are constructing a new HVirtualDiskImage object. If there
1417 * is a fatal accessibility error (we cannot read image UUID),
1418 * we have to fail. We do so even on non-fatal errors as well,
1419 * because it's not worth to keep going with the inaccessible
1420 * image from the very beginning (when nothing else depends on
1421 * it yet). */
1422 if (!errMsg.isNull())
1423 rc = setErrorBstr (E_FAIL, errMsg);
1424 }
1425 }
1426 else
1427 {
1428 mRegistered = FALSE;
1429 mState = NotCreated;
1430 mId.create();
1431 }
1432 }
1433 while (0);
1434
1435 if (FAILED (rc))
1436 uninit();
1437
1438 return rc;
1439}
1440
1441/**
1442 * Uninitializes the instance and sets the ready flag to FALSE.
1443 * Called either from FinalRelease(), by the parent when it gets destroyed,
1444 * or by a third party when it decides this object is no more valid.
1445 */
1446void HVirtualDiskImage::uninit()
1447{
1448 LogFlowMember (("HVirtualDiskImage::uninit()\n"));
1449
1450 AutoLock alock (this);
1451 if (!isReady())
1452 return;
1453
1454 HardDisk::protectedUninit (alock);
1455}
1456
1457// IHardDisk properties
1458////////////////////////////////////////////////////////////////////////////////
1459
1460STDMETHODIMP HVirtualDiskImage::COMGETTER(Description) (BSTR *aDescription)
1461{
1462 if (!aDescription)
1463 return E_POINTER;
1464
1465 AutoLock alock (this);
1466 CHECK_READY();
1467
1468 mDescription.cloneTo (aDescription);
1469 return S_OK;
1470}
1471
1472STDMETHODIMP HVirtualDiskImage::COMSETTER(Description) (INPTR BSTR aDescription)
1473{
1474 AutoLock alock (this);
1475 CHECK_READY();
1476
1477 CHECK_BUSY_AND_READERS();
1478
1479 if (mState >= Created)
1480 {
1481 int vrc = VDISetImageComment (Utf8Str (mFilePathFull), Utf8Str (aDescription));
1482 if (VBOX_FAILURE (vrc))
1483 return setError (E_FAIL,
1484 tr ("Could not change the description of the VDI hard disk '%ls' "
1485 "(%Vrc)"),
1486 toString().raw(), vrc);
1487 }
1488
1489 mDescription = aDescription;
1490 return S_OK;
1491}
1492
1493STDMETHODIMP HVirtualDiskImage::COMGETTER(Size) (ULONG64 *aSize)
1494{
1495 if (!aSize)
1496 return E_POINTER;
1497
1498 AutoLock alock (this);
1499 CHECK_READY();
1500
1501 /* only a non-differencing image knows the logical size */
1502 if (isDifferencing())
1503 return root()->COMGETTER(Size) (aSize);
1504
1505 *aSize = mSize;
1506 return S_OK;
1507}
1508
1509STDMETHODIMP HVirtualDiskImage::COMGETTER(ActualSize) (ULONG64 *aActualSize)
1510{
1511 if (!aActualSize)
1512 return E_POINTER;
1513
1514 AutoLock alock (this);
1515 CHECK_READY();
1516
1517 *aActualSize = mActualSize;
1518 return S_OK;
1519}
1520
1521// IVirtualDiskImage properties
1522////////////////////////////////////////////////////////////////////////////////
1523
1524STDMETHODIMP HVirtualDiskImage::COMGETTER(FilePath) (BSTR *aFilePath)
1525{
1526 if (!aFilePath)
1527 return E_POINTER;
1528
1529 AutoLock alock (this);
1530 CHECK_READY();
1531
1532 mFilePathFull.cloneTo (aFilePath);
1533 return S_OK;
1534}
1535
1536STDMETHODIMP HVirtualDiskImage::COMSETTER(FilePath) (INPTR BSTR aFilePath)
1537{
1538 AutoLock alock (this);
1539 CHECK_READY();
1540
1541 if (mState != NotCreated)
1542 return setError (E_ACCESSDENIED,
1543 tr ("Cannot change the file path of the existing hard disk '%ls'"),
1544 toString().raw());
1545
1546 CHECK_BUSY_AND_READERS();
1547
1548 // append the default path if only a name is given
1549 Bstr path = aFilePath;
1550 if (aFilePath && *aFilePath)
1551 {
1552 Utf8Str fp = aFilePath;
1553 if (!RTPathHavePath (fp))
1554 {
1555 AutoReaderLock propsLock (mVirtualBox->systemProperties());
1556 path = Utf8StrFmt ("%ls%c%s",
1557 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
1558 RTPATH_DELIMITER,
1559 fp.raw());
1560 }
1561 }
1562
1563 return setFilePath (path);
1564}
1565
1566STDMETHODIMP HVirtualDiskImage::COMGETTER(Created) (BOOL *aCreated)
1567{
1568 if (!aCreated)
1569 return E_POINTER;
1570
1571 AutoLock alock (this);
1572 CHECK_READY();
1573
1574 *aCreated = mState >= Created;
1575 return S_OK;
1576}
1577
1578// IVirtualDiskImage methods
1579/////////////////////////////////////////////////////////////////////////////
1580
1581STDMETHODIMP HVirtualDiskImage::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
1582{
1583 if (!aProgress)
1584 return E_POINTER;
1585
1586 AutoLock alock (this);
1587 CHECK_READY();
1588
1589 return createImage (aSize, TRUE /* aDynamic */, aProgress);
1590}
1591
1592STDMETHODIMP HVirtualDiskImage::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
1593{
1594 if (!aProgress)
1595 return E_POINTER;
1596
1597 AutoLock alock (this);
1598 CHECK_READY();
1599
1600 return createImage (aSize, FALSE /* aDynamic */, aProgress);
1601}
1602
1603STDMETHODIMP HVirtualDiskImage::DeleteImage()
1604{
1605 AutoLock alock (this);
1606 CHECK_READY();
1607 CHECK_BUSY_AND_READERS();
1608
1609 if (mRegistered)
1610 return setError (E_ACCESSDENIED,
1611 tr ("Cannot delete an image of the registered hard disk image '%ls"),
1612 mFilePathFull.raw());
1613 if (mState == NotCreated)
1614 return setError (E_FAIL,
1615 tr ("Hard disk image has been already deleted or never created"));
1616
1617 int vrc = RTFileDelete (Utf8Str (mFilePathFull));
1618 if (VBOX_FAILURE (vrc))
1619 return setError (E_FAIL, tr ("Could not delete the image file '%ls' (%Vrc)"),
1620 mFilePathFull.raw(), vrc);
1621
1622 mState = NotCreated;
1623
1624 // reset the fields
1625 mSize = 0;
1626 mActualSize = 0;
1627
1628 return S_OK;
1629}
1630
1631// public/protected methods for internal purposes only
1632/////////////////////////////////////////////////////////////////////////////
1633
1634/**
1635 * Attempts to mark the hard disk as registered.
1636 * Only VirtualBox can call this method.
1637 */
1638HRESULT HVirtualDiskImage::trySetRegistered (BOOL aRegistered)
1639{
1640 AutoLock alock (this);
1641 CHECK_READY();
1642
1643 if (aRegistered)
1644 {
1645 if (mState == NotCreated)
1646 return setError (E_FAIL,
1647 tr ("Image file '%ls' is not yet created for this hard disk"),
1648 mFilePathFull.raw());
1649 if (isDifferencing())
1650 return setError (E_FAIL,
1651 tr ("Hard disk '%ls' is differencing and cannot be unregistered "
1652 "explicitly"),
1653 mFilePathFull.raw());
1654 }
1655 else
1656 {
1657 ComAssertRet (mState >= Created, E_FAIL);
1658 }
1659
1660 return HardDisk::trySetRegistered (aRegistered);
1661}
1662
1663/**
1664 * Checks accessibility of this hard disk image only (w/o parents).
1665 *
1666 * @param aAccessError on output, a null string indicates the hard disk is
1667 * accessible, otherwise contains a message describing
1668 * the reason of inaccessibility.
1669 */
1670HRESULT HVirtualDiskImage::getAccessible (Bstr &aAccessError)
1671{
1672 AutoLock alock (this);
1673 CHECK_READY();
1674
1675 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
1676 {
1677 /* An accessibility check in progress on some other thread,
1678 * wait for it to finish. */
1679
1680 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL);
1681 ++ mStateCheckWaiters;
1682 alock.leave();
1683
1684 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
1685
1686 alock.enter();
1687 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
1688 -- mStateCheckWaiters;
1689 if (mStateCheckWaiters == 0)
1690 {
1691 RTSemEventMultiDestroy (mStateCheckSem);
1692 mStateCheckSem = NIL_RTSEMEVENTMULTI;
1693 }
1694
1695 AssertRCReturn (vrc, E_FAIL);
1696
1697 /* don't touch aAccessError, it has been already set */
1698 return S_OK;
1699 }
1700
1701 /* check the basic accessibility */
1702 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */);
1703 if (FAILED (rc) || !aAccessError.isNull())
1704 return rc;
1705
1706 if (mState >= Created)
1707 {
1708 return queryInformation (&aAccessError);
1709 }
1710
1711 aAccessError = Utf8StrFmt ("Hard disk image '%ls' is not yet created",
1712 mFilePathFull.raw());
1713 return S_OK;
1714}
1715
1716/**
1717 * Saves hard disk settings to the specified storage node and saves
1718 * all children to the specified hard disk node
1719 *
1720 * @param aHDNode <HardDisk> or <DiffHardDisk> node
1721 * @param aStorageNode <VirtualDiskImage> node
1722 */
1723HRESULT HVirtualDiskImage::saveSettings (CFGNODE aHDNode, CFGNODE aStorageNode)
1724{
1725 AssertReturn (aHDNode && aStorageNode, E_FAIL);
1726
1727 AutoLock alock (this);
1728 CHECK_READY();
1729
1730 // filePath (required)
1731 CFGLDRSetBSTR (aStorageNode, "filePath", mFilePath);
1732
1733 // save basic settings and children
1734 return HardDisk::saveSettings (aHDNode);
1735}
1736
1737/**
1738 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
1739 * of this hard disk and updates it if necessary to reflect the new location.
1740 * Intended to be from HardDisk::updatePaths().
1741 *
1742 * @param aOldPath old path (full)
1743 * @param aNewPath new path (full)
1744 *
1745 * @note Locks this object for writing.
1746 */
1747void HVirtualDiskImage::updatePath (const char *aOldPath, const char *aNewPath)
1748{
1749 AssertReturnVoid (aOldPath);
1750 AssertReturnVoid (aNewPath);
1751
1752 AutoLock alock (this);
1753 AssertReturnVoid (isReady());
1754
1755 size_t oldPathLen = strlen (aOldPath);
1756
1757 Utf8Str path = mFilePathFull;
1758 LogFlowThisFunc (("VDI.fullPath={%s}\n", path.raw()));
1759
1760 if (RTPathStartsWith (path, aOldPath))
1761 {
1762 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
1763 path.raw() + oldPathLen);
1764 path = newPath;
1765
1766 mVirtualBox->calculateRelativePath (path, path);
1767
1768 unconst (mFilePathFull) = newPath;
1769 unconst (mFilePath) = path;
1770
1771 LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
1772 newPath.raw(), path.raw()));
1773 }
1774}
1775
1776/**
1777 * Returns the string representation of this hard disk.
1778 * When \a aShort is false, returns the full image file path.
1779 * Otherwise, returns the image file name only.
1780 *
1781 * @param aShort if true, a short representation is returned
1782 */
1783Bstr HVirtualDiskImage::toString (bool aShort /* = false */)
1784{
1785 AutoLock alock (this);
1786
1787 if (!aShort)
1788 return mFilePathFull;
1789 else
1790 {
1791 Utf8Str fname = mFilePathFull;
1792 return RTPathFilename (fname.mutableRaw());
1793 }
1794}
1795
1796/**
1797 * Creates a clone of this hard disk by storing hard disk data in the given
1798 * VDI file name.
1799 *
1800 * @param aId UUID to assign to the created image
1801 * @param aTargetPath VDI file where the cloned image is to be to stored
1802 * @param aProgress progress object to run during operation
1803 */
1804HRESULT
1805HVirtualDiskImage::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
1806 Progress *aProgress)
1807{
1808 AssertReturn (!aId.isEmpty(), E_FAIL);
1809 AssertReturn (!aTargetPath.isNull(), E_FAIL);
1810 AssertReturn (aProgress, E_FAIL);
1811
1812 AutoLock alock (this);
1813 AssertReturn (isReady(), E_FAIL);
1814
1815 AssertReturn (isBusy() == false, E_FAIL);
1816
1817 /// @todo (dmik) cloning of differencing images is not yet supported
1818 AssertReturn (mParent.isNull(), E_FAIL);
1819
1820 Utf8Str filePathFull = mFilePathFull;
1821
1822 if (mState == NotCreated)
1823 return setError (E_FAIL,
1824 tr ("Source hard disk image '%s' is not yet created"),
1825 filePathFull.raw());
1826
1827 addReader();
1828 alock.leave();
1829
1830 int vrc = VDICopyImage (aTargetPath, filePathFull, NULL,
1831 progressCallback,
1832 static_cast <Progress *> (aProgress));
1833
1834 alock.enter();
1835 releaseReader();
1836
1837 if (VBOX_SUCCESS (vrc))
1838 vrc = VDISetImageUUIDs (aTargetPath, aId, NULL, NULL, NULL);
1839
1840 if (VBOX_FAILURE (vrc))
1841 return setError (E_FAIL,
1842 tr ("Could not copy the hard disk image '%s' to '%s' (%Vrc)"),
1843 filePathFull.raw(), aTargetPath.raw(), vrc);
1844
1845 return S_OK;
1846}
1847
1848/**
1849 * Creates a new differencing image for this hard disk with the given
1850 * VDI file name.
1851 *
1852 * @param aId UUID to assign to the created image
1853 * @param aTargetPath VDI file where to store the created differencing image
1854 * @param aProgress progress object to run during operation
1855 * (can be NULL)
1856 */
1857HRESULT
1858HVirtualDiskImage::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
1859 Progress *aProgress)
1860{
1861 AssertReturn (!aId.isEmpty(), E_FAIL);
1862 AssertReturn (!aTargetPath.isNull(), E_FAIL);
1863
1864 AutoLock alock (this);
1865 AssertReturn (isReady(), E_FAIL);
1866
1867 AssertReturn (isBusy() == false, E_FAIL);
1868 AssertReturn (mState >= Created, E_FAIL);
1869
1870 addReader();
1871 alock.leave();
1872
1873 int vrc = VDICreateDifferenceImage (aTargetPath, Utf8Str (mFilePathFull),
1874 NULL, aProgress ? progressCallback : NULL,
1875 static_cast <Progress *> (aProgress));
1876 alock.enter();
1877 releaseReader();
1878
1879 /* update the UUID to correspond to the file name */
1880 if (VBOX_SUCCESS (vrc))
1881 vrc = VDISetImageUUIDs (aTargetPath, aId, NULL, NULL, NULL);
1882
1883 if (VBOX_FAILURE (vrc))
1884 return setError (E_FAIL,
1885 tr ("Could not create a differencing hard disk '%s' (%Vrc)"),
1886 aTargetPath.raw(), vrc);
1887
1888 return S_OK;
1889}
1890
1891/**
1892 * Copies the image file of this hard disk to a separate VDI file (with an
1893 * unique creation UUID) and creates a new hard disk object for the copied
1894 * image. The copy will be created as a child of this hard disk's parent
1895 * (so that this hard disk must be a differencing one).
1896 *
1897 * The specified progress object (if not NULL) receives the percentage
1898 * of the operation completion. However, it is responsibility of the caller to
1899 * call Progress::notifyComplete() after this method returns.
1900 *
1901 * @param aFolder folder where to create a copy (must be a full path)
1902 * @param aMachineId machine ID the new hard disk will belong to
1903 * @param aHardDisk resulting hard disk object
1904 * @param aProgress progress object to run during copy operation
1905 * (may be NULL)
1906 *
1907 * @note
1908 * Must be NOT called from under locks of other objects that need external
1909 * access dirung this method execurion!
1910 */
1911HRESULT
1912HVirtualDiskImage::cloneDiffImage (const Bstr &aFolder, const Guid &aMachineId,
1913 ComObjPtr <HVirtualDiskImage> &aHardDisk,
1914 Progress *aProgress)
1915{
1916 AssertReturn (!aFolder.isEmpty() && !aMachineId.isEmpty(),
1917 E_FAIL);
1918
1919 AutoLock alock (this);
1920 CHECK_READY();
1921
1922 AssertReturn (!mParent.isNull(), E_FAIL);
1923
1924 ComAssertRet (isBusy() == false, E_FAIL);
1925 ComAssertRet (mState >= Created, E_FAIL);
1926
1927 Guid id;
1928 id.create();
1929
1930 Utf8Str filePathTo = Utf8StrFmt ("%ls%c{%Vuuid}.vdi",
1931 aFolder.raw(), RTPATH_DELIMITER, id.ptr());
1932
1933 /* try to make the path relative to the vbox home dir */
1934 const char *filePathToRel = filePathTo;
1935 {
1936 const Utf8Str &homeDir = mVirtualBox->homeDir();
1937 if (!strncmp (filePathTo, homeDir, homeDir.length()))
1938 filePathToRel = (filePathToRel + homeDir.length() + 1);
1939 }
1940
1941 /* first ensure the directory exists */
1942 {
1943 Utf8Str dir = aFolder;
1944 if (!RTDirExists (dir))
1945 {
1946 int vrc = RTDirCreateFullPath (dir, 0777);
1947 if (VBOX_FAILURE (vrc))
1948 {
1949 return setError (E_FAIL,
1950 tr ("Could not create a directory '%s' "
1951 "to store the image file (%Vrc)"),
1952 dir.raw(), vrc);
1953 }
1954 }
1955 }
1956
1957 Utf8Str filePathFull = mFilePathFull;
1958
1959 alock.leave();
1960
1961 int vrc = VDICopyImage (filePathTo, filePathFull, NULL,
1962 progressCallback,
1963 static_cast <Progress *> (aProgress));
1964
1965 alock.enter();
1966
1967 /* get modification and parent UUIDs of this image */
1968 RTUUID modUuid, parentUuid, parentModUuid;
1969 if (VBOX_SUCCESS (vrc))
1970 vrc = VDIGetImageUUIDs (filePathFull, NULL, &modUuid,
1971 &parentUuid, &parentModUuid);
1972
1973 // update the UUID of the copy to correspond to the file name
1974 // and copy all other UUIDs from this image
1975 if (VBOX_SUCCESS (vrc))
1976 vrc = VDISetImageUUIDs (filePathTo, id, &modUuid,
1977 &parentUuid, &parentModUuid);
1978
1979 if (VBOX_FAILURE (vrc))
1980 return setError (E_FAIL,
1981 tr ("Could not copy the hard disk image '%s' to '%s' (%Vrc)"),
1982 filePathFull.raw(), filePathTo.raw(), vrc);
1983
1984 ComObjPtr <HVirtualDiskImage> vdi;
1985 vdi.createObject();
1986 HRESULT rc = vdi->init (mVirtualBox, mParent, Bstr (filePathToRel),
1987 TRUE /* aRegistered */);
1988 if (FAILED (rc))
1989 return rc;
1990
1991 /* associate the created hard disk with the given machine */
1992 vdi->setMachineId (aMachineId);
1993
1994 rc = mVirtualBox->registerHardDisk (vdi, VirtualBox::RHD_Internal);
1995 if (FAILED (rc))
1996 return rc;
1997
1998 aHardDisk = vdi;
1999
2000 return S_OK;
2001}
2002
2003/**
2004 * Merges this child image to its parent image and updates the parent UUID
2005 * of all children of this image (to point to this image's parent).
2006 * It's a responsibility of the caller to unregister and uninitialize
2007 * the merged image on success.
2008 *
2009 * This method is intended to be called on a worker thread (the operation
2010 * can be time consuming).
2011 *
2012 * The specified progress object (if not NULL) receives the percentage
2013 * of the operation completion. However, it is responsibility of the caller to
2014 * call Progress::notifyComplete() after this method returns.
2015 *
2016 * @param aProgress progress object to run during copy operation
2017 * (may be NULL)
2018 *
2019 * @note
2020 * This method expects that both this hard disk and the paret hard disk
2021 * are marked as busy using #setBusyWithChildren() prior to calling it!
2022 * Busy flags of both hard disks will be cleared by this method
2023 * on a successful return. In case of failure, #clearBusyWithChildren()
2024 * must be called on a parent.
2025 *
2026 * @note
2027 * Must be NOT called from under locks of other objects that need external
2028 * access dirung this method execurion!
2029 */
2030HRESULT HVirtualDiskImage::mergeImageToParent (Progress *aProgress)
2031{
2032 LogFlowMember (("HVirtualDiskImage::mergeImageToParent(): image='%ls'\n",
2033 mFilePathFull.raw()));
2034
2035 AutoLock alock (this);
2036 CHECK_READY();
2037
2038 AssertReturn (!mParent.isNull(), E_FAIL);
2039 AutoLock parentLock (mParent);
2040
2041 ComAssertRet (isBusy() == true, E_FAIL);
2042 ComAssertRet (mParent->isBusy() == true, E_FAIL);
2043
2044 ComAssertRet (mState >= Created && mParent->asVDI()->mState >= Created, E_FAIL);
2045
2046 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2047 ("non VDI storage types are not yet supported!"), E_FAIL);
2048
2049 parentLock.leave();
2050 alock.leave();
2051
2052 int vrc = VDIMergeImage (Utf8Str (mFilePathFull),
2053 Utf8Str (mParent->asVDI()->mFilePathFull),
2054 progressCallback,
2055 static_cast <Progress *> (aProgress));
2056 alock.enter();
2057 parentLock.enter();
2058
2059 if (VBOX_FAILURE (vrc))
2060 return setError (E_FAIL,
2061 tr ("Could not merge the hard disk image '%ls' to "
2062 "its parent image '%ls' (%Vrc)"),
2063 mFilePathFull.raw(), mParent->asVDI()->mFilePathFull.raw(), vrc);
2064
2065 {
2066 HRESULT rc = S_OK;
2067
2068 AutoLock chLock (childrenLock());
2069
2070 for (HardDiskList::const_iterator it = children().begin();
2071 it != children().end(); ++ it)
2072 {
2073 ComObjPtr <HVirtualDiskImage> child = (*it)->asVDI();
2074 AutoLock childLock (child);
2075
2076 /* reparent the child */
2077 child->mParent = mParent;
2078 if (mParent)
2079 mParent->addDependentChild (child);
2080
2081 /* change the parent UUID in the image as well */
2082 RTUUID parentUuid, parentModUuid;
2083 vrc = VDIGetImageUUIDs (Utf8Str (mParent->asVDI()->mFilePathFull),
2084 &parentUuid, &parentModUuid, NULL, NULL);
2085 if (VBOX_FAILURE (vrc))
2086 {
2087 rc = setError (E_FAIL,
2088 tr ("Could not access the hard disk image '%ls' (%Vrc)"),
2089 mParent->asVDI()->mFilePathFull.raw(), vrc);
2090 break;
2091 }
2092 ComAssertBreak (mParent->id() == Guid (parentUuid), rc = E_FAIL);
2093 vrc = VDISetImageUUIDs (Utf8Str (child->mFilePathFull),
2094 NULL, NULL, &parentUuid, &parentModUuid);
2095 if (VBOX_FAILURE (vrc))
2096 {
2097 rc = setError (E_FAIL,
2098 tr ("Could not update parent UUID of the hard disk image "
2099 "'%ls' (%Vrc)"),
2100 child->mFilePathFull.raw(), vrc);
2101 break;
2102 }
2103 }
2104
2105 if (FAILED (rc))
2106 return rc;
2107 }
2108
2109 /* detach all our children to avoid their uninit in #uninit() */
2110 removeDependentChildren();
2111
2112 mParent->clearBusy();
2113 clearBusy();
2114
2115 return S_OK;
2116}
2117
2118/**
2119 * Merges this image to all its child images, updates the parent UUID
2120 * of all children of this image (to point to this image's parent).
2121 * It's a responsibility of the caller to unregister and uninitialize
2122 * the merged image on success.
2123 *
2124 * This method is intended to be called on a worker thread (the operation
2125 * can be time consuming).
2126 *
2127 * The specified progress object (if not NULL) receives the percentage
2128 * of the operation completion. However, it is responsibility of the caller to
2129 * call Progress::notifyComplete() after this method returns.
2130 *
2131 * @param aProgress progress object to run during copy operation
2132 * (may be NULL)
2133 *
2134 * @note
2135 * This method expects that both this hard disk and all children
2136 * are marked as busy using setBusyWithChildren() prior to calling it!
2137 * Busy flags of all affected hard disks will be cleared by this method
2138 * on a successful return. In case of failure, #clearBusyWithChildren()
2139 * must be called for this hard disk.
2140 *
2141 * @note
2142 * Must be NOT called from under locks of other objects that need external
2143 * access dirung this method execurion!
2144 */
2145HRESULT HVirtualDiskImage::mergeImageToChildren (Progress *aProgress)
2146{
2147 LogFlowMember (("HVirtualDiskImage::mergeImageToChildren(): image='%ls'\n",
2148 mFilePathFull.raw()));
2149
2150 AutoLock alock (this);
2151 CHECK_READY();
2152
2153 /* this must be a diff image */
2154 AssertReturn (isDifferencing(), E_FAIL);
2155
2156 ComAssertRet (isBusy() == true, E_FAIL);
2157 ComAssertRet (mState >= Created, E_FAIL);
2158
2159 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2160 ("non VDI storage types are not yet supported!"), E_FAIL);
2161
2162 {
2163 HRESULT rc = S_OK;
2164
2165 AutoLock chLock (childrenLock());
2166
2167 /* iterate over a copy since we will modify the list */
2168 HardDiskList list = children();
2169
2170 for (HardDiskList::const_iterator it = list.begin();
2171 it != list.end(); ++ it)
2172 {
2173 ComObjPtr <HardDisk> hd = *it;
2174 ComObjPtr <HVirtualDiskImage> child = hd->asVDI();
2175 AutoLock childLock (child);
2176
2177 ComAssertRet (child->isBusy() == true, E_FAIL);
2178 ComAssertBreak (child->mState >= Created, rc = E_FAIL);
2179
2180 childLock.leave();
2181 alock.leave();
2182
2183 int vrc = VDIMergeImage (Utf8Str (mFilePathFull),
2184 Utf8Str (child->mFilePathFull),
2185 progressCallback,
2186 static_cast <Progress *> (aProgress));
2187 alock.enter();
2188 childLock.enter();
2189
2190 if (VBOX_FAILURE (vrc))
2191 {
2192 rc = setError (E_FAIL,
2193 tr ("Could not merge the hard disk image '%ls' to "
2194 "its parent image '%ls' (%Vrc)"),
2195 mFilePathFull.raw(), child->mFilePathFull.raw(), vrc);
2196 break;
2197 }
2198
2199 /* reparent the child */
2200 child->mParent = mParent;
2201 if (mParent)
2202 mParent->addDependentChild (child);
2203
2204 /* change the parent UUID in the image as well */
2205 RTUUID parentUuid, parentModUuid;
2206 vrc = VDIGetImageUUIDs (Utf8Str (mParent->asVDI()->mFilePathFull),
2207 &parentUuid, &parentModUuid, NULL, NULL);
2208 if (VBOX_FAILURE (vrc))
2209 {
2210 rc = setError (E_FAIL,
2211 tr ("Could not access the hard disk image '%ls' (%Vrc)"),
2212 mParent->asVDI()->mFilePathFull.raw(), vrc);
2213 break;
2214 }
2215 ComAssertBreak (mParent->id() == Guid (parentUuid), rc = E_FAIL);
2216 vrc = VDISetImageUUIDs (Utf8Str (child->mFilePathFull),
2217 NULL, NULL, &parentUuid, &parentModUuid);
2218 if (VBOX_FAILURE (vrc))
2219 {
2220 rc = setError (E_FAIL,
2221 tr ("Could not update parent UUID of the hard disk image "
2222 "'%ls' (%Vrc)"),
2223 child->mFilePathFull.raw(), vrc);
2224 break;
2225 }
2226
2227 /* detach child to avoid its uninit in #uninit() */
2228 removeDependentChild (child);
2229
2230 /* remove the busy flag */
2231 child->clearBusy();
2232 }
2233
2234 if (FAILED (rc))
2235 return rc;
2236 }
2237
2238 clearBusy();
2239
2240 return S_OK;
2241}
2242
2243/**
2244 * Deletes and recreates the differencing hard disk image from scratch.
2245 * The file name and UUID remain the same.
2246 */
2247HRESULT HVirtualDiskImage::wipeOutImage()
2248{
2249 AutoLock alock (this);
2250 CHECK_READY();
2251
2252 AssertReturn (isDifferencing(), E_FAIL);
2253 AssertReturn (mRegistered, E_FAIL);
2254 AssertReturn (mState >= Created, E_FAIL);
2255
2256 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2257 ("non-VDI storage types are not yet supported!"), E_FAIL);
2258
2259 Utf8Str filePathFull = mFilePathFull;
2260
2261 int vrc = RTFileDelete (filePathFull);
2262 if (VBOX_FAILURE (vrc))
2263 return setError (E_FAIL,
2264 tr ("Could not delete the image file '%s' (%Vrc)"),
2265 filePathFull.raw(), vrc);
2266
2267 vrc = VDICreateDifferenceImage (filePathFull,
2268 Utf8Str (mParent->asVDI()->mFilePathFull),
2269 NULL, NULL, NULL);
2270 /* update the UUID to correspond to the file name */
2271 if (VBOX_SUCCESS (vrc))
2272 vrc = VDISetImageUUIDs (filePathFull, mId, NULL, NULL, NULL);
2273
2274 if (VBOX_FAILURE (vrc))
2275 return setError (E_FAIL,
2276 tr ("Could not create a differencing hard disk '%s' (%Vrc)"),
2277 filePathFull.raw(), vrc);
2278
2279 return S_OK;
2280}
2281
2282// private methods
2283/////////////////////////////////////////////////////////////////////////////
2284
2285/**
2286 * Helper to set a new file path.
2287 * Resolves a path relatively to the Virtual Box home directory.
2288 *
2289 * @note
2290 * Must be called from under the object's lock!
2291 */
2292HRESULT HVirtualDiskImage::setFilePath (const BSTR aFilePath)
2293{
2294 if (aFilePath && *aFilePath)
2295 {
2296 /* get the full file name */
2297 char filePathFull [RTPATH_MAX];
2298 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aFilePath),
2299 filePathFull, sizeof (filePathFull));
2300 if (VBOX_FAILURE (vrc))
2301 return setError (E_FAIL,
2302 tr ("Invalid image file path '%ls' (%Vrc)"), aFilePath, vrc);
2303
2304 mFilePath = aFilePath;
2305 mFilePathFull = filePathFull;
2306 }
2307 else
2308 {
2309 mFilePath.setNull();
2310 mFilePathFull.setNull();
2311 }
2312
2313 return S_OK;
2314}
2315
2316/**
2317 * Helper to query information about the VDI hard disk.
2318 *
2319 * @param aAccessError not used when NULL, otherwise see #getAccessible()
2320 *
2321 * @note Must be called from under the object's lock, only after
2322 * CHECK_BUSY_AND_READERS() succeeds.
2323 */
2324HRESULT HVirtualDiskImage::queryInformation (Bstr *aAccessError)
2325{
2326 AssertReturn (isLockedOnCurrentThread(), E_FAIL);
2327
2328 /* create a lock object to completely release it later */
2329 AutoLock alock (this);
2330
2331 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
2332
2333 ComAssertRet (mState >= Created, E_FAIL);
2334
2335 HRESULT rc = S_OK;
2336 int vrc = VINF_SUCCESS;
2337
2338 /* lazily create a semaphore */
2339 vrc = RTSemEventMultiCreate (&mStateCheckSem);
2340 ComAssertRCRet (vrc, E_FAIL);
2341
2342 /* Reference the disk to prevent any concurrent modifications
2343 * after releasing the lock below (to unblock getters before
2344 * a lengthy operation). */
2345 addReader();
2346
2347 alock.leave();
2348
2349 /* VBoxVHDD management interface needs to be optimized: we're opening a
2350 * file three times in a raw to get three bits of information. */
2351
2352 Utf8Str filePath = mFilePathFull;
2353 Bstr errMsg;
2354
2355 do
2356 {
2357 /* check the image file */
2358 Guid id, parentId;
2359 vrc = VDICheckImage (filePath, NULL, NULL, NULL,
2360 id.ptr(), parentId.ptr(), NULL, 0);
2361
2362 if (VBOX_FAILURE (vrc))
2363 break;
2364
2365 if (!mId.isEmpty())
2366 {
2367 /* check that the actual UUID of the image matches the stored UUID */
2368 if (mId != id)
2369 {
2370 errMsg = Utf8StrFmt (
2371 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
2372 "match UUID {%Vuuid} stored in the registry"),
2373 id.ptr(), filePath.raw(), mId.ptr());
2374 break;
2375 }
2376 }
2377 else
2378 {
2379 /* assgn an UUID read from the image file */
2380 mId = id;
2381 }
2382
2383 if (mParent)
2384 {
2385 /* check parent UUID */
2386 AutoLock parentLock (mParent);
2387 if (mParent->id() != parentId)
2388 {
2389 errMsg = Utf8StrFmt (
2390 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
2391 "the hard disk image file '%s' doesn't match "
2392 "UUID {%Vuuid} stored in the registry"),
2393 parentId.raw(), mParent->toString().raw(),
2394 filePath.raw(), mParent->id().raw());
2395 break;
2396 }
2397 }
2398 else if (!parentId.isEmpty())
2399 {
2400 errMsg = Utf8StrFmt (
2401 tr ("Hard disk image '%s' is a differencing image that is linked "
2402 "to a hard disk with UUID {%Vuuid} and cannot be used "
2403 "directly as a base hard disk"),
2404 filePath.raw(), parentId.raw());
2405 break;
2406 }
2407
2408 {
2409 RTFILE file = NIL_RTFILE;
2410 vrc = RTFileOpen (&file, filePath, RTFILE_O_READ);
2411 if (VBOX_SUCCESS (vrc))
2412 {
2413 uint64_t size = 0;
2414 vrc = RTFileGetSize (file, &size);
2415 if (VBOX_SUCCESS (vrc))
2416 mActualSize = size;
2417 RTFileClose (file);
2418 }
2419 if (VBOX_FAILURE (vrc))
2420 break;
2421 }
2422
2423 if (!mParent)
2424 {
2425 /* query logical size only for non-differencing images */
2426
2427 PVDIDISK disk = VDIDiskCreate();
2428 vrc = VDIDiskOpenImage (disk, Utf8Str (mFilePathFull),
2429 VDI_OPEN_FLAGS_READONLY);
2430 if (VBOX_SUCCESS (vrc))
2431 {
2432 uint64_t size = VDIDiskGetSize (disk);
2433 /* convert to MBytes */
2434 mSize = size / 1024 / 1024;
2435 }
2436
2437 VDIDiskDestroy (disk);
2438 if (VBOX_FAILURE (vrc))
2439 break;
2440 }
2441 }
2442 while (0);
2443
2444 /* enter the lock again */
2445 alock.enter();
2446
2447 /* remove the reference */
2448 releaseReader();
2449
2450 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
2451 {
2452 LogWarningFunc (("'%ls' is not accessible "
2453 "(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
2454 mFilePathFull.raw(), rc, vrc, errMsg.raw()));
2455
2456 if (aAccessError)
2457 {
2458 if (!errMsg.isNull())
2459 *aAccessError = errMsg;
2460 else if (VBOX_FAILURE (vrc))
2461 *aAccessError = Utf8StrFmt (
2462 tr ("Could not access hard disk image '%ls' (%Vrc)"),
2463 mFilePathFull.raw(), vrc);
2464 }
2465
2466 /* downgrade to not accessible */
2467 mState = Created;
2468 }
2469 else
2470 {
2471 if (aAccessError)
2472 aAccessError->setNull();
2473
2474 mState = Accessible;
2475 }
2476
2477 /* inform waiters if there are any */
2478 if (mStateCheckWaiters > 0)
2479 {
2480 RTSemEventMultiSignal (mStateCheckSem);
2481 }
2482 else
2483 {
2484 /* delete the semaphore ourselves */
2485 RTSemEventMultiDestroy (mStateCheckSem);
2486 mStateCheckSem = NIL_RTSEMEVENTMULTI;
2487 }
2488
2489 return rc;
2490}
2491
2492/**
2493 * Helper to create hard disk images.
2494 *
2495 * @param aSize size in MB
2496 * @param aDynamic dynamic or fixed image
2497 * @param aProgress address of IProgress pointer to return
2498 */
2499HRESULT HVirtualDiskImage::createImage (ULONG64 aSize, BOOL aDynamic,
2500 IProgress **aProgress)
2501{
2502 AutoLock alock (this);
2503
2504 CHECK_BUSY_AND_READERS();
2505
2506 if (mState != NotCreated)
2507 return setError (E_ACCESSDENIED,
2508 tr ("Hard disk image '%ls' is already created"),
2509 mFilePathFull.raw());
2510
2511 if (!mFilePathFull)
2512 return setError (E_ACCESSDENIED,
2513 tr ("Cannot create a hard disk image using an empty (null) file path"),
2514 mFilePathFull.raw());
2515
2516 /* first ensure the directory exists */
2517 {
2518 Utf8Str imageDir = mFilePathFull;
2519 RTPathStripFilename (imageDir.mutableRaw());
2520 if (!RTDirExists (imageDir))
2521 {
2522 int vrc = RTDirCreateFullPath (imageDir, 0777);
2523 if (VBOX_FAILURE (vrc))
2524 {
2525 return setError (E_FAIL,
2526 tr ("Could not create a directory '%s' "
2527 "to store the image file (%Vrc)"),
2528 imageDir.raw(), vrc);
2529 }
2530 }
2531 }
2532
2533 /* check whether the given file exists or not */
2534 RTFILE file;
2535 int vrc = RTFileOpen (&file, Utf8Str (mFilePathFull),
2536 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
2537 if (vrc != VERR_FILE_NOT_FOUND)
2538 {
2539 if (VBOX_SUCCESS (vrc))
2540 RTFileClose (file);
2541 switch(vrc)
2542 {
2543 case VINF_SUCCESS:
2544 return setError (E_FAIL,
2545 tr ("Image file '%ls' already exists"),
2546 mFilePathFull.raw());
2547
2548 default:
2549 return setError(E_FAIL,
2550 tr ("Invalid image file path '%ls' (%Vrc)"),
2551 mFilePathFull.raw(), vrc);
2552 }
2553 }
2554
2555 /* check VDI size limits */
2556 {
2557 HRESULT rc;
2558 ULONG64 maxVDISize;
2559 ComPtr <ISystemProperties> props;
2560 rc = mVirtualBox->COMGETTER(SystemProperties) (props.asOutParam());
2561 ComAssertComRCRet (rc, E_FAIL);
2562 rc = props->COMGETTER(MaxVDISize) (&maxVDISize);
2563 ComAssertComRCRet (rc, E_FAIL);
2564
2565 if (aSize < 1 || aSize > maxVDISize)
2566 return setError (E_INVALIDARG,
2567 tr ("Invalid VDI size: %llu MB (must be in range [1, %llu] MB)"),
2568 aSize, maxVDISize);
2569 }
2570
2571 HRESULT rc;
2572
2573 /* create a project object */
2574 ComObjPtr <Progress> progress;
2575 progress.createObject();
2576 {
2577 Bstr desc = aDynamic ? tr ("Creating a dynamically expanding hard disk")
2578 : tr ("Creating a fixed-size hard disk");
2579 rc = progress->init (mVirtualBox, (IVirtualDiskImage *) this, desc,
2580 FALSE /* aCancelable */);
2581 CheckComRCReturnRC (rc);
2582 }
2583
2584 /* mark as busy (being created)
2585 * (VDI task thread will unmark it) */
2586 setBusy();
2587
2588 /* fill in VDI task data */
2589 VDITask *task = new VDITask (aDynamic ? VDITask::CreateDynamic
2590 : VDITask::CreateStatic,
2591 this, progress);
2592 task->size = aSize;
2593 task->size *= 1024 * 1024; /* convert to bytes */
2594
2595 /* create the hard disk creation thread, pass operation data */
2596 vrc = RTThreadCreate (NULL, vdiTaskThread, (void *) task, 0,
2597 RTTHREADTYPE_MAIN_HEAVY_WORKER, 0, "VDITask");
2598 ComAssertMsgRC (vrc, ("Could not create a thread (%Vrc)", vrc));
2599 if (VBOX_FAILURE (vrc))
2600 {
2601 clearBusy();
2602 delete task;
2603 rc = E_FAIL;
2604 }
2605 else
2606 {
2607 /* get one interface for the caller */
2608 progress.queryInterfaceTo (aProgress);
2609 }
2610
2611 return rc;
2612}
2613
2614/* static */
2615DECLCALLBACK(int) HVirtualDiskImage::vdiTaskThread (RTTHREAD thread, void *pvUser)
2616{
2617 VDITask *task = static_cast <VDITask *> (pvUser);
2618 AssertReturn (task, VERR_GENERAL_FAILURE);
2619
2620 LogFlow (("vdiTaskThread(): operation=%d, size=%llu\n",
2621 task->operation, task->size));
2622
2623 VDIIMAGETYPE type = (VDIIMAGETYPE) 0;
2624
2625 switch (task->operation)
2626 {
2627 case VDITask::CreateDynamic: type = VDI_IMAGE_TYPE_NORMAL; break;
2628 case VDITask::CreateStatic: type = VDI_IMAGE_TYPE_FIXED; break;
2629 case VDITask::CloneToImage: break;
2630 default: AssertFailedReturn (VERR_GENERAL_FAILURE); break;
2631 }
2632
2633 HRESULT rc = S_OK;
2634 Utf8Str errorMsg;
2635
2636 if (task->operation == VDITask::CloneToImage)
2637 {
2638 Assert (!task->vdi->id().isEmpty());
2639 /// @todo (dmik) check locks
2640 AutoLock sourceLock (task->source);
2641 rc = task->source->cloneToImage (task->vdi->id(),
2642 Utf8Str (task->vdi->filePathFull()),
2643 task->progress);
2644
2645 /* release reader added in HardDisk::CloneToImage() */
2646 task->source->releaseReader();
2647 }
2648 else
2649 {
2650 int vrc = VDICreateBaseImage (Utf8Str (task->vdi->filePathFull()),
2651 type, task->size,
2652 Utf8Str (task->vdi->mDescription),
2653 progressCallback,
2654 static_cast <Progress *> (task->progress));
2655
2656 if (VBOX_SUCCESS (vrc) && task->vdi->id())
2657 {
2658 /* we have a non-null UUID, update the created image */
2659 vrc = VDISetImageUUIDs (Utf8Str (task->vdi->filePathFull()),
2660 task->vdi->id().raw(), NULL, NULL, NULL);
2661 }
2662
2663 if (VBOX_FAILURE (vrc))
2664 {
2665 errorMsg = Utf8StrFmt (
2666 tr ("Falied to create a hard disk image '%ls' (%Vrc)"),
2667 task->vdi->filePathFull().raw(), vrc);
2668 rc = E_FAIL;
2669 }
2670 }
2671
2672 LogFlow (("vdiTaskThread(): rc=%08X\n", rc));
2673
2674 AutoLock alock (task->vdi);
2675
2676 /* clear busy set in in HardDisk::CloneToImage() or
2677 * in HVirtualDiskImage::createImage() */
2678 task->vdi->clearBusy();
2679
2680 if (SUCCEEDED (rc))
2681 {
2682 task->vdi->mState = HVirtualDiskImage::Created;
2683 /* update VDI data fields */
2684 Bstr errMsg;
2685 rc = task->vdi->queryInformation (&errMsg);
2686 /* we want to deliver the access check result to the caller
2687 * immediately, before he calls HardDisk::GetAccssible() himself. */
2688 if (SUCCEEDED (rc) && !errMsg.isNull())
2689 task->progress->notifyCompleteBstr (
2690 E_FAIL, COM_IIDOF (IVirtualDiskImage), getComponentName(),
2691 errMsg);
2692 else
2693 task->progress->notifyComplete (rc);
2694 }
2695 else
2696 {
2697 /* delete the target file so we don't have orphaned files */
2698 RTFileDelete(Utf8Str (task->vdi->filePathFull()));
2699
2700 task->vdi->mState = HVirtualDiskImage::NotCreated;
2701 /* complete the progress object */
2702 if (errorMsg)
2703 task->progress->notifyComplete (
2704 E_FAIL, COM_IIDOF (IVirtualDiskImage), getComponentName(),
2705 errorMsg);
2706 else
2707 task->progress->notifyComplete (rc);
2708 }
2709
2710 delete task;
2711
2712 return VINF_SUCCESS;
2713}
2714
2715////////////////////////////////////////////////////////////////////////////////
2716// HISCSIHardDisk class
2717////////////////////////////////////////////////////////////////////////////////
2718
2719// constructor / destructor
2720////////////////////////////////////////////////////////////////////////////////
2721
2722HRESULT HISCSIHardDisk::FinalConstruct()
2723{
2724 HRESULT rc = HardDisk::FinalConstruct();
2725 if (FAILED (rc))
2726 return rc;
2727
2728 mSize = 0;
2729 mActualSize = 0;
2730
2731 mPort = 0;
2732 mLun = 0;
2733
2734 return S_OK;
2735}
2736
2737void HISCSIHardDisk::FinalRelease()
2738{
2739 HardDisk::FinalRelease();
2740}
2741
2742// public initializer/uninitializer for internal purposes only
2743////////////////////////////////////////////////////////////////////////////////
2744
2745// public methods for internal purposes only
2746/////////////////////////////////////////////////////////////////////////////
2747
2748/**
2749 * Initializes the iSCSI hard disk object by reading its properties from
2750 * the given configuration node. The created hard disk will be marked as
2751 * registered on success.
2752 *
2753 * @param aHDNode <HardDisk> node
2754 * @param aVDINod <ISCSIHardDisk> node
2755 */
2756HRESULT HISCSIHardDisk::init (VirtualBox *aVirtualBox,
2757 CFGNODE aHDNode, CFGNODE aISCSINode)
2758{
2759 LogFlowMember (("HISCSIHardDisk::init (load)\n"));
2760
2761 AssertReturn (aHDNode && aISCSINode, E_FAIL);
2762
2763 AutoLock alock (this);
2764 ComAssertRet (!isReady(), E_UNEXPECTED);
2765
2766 mStorageType = HardDiskStorageType_ISCSIHardDisk;
2767
2768 HRESULT rc = S_OK;
2769
2770 do
2771 {
2772 rc = protectedInit (aVirtualBox, NULL);
2773 CheckComRCBreakRC (rc);
2774
2775 /* set ready to let protectedUninit() be called on failure */
2776 setReady (true);
2777
2778 /* server (required) */
2779 CFGLDRQueryBSTR (aISCSINode, "server", mServer.asOutParam());
2780 /* target (required) */
2781 CFGLDRQueryBSTR (aISCSINode, "target", mTarget.asOutParam());
2782
2783 /* port (optional) */
2784 CFGLDRQueryUInt16 (aISCSINode, "port", &mPort);
2785 /* lun (optional) */
2786 CFGLDRQueryUInt64 (aISCSINode, "lun", &mLun);
2787 /* userName (optional) */
2788 CFGLDRQueryBSTR (aISCSINode, "userName", mUserName.asOutParam());
2789 /* password (optional) */
2790 CFGLDRQueryBSTR (aISCSINode, "password", mPassword.asOutParam());
2791
2792 LogFlowMember ((" 'iscsi:%ls:%hu@%ls/%ls:%llu'\n",
2793 mServer.raw(), mPort, mUserName.raw(), mTarget.raw(),
2794 mLun));
2795
2796 /* load basic settings and children */
2797 rc = loadSettings (aHDNode);
2798 CheckComRCBreakRC (rc);
2799
2800 if (mType != HardDiskType_WritethroughHardDisk)
2801 {
2802 rc = setError (E_FAIL,
2803 tr ("Currently, non-Writethrough iSCSI hard disks are not "
2804 "allowed ('%ls')"),
2805 toString().raw());
2806 break;
2807 }
2808
2809 mRegistered = TRUE;
2810 }
2811 while (0);
2812
2813 if (FAILED (rc))
2814 uninit();
2815
2816 return rc;
2817}
2818
2819/**
2820 * Initializes the iSCSI hard disk object using default values for all
2821 * properties. The created hard disk will NOT be marked as registered.
2822 */
2823HRESULT HISCSIHardDisk::init (VirtualBox *aVirtualBox)
2824{
2825 LogFlowMember (("HISCSIHardDisk::init()\n"));
2826
2827 AutoLock alock (this);
2828 ComAssertRet (!isReady(), E_UNEXPECTED);
2829
2830 mStorageType = HardDiskStorageType_ISCSIHardDisk;
2831
2832 HRESULT rc = S_OK;
2833
2834 do
2835 {
2836 rc = protectedInit (aVirtualBox, NULL);
2837 CheckComRCBreakRC (rc);
2838
2839 /* set ready to let protectedUninit() be called on failure */
2840 setReady (true);
2841
2842 /* we have to generate a new UUID */
2843 mId.create();
2844 /* currently, all iSCSI hard disks are writethrough */
2845 mType = HardDiskType_WritethroughHardDisk;
2846 mRegistered = FALSE;
2847 }
2848 while (0);
2849
2850 if (FAILED (rc))
2851 uninit();
2852
2853 return rc;
2854}
2855
2856/**
2857 * Uninitializes the instance and sets the ready flag to FALSE.
2858 * Called either from FinalRelease(), by the parent when it gets destroyed,
2859 * or by a third party when it decides this object is no more valid.
2860 */
2861void HISCSIHardDisk::uninit()
2862{
2863 LogFlowMember (("HISCSIHardDisk::uninit()\n"));
2864
2865 AutoLock alock (this);
2866 if (!isReady())
2867 return;
2868
2869 HardDisk::protectedUninit (alock);
2870}
2871
2872// IHardDisk properties
2873////////////////////////////////////////////////////////////////////////////////
2874
2875STDMETHODIMP HISCSIHardDisk::COMGETTER(Description) (BSTR *aDescription)
2876{
2877 if (!aDescription)
2878 return E_POINTER;
2879
2880 AutoLock alock (this);
2881 CHECK_READY();
2882
2883 mDescription.cloneTo (aDescription);
2884 return S_OK;
2885}
2886
2887STDMETHODIMP HISCSIHardDisk::COMSETTER(Description) (INPTR BSTR aDescription)
2888{
2889 AutoLock alock (this);
2890 CHECK_READY();
2891
2892 CHECK_BUSY_AND_READERS();
2893
2894 if (mDescription != aDescription)
2895 {
2896 mDescription = aDescription;
2897 if (mRegistered)
2898 return mVirtualBox->saveSettings();
2899 }
2900
2901 return S_OK;
2902}
2903
2904STDMETHODIMP HISCSIHardDisk::COMGETTER(Size) (ULONG64 *aSize)
2905{
2906 if (!aSize)
2907 return E_POINTER;
2908
2909 AutoLock alock (this);
2910 CHECK_READY();
2911
2912 *aSize = mSize;
2913 return S_OK;
2914}
2915
2916STDMETHODIMP HISCSIHardDisk::COMGETTER(ActualSize) (ULONG64 *aActualSize)
2917{
2918 if (!aActualSize)
2919 return E_POINTER;
2920
2921 AutoLock alock (this);
2922 CHECK_READY();
2923
2924 *aActualSize = mActualSize;
2925 return S_OK;
2926}
2927
2928// IISCSIHardDisk properties
2929////////////////////////////////////////////////////////////////////////////////
2930
2931STDMETHODIMP HISCSIHardDisk::COMGETTER(Server) (BSTR *aServer)
2932{
2933 if (!aServer)
2934 return E_POINTER;
2935
2936 AutoLock alock (this);
2937 CHECK_READY();
2938
2939 mServer.cloneTo (aServer);
2940 return S_OK;
2941}
2942
2943STDMETHODIMP HISCSIHardDisk::COMSETTER(Server) (INPTR BSTR aServer)
2944{
2945 if (!aServer || !*aServer)
2946 return E_INVALIDARG;
2947
2948 AutoLock alock (this);
2949 CHECK_READY();
2950
2951 CHECK_BUSY_AND_READERS();
2952
2953 if (mServer != aServer)
2954 {
2955 mServer = aServer;
2956 if (mRegistered)
2957 return mVirtualBox->saveSettings();
2958 }
2959
2960 return S_OK;
2961}
2962
2963STDMETHODIMP HISCSIHardDisk::COMGETTER(Port) (USHORT *aPort)
2964{
2965 if (!aPort)
2966 return E_POINTER;
2967
2968 AutoLock alock (this);
2969 CHECK_READY();
2970
2971 *aPort = mPort;
2972 return S_OK;
2973}
2974
2975STDMETHODIMP HISCSIHardDisk::COMSETTER(Port) (USHORT aPort)
2976{
2977 AutoLock alock (this);
2978 CHECK_READY();
2979
2980 CHECK_BUSY_AND_READERS();
2981
2982 if (mPort != aPort)
2983 {
2984 mPort = aPort;
2985 if (mRegistered)
2986 return mVirtualBox->saveSettings();
2987 }
2988
2989 return S_OK;
2990}
2991
2992STDMETHODIMP HISCSIHardDisk::COMGETTER(Target) (BSTR *aTarget)
2993{
2994 if (!aTarget)
2995 return E_POINTER;
2996
2997 AutoLock alock (this);
2998 CHECK_READY();
2999
3000 mTarget.cloneTo (aTarget);
3001 return S_OK;
3002}
3003
3004STDMETHODIMP HISCSIHardDisk::COMSETTER(Target) (INPTR BSTR aTarget)
3005{
3006 if (!aTarget || !*aTarget)
3007 return E_INVALIDARG;
3008
3009 AutoLock alock (this);
3010 CHECK_READY();
3011
3012 CHECK_BUSY_AND_READERS();
3013
3014 if (mTarget != aTarget)
3015 {
3016 mTarget = aTarget;
3017 if (mRegistered)
3018 return mVirtualBox->saveSettings();
3019 }
3020
3021 return S_OK;
3022}
3023
3024STDMETHODIMP HISCSIHardDisk::COMGETTER(Lun) (ULONG64 *aLun)
3025{
3026 if (!aLun)
3027 return E_POINTER;
3028
3029 AutoLock alock (this);
3030 CHECK_READY();
3031
3032 *aLun = mLun;
3033 return S_OK;
3034}
3035
3036STDMETHODIMP HISCSIHardDisk::COMSETTER(Lun) (ULONG64 aLun)
3037{
3038 AutoLock alock (this);
3039 CHECK_READY();
3040
3041 CHECK_BUSY_AND_READERS();
3042
3043 if (mLun != aLun)
3044 {
3045 mLun = aLun;
3046 if (mRegistered)
3047 return mVirtualBox->saveSettings();
3048 }
3049
3050 return S_OK;
3051}
3052
3053STDMETHODIMP HISCSIHardDisk::COMGETTER(UserName) (BSTR *aUserName)
3054{
3055 if (!aUserName)
3056 return E_POINTER;
3057
3058 AutoLock alock (this);
3059 CHECK_READY();
3060
3061 mUserName.cloneTo (aUserName);
3062 return S_OK;
3063}
3064
3065STDMETHODIMP HISCSIHardDisk::COMSETTER(UserName) (INPTR BSTR aUserName)
3066{
3067 AutoLock alock (this);
3068 CHECK_READY();
3069
3070 CHECK_BUSY_AND_READERS();
3071
3072 if (mUserName != aUserName)
3073 {
3074 mUserName = aUserName;
3075 if (mRegistered)
3076 return mVirtualBox->saveSettings();
3077 }
3078
3079 return S_OK;
3080}
3081
3082STDMETHODIMP HISCSIHardDisk::COMGETTER(Password) (BSTR *aPassword)
3083{
3084 if (!aPassword)
3085 return E_POINTER;
3086
3087 AutoLock alock (this);
3088 CHECK_READY();
3089
3090 mPassword.cloneTo (aPassword);
3091 return S_OK;
3092}
3093
3094STDMETHODIMP HISCSIHardDisk::COMSETTER(Password) (INPTR BSTR aPassword)
3095{
3096 AutoLock alock (this);
3097 CHECK_READY();
3098
3099 CHECK_BUSY_AND_READERS();
3100
3101 if (mPassword != aPassword)
3102 {
3103 mPassword = aPassword;
3104 if (mRegistered)
3105 return mVirtualBox->saveSettings();
3106 }
3107
3108 return S_OK;
3109}
3110
3111// public/protected methods for internal purposes only
3112/////////////////////////////////////////////////////////////////////////////
3113
3114/**
3115 * Attempts to mark the hard disk as registered.
3116 * Only VirtualBox can call this method.
3117 */
3118HRESULT HISCSIHardDisk::trySetRegistered (BOOL aRegistered)
3119{
3120 AutoLock alock (this);
3121 CHECK_READY();
3122
3123 if (aRegistered)
3124 {
3125 if (mServer.isEmpty() || mTarget.isEmpty())
3126 return setError (E_FAIL,
3127 tr ("iSCSI Hard disk has no server or target defined"));
3128 }
3129 else
3130 {
3131 }
3132
3133 return HardDisk::trySetRegistered (aRegistered);
3134}
3135
3136/**
3137 * Checks accessibility of this iSCSI hard disk.
3138 */
3139HRESULT HISCSIHardDisk::getAccessible (Bstr &aAccessError)
3140{
3141 AutoLock alock (this);
3142 CHECK_READY();
3143
3144 /* check the basic accessibility */
3145 HRESULT rc = getBaseAccessible (aAccessError);
3146 if (FAILED (rc) || !aAccessError.isNull())
3147 return rc;
3148
3149 return queryInformation (aAccessError);
3150}
3151
3152/**
3153 * Saves hard disk settings to the specified storage node and saves
3154 * all children to the specified hard disk node
3155 *
3156 * @param aHDNode <HardDisk>
3157 * @param aStorageNode <ISCSIHardDisk> node
3158 */
3159HRESULT HISCSIHardDisk::saveSettings (CFGNODE aHDNode, CFGNODE aStorageNode)
3160{
3161 AssertReturn (aHDNode && aStorageNode, E_FAIL);
3162
3163 AutoLock alock (this);
3164 CHECK_READY();
3165
3166 /* server (required) */
3167 CFGLDRSetBSTR (aStorageNode, "server", mServer);
3168 /* target (required) */
3169 CFGLDRSetBSTR (aStorageNode, "target", mTarget);
3170
3171 /* port (optional) */
3172 if (mPort != 0)
3173 CFGLDRSetUInt16 (aStorageNode, "port", mPort);
3174 else
3175 CFGLDRDeleteAttribute (aStorageNode, "port");
3176 /* lun (optional) */
3177 if (mLun != 0)
3178 CFGLDRSetUInt64 (aStorageNode, "lun", mLun);
3179 else
3180 CFGLDRDeleteAttribute (aStorageNode, "lun");
3181 /* userName (optional) */
3182 if (!mUserName.isNull())
3183 CFGLDRSetBSTR (aStorageNode, "userName", mUserName);
3184 else
3185 CFGLDRDeleteAttribute (aStorageNode, "userName");
3186 /* password (optional) */
3187 if (!mPassword.isNull())
3188 CFGLDRSetBSTR (aStorageNode, "password", mPassword);
3189 else
3190 CFGLDRDeleteAttribute (aStorageNode, "password");
3191
3192 /* save basic settings and children */
3193 return HardDisk::saveSettings (aHDNode);
3194}
3195
3196/**
3197 * Returns the string representation of this hard disk.
3198 * When \a aShort is false, returns the full image file path.
3199 * Otherwise, returns the image file name only.
3200 *
3201 * @param aShort if true, a short representation is returned
3202 */
3203Bstr HISCSIHardDisk::toString (bool aShort /* = false */)
3204{
3205 AutoLock alock (this);
3206
3207 Bstr str;
3208 if (!aShort)
3209 {
3210 /* the format is iscsi:[<user@>]<server>[:<port>]/<target>[:<lun>] */
3211 str = Utf8StrFmt ("iscsi:%s%ls%s/%ls%s",
3212 !mUserName.isNull() ? Utf8StrFmt ("%ls@", mUserName.raw()).raw() : "",
3213 mServer.raw(),
3214 mPort != 0 ? Utf8StrFmt (":%hu", mPort).raw() : "",
3215 mTarget.raw(),
3216 mLun != 0 ? Utf8StrFmt (":%llu", mLun).raw() : "");
3217 }
3218 else
3219 {
3220 str = Utf8StrFmt ("%ls%s",
3221 mTarget.raw(),
3222 mLun != 0 ? Utf8StrFmt (":%llu", mLun).raw() : "");
3223 }
3224
3225 return str;
3226}
3227
3228/**
3229 * Creates a clone of this hard disk by storing hard disk data in the given
3230 * VDI file name.
3231 *
3232 * @param aId UUID to assign to the created image
3233 * @param aTargetPath VDI file where the cloned image is to be to stored
3234 * @param aProgress progress object to run during operation
3235 */
3236HRESULT
3237HISCSIHardDisk::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
3238 Progress *aProgress)
3239{
3240 ComAssertMsgFailed (("Not implemented"));
3241 return E_NOTIMPL;
3242
3243// AssertReturn (isBusy() == false, E_FAIL);
3244// addReader();
3245// releaseReader();
3246}
3247
3248/**
3249 * Creates a new differencing image for this hard disk with the given
3250 * VDI file name.
3251 *
3252 * @param aId UUID to assign to the created image
3253 * @param aTargetPath VDI file where to store the created differencing image
3254 * @param aProgress progress object to run during operation
3255 * (can be NULL)
3256 */
3257HRESULT
3258HISCSIHardDisk::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
3259 Progress *aProgress)
3260{
3261 ComAssertMsgFailed (("Not implemented"));
3262 return E_NOTIMPL;
3263
3264// AssertReturn (isBusy() == false, E_FAIL);
3265// addReader();
3266// releaseReader();
3267}
3268
3269// private methods
3270/////////////////////////////////////////////////////////////////////////////
3271
3272/**
3273 * Helper to query information about the iSCSI hard disk.
3274 *
3275 * @param aAccessError see #getAccessible()
3276 * @note
3277 * Must be called from under the object's lock!
3278 */
3279HRESULT HISCSIHardDisk::queryInformation (Bstr &aAccessError)
3280{
3281 /// @todo (dmik) query info about this iSCSI disk,
3282 // set mSize and mActualSize,
3283 // or set aAccessError in case of failure
3284
3285 aAccessError.setNull();
3286 return S_OK;
3287}
3288
3289////////////////////////////////////////////////////////////////////////////////
3290// HVMDKImage class
3291////////////////////////////////////////////////////////////////////////////////
3292
3293// constructor / destructor
3294////////////////////////////////////////////////////////////////////////////////
3295
3296HRESULT HVMDKImage::FinalConstruct()
3297{
3298 HRESULT rc = HardDisk::FinalConstruct();
3299 if (FAILED (rc))
3300 return rc;
3301
3302 mState = NotCreated;
3303
3304 mStateCheckSem = NIL_RTSEMEVENTMULTI;
3305 mStateCheckWaiters = 0;
3306
3307 mSize = 0;
3308 mActualSize = 0;
3309
3310 return S_OK;
3311}
3312
3313void HVMDKImage::FinalRelease()
3314{
3315 HardDisk::FinalRelease();
3316}
3317
3318// public initializer/uninitializer for internal purposes only
3319////////////////////////////////////////////////////////////////////////////////
3320
3321// public methods for internal purposes only
3322/////////////////////////////////////////////////////////////////////////////
3323
3324/**
3325 * Initializes the VMDK hard disk object by reading its properties from
3326 * the given configuration node. The created hard disk will be marked as
3327 * registered on success.
3328 *
3329 * @param aHDNode <HardDisk> node
3330 * @param aVMDKNode <VirtualDiskImage> node
3331 */
3332HRESULT HVMDKImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
3333 CFGNODE aHDNode, CFGNODE aVMDKNode)
3334{
3335 LogFlowMember (("HVMDKImage::init (load)\n"));
3336
3337 AssertReturn (aHDNode && aVMDKNode, E_FAIL);
3338
3339 AutoLock alock (this);
3340 ComAssertRet (!isReady(), E_UNEXPECTED);
3341
3342 mStorageType = HardDiskStorageType_VMDKImage;
3343
3344 HRESULT rc = S_OK;
3345
3346 do
3347 {
3348 rc = protectedInit (aVirtualBox, aParent);
3349 CheckComRCBreakRC (rc);
3350
3351 /* set ready to let protectedUninit() be called on failure */
3352 setReady (true);
3353
3354 /* filePath (required) */
3355 Bstr filePath;
3356 CFGLDRQueryBSTR (aVMDKNode, "filePath", filePath.asOutParam());
3357
3358 rc = setFilePath (filePath);
3359 CheckComRCBreakRC (rc);
3360
3361 LogFlowMember ((" '%ls'\n", mFilePathFull.raw()));
3362
3363 /* load basic settings and children */
3364 rc = loadSettings (aHDNode);
3365 CheckComRCBreakRC (rc);
3366
3367 if (mType != HardDiskType_WritethroughHardDisk)
3368 {
3369 rc = setError (E_FAIL,
3370 tr ("Currently, non-Writethrough VMDK images are not "
3371 "allowed ('%ls')"),
3372 toString().raw());
3373 break;
3374 }
3375
3376 mState = Created;
3377 mRegistered = TRUE;
3378
3379 /* Don't call queryInformation() for registered hard disks to
3380 * prevent the calling thread (i.e. the VirtualBox server startup
3381 * thread) from an unexpected freeze. The vital mId property (UUID)
3382 * is read from the registry file in loadSettings(). To get the rest,
3383 * the user will have to call COMGETTER(Accessible) manually. */
3384 }
3385 while (0);
3386
3387 if (FAILED (rc))
3388 uninit();
3389
3390 return rc;
3391}
3392
3393/**
3394 * Initializes the VMDK hard disk object using the given image file name.
3395 *
3396 * @param aVirtualBox VirtualBox parent.
3397 * @param aParent Currently, must always be @c NULL.
3398 * @param aFilePath Path to the image file, or @c NULL to create an
3399 * image-less object.
3400 * @param aRegistered Whether to mark this disk as registered or not
3401 * (ignored when @a aFilePath is @c NULL, assuming @c FALSE)
3402 */
3403HRESULT HVMDKImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
3404 const BSTR aFilePath, BOOL aRegistered /* = FALSE */)
3405{
3406 LogFlowMember (("HVMDKImage::init (aFilePath='%ls', aRegistered=%d)\n",
3407 aFilePath, aRegistered));
3408
3409 AssertReturn (aParent == NULL, E_FAIL);
3410
3411 AutoLock alock (this);
3412 ComAssertRet (!isReady(), E_UNEXPECTED);
3413
3414 mStorageType = HardDiskStorageType_VMDKImage;
3415
3416 HRESULT rc = S_OK;
3417
3418 do
3419 {
3420 rc = protectedInit (aVirtualBox, aParent);
3421 CheckComRCBreakRC (rc);
3422
3423 /* set ready to let protectedUninit() be called on failure */
3424 setReady (true);
3425
3426 rc = setFilePath (aFilePath);
3427 CheckComRCBreakRC (rc);
3428
3429 /* currently, all VMDK hard disks are writethrough */
3430 mType = HardDiskType_WritethroughHardDisk;
3431
3432 Assert (mId.isEmpty());
3433
3434 if (aFilePath && *aFilePath)
3435 {
3436 mRegistered = aRegistered;
3437 mState = Created;
3438
3439 /* Call queryInformation() anyway (even if it will block), because
3440 * it is the only way to get the UUID of the existing VDI and
3441 * initialize the vital mId property. */
3442 Bstr errMsg;
3443 rc = queryInformation (&errMsg);
3444 if (SUCCEEDED (rc))
3445 {
3446 /* We are constructing a new HVirtualDiskImage object. If there
3447 * is a fatal accessibility error (we cannot read image UUID),
3448 * we have to fail. We do so even on non-fatal errors as well,
3449 * because it's not worth to keep going with the inaccessible
3450 * image from the very beginning (when nothing else depends on
3451 * it yet). */
3452 if (!errMsg.isNull())
3453 rc = setErrorBstr (E_FAIL, errMsg);
3454 }
3455 }
3456 else
3457 {
3458 mRegistered = FALSE;
3459 mState = NotCreated;
3460 mId.create();
3461 }
3462 }
3463 while (0);
3464
3465 if (FAILED (rc))
3466 uninit();
3467
3468 return rc;
3469}
3470
3471/**
3472 * Uninitializes the instance and sets the ready flag to FALSE.
3473 * Called either from FinalRelease(), by the parent when it gets destroyed,
3474 * or by a third party when it decides this object is no more valid.
3475 */
3476void HVMDKImage::uninit()
3477{
3478 LogFlowMember (("HVMDKImage::uninit()\n"));
3479
3480 AutoLock alock (this);
3481 if (!isReady())
3482 return;
3483
3484 HardDisk::protectedUninit (alock);
3485}
3486
3487// IHardDisk properties
3488////////////////////////////////////////////////////////////////////////////////
3489
3490STDMETHODIMP HVMDKImage::COMGETTER(Description) (BSTR *aDescription)
3491{
3492 if (!aDescription)
3493 return E_POINTER;
3494
3495 AutoLock alock (this);
3496 CHECK_READY();
3497
3498 mDescription.cloneTo (aDescription);
3499 return S_OK;
3500}
3501
3502STDMETHODIMP HVMDKImage::COMSETTER(Description) (INPTR BSTR aDescription)
3503{
3504 AutoLock alock (this);
3505 CHECK_READY();
3506
3507 CHECK_BUSY_AND_READERS();
3508
3509 return E_NOTIMPL;
3510
3511/// @todo (r=dmik) implement
3512//
3513// if (mState >= Created)
3514// {
3515// int vrc = VDISetImageComment (Utf8Str (mFilePathFull), Utf8Str (aDescription));
3516// if (VBOX_FAILURE (vrc))
3517// return setError (E_FAIL,
3518// tr ("Could not change the description of the VDI hard disk '%ls' "
3519// "(%Vrc)"),
3520// toString().raw(), vrc);
3521// }
3522//
3523// mDescription = aDescription;
3524// return S_OK;
3525}
3526
3527STDMETHODIMP HVMDKImage::COMGETTER(Size) (ULONG64 *aSize)
3528{
3529 if (!aSize)
3530 return E_POINTER;
3531
3532 AutoLock alock (this);
3533 CHECK_READY();
3534
3535/// @todo (r=dmik) will need this if we add suppord for differencing VMDKs
3536//
3537// /* only a non-differencing image knows the logical size */
3538// if (isDifferencing())
3539// return root()->COMGETTER(Size) (aSize);
3540
3541 *aSize = mSize;
3542 return S_OK;
3543}
3544
3545STDMETHODIMP HVMDKImage::COMGETTER(ActualSize) (ULONG64 *aActualSize)
3546{
3547 if (!aActualSize)
3548 return E_POINTER;
3549
3550 AutoLock alock (this);
3551 CHECK_READY();
3552
3553 *aActualSize = mActualSize;
3554 return S_OK;
3555}
3556
3557// IVirtualDiskImage properties
3558////////////////////////////////////////////////////////////////////////////////
3559
3560STDMETHODIMP HVMDKImage::COMGETTER(FilePath) (BSTR *aFilePath)
3561{
3562 if (!aFilePath)
3563 return E_POINTER;
3564
3565 AutoLock alock (this);
3566 CHECK_READY();
3567
3568 mFilePathFull.cloneTo (aFilePath);
3569 return S_OK;
3570}
3571
3572STDMETHODIMP HVMDKImage::COMSETTER(FilePath) (INPTR BSTR aFilePath)
3573{
3574 AutoLock alock (this);
3575 CHECK_READY();
3576
3577 if (mState != NotCreated)
3578 return setError (E_ACCESSDENIED,
3579 tr ("Cannot change the file path of the existing hard disk '%ls'"),
3580 toString().raw());
3581
3582 CHECK_BUSY_AND_READERS();
3583
3584 /* append the default path if only a name is given */
3585 Bstr path = aFilePath;
3586 if (aFilePath && *aFilePath)
3587 {
3588 Utf8Str fp = aFilePath;
3589 if (!RTPathHavePath (fp))
3590 {
3591 AutoReaderLock propsLock (mVirtualBox->systemProperties());
3592 path = Utf8StrFmt ("%ls%c%s",
3593 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
3594 RTPATH_DELIMITER,
3595 fp.raw());
3596 }
3597 }
3598
3599 return setFilePath (path);
3600}
3601
3602STDMETHODIMP HVMDKImage::COMGETTER(Created) (BOOL *aCreated)
3603{
3604 if (!aCreated)
3605 return E_POINTER;
3606
3607 AutoLock alock (this);
3608 CHECK_READY();
3609
3610 *aCreated = mState >= Created;
3611 return S_OK;
3612}
3613
3614// IVMDKImage methods
3615/////////////////////////////////////////////////////////////////////////////
3616
3617STDMETHODIMP HVMDKImage::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
3618{
3619 if (!aProgress)
3620 return E_POINTER;
3621
3622 AutoLock alock (this);
3623 CHECK_READY();
3624
3625 return createImage (aSize, TRUE /* aDynamic */, aProgress);
3626}
3627
3628STDMETHODIMP HVMDKImage::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
3629{
3630 if (!aProgress)
3631 return E_POINTER;
3632
3633 AutoLock alock (this);
3634 CHECK_READY();
3635
3636 return createImage (aSize, FALSE /* aDynamic */, aProgress);
3637}
3638
3639STDMETHODIMP HVMDKImage::DeleteImage()
3640{
3641 AutoLock alock (this);
3642 CHECK_READY();
3643 CHECK_BUSY_AND_READERS();
3644
3645 return E_NOTIMPL;
3646
3647/// @todo (r=dmik) later
3648// We will need to parse the file in order to delete all related delta and
3649// sparse images etc. We may also want to obey the .vmdk.lck file
3650// which is (as far as I understood) created when the VMware VM is
3651// running or saved etc.
3652//
3653// if (mRegistered)
3654// return setError (E_ACCESSDENIED,
3655// tr ("Cannot delete an image of the registered hard disk image '%ls"),
3656// mFilePathFull.raw());
3657// if (mState == NotCreated)
3658// return setError (E_FAIL,
3659// tr ("Hard disk image has been already deleted or never created"));
3660//
3661// int vrc = RTFileDelete (Utf8Str (mFilePathFull));
3662// if (VBOX_FAILURE (vrc))
3663// return setError (E_FAIL, tr ("Could not delete the image file '%ls' (%Vrc)"),
3664// mFilePathFull.raw(), vrc);
3665//
3666// mState = NotCreated;
3667//
3668// /* reset the fields */
3669// mSize = 0;
3670// mActualSize = 0;
3671//
3672// return S_OK;
3673}
3674
3675// public/protected methods for internal purposes only
3676/////////////////////////////////////////////////////////////////////////////
3677
3678/**
3679 * Attempts to mark the hard disk as registered.
3680 * Only VirtualBox can call this method.
3681 */
3682HRESULT HVMDKImage::trySetRegistered (BOOL aRegistered)
3683{
3684 AutoLock alock (this);
3685 CHECK_READY();
3686
3687 if (aRegistered)
3688 {
3689 if (mState == NotCreated)
3690 return setError (E_FAIL,
3691 tr ("Image file '%ls' is not yet created for this hard disk"),
3692 mFilePathFull.raw());
3693
3694/// @todo (r=dmik) will need this if we add suppord for differencing VMDKs
3695// if (isDifferencing())
3696// return setError (E_FAIL,
3697// tr ("Hard disk '%ls' is differencing and cannot be unregistered "
3698// "explicitly"),
3699// mFilePathFull.raw());
3700 }
3701 else
3702 {
3703 ComAssertRet (mState >= Created, E_FAIL);
3704 }
3705
3706 return HardDisk::trySetRegistered (aRegistered);
3707}
3708
3709/**
3710 * Checks accessibility of this hard disk image only (w/o parents).
3711 *
3712 * @param aAccessError on output, a null string indicates the hard disk is
3713 * accessible, otherwise contains a message describing
3714 * the reason of inaccessibility.
3715 */
3716HRESULT HVMDKImage::getAccessible (Bstr &aAccessError)
3717{
3718 AutoLock alock (this);
3719 CHECK_READY();
3720
3721 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
3722 {
3723 /* An accessibility check in progress on some other thread,
3724 * wait for it to finish. */
3725
3726 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL);
3727 ++ mStateCheckWaiters;
3728 alock.leave();
3729
3730 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
3731
3732 alock.enter();
3733 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
3734 -- mStateCheckWaiters;
3735 if (mStateCheckWaiters == 0)
3736 {
3737 RTSemEventMultiDestroy (mStateCheckSem);
3738 mStateCheckSem = NIL_RTSEMEVENTMULTI;
3739 }
3740
3741 AssertRCReturn (vrc, E_FAIL);
3742
3743 /* don't touch aAccessError, it has been already set */
3744 return S_OK;
3745 }
3746
3747 /* check the basic accessibility */
3748 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */);
3749 if (FAILED (rc) || !aAccessError.isNull())
3750 return rc;
3751
3752 if (mState >= Created)
3753 {
3754 return queryInformation (&aAccessError);
3755 }
3756
3757 aAccessError = Utf8StrFmt ("Hard disk image '%ls' is not yet created",
3758 mFilePathFull.raw());
3759 return S_OK;
3760}
3761
3762/**
3763 * Saves hard disk settings to the specified storage node and saves
3764 * all children to the specified hard disk node
3765 *
3766 * @param aHDNode <HardDisk> or <DiffHardDisk> node
3767 * @param aStorageNode <VirtualDiskImage> node
3768 */
3769HRESULT HVMDKImage::saveSettings (CFGNODE aHDNode, CFGNODE aStorageNode)
3770{
3771 AssertReturn (aHDNode && aStorageNode, E_FAIL);
3772
3773 AutoLock alock (this);
3774 CHECK_READY();
3775
3776 /* filePath (required) */
3777 CFGLDRSetBSTR (aStorageNode, "filePath", mFilePath);
3778
3779 /* save basic settings and children */
3780 return HardDisk::saveSettings (aHDNode);
3781}
3782
3783/**
3784 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
3785 * of this hard disk and updates it if necessary to reflect the new location.
3786 * Intended to be from HardDisk::updatePaths().
3787 *
3788 * @param aOldPath old path (full)
3789 * @param aNewPath new path (full)
3790 *
3791 * @note Locks this object for writing.
3792 */
3793void HVMDKImage::updatePath (const char *aOldPath, const char *aNewPath)
3794{
3795 AssertReturnVoid (aOldPath);
3796 AssertReturnVoid (aNewPath);
3797
3798 AutoLock alock (this);
3799 AssertReturnVoid (isReady());
3800
3801 size_t oldPathLen = strlen (aOldPath);
3802
3803 Utf8Str path = mFilePathFull;
3804 LogFlowThisFunc (("VMDK.fullPath={%s}\n", path.raw()));
3805
3806 if (RTPathStartsWith (path, aOldPath))
3807 {
3808 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
3809 path.raw() + oldPathLen);
3810 path = newPath;
3811
3812 mVirtualBox->calculateRelativePath (path, path);
3813
3814 unconst (mFilePathFull) = newPath;
3815 unconst (mFilePath) = path;
3816
3817 LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
3818 newPath.raw(), path.raw()));
3819 }
3820}
3821
3822/**
3823 * Returns the string representation of this hard disk.
3824 * When \a aShort is false, returns the full image file path.
3825 * Otherwise, returns the image file name only.
3826 *
3827 * @param aShort if true, a short representation is returned
3828 */
3829Bstr HVMDKImage::toString (bool aShort /* = false */)
3830{
3831 AutoLock alock (this);
3832
3833 if (!aShort)
3834 return mFilePathFull;
3835 else
3836 {
3837 Utf8Str fname = mFilePathFull;
3838 return RTPathFilename (fname.mutableRaw());
3839 }
3840}
3841
3842/**
3843 * Creates a clone of this hard disk by storing hard disk data in the given
3844 * VDI file name.
3845 *
3846 * @param aId UUID to assign to the created image
3847 * @param aTargetPath VDI file where the cloned image is to be to stored
3848 * @param aProgress progress object to run during operation
3849 */
3850HRESULT
3851HVMDKImage::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
3852 Progress *aProgress)
3853{
3854 ComAssertMsgFailed (("Not implemented"));
3855 return E_NOTIMPL;
3856
3857/// @todo (r=dmik) will need this if we add suppord for differencing VMDKs
3858// Use code from HVirtualDiskImage::cloneToImage as an example.
3859}
3860
3861/**
3862 * Creates a new differencing image for this hard disk with the given
3863 * VDI file name.
3864 *
3865 * @param aId UUID to assign to the created image
3866 * @param aTargetPath VDI file where to store the created differencing image
3867 * @param aProgress progress object to run during operation
3868 * (can be NULL)
3869 */
3870HRESULT
3871HVMDKImage::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
3872 Progress *aProgress)
3873{
3874 ComAssertMsgFailed (("Not implemented"));
3875 return E_NOTIMPL;
3876
3877/// @todo (r=dmik) will need this if we add suppord for differencing VMDKs
3878// Use code from HVirtualDiskImage::createDiffImage as an example.
3879}
3880
3881// private methods
3882/////////////////////////////////////////////////////////////////////////////
3883
3884/**
3885 * Helper to set a new file path.
3886 * Resolves a path relatively to the Virtual Box home directory.
3887 *
3888 * @note
3889 * Must be called from under the object's lock!
3890 */
3891HRESULT HVMDKImage::setFilePath (const BSTR aFilePath)
3892{
3893 if (aFilePath && *aFilePath)
3894 {
3895 /* get the full file name */
3896 char filePathFull [RTPATH_MAX];
3897 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aFilePath),
3898 filePathFull, sizeof (filePathFull));
3899 if (VBOX_FAILURE (vrc))
3900 return setError (E_FAIL,
3901 tr ("Invalid image file path '%ls' (%Vrc)"), aFilePath, vrc);
3902
3903 mFilePath = aFilePath;
3904 mFilePathFull = filePathFull;
3905 }
3906 else
3907 {
3908 mFilePath.setNull();
3909 mFilePathFull.setNull();
3910 }
3911
3912 return S_OK;
3913}
3914
3915DECLCALLBACK(void) VDError (void *pvUser, int rc, RT_SRC_POS_DECL,
3916 const char *pszFormat, va_list va)
3917{
3918 /// @todo pass the error message to the operation initiator
3919}
3920
3921/**
3922 * Helper to query information about the VDI hard disk.
3923 *
3924 * @param aAccessError not used when NULL, otherwise see #getAccessible()
3925 *
3926 * @note Must be called from under the object's lock, only after
3927 * CHECK_BUSY_AND_READERS() succeeds.
3928 */
3929HRESULT HVMDKImage::queryInformation (Bstr *aAccessError)
3930{
3931 AssertReturn (isLockedOnCurrentThread(), E_FAIL);
3932
3933 /* create a lock object to completely release it later */
3934 AutoLock alock (this);
3935
3936 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
3937
3938 ComAssertRet (mState >= Created, E_FAIL);
3939
3940 HRESULT rc = S_OK;
3941 int vrc = VINF_SUCCESS;
3942
3943 /* lazily create a semaphore */
3944 vrc = RTSemEventMultiCreate (&mStateCheckSem);
3945 ComAssertRCRet (vrc, E_FAIL);
3946
3947 /* Reference the disk to prevent any concurrent modifications
3948 * after releasing the lock below (to unblock getters before
3949 * a lengthy operation). */
3950 addReader();
3951
3952 alock.leave();
3953
3954 /* VBoxVHDD management interface needs to be optimized: we're opening a
3955 * file three times in a raw to get three bits of information. */
3956
3957 Utf8Str filePath = mFilePathFull;
3958 Bstr errMsg;
3959
3960 PVBOXHDD hdd = NULL;
3961
3962 do
3963 {
3964 Guid id, parentId;
3965
3966 /// @todo make PVBOXHDD a member variable and init/destroy it upon
3967 /// image creation/deletion instead of doing that here every time.
3968
3969 vrc = VDCreate ("VMDK", VDError, NULL, &hdd);
3970 if (VBOX_FAILURE (vrc))
3971 break;
3972
3973 /// @todo changed from VD_OPEN_FLAGS_READONLY to VD_OPEN_FLAGS_NORMAL,
3974 /// because otherwise registering a VMDK which so far has no UUID will
3975 /// yield a null UUID. It cannot be added to a VMDK opened readonly,
3976 /// obviously. This of course changes locking behavior, but for now
3977 /// this is acceptable. A better solution needs to be found later.
3978 vrc = VDOpen (hdd, filePath, VD_OPEN_FLAGS_NORMAL);
3979 if (VBOX_FAILURE (vrc))
3980 break;
3981
3982 vrc = VDGetUuid (hdd, 0, id.ptr());
3983 if (VBOX_FAILURE (vrc))
3984 break;
3985 vrc = VDGetParentUuid (hdd, 0, parentId.ptr());
3986 if (VBOX_FAILURE (vrc))
3987 break;
3988
3989 if (!mId.isEmpty())
3990 {
3991 /* check that the actual UUID of the image matches the stored UUID */
3992 if (mId != id)
3993 {
3994 errMsg = Utf8StrFmt (
3995 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
3996 "match UUID {%Vuuid} stored in the registry"),
3997 id.ptr(), filePath.raw(), mId.ptr());
3998 break;
3999 }
4000 }
4001 else
4002 {
4003 /* assgn an UUID read from the image file */
4004 mId = id;
4005 }
4006
4007 if (mParent)
4008 {
4009 /* check parent UUID */
4010 AutoLock parentLock (mParent);
4011 if (mParent->id() != parentId)
4012 {
4013 errMsg = Utf8StrFmt (
4014 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
4015 "the hard disk image file '%s' doesn't match "
4016 "UUID {%Vuuid} stored in the registry"),
4017 parentId.raw(), mParent->toString().raw(),
4018 filePath.raw(), mParent->id().raw());
4019 break;
4020 }
4021 }
4022 else if (!parentId.isEmpty())
4023 {
4024 errMsg = Utf8StrFmt (
4025 tr ("Hard disk image '%s' is a differencing image that is linked "
4026 "to a hard disk with UUID {%Vuuid} and cannot be used "
4027 "directly as a base hard disk"),
4028 filePath.raw(), parentId.raw());
4029 break;
4030 }
4031
4032 /* get actual file size */
4033 /// @todo is there a direct method in RT?
4034 {
4035 RTFILE file = NIL_RTFILE;
4036 vrc = RTFileOpen (&file, filePath, RTFILE_O_READ);
4037 if (VBOX_SUCCESS (vrc))
4038 {
4039 uint64_t size = 0;
4040 vrc = RTFileGetSize (file, &size);
4041 if (VBOX_SUCCESS (vrc))
4042 mActualSize = size;
4043 RTFileClose (file);
4044 }
4045 if (VBOX_FAILURE (vrc))
4046 break;
4047 }
4048
4049 /* query logical size only for non-differencing images */
4050 if (!mParent)
4051 {
4052 uint64_t size = VDGetSize (hdd);
4053 /* convert to MBytes */
4054 mSize = size / 1024 / 1024;
4055 }
4056 }
4057 while (0);
4058
4059 if (hdd)
4060 VDDestroy (hdd);
4061
4062 /* enter the lock again */
4063 alock.enter();
4064
4065 /* remove the reference */
4066 releaseReader();
4067
4068 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
4069 {
4070 LogWarningFunc (("'%ls' is not accessible "
4071 "(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
4072 mFilePathFull.raw(), rc, vrc, errMsg.raw()));
4073
4074 if (aAccessError)
4075 {
4076 if (!errMsg.isNull())
4077 *aAccessError = errMsg;
4078 else if (VBOX_FAILURE (vrc))
4079 *aAccessError = Utf8StrFmt (
4080 tr ("Could not access hard disk image '%ls' (%Vrc)"),
4081 mFilePathFull.raw(), vrc);
4082 }
4083
4084 /* downgrade to not accessible */
4085 mState = Created;
4086 }
4087 else
4088 {
4089 if (aAccessError)
4090 aAccessError->setNull();
4091
4092 mState = Accessible;
4093 }
4094
4095 /* inform waiters if there are any */
4096 if (mStateCheckWaiters > 0)
4097 {
4098 RTSemEventMultiSignal (mStateCheckSem);
4099 }
4100 else
4101 {
4102 /* delete the semaphore ourselves */
4103 RTSemEventMultiDestroy (mStateCheckSem);
4104 mStateCheckSem = NIL_RTSEMEVENTMULTI;
4105 }
4106
4107 return rc;
4108}
4109
4110/**
4111 * Helper to create hard disk images.
4112 *
4113 * @param aSize size in MB
4114 * @param aDynamic dynamic or fixed image
4115 * @param aProgress address of IProgress pointer to return
4116 */
4117HRESULT HVMDKImage::createImage (ULONG64 aSize, BOOL aDynamic,
4118 IProgress **aProgress)
4119{
4120 ComAssertMsgFailed (("Not implemented"));
4121 return E_NOTIMPL;
4122
4123/// @todo (r=dmik) later
4124// Use code from HVirtualDiskImage::createImage as an example.
4125}
4126
4127/* static */
4128DECLCALLBACK(int) HVMDKImage::vdiTaskThread (RTTHREAD thread, void *pvUser)
4129{
4130 AssertMsgFailed (("Not implemented"));
4131 return VERR_GENERAL_FAILURE;
4132
4133/// @todo (r=dmik) later
4134// Use code from HVirtualDiskImage::vdiTaskThread as an example.
4135}
Note: See TracBrowser for help on using the repository browser.

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