VirtualBox

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

Last change on this file since 3254 was 3093, checked in by vboxsync, 17 years ago

Hack around incomprehensible VDI error message for VMDK files that fail
to open for any reason (most popular is access problems).

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

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