VirtualBox

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

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

Main: Fixed an assertion hit when opening the VDM dialog from the console window (for example, to mount a CD media) of a running VM or doing any other media accessibility check on it that could leave to undefined behavior in the release builds.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 87.5 KB
Line 
1/** @file
2 *
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung 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 <VBox/VBoxHDD.h>
34#include <VBox/err.h>
35
36#include <algorithm>
37
38#define CHECK_BUSY() \
39 do { \
40 if (isBusy()) \
41 return setError (E_UNEXPECTED, \
42 tr ("Hard disk '%ls' is being used by another task"), \
43 toString().raw()); \
44 } while (0)
45
46#define CHECK_BUSY_AND_READERS() \
47do { \
48 if (readers() > 0 || isBusy()) \
49 return setError (E_UNEXPECTED, \
50 tr ("Hard disk '%ls' is being used by another task"), \
51 toString().raw()); \
52} while (0)
53
54/** Task structure for asynchronous VDI operations */
55struct VDITask
56{
57 enum Op { CreateDynamic, CreateStatic, CloneToImage };
58
59 VDITask (Op op, HVirtualDiskImage *i, Progress *p)
60 : operation (op)
61 , vdi (i)
62 , progress (p)
63 {}
64
65 Op operation;
66 ComObjPtr <HVirtualDiskImage> vdi;
67 ComObjPtr <Progress> progress;
68
69 /* for CreateDynamic, CreateStatic */
70 uint64_t size;
71
72 /* for CloneToImage */
73 ComObjPtr <HardDisk> source;
74};
75
76/**
77 * Progress callback handler for VDI operations.
78 *
79 * @param uPercent Completetion precentage (0-100).
80 * @param pvUser Pointer to the Progress instance.
81 */
82static DECLCALLBACK(int) progressCallback (PVM /* pVM */, unsigned uPercent, void *pvUser)
83{
84 Progress *progress = static_cast <Progress *> (pvUser);
85
86 /* update the progress object */
87 if (progress)
88 progress->notifyProgress (uPercent);
89
90 return VINF_SUCCESS;
91}
92
93////////////////////////////////////////////////////////////////////////////////
94// HardDisk class
95////////////////////////////////////////////////////////////////////////////////
96
97// constructor / destructor
98////////////////////////////////////////////////////////////////////////////////
99
100/** Shold be called by subclasses from #FinalConstruct() */
101HRESULT HardDisk::FinalConstruct()
102{
103 mRegistered = FALSE;
104
105 mStorageType = HardDiskStorageType_VirtualDiskImage;
106 mType = HardDiskType_NormalHardDisk;
107
108 mBusy = false;
109 mReaders = 0;
110
111 return S_OK;
112}
113
114/**
115 * Shold be called by subclasses from #FinalRelease().
116 * Uninitializes this object by calling #uninit() if it's not yet done.
117 */
118void HardDisk::FinalRelease()
119{
120 uninit();
121}
122
123// protected initializer/uninitializer for internal purposes only
124////////////////////////////////////////////////////////////////////////////////
125
126/**
127 * Initializes the hard disk object.
128 *
129 * Subclasses should call this or any other #init() method from their
130 * init() implementations.
131 *
132 * @note
133 * This method doesn't do |isReady()| check and doesn't call
134 * |setReady (true)| on success!
135 * @note
136 * This method must be called from under the object's lock!
137 */
138HRESULT HardDisk::protectedInit (VirtualBox *aVirtualBox, HardDisk *aParent)
139{
140 LogFlowMember (("HardDisk::protectedInit (aParent=%p)\n", aParent));
141
142 ComAssertRet (aVirtualBox, E_INVALIDARG);
143
144 mVirtualBox = aVirtualBox;
145 mParent = aParent;
146
147 if (!aParent)
148 aVirtualBox->addDependentChild (this);
149 else
150 aParent->addDependentChild (this);
151
152 return S_OK;
153}
154
155/**
156 * Uninitializes the instance.
157 * Subclasses should call this from their uninit() implementations.
158 * The readiness flag must be true on input and will be set to false
159 * on output.
160 *
161 * @param alock this object's autolock
162 *
163 * @note
164 * Using mParent and mVirtualBox members after this method returns
165 * is forbidden.
166 */
167void HardDisk::protectedUninit (AutoLock &alock)
168{
169 LogFlowMember (("HardDisk::protectedUninit()\n"));
170
171 Assert (alock.belongsTo (this));
172 Assert (isReady());
173
174 /* uninit all children */
175 uninitDependentChildren();
176
177 setReady (false);
178
179 if (mParent)
180 mParent->removeDependentChild (this);
181 else
182 {
183 alock.leave();
184 mVirtualBox->removeDependentChild (this);
185 alock.enter();
186 }
187
188 mParent.setNull();
189 mVirtualBox.setNull();
190}
191
192// IHardDisk properties
193/////////////////////////////////////////////////////////////////////////////
194
195STDMETHODIMP HardDisk::COMGETTER(Id) (GUIDPARAMOUT aId)
196{
197 if (!aId)
198 return E_POINTER;
199
200 AutoLock alock (this);
201 CHECK_READY();
202
203 mId.cloneTo (aId);
204 return S_OK;
205}
206
207STDMETHODIMP HardDisk::COMGETTER(StorageType) (HardDiskStorageType_T *aStorageType)
208{
209 if (!aStorageType)
210 return E_POINTER;
211
212 AutoLock alock (this);
213 CHECK_READY();
214
215 *aStorageType = mStorageType;
216 return S_OK;
217}
218
219STDMETHODIMP HardDisk::COMGETTER(Location) (BSTR *aLocation)
220{
221 if (!aLocation)
222 return E_POINTER;
223
224 AutoLock alock (this);
225 CHECK_READY();
226
227 toString (false /* aShort */).cloneTo (aLocation);
228 return S_OK;
229}
230
231STDMETHODIMP HardDisk::COMGETTER(Type) (HardDiskType_T *aType)
232{
233 if (!aType)
234 return E_POINTER;
235
236 AutoLock alock (this);
237 CHECK_READY();
238
239 *aType = mType;
240 return S_OK;
241}
242
243STDMETHODIMP HardDisk::COMSETTER(Type) (HardDiskType_T aType)
244{
245 AutoLock alock (this);
246 CHECK_READY();
247
248 if (mRegistered)
249 return setError (E_FAIL,
250 tr ("You cannot change the type of the registered hard disk '%ls'"),
251 toString().raw());
252
253 if (mStorageType == HardDiskStorageType_ISCSIHardDisk)
254 return setError (E_FAIL,
255 tr ("Currently, changing the type of iSCSI hard disks is not allowed"));
256
257 /// @todo (dmik) later: allow to change the type on any registered hard disk
258 // depending on whether it is attached or not, has children etc.
259 // Don't forget to save hdd configuration afterwards.
260
261 mType = aType;
262 return S_OK;
263}
264
265STDMETHODIMP HardDisk::COMGETTER(Parent) (IHardDisk **aParent)
266{
267 if (!aParent)
268 return E_POINTER;
269
270 AutoLock alock (this);
271 CHECK_READY();
272
273 mParent.queryInterfaceTo (aParent);
274 return S_OK;
275}
276
277STDMETHODIMP HardDisk::COMGETTER(Children) (IHardDiskCollection **aChildren)
278{
279 if (!aChildren)
280 return E_POINTER;
281
282 AutoLock lock(this);
283 CHECK_READY();
284
285 AutoLock chLock (childrenLock());
286
287 ComObjPtr <HardDiskCollection> collection;
288 collection.createObject();
289 collection->init (children());
290 collection.queryInterfaceTo (aChildren);
291 return S_OK;
292}
293
294STDMETHODIMP HardDisk::COMGETTER(Root) (IHardDisk **aRoot)
295{
296 if (!aRoot)
297 return E_POINTER;
298
299 AutoLock lock(this);
300 CHECK_READY();
301
302 root().queryInterfaceTo (aRoot);
303 return S_OK;
304}
305
306STDMETHODIMP HardDisk::COMGETTER(Accessible) (BOOL *aAccessible)
307{
308 if (!aAccessible)
309 return E_POINTER;
310
311 AutoLock alock (this);
312 CHECK_READY();
313
314 HRESULT rc = getAccessible (mLastAccessError);
315 if (FAILED (rc))
316 return rc;
317
318 *aAccessible = mLastAccessError.isNull();
319 return S_OK;
320}
321
322STDMETHODIMP HardDisk::COMGETTER(AllAccessible) (BOOL *aAllAccessible)
323{
324 if (!aAllAccessible)
325 return E_POINTER;
326
327 AutoLock alock (this);
328 CHECK_READY();
329
330 if (mParent)
331 {
332 HRESULT rc = S_OK;
333
334 /* check the accessibility state of all ancestors */
335 ComObjPtr <HardDisk> parent = (HardDisk *) mParent;
336 while (parent)
337 {
338 AutoLock parentLock (parent);
339 HRESULT rc = parent->getAccessible (mLastAccessError);
340 if (FAILED (rc))
341 break;
342 *aAllAccessible = mLastAccessError.isNull();
343 if (!*aAllAccessible)
344 break;
345 parent = parent->mParent;
346 }
347
348 return rc;
349 }
350
351 HRESULT rc = getAccessible (mLastAccessError);
352 if (FAILED (rc))
353 return rc;
354
355 *aAllAccessible = mLastAccessError.isNull();
356 return S_OK;
357}
358
359STDMETHODIMP HardDisk::COMGETTER(LastAccessError) (BSTR *aLastAccessError)
360{
361 if (!aLastAccessError)
362 return E_POINTER;
363
364 AutoLock alock (this);
365 CHECK_READY();
366
367 mLastAccessError.cloneTo (aLastAccessError);
368 return S_OK;
369}
370
371STDMETHODIMP HardDisk::COMGETTER(MachineId) (GUIDPARAMOUT aMachineId)
372{
373 if (!aMachineId)
374 return E_POINTER;
375
376 AutoLock alock (this);
377 CHECK_READY();
378
379 mMachineId.cloneTo (aMachineId);
380 return S_OK;
381}
382
383STDMETHODIMP HardDisk::COMGETTER(SnapshotId) (GUIDPARAMOUT aSnapshotId)
384{
385 if (!aSnapshotId)
386 return E_POINTER;
387
388 AutoLock alock (this);
389 CHECK_READY();
390
391 mSnapshotId.cloneTo (aSnapshotId);
392 return S_OK;
393}
394
395STDMETHODIMP HardDisk::CloneToImage (INPTR BSTR aFilePath,
396 IVirtualDiskImage **aImage,
397 IProgress **aProgress)
398{
399 if (!aFilePath || !(*aFilePath))
400 return E_INVALIDARG;
401 if (!aImage || !aProgress)
402 return E_POINTER;
403
404 AutoLock alock (this);
405 CHECK_READY();
406 CHECK_BUSY();
407
408 if (!mParent.isNull())
409 return setError (E_FAIL,
410 tr ("Cloning differencing VDI images is not yet supported ('%ls')"),
411 toString().raw());
412
413 HRESULT rc = S_OK;
414
415 /* create a project object */
416 ComObjPtr <Progress> progress;
417 progress.createObject();
418 rc = progress->init (mVirtualBox, (IVirtualDiskImage *) this,
419 Bstr (tr ("Creating a hard disk clone")),
420 FALSE /* aCancelable */);
421 CheckComRCReturnRC (rc);
422
423 /* create an imageless resulting object */
424 ComObjPtr <HVirtualDiskImage> image;
425 image.createObject();
426 rc = image->init (mVirtualBox, NULL, NULL);
427 CheckComRCReturnRC (rc);
428
429 /* append the default path if only a name is given */
430 Bstr path = aFilePath;
431 {
432 Utf8Str fp = aFilePath;
433 if (!RTPathHavePath (fp))
434 {
435 AutoReaderLock propsLock (mVirtualBox->systemProperties());
436 path = Utf8StrFmt ("%ls%c%s",
437 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
438 RTPATH_DELIMITER,
439 fp.raw());
440 }
441 }
442
443 /* set the desired path */
444 rc = image->setFilePath (path);
445 CheckComRCReturnRC (rc);
446
447 /* ensure the directory exists */
448 {
449 Utf8Str imageDir = image->filePath();
450 RTPathStripFilename (imageDir.mutableRaw());
451 if (!RTDirExists (imageDir))
452 {
453 int vrc = RTDirCreateFullPath (imageDir, 0777);
454 if (VBOX_FAILURE (vrc))
455 {
456 return setError (E_FAIL,
457 tr ("Could not create a directory '%s' "
458 "to store the image file (%Vrc)"),
459 imageDir.raw(), vrc);
460 }
461 }
462 }
463
464 /* mark as busy (being created)
465 * (VDI task thread will unmark it) */
466 image->setBusy();
467
468 /* fill in a VDI task data */
469 VDITask *task = new VDITask (VDITask::CloneToImage, image, progress);
470 task->source = this;
471
472 /* increase readers until finished
473 * (VDI task thread will decrease them) */
474 addReader();
475
476 /* create the hard disk creation thread, pass operation data */
477 int vrc = RTThreadCreate (NULL, HVirtualDiskImage::vdiTaskThread,
478 (void *) task, 0, RTTHREADTYPE_MAIN_HEAVY_WORKER,
479 0, "VDITask");
480 ComAssertMsgRC (vrc, ("Could not create a thread (%Vrc)", vrc));
481 if (VBOX_FAILURE (vrc))
482 {
483 releaseReader();
484 image->clearBusy();
485 delete task;
486 return E_FAIL;
487 }
488
489 /* return interfaces to the caller */
490 image.queryInterfaceTo (aImage);
491 progress.queryInterfaceTo (aProgress);
492
493 return S_OK;
494}
495
496// public methods for internal purposes only
497/////////////////////////////////////////////////////////////////////////////
498
499/**
500 * Returns the very first (grand-) parent of this hard disk or the hard
501 * disk itself, if it doesn't have a parent.
502 *
503 * @note
504 * Must be called from under the object's lock
505 */
506ComObjPtr <HardDisk> HardDisk::root() const
507{
508 ComObjPtr <HardDisk> root = const_cast <HardDisk *> (this);
509 ComObjPtr <HardDisk> parent;
510 while ((parent = root->parent()))
511 root = parent;
512
513 return root;
514}
515
516/**
517 * Attempts to mark the hard disk as registered.
518 * Must be always called by every reimplementation.
519 * Only VirtualBox can call this method.
520 *
521 * @param aRegistered true to set registered and false to set unregistered
522 */
523HRESULT HardDisk::trySetRegistered (BOOL aRegistered)
524{
525 AutoLock alock (this);
526 CHECK_READY();
527
528 if (aRegistered)
529 {
530 ComAssertRet (mMachineId.isEmpty(), E_FAIL);
531 ComAssertRet (mId && children().size() == 0, E_FAIL);
532
533 if (mRegistered)
534 return setError (E_FAIL,
535 tr ("Hard disk '%ls' is already registered"),
536 toString().raw());
537
538 CHECK_BUSY();
539 }
540 else
541 {
542 if (!mRegistered)
543 return setError (E_FAIL,
544 tr ("Hard disk '%ls' is already unregistered"),
545 toString().raw());
546
547 if (!mMachineId.isEmpty())
548 return setError (E_FAIL,
549 tr ("Hard disk '%ls' is attached to a virtual machine with UUID {%s}"),
550 toString().raw(), mMachineId.toString().raw());
551
552 if (children().size() > 0)
553 return setError (E_FAIL,
554 tr ("Hard disk '%ls' has %d differencing hard disks based on it"),
555 toString().raw(), children().size());
556
557 CHECK_BUSY_AND_READERS();
558 }
559
560 mRegistered = aRegistered;
561 return S_OK;
562}
563
564/**
565 * Checks basic accessibility of this hard disk only (w/o parents).
566 * Must be always called by every HardDisk::getAccessible() reimplementation
567 * in the first place.
568 *
569 * When @a aCheckBusy is true, this method checks that mBusy = false and
570 * mReaders = 0 (and returns an appropriate error if not). This lets
571 * reimplementations successfully call setBusy() to lock the disk for the
572 * duration of the check if they need. Note that in this case, the
573 * reimplementation must enter the object lock before calling this method
574 * and not leave it before calling setBusy() to avoid race condition.
575 *
576 * @param aAccessError On output, a null string indicates the hard disk is
577 * accessible, otherwise contains a message describing
578 * the reason of inaccessibility.
579 * @param aCheckBusy Whether to do the busy check or not.
580 */
581HRESULT HardDisk::getBaseAccessible (Bstr &aAccessError,
582 bool aCheckBusy /* = false */)
583{
584 AutoLock alock (this);
585 CHECK_READY();
586
587 aAccessError.setNull();
588
589 if (aCheckBusy)
590 {
591 if (mBusy)
592 {
593 aAccessError = Utf8StrFmt (
594 tr ("Hard disk '%ls' is being exclusively used by another task"),
595 toString().raw());
596 }
597 else if (mReaders > 0)
598 {
599 aAccessError = Utf8StrFmt (
600 tr ("Hard disk '%ls' is being used by another task (%d readers)"),
601 toString().raw(), mReaders);
602 }
603 }
604
605 return S_OK;
606}
607
608/**
609 * Returns true if the set of properties that makes this object unique
610 * is equal to the same set of properties in the given object.
611 */
612bool HardDisk::sameAs (HardDisk *that)
613{
614 AutoLock alock (this);
615 if (!isReady())
616 return false;
617
618 /// @todo (dmik) besides UUID, we temporarily use toString() to uniquely
619 // identify objects. This is ok for VDIs but may be not good for iSCSI,
620 // so it will need a reimp of this method.
621
622 return that->mId == mId ||
623 toString (false /* aShort */) == that->toString (false /* aShort */);
624}
625
626/**
627 * Marks this hard disk as busy.
628 * A busy hard disk cannot have readers and its properties (UUID, description)
629 * cannot be externally modified.
630 */
631void HardDisk::setBusy()
632{
633 AutoLock alock (this);
634 AssertReturn (isReady(), (void) 0);
635
636 AssertReturn (mBusy == false, (void) 0);
637 AssertReturn (mReaders == 0, (void) 0);
638
639 mBusy = true;
640}
641
642/**
643 * Clears the busy flag previously set by #setBusy().
644 */
645void HardDisk::clearBusy()
646{
647 AutoLock alock (this);
648 AssertReturn (isReady(), (void) 0);
649
650 AssertReturn (mBusy == true, (void) 0);
651
652 mBusy = false;
653}
654
655/**
656 * Increases the number of readers of this hard disk.
657 * A hard disk that have readers cannot be marked as busy (and vice versa)
658 * and its properties (UUID, description) cannot be externally modified.
659 */
660void HardDisk::addReader()
661{
662 AutoLock alock (this);
663 AssertReturn (isReady(), (void) 0);
664
665 AssertReturn (mBusy == false, (void) 0);
666
667 ++ mReaders;
668}
669
670/**
671 * Decreases the number of readers of this hard disk.
672 */
673void HardDisk::releaseReader()
674{
675 AutoLock alock (this);
676 AssertReturn (isReady(), (void) 0);
677
678 AssertReturn (mBusy == false, (void) 0);
679 AssertReturn (mReaders > 0, (void) 0);
680
681 -- mReaders;
682}
683
684/**
685 * Increases the number of readers on all ancestors of this hard disk.
686 */
687void HardDisk::addReaderOnAncestors()
688{
689 AutoLock alock (this);
690 AssertReturn (isReady(), (void) 0);
691
692 if (mParent)
693 {
694 AutoLock alock (mParent);
695 mParent->addReader();
696 mParent->addReaderOnAncestors();
697 }
698}
699
700/**
701 * Decreases the number of readers on all ancestors of this hard disk.
702 */
703void HardDisk::releaseReaderOnAncestors()
704{
705 AutoLock alock (this);
706 AssertReturn (isReady(), (void) 0);
707
708 if (mParent)
709 {
710 AutoLock alock (mParent);
711 mParent->releaseReaderOnAncestors();
712 mParent->releaseReader();
713 }
714}
715
716/**
717 * Returns true if this hard disk has children not belonging to the same
718 * machine.
719 */
720bool HardDisk::hasForeignChildren()
721{
722 AutoLock alock (this);
723 AssertReturn (isReady(), false);
724
725 AssertReturn (!mMachineId.isEmpty(), false);
726
727 /* check all children */
728 AutoLock chLock (childrenLock());
729 for (HardDiskList::const_iterator it = children().begin();
730 it != children().end();
731 ++ it)
732 {
733 ComObjPtr <HardDisk> child = *it;
734 AutoLock childLock (child);
735 if (child->mMachineId != mMachineId)
736 return true;
737 }
738
739 return false;
740}
741
742/**
743 * Marks this hard disk and all its children as busy.
744 * Used for merge operations.
745 * Returns a meaningful error info on failure.
746 */
747HRESULT HardDisk::setBusyWithChildren()
748{
749 AutoLock alock (this);
750 AssertReturn (isReady(), E_FAIL);
751
752 const char *errMsg = tr ("Hard disk '%ls' is being used by another task");
753
754 if (mReaders > 0 || mBusy)
755 return setError (E_FAIL, errMsg, toString().raw());
756
757 AutoLock chLock (childrenLock());
758
759 for (HardDiskList::const_iterator it = children().begin();
760 it != children().end();
761 ++ it)
762 {
763 ComObjPtr <HardDisk> child = *it;
764 AutoLock childLock (child);
765 if (child->mReaders > 0 || child->mBusy)
766 {
767 /* reset the busy flag of all previous children */
768 while (it != children().begin())
769 (*(-- it))->clearBusy();
770 return setError (E_FAIL, errMsg, child->toString().raw());
771 }
772 else
773 child->mBusy = true;
774 }
775
776 mBusy = true;
777
778 return S_OK;
779}
780
781/**
782 * Clears the busy flag of this hard disk and all its children.
783 * An opposite to #setBusyWithChildren.
784 */
785void HardDisk::clearBusyWithChildren()
786{
787 AutoLock alock (this);
788 AssertReturn (isReady(), (void) 0);
789
790 AssertReturn (mBusy == true, (void) 0);
791
792 AutoLock chLock (childrenLock());
793
794 for (HardDiskList::const_iterator it = children().begin();
795 it != children().end();
796 ++ it)
797 {
798 ComObjPtr <HardDisk> child = *it;
799 AutoLock childLock (child);
800 Assert (child->mBusy == true);
801 child->mBusy = false;
802 }
803
804 mBusy = false;
805}
806
807/**
808 * Checks that this hard disk and all its direct children are accessible.
809 */
810HRESULT HardDisk::getAccessibleWithChildren (Bstr &aAccessError)
811{
812 AutoLock alock (this);
813 AssertReturn (isReady(), E_FAIL);
814
815 HRESULT rc = getAccessible (aAccessError);
816 if (FAILED (rc) || !aAccessError.isNull())
817 return rc;
818
819 AutoLock chLock (childrenLock());
820
821 for (HardDiskList::const_iterator it = children().begin();
822 it != children().end();
823 ++ it)
824 {
825 ComObjPtr <HardDisk> child = *it;
826 rc = child->getAccessible (aAccessError);
827 if (FAILED (rc) || !aAccessError.isNull())
828 return rc;
829 }
830
831 return rc;
832}
833
834/**
835 * Checks that this hard disk and all its descendants are consistent.
836 * For now, the consistency means that:
837 *
838 * 1) every differencing image is associated with a registered machine
839 * 2) every root image that has differencing children is associated with
840 * a registered machine.
841 *
842 * This method is used by the VirtualBox constructor after loading all hard
843 * disks and all machines.
844 */
845HRESULT HardDisk::checkConsistency()
846{
847 AutoLock alock (this);
848 AssertReturn (isReady(), E_FAIL);
849
850 if (isDifferencing())
851 {
852 Assert (mVirtualBox->isMachineIdValid (mMachineId) ||
853 mMachineId.isEmpty());
854
855 if (mMachineId.isEmpty())
856 return setError (E_FAIL,
857 tr ("Differencing hard disk '%ls' is not associated with "
858 "any registered virtual machine or snapshot"),
859 toString().raw());
860 }
861
862 HRESULT rc = S_OK;
863
864 AutoLock chLock (childrenLock());
865
866 if (mParent.isNull() && mType == HardDiskType_NormalHardDisk &&
867 children().size() != 0)
868 {
869 if (mMachineId.isEmpty())
870 return setError (E_FAIL,
871 tr ("Hard disk '%ls' is not associated with any registered "
872 "virtual machine or snapshot, but has differencing child "
873 "hard disks based on it"),
874 toString().raw());
875 }
876
877 for (HardDiskList::const_iterator it = children().begin();
878 it != children().end() && SUCCEEDED (rc);
879 ++ it)
880 {
881 rc = (*it)->checkConsistency();
882 }
883
884 return rc;
885}
886
887/**
888 * Creates a differencing hard disk for this hard disk and returns the
889 * created hard disk object to the caller.
890 *
891 * The created differencing hard disk is automatically added to the list of
892 * children of this hard disk object and registered within VirtualBox.
893
894 * The specified progress object (if not NULL) receives the percentage
895 * of the operation completion. However, it is responsibility of the caller to
896 * call Progress::notifyComplete() after this method returns.
897 *
898 * @param aFolder folder where to create the differencing disk
899 * (must be a full path)
900 * @param aMachineId machine ID the new hard disk will belong to
901 * @param aHardDisk resulting hard disk object
902 * @param aProgress progress object to run during copy operation
903 * (may be NULL)
904 *
905 * @note
906 * Must be NOT called from under locks of other objects that need external
907 * access dirung this method execurion!
908 */
909HRESULT HardDisk::createDiffHardDisk (const Bstr &aFolder, const Guid &aMachineId,
910 ComObjPtr <HVirtualDiskImage> &aHardDisk,
911 Progress *aProgress)
912{
913 AssertReturn (!aFolder.isEmpty() && !aMachineId.isEmpty(),
914 E_FAIL);
915
916 AutoLock alock (this);
917 CHECK_READY();
918
919 ComAssertRet (isBusy() == false, E_FAIL);
920
921 Guid id;
922 id.create();
923
924 Utf8Str filePathTo = Utf8StrFmt ("%ls%c{%Vuuid}.vdi",
925 aFolder.raw(), RTPATH_DELIMITER, id.ptr());
926
927 /* try to make the path relative to the vbox home dir */
928 const char *filePathToRel = filePathTo;
929 {
930 const Utf8Str &homeDir = mVirtualBox->homeDir();
931 if (!strncmp (filePathTo, homeDir, homeDir.length()))
932 filePathToRel = (filePathToRel + homeDir.length() + 1);
933 }
934
935 /* first ensure the directory exists */
936 {
937 Utf8Str dir = aFolder;
938 if (!RTDirExists (dir))
939 {
940 int vrc = RTDirCreateFullPath (dir, 0777);
941 if (VBOX_FAILURE (vrc))
942 {
943 return setError (E_FAIL,
944 tr ("Could not create a directory '%s' "
945 "to store the image file (%Vrc)"),
946 dir.raw(), vrc);
947 }
948 }
949 }
950
951 alock.leave();
952
953 /* call storage type specific diff creation method */
954 HRESULT rc = createDiffImage (id, filePathTo, aProgress);
955
956 alock.enter();
957
958 CheckComRCReturnRC (rc);
959
960 ComObjPtr <HVirtualDiskImage> vdi;
961 vdi.createObject();
962 rc = vdi->init (mVirtualBox, this, Bstr (filePathToRel),
963 TRUE /* aRegistered */);
964 CheckComRCReturnRC (rc);
965
966 /* associate the created hard disk with the given machine */
967 vdi->setMachineId (aMachineId);
968
969 rc = mVirtualBox->registerHardDisk (vdi, VirtualBox::RHD_Internal);
970 CheckComRCReturnRC (rc);
971
972 aHardDisk = vdi;
973
974 return S_OK;
975}
976
977/**
978 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
979 * of this hard disk or any of its children and updates it if necessary (by
980 * calling #updatePath()). Intended to be called only by
981 * VirtualBox::updateSettings() if a machine's name change causes directory
982 * renaming that affects this image.
983 *
984 * @param aOldPath old path (full)
985 * @param aNewPath new path (full)
986 *
987 * @note Locks this object and all children for writing.
988 */
989void HardDisk::updatePaths (const char *aOldPath, const char *aNewPath)
990{
991 AssertReturnVoid (aOldPath);
992 AssertReturnVoid (aNewPath);
993
994 AutoLock alock (this);
995 AssertReturnVoid (isReady());
996
997 updatePath (aOldPath, aNewPath);
998
999 /* update paths of all children */
1000 AutoLock chLock (childrenLock());
1001 for (HardDiskList::const_iterator it = children().begin();
1002 it != children().end();
1003 ++ it)
1004 {
1005 (*it)->updatePaths (aOldPath, aNewPath);
1006 }
1007}
1008
1009// protected methods
1010/////////////////////////////////////////////////////////////////////////////
1011
1012/**
1013 * Loads the base settings of the hard disk from the given node, registers
1014 * it and loads and registers all child hard disks as HVirtualDiskImage
1015 * instances.
1016 *
1017 * Subclasses must call this method in their init() or loadSettings() methods
1018 * *after* they load specific parts of data (at least, necessary to let
1019 * toString() function correctly), in order to be properly loaded from the
1020 * settings file and registered.
1021 *
1022 * @param aHDNode <HardDisk> node when #isDifferencing() = false, or
1023 * <DiffHardDisk> node otherwise
1024 *
1025 * @note
1026 * Must be called from under the object's lock
1027 */
1028HRESULT HardDisk::loadSettings (CFGNODE aHDNode)
1029{
1030 AssertReturn (aHDNode, E_FAIL);
1031
1032 Guid uuid; /* uuid (required) */
1033 CFGLDRQueryUUID (aHDNode, "uuid", uuid.ptr());
1034 mId = uuid;
1035
1036 if (!isDifferencing())
1037 {
1038 Bstr type; /* type (required for <HardDisk> nodes only) */
1039 CFGLDRQueryBSTR (aHDNode, "type", type.asOutParam());
1040 if (type == L"normal")
1041 mType = HardDiskType_NormalHardDisk;
1042 else if (type == L"immutable")
1043 mType = HardDiskType_ImmutableHardDisk;
1044 else if (type == L"writethrough")
1045 mType = HardDiskType_WritethroughHardDisk;
1046 else
1047 ComAssertMsgFailedRet (("Invalid hard disk type '%ls'\n", type.raw()),
1048 E_FAIL);
1049 }
1050 else
1051 mType = HardDiskType_NormalHardDisk;
1052
1053 HRESULT rc = mVirtualBox->registerHardDisk (this, VirtualBox::RHD_OnStartUp);
1054 if (FAILED (rc))
1055 return rc;
1056
1057 /* load all children */
1058 unsigned count = 0;
1059 CFGLDRCountChildren (aHDNode, "DiffHardDisk", &count);
1060 for (unsigned i = 0; i < count && SUCCEEDED (rc); ++ i)
1061 {
1062 CFGNODE hdNode = 0;
1063
1064 CFGLDRGetChildNode (aHDNode, "DiffHardDisk", i, &hdNode);
1065 ComAssertBreak (hdNode, rc = E_FAIL);
1066
1067 do
1068 {
1069 CFGNODE vdiNode = 0;
1070 CFGLDRGetChildNode (hdNode, "VirtualDiskImage", 0, &vdiNode);
1071 ComAssertBreak (vdiNode, rc = E_FAIL);
1072
1073 ComObjPtr <HVirtualDiskImage> vdi;
1074 vdi.createObject();
1075 rc = vdi->init (mVirtualBox, this, hdNode, vdiNode);
1076
1077 CFGLDRReleaseNode (vdiNode);
1078 }
1079 while (0);
1080
1081 CFGLDRReleaseNode (hdNode);
1082 }
1083
1084 return rc;
1085}
1086
1087/**
1088 * Saves the base settings of the hard disk to the given node
1089 * and saves all child hard disks as <DiffHardDisk> nodes.
1090 *
1091 * Subclasses must call this method in their saveSettings() methods
1092 * in order to be properly saved to the settings file.
1093 *
1094 * @param aHDNode <HardDisk> node when #isDifferencing() = false, or
1095 * <DiffHardDisk> node otherwise
1096 *
1097 * @note
1098 * Must be called from under the object's lock
1099 */
1100HRESULT HardDisk::saveSettings (CFGNODE aHDNode)
1101{
1102 AssertReturn (aHDNode, E_FAIL);
1103
1104 /* uuid (required) */
1105 CFGLDRSetUUID (aHDNode, "uuid", mId.ptr());
1106
1107 if (!isDifferencing())
1108 {
1109 /* type (required) */
1110 const char *type = NULL;
1111 switch (mType)
1112 {
1113 case HardDiskType_NormalHardDisk:
1114 type = "normal";
1115 break;
1116 case HardDiskType_ImmutableHardDisk:
1117 type = "immutable";
1118 break;
1119 case HardDiskType_WritethroughHardDisk:
1120 type = "writethrough";
1121 break;
1122 }
1123 CFGLDRSetString (aHDNode, "type", type);
1124 }
1125
1126 HRESULT rc = S_OK;
1127
1128 /* save all children */
1129 AutoLock chLock (childrenLock());
1130 for (HardDiskList::const_iterator it = children().begin();
1131 it != children().end() && SUCCEEDED (rc);
1132 ++ it)
1133 {
1134 ComObjPtr <HardDisk> child = *it;
1135 AutoLock childLock (child);
1136
1137 CFGNODE hdNode = 0;
1138 CFGLDRAppendChildNode (aHDNode, "DiffHardDisk", &hdNode);
1139 ComAssertBreak (hdNode, rc = E_FAIL);
1140
1141 do
1142 {
1143 CFGNODE vdiNode = 0;
1144 CFGLDRAppendChildNode (hdNode, "VirtualDiskImage", &vdiNode);
1145 ComAssertBreak (vdiNode, rc = E_FAIL);
1146
1147 rc = child->saveSettings (hdNode, vdiNode);
1148
1149 CFGLDRReleaseNode (vdiNode);
1150 }
1151 while (0);
1152
1153 CFGLDRReleaseNode (hdNode);
1154 }
1155
1156 return rc;
1157}
1158
1159////////////////////////////////////////////////////////////////////////////////
1160// HVirtualDiskImage class
1161////////////////////////////////////////////////////////////////////////////////
1162
1163// constructor / destructor
1164////////////////////////////////////////////////////////////////////////////////
1165
1166HRESULT HVirtualDiskImage::FinalConstruct()
1167{
1168 HRESULT rc = HardDisk::FinalConstruct();
1169 if (FAILED (rc))
1170 return rc;
1171
1172 mState = NotCreated;
1173
1174 mStateCheckSem = NIL_RTSEMEVENTMULTI;
1175 mStateCheckWaiters = 0;
1176
1177 mSize = 0;
1178 mActualSize = 0;
1179
1180 return S_OK;
1181}
1182
1183void HVirtualDiskImage::FinalRelease()
1184{
1185 HardDisk::FinalRelease();
1186}
1187
1188// public initializer/uninitializer for internal purposes only
1189////////////////////////////////////////////////////////////////////////////////
1190
1191// public methods for internal purposes only
1192/////////////////////////////////////////////////////////////////////////////
1193
1194/**
1195 * Initializes the VDI hard disk object by reading its properties from
1196 * the given configuration node. The created hard disk will be marked as
1197 * registered on success.
1198 *
1199 * @param aHDNode <HardDisk> node
1200 * @param aVDINod <VirtualDiskImage> node
1201 */
1202HRESULT HVirtualDiskImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
1203 CFGNODE aHDNode, CFGNODE aVDINode)
1204{
1205 LogFlowMember (("HVirtualDiskImage::init (load)\n"));
1206
1207 AssertReturn (aHDNode && aVDINode, E_FAIL);
1208
1209 AutoLock alock (this);
1210 ComAssertRet (!isReady(), E_UNEXPECTED);
1211
1212 mStorageType = HardDiskStorageType_VirtualDiskImage;
1213
1214 HRESULT rc = S_OK;
1215
1216 do
1217 {
1218 rc = protectedInit (aVirtualBox, aParent);
1219 CheckComRCBreakRC (rc);
1220
1221 /* set ready to let protectedUninit() be called on failure */
1222 setReady (true);
1223
1224 /* filePath (required) */
1225 Bstr filePath;
1226 CFGLDRQueryBSTR (aVDINode, "filePath", filePath.asOutParam());
1227
1228 rc = setFilePath (filePath);
1229 CheckComRCBreakRC (rc);
1230
1231 LogFlowMember ((" '%ls'\n", mFilePathFull.raw()));
1232
1233 /* load basic settings and children */
1234 rc = loadSettings (aHDNode);
1235 CheckComRCBreakRC (rc);
1236
1237 mState = Created;
1238 mRegistered = TRUE;
1239
1240 /* Don't call queryInformation() for registered hard disks to
1241 * prevent the calling thread (i.e. the VirtualBox server startup
1242 * thread) from an unexpected freeze. The vital mId property (UUID)
1243 * is read from the registry file in loadSettings(). To get the rest,
1244 * the user will have to call COMGETTER(Accessible) manually. */
1245 }
1246 while (0);
1247
1248 if (FAILED (rc))
1249 uninit();
1250
1251 return rc;
1252}
1253
1254/**
1255 * Initializes the VDI hard disk object using the given image file name.
1256 *
1257 * @param aFilePath path to the image file (can be NULL to create an
1258 * imageless object)
1259 * @param aRegistered whether to mark this disk as registered or not
1260 * (ignored when \a aFilePath is NULL, assuming FALSE)
1261 */
1262HRESULT HVirtualDiskImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
1263 const BSTR aFilePath, BOOL aRegistered /* = FALSE */)
1264{
1265 LogFlowMember (("HVirtualDiskImage::init (aFilePath='%ls', aRegistered=%d)\n",
1266 aFilePath, aRegistered));
1267
1268 AutoLock alock (this);
1269 ComAssertRet (!isReady(), E_UNEXPECTED);
1270
1271 mStorageType = HardDiskStorageType_VirtualDiskImage;
1272
1273 HRESULT rc = S_OK;
1274
1275 do
1276 {
1277 rc = protectedInit (aVirtualBox, aParent);
1278 CheckComRCBreakRC (rc);
1279
1280 /* set ready to let protectedUninit() be called on failure */
1281 setReady (true);
1282
1283 rc = setFilePath (aFilePath);
1284 CheckComRCBreakRC (rc);
1285
1286 Assert (mId.isEmpty());
1287
1288 if (aFilePath && *aFilePath)
1289 {
1290 mRegistered = aRegistered;
1291 mState = Created;
1292
1293 /* Call queryInformation() anyway (even if it will block), because
1294 * it is the only way to get the UUID of the existing VDI and
1295 * initialize the vital mId property. */
1296 Bstr errMsg;
1297 rc = queryInformation (&errMsg);
1298 if (SUCCEEDED (rc))
1299 {
1300 /* We are constructing a new HVirtualDiskImage object. If there
1301 * is a fatal accessibility error (we cannot read image UUID),
1302 * we have to fail. We do so even on non-fatal errors as well,
1303 * because it's not worth to keep going with the inaccessible
1304 * image from the very beginning (when nothing else depends on
1305 * it yet). */
1306 if (!errMsg.isNull())
1307 rc = setErrorBstr (E_FAIL, errMsg);
1308 }
1309 }
1310 else
1311 {
1312 mRegistered = FALSE;
1313 mState = NotCreated;
1314 mId.create();
1315 }
1316 }
1317 while (0);
1318
1319 if (FAILED (rc))
1320 uninit();
1321
1322 return rc;
1323}
1324
1325/**
1326 * Uninitializes the instance and sets the ready flag to FALSE.
1327 * Called either from FinalRelease(), by the parent when it gets destroyed,
1328 * or by a third party when it decides this object is no more valid.
1329 */
1330void HVirtualDiskImage::uninit()
1331{
1332 LogFlowMember (("HVirtualDiskImage::uninit()\n"));
1333
1334 AutoLock alock (this);
1335 if (!isReady())
1336 return;
1337
1338 HardDisk::protectedUninit (alock);
1339}
1340
1341// IHardDisk properties
1342////////////////////////////////////////////////////////////////////////////////
1343
1344STDMETHODIMP HVirtualDiskImage::COMGETTER(Description) (BSTR *aDescription)
1345{
1346 if (!aDescription)
1347 return E_POINTER;
1348
1349 AutoLock alock (this);
1350 CHECK_READY();
1351
1352 mDescription.cloneTo (aDescription);
1353 return S_OK;
1354}
1355
1356STDMETHODIMP HVirtualDiskImage::COMSETTER(Description) (INPTR BSTR aDescription)
1357{
1358 AutoLock alock (this);
1359 CHECK_READY();
1360
1361 CHECK_BUSY_AND_READERS();
1362
1363 if (mState >= Created)
1364 {
1365 int vrc = VDISetImageComment (Utf8Str (mFilePathFull), Utf8Str (aDescription));
1366 if (VBOX_FAILURE (vrc))
1367 return setError (E_FAIL,
1368 tr ("Could not change the description of the VDI hard disk '%ls' "
1369 "(%Vrc)"),
1370 toString().raw(), vrc);
1371 }
1372
1373 mDescription = aDescription;
1374 return S_OK;
1375}
1376
1377STDMETHODIMP HVirtualDiskImage::COMGETTER(Size) (ULONG64 *aSize)
1378{
1379 if (!aSize)
1380 return E_POINTER;
1381
1382 AutoLock alock (this);
1383 CHECK_READY();
1384
1385 /* only a non-differencing image knows the logical size */
1386 if (isDifferencing())
1387 return root()->COMGETTER(Size) (aSize);
1388
1389 *aSize = mSize;
1390 return S_OK;
1391}
1392
1393STDMETHODIMP HVirtualDiskImage::COMGETTER(ActualSize) (ULONG64 *aActualSize)
1394{
1395 if (!aActualSize)
1396 return E_POINTER;
1397
1398 AutoLock alock (this);
1399 CHECK_READY();
1400
1401 *aActualSize = mActualSize;
1402 return S_OK;
1403}
1404
1405// IVirtualDiskImage properties
1406////////////////////////////////////////////////////////////////////////////////
1407
1408STDMETHODIMP HVirtualDiskImage::COMGETTER(FilePath) (BSTR *aFilePath)
1409{
1410 if (!aFilePath)
1411 return E_POINTER;
1412
1413 AutoLock alock (this);
1414 CHECK_READY();
1415
1416 mFilePathFull.cloneTo (aFilePath);
1417 return S_OK;
1418}
1419
1420STDMETHODIMP HVirtualDiskImage::COMSETTER(FilePath) (INPTR BSTR aFilePath)
1421{
1422 AutoLock alock (this);
1423 CHECK_READY();
1424
1425 if (mState != NotCreated)
1426 return setError (E_ACCESSDENIED,
1427 tr ("Cannot change the file path of the existing VDI hard disk '%ls'"),
1428 toString().raw());
1429
1430 CHECK_BUSY_AND_READERS();
1431
1432 // append the default path if only a name is given
1433 Bstr path = aFilePath;
1434 if (aFilePath && *aFilePath)
1435 {
1436 Utf8Str fp = aFilePath;
1437 if (!RTPathHavePath (fp))
1438 {
1439 AutoReaderLock propsLock (mVirtualBox->systemProperties());
1440 path = Utf8StrFmt ("%ls%c%s",
1441 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
1442 RTPATH_DELIMITER,
1443 fp.raw());
1444 }
1445 }
1446
1447 return setFilePath (path);
1448}
1449
1450STDMETHODIMP HVirtualDiskImage::COMGETTER(Created) (BOOL *aCreated)
1451{
1452 if (!aCreated)
1453 return E_POINTER;
1454
1455 AutoLock alock (this);
1456 CHECK_READY();
1457
1458 *aCreated = mState >= Created;
1459 return S_OK;
1460}
1461
1462// IVirtualDiskImage methods
1463/////////////////////////////////////////////////////////////////////////////
1464
1465STDMETHODIMP HVirtualDiskImage::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
1466{
1467 if (!aProgress)
1468 return E_POINTER;
1469
1470 AutoLock alock (this);
1471 CHECK_READY();
1472
1473 return createImage (aSize, TRUE /* aDynamic */, aProgress);
1474}
1475
1476STDMETHODIMP HVirtualDiskImage::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
1477{
1478 if (!aProgress)
1479 return E_POINTER;
1480
1481 AutoLock alock (this);
1482 CHECK_READY();
1483
1484 return createImage (aSize, FALSE /* aDynamic */, aProgress);
1485}
1486
1487STDMETHODIMP HVirtualDiskImage::DeleteImage()
1488{
1489 AutoLock alock (this);
1490 CHECK_READY();
1491 CHECK_BUSY_AND_READERS();
1492
1493 if (mRegistered)
1494 return setError (E_ACCESSDENIED,
1495 tr ("Cannot delete an image of the registered hard disk image '%ls"),
1496 mFilePathFull.raw());
1497 if (mState == NotCreated)
1498 return setError (E_FAIL,
1499 tr ("Hard disk image has been already deleted or never created"));
1500
1501 int vrc = RTFileDelete (Utf8Str (mFilePathFull));
1502 if (VBOX_FAILURE (vrc))
1503 return setError (E_FAIL, tr ("Could not delete the image file '%ls' (%Vrc)"),
1504 mFilePathFull.raw(), vrc);
1505
1506 mState = NotCreated;
1507
1508 // reset the fields
1509 mSize = 0;
1510 mActualSize = 0;
1511
1512 return S_OK;
1513}
1514
1515// public/protected methods for internal purposes only
1516/////////////////////////////////////////////////////////////////////////////
1517
1518/**
1519 * Attempts to mark the hard disk as registered.
1520 * Only VirtualBox can call this method.
1521 */
1522HRESULT HVirtualDiskImage::trySetRegistered (BOOL aRegistered)
1523{
1524 AutoLock alock (this);
1525 CHECK_READY();
1526
1527 if (aRegistered)
1528 {
1529 if (mState == NotCreated)
1530 return setError (E_FAIL,
1531 tr ("Image file '%ls' is not yet created for this hard disk"),
1532 mFilePathFull.raw());
1533 if (isDifferencing())
1534 return setError (E_FAIL,
1535 tr ("Hard disk '%ls' is differencing and cannot be unregistered "
1536 "explicitly"),
1537 mFilePathFull.raw());
1538 }
1539 else
1540 {
1541 ComAssertRet (mState >= Created, E_FAIL);
1542 }
1543
1544 return HardDisk::trySetRegistered (aRegistered);
1545}
1546
1547/**
1548 * Checks accessibility of this hard disk image only (w/o parents).
1549 *
1550 * @param aAccessError on output, a null string indicates the hard disk is
1551 * accessible, otherwise contains a message describing
1552 * the reason of inaccessibility.
1553 */
1554HRESULT HVirtualDiskImage::getAccessible (Bstr &aAccessError)
1555{
1556 AutoLock alock (this);
1557 CHECK_READY();
1558
1559 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
1560 {
1561 /* An accessibility check in progress on some other thread,
1562 * wait for it to finish. */
1563
1564 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL);
1565 ++ mStateCheckWaiters;
1566 alock.leave();
1567
1568 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
1569
1570 alock.enter();
1571 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
1572 -- mStateCheckWaiters;
1573 if (mStateCheckWaiters == 0)
1574 {
1575 RTSemEventMultiDestroy (mStateCheckSem);
1576 mStateCheckSem = NIL_RTSEMEVENTMULTI;
1577 }
1578
1579 AssertRCReturn (vrc, E_FAIL);
1580
1581 /* don't touch aAccessError, it has been already set */
1582 return S_OK;
1583 }
1584
1585 /* check the basic accessibility */
1586 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */);
1587 if (FAILED (rc) || !aAccessError.isNull())
1588 return rc;
1589
1590 if (mState >= Created)
1591 {
1592 return queryInformation (&aAccessError);
1593 }
1594
1595 aAccessError = Utf8StrFmt ("Hard disk image '%ls' is not yet created",
1596 mFilePathFull.raw());
1597 return S_OK;
1598}
1599
1600/**
1601 * Saves hard disk settings to the specified storage node and saves
1602 * all children to the specified hard disk node
1603 *
1604 * @param aHDNode <HardDisk> or <DiffHardDisk> node
1605 * @param aStorageNode <VirtualDiskImage> node
1606 */
1607HRESULT HVirtualDiskImage::saveSettings (CFGNODE aHDNode, CFGNODE aStorageNode)
1608{
1609 AssertReturn (aHDNode && aStorageNode, E_FAIL);
1610
1611 AutoLock alock (this);
1612 CHECK_READY();
1613
1614 // filePath (required)
1615 CFGLDRSetBSTR (aStorageNode, "filePath", mFilePath);
1616
1617 // save basic settings and children
1618 return HardDisk::saveSettings (aHDNode);
1619}
1620
1621/**
1622 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
1623 * of this hard disk and updates it if necessary to reflect the new location.
1624 * Intended to be from HardDisk::updatePaths().
1625 *
1626 * @param aOldPath old path (full)
1627 * @param aNewPath new path (full)
1628 *
1629 * @note Locks this object for writing.
1630 */
1631void HVirtualDiskImage::updatePath (const char *aOldPath, const char *aNewPath)
1632{
1633 AssertReturnVoid (aOldPath);
1634 AssertReturnVoid (aNewPath);
1635
1636 AutoLock alock (this);
1637 AssertReturnVoid (isReady());
1638
1639 size_t oldPathLen = strlen (aOldPath);
1640
1641 Utf8Str path = mFilePathFull;
1642 LogFlowThisFunc (("VDI.fullPath={%s}\n", path.raw()));
1643
1644 if (RTPathStartsWith (path, aOldPath))
1645 {
1646 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
1647 path.raw() + oldPathLen);
1648 path = newPath;
1649
1650 mVirtualBox->calculateRelativePath (path, path);
1651
1652 unconst (mFilePathFull) = newPath;
1653 unconst (mFilePath) = path;
1654
1655 LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
1656 newPath.raw(), path.raw()));
1657 }
1658}
1659
1660/**
1661 * Returns the string representation of this hard disk.
1662 * When \a aShort is false, returns the full image file path.
1663 * Otherwise, returns the image file name only.
1664 *
1665 * @param aShort if true, a short representation is returned
1666 */
1667Bstr HVirtualDiskImage::toString (bool aShort /* = false */)
1668{
1669 AutoLock alock (this);
1670
1671 if (!aShort)
1672 return mFilePathFull;
1673 else
1674 {
1675 Utf8Str fname = mFilePathFull;
1676 return RTPathFilename (fname.mutableRaw());
1677 }
1678}
1679
1680/**
1681 * Creates a clone of this hard disk by storing hard disk data in the given
1682 * VDI file name.
1683 *
1684 * @param aId UUID to assign to the created image
1685 * @param aTargetPath VDI file where the cloned image is to be to stored
1686 * @param aProgress progress object to run during operation
1687 */
1688HRESULT
1689HVirtualDiskImage::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
1690 Progress *aProgress)
1691{
1692 AssertReturn (!aId.isEmpty(), E_FAIL);
1693 AssertReturn (!aTargetPath.isNull(), E_FAIL);
1694 AssertReturn (aProgress, E_FAIL);
1695
1696 AutoLock alock (this);
1697 AssertReturn (isReady(), E_FAIL);
1698
1699 AssertReturn (isBusy() == false, E_FAIL);
1700
1701 /// @todo (dmik) cloning of differencing images is not yet supported
1702 AssertReturn (mParent.isNull(), E_FAIL);
1703
1704 Utf8Str filePathFull = mFilePathFull;
1705
1706 if (mState == NotCreated)
1707 return setError (E_FAIL,
1708 tr ("Source hard disk image '%s' is not yet created"),
1709 filePathFull.raw());
1710
1711 addReader();
1712 alock.leave();
1713
1714 int vrc = VDICopyImage (aTargetPath, filePathFull, NULL,
1715 progressCallback,
1716 static_cast <Progress *> (aProgress));
1717
1718 alock.enter();
1719 releaseReader();
1720
1721 if (VBOX_SUCCESS (vrc))
1722 vrc = VDISetImageUUIDs (aTargetPath, aId, NULL, NULL, NULL);
1723
1724 if (VBOX_FAILURE (vrc))
1725 return setError (E_FAIL,
1726 tr ("Could not copy the hard disk image '%s' to '%s' (%Vrc)"),
1727 filePathFull.raw(), aTargetPath.raw(), vrc);
1728
1729 return S_OK;
1730}
1731
1732/**
1733 * Creates a new differencing image for this hard disk with the given
1734 * VDI file name.
1735 *
1736 * @param aId UUID to assign to the created image
1737 * @param aTargetPath VDI file where to store the created differencing image
1738 * @param aProgress progress object to run during operation
1739 * (can be NULL)
1740 */
1741HRESULT
1742HVirtualDiskImage::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
1743 Progress *aProgress)
1744{
1745 AssertReturn (!aId.isEmpty(), E_FAIL);
1746 AssertReturn (!aTargetPath.isNull(), E_FAIL);
1747
1748 AutoLock alock (this);
1749 AssertReturn (isReady(), E_FAIL);
1750
1751 AssertReturn (isBusy() == false, E_FAIL);
1752 AssertReturn (mState >= Created, E_FAIL);
1753
1754 addReader();
1755 alock.leave();
1756
1757 int vrc = VDICreateDifferenceImage (aTargetPath, Utf8Str (mFilePathFull),
1758 NULL, aProgress ? progressCallback : NULL,
1759 static_cast <Progress *> (aProgress));
1760 alock.enter();
1761 releaseReader();
1762
1763 /* update the UUID to correspond to the file name */
1764 if (VBOX_SUCCESS (vrc))
1765 vrc = VDISetImageUUIDs (aTargetPath, aId, NULL, NULL, NULL);
1766
1767 if (VBOX_FAILURE (vrc))
1768 return setError (E_FAIL,
1769 tr ("Could not create a differencing hard disk '%s' (%Vrc)"),
1770 aTargetPath.raw(), vrc);
1771
1772 return S_OK;
1773}
1774
1775/**
1776 * Copies the image file of this hard disk to a separate VDI file (with an
1777 * unique creation UUID) and creates a new hard disk object for the copied
1778 * image. The copy will be created as a child of this hard disk's parent
1779 * (so that this hard disk must be a differencing one).
1780 *
1781 * The specified progress object (if not NULL) receives the percentage
1782 * of the operation completion. However, it is responsibility of the caller to
1783 * call Progress::notifyComplete() after this method returns.
1784 *
1785 * @param aFolder folder where to create a copy (must be a full path)
1786 * @param aMachineId machine ID the new hard disk will belong to
1787 * @param aHardDisk resulting hard disk object
1788 * @param aProgress progress object to run during copy operation
1789 * (may be NULL)
1790 *
1791 * @note
1792 * Must be NOT called from under locks of other objects that need external
1793 * access dirung this method execurion!
1794 */
1795HRESULT
1796HVirtualDiskImage::cloneDiffImage (const Bstr &aFolder, const Guid &aMachineId,
1797 ComObjPtr <HVirtualDiskImage> &aHardDisk,
1798 Progress *aProgress)
1799{
1800 AssertReturn (!aFolder.isEmpty() && !aMachineId.isEmpty(),
1801 E_FAIL);
1802
1803 AutoLock alock (this);
1804 CHECK_READY();
1805
1806 AssertReturn (!mParent.isNull(), E_FAIL);
1807
1808 ComAssertRet (isBusy() == false, E_FAIL);
1809 ComAssertRet (mState >= Created, E_FAIL);
1810
1811 Guid id;
1812 id.create();
1813
1814 Utf8Str filePathTo = Utf8StrFmt ("%ls%c{%Vuuid}.vdi",
1815 aFolder.raw(), RTPATH_DELIMITER, id.ptr());
1816
1817 /* try to make the path relative to the vbox home dir */
1818 const char *filePathToRel = filePathTo;
1819 {
1820 const Utf8Str &homeDir = mVirtualBox->homeDir();
1821 if (!strncmp (filePathTo, homeDir, homeDir.length()))
1822 filePathToRel = (filePathToRel + homeDir.length() + 1);
1823 }
1824
1825 /* first ensure the directory exists */
1826 {
1827 Utf8Str dir = aFolder;
1828 if (!RTDirExists (dir))
1829 {
1830 int vrc = RTDirCreateFullPath (dir, 0777);
1831 if (VBOX_FAILURE (vrc))
1832 {
1833 return setError (E_FAIL,
1834 tr ("Could not create a directory '%s' "
1835 "to store the image file (%Vrc)"),
1836 dir.raw(), vrc);
1837 }
1838 }
1839 }
1840
1841 Utf8Str filePathFull = mFilePathFull;
1842
1843 alock.leave();
1844
1845 int vrc = VDICopyImage (filePathTo, filePathFull, NULL,
1846 progressCallback,
1847 static_cast <Progress *> (aProgress));
1848
1849 alock.enter();
1850
1851 /* get modification and parent UUIDs of this image */
1852 RTUUID modUuid, parentUuid, parentModUuid;
1853 if (VBOX_SUCCESS (vrc))
1854 vrc = VDIGetImageUUIDs (filePathFull, NULL, &modUuid,
1855 &parentUuid, &parentModUuid);
1856
1857 // update the UUID of the copy to correspond to the file name
1858 // and copy all other UUIDs from this image
1859 if (VBOX_SUCCESS (vrc))
1860 vrc = VDISetImageUUIDs (filePathTo, id, &modUuid,
1861 &parentUuid, &parentModUuid);
1862
1863 if (VBOX_FAILURE (vrc))
1864 return setError (E_FAIL,
1865 tr ("Could not copy the hard disk image '%s' to '%s' (%Vrc)"),
1866 filePathFull.raw(), filePathTo.raw(), vrc);
1867
1868 ComObjPtr <HVirtualDiskImage> vdi;
1869 vdi.createObject();
1870 HRESULT rc = vdi->init (mVirtualBox, mParent, Bstr (filePathToRel),
1871 TRUE /* aRegistered */);
1872 if (FAILED (rc))
1873 return rc;
1874
1875 /* associate the created hard disk with the given machine */
1876 vdi->setMachineId (aMachineId);
1877
1878 rc = mVirtualBox->registerHardDisk (vdi, VirtualBox::RHD_Internal);
1879 if (FAILED (rc))
1880 return rc;
1881
1882 aHardDisk = vdi;
1883
1884 return S_OK;
1885}
1886
1887/**
1888 * Merges this child image to its parent image and updates the parent UUID
1889 * of all children of this image (to point to this image's parent).
1890 * It's a responsibility of the caller to unregister and uninitialize
1891 * the merged image on success.
1892 *
1893 * This method is intended to be called on a worker thread (the operation
1894 * can be time consuming).
1895 *
1896 * The specified progress object (if not NULL) receives the percentage
1897 * of the operation completion. However, it is responsibility of the caller to
1898 * call Progress::notifyComplete() after this method returns.
1899 *
1900 * @param aProgress progress object to run during copy operation
1901 * (may be NULL)
1902 *
1903 * @note
1904 * This method expects that both this hard disk and the paret hard disk
1905 * are marked as busy using #setBusyWithChildren() prior to calling it!
1906 * Busy flags of both hard disks will be cleared by this method
1907 * on a successful return. In case of failure, #clearBusyWithChildren()
1908 * must be called on a parent.
1909 *
1910 * @note
1911 * Must be NOT called from under locks of other objects that need external
1912 * access dirung this method execurion!
1913 */
1914HRESULT HVirtualDiskImage::mergeImageToParent (Progress *aProgress)
1915{
1916 LogFlowMember (("HVirtualDiskImage::mergeImageToParent(): image='%ls'\n",
1917 mFilePathFull.raw()));
1918
1919 AutoLock alock (this);
1920 CHECK_READY();
1921
1922 AssertReturn (!mParent.isNull(), E_FAIL);
1923 AutoLock parentLock (mParent);
1924
1925 ComAssertRet (isBusy() == true, E_FAIL);
1926 ComAssertRet (mParent->isBusy() == true, E_FAIL);
1927
1928 ComAssertRet (mState >= Created && mParent->asVDI()->mState >= Created, E_FAIL);
1929
1930 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
1931 ("non VDI storage types are not yet supported!"), E_FAIL);
1932
1933 parentLock.leave();
1934 alock.leave();
1935
1936 int vrc = VDIMergeImage (Utf8Str (mFilePathFull),
1937 Utf8Str (mParent->asVDI()->mFilePathFull),
1938 progressCallback,
1939 static_cast <Progress *> (aProgress));
1940 alock.enter();
1941 parentLock.enter();
1942
1943 if (VBOX_FAILURE (vrc))
1944 return setError (E_FAIL,
1945 tr ("Could not merge the hard disk image '%ls' to "
1946 "its parent image '%ls' (%Vrc)"),
1947 mFilePathFull.raw(), mParent->asVDI()->mFilePathFull.raw(), vrc);
1948
1949 {
1950 HRESULT rc = S_OK;
1951
1952 AutoLock chLock (childrenLock());
1953
1954 for (HardDiskList::const_iterator it = children().begin();
1955 it != children().end(); ++ it)
1956 {
1957 ComObjPtr <HVirtualDiskImage> child = (*it)->asVDI();
1958 AutoLock childLock (child);
1959
1960 /* reparent the child */
1961 child->mParent = mParent;
1962 if (mParent)
1963 mParent->addDependentChild (child);
1964
1965 /* change the parent UUID in the image as well */
1966 RTUUID parentUuid, parentModUuid;
1967 vrc = VDIGetImageUUIDs (Utf8Str (mParent->asVDI()->mFilePathFull),
1968 &parentUuid, &parentModUuid, NULL, NULL);
1969 if (VBOX_FAILURE (vrc))
1970 {
1971 rc = setError (E_FAIL,
1972 tr ("Could not access the hard disk image '%ls' (%Vrc)"),
1973 mParent->asVDI()->mFilePathFull.raw(), vrc);
1974 break;
1975 }
1976 ComAssertBreak (mParent->id() == Guid (parentUuid), rc = E_FAIL);
1977 vrc = VDISetImageUUIDs (Utf8Str (child->mFilePathFull),
1978 NULL, NULL, &parentUuid, &parentModUuid);
1979 if (VBOX_FAILURE (vrc))
1980 {
1981 rc = setError (E_FAIL,
1982 tr ("Could not update parent UUID of the hard disk image "
1983 "'%ls' (%Vrc)"),
1984 child->mFilePathFull.raw(), vrc);
1985 break;
1986 }
1987 }
1988
1989 if (FAILED (rc))
1990 return rc;
1991 }
1992
1993 /* detach all our children to avoid their uninit in #uninit() */
1994 removeDependentChildren();
1995
1996 mParent->clearBusy();
1997 clearBusy();
1998
1999 return S_OK;
2000}
2001
2002/**
2003 * Merges this image to all its child images, updates the parent UUID
2004 * of all children of this image (to point to this image's parent).
2005 * It's a responsibility of the caller to unregister and uninitialize
2006 * the merged image on success.
2007 *
2008 * This method is intended to be called on a worker thread (the operation
2009 * can be time consuming).
2010 *
2011 * The specified progress object (if not NULL) receives the percentage
2012 * of the operation completion. However, it is responsibility of the caller to
2013 * call Progress::notifyComplete() after this method returns.
2014 *
2015 * @param aProgress progress object to run during copy operation
2016 * (may be NULL)
2017 *
2018 * @note
2019 * This method expects that both this hard disk and all children
2020 * are marked as busy using setBusyWithChildren() prior to calling it!
2021 * Busy flags of all affected hard disks will be cleared by this method
2022 * on a successful return. In case of failure, #clearBusyWithChildren()
2023 * must be called for this hard disk.
2024 *
2025 * @note
2026 * Must be NOT called from under locks of other objects that need external
2027 * access dirung this method execurion!
2028 */
2029HRESULT HVirtualDiskImage::mergeImageToChildren (Progress *aProgress)
2030{
2031 LogFlowMember (("HVirtualDiskImage::mergeImageToChildren(): image='%ls'\n",
2032 mFilePathFull.raw()));
2033
2034 AutoLock alock (this);
2035 CHECK_READY();
2036
2037 /* this must be a diff image */
2038 AssertReturn (isDifferencing(), E_FAIL);
2039
2040 ComAssertRet (isBusy() == true, E_FAIL);
2041 ComAssertRet (mState >= Created, E_FAIL);
2042
2043 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2044 ("non VDI storage types are not yet supported!"), E_FAIL);
2045
2046 {
2047 HRESULT rc = S_OK;
2048
2049 AutoLock chLock (childrenLock());
2050
2051 /* iterate over a copy since we will modify the list */
2052 HardDiskList list = children();
2053
2054 for (HardDiskList::const_iterator it = list.begin();
2055 it != list.end(); ++ it)
2056 {
2057 ComObjPtr <HardDisk> hd = *it;
2058 ComObjPtr <HVirtualDiskImage> child = hd->asVDI();
2059 AutoLock childLock (child);
2060
2061 ComAssertRet (child->isBusy() == true, E_FAIL);
2062 ComAssertBreak (child->mState >= Created, rc = E_FAIL);
2063
2064 childLock.leave();
2065 alock.leave();
2066
2067 int vrc = VDIMergeImage (Utf8Str (mFilePathFull),
2068 Utf8Str (child->mFilePathFull),
2069 progressCallback,
2070 static_cast <Progress *> (aProgress));
2071 alock.enter();
2072 childLock.enter();
2073
2074 if (VBOX_FAILURE (vrc))
2075 {
2076 rc = setError (E_FAIL,
2077 tr ("Could not merge the hard disk image '%ls' to "
2078 "its parent image '%ls' (%Vrc)"),
2079 mFilePathFull.raw(), child->mFilePathFull.raw(), vrc);
2080 break;
2081 }
2082
2083 /* reparent the child */
2084 child->mParent = mParent;
2085 if (mParent)
2086 mParent->addDependentChild (child);
2087
2088 /* change the parent UUID in the image as well */
2089 RTUUID parentUuid, parentModUuid;
2090 vrc = VDIGetImageUUIDs (Utf8Str (mParent->asVDI()->mFilePathFull),
2091 &parentUuid, &parentModUuid, NULL, NULL);
2092 if (VBOX_FAILURE (vrc))
2093 {
2094 rc = setError (E_FAIL,
2095 tr ("Could not access the hard disk image '%ls' (%Vrc)"),
2096 mParent->asVDI()->mFilePathFull.raw(), vrc);
2097 break;
2098 }
2099 ComAssertBreak (mParent->id() == Guid (parentUuid), rc = E_FAIL);
2100 vrc = VDISetImageUUIDs (Utf8Str (child->mFilePathFull),
2101 NULL, NULL, &parentUuid, &parentModUuid);
2102 if (VBOX_FAILURE (vrc))
2103 {
2104 rc = setError (E_FAIL,
2105 tr ("Could not update parent UUID of the hard disk image "
2106 "'%ls' (%Vrc)"),
2107 child->mFilePathFull.raw(), vrc);
2108 break;
2109 }
2110
2111 /* detach child to avoid its uninit in #uninit() */
2112 removeDependentChild (child);
2113
2114 /* remove the busy flag */
2115 child->clearBusy();
2116 }
2117
2118 if (FAILED (rc))
2119 return rc;
2120 }
2121
2122 clearBusy();
2123
2124 return S_OK;
2125}
2126
2127/**
2128 * Deletes and recreates the differencing hard disk image from scratch.
2129 * The file name and UUID remain the same.
2130 */
2131HRESULT HVirtualDiskImage::wipeOutImage()
2132{
2133 AutoLock alock (this);
2134 CHECK_READY();
2135
2136 AssertReturn (isDifferencing(), E_FAIL);
2137 AssertReturn (mRegistered, E_FAIL);
2138 AssertReturn (mState >= Created, E_FAIL);
2139
2140 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2141 ("non-VDI storage types are not yet supported!"), E_FAIL);
2142
2143 Utf8Str filePathFull = mFilePathFull;
2144
2145 int vrc = RTFileDelete (filePathFull);
2146 if (VBOX_FAILURE (vrc))
2147 return setError (E_FAIL,
2148 tr ("Could not delete the image file '%s' (%Vrc)"),
2149 filePathFull.raw(), vrc);
2150
2151 vrc = VDICreateDifferenceImage (filePathFull,
2152 Utf8Str (mParent->asVDI()->mFilePathFull),
2153 NULL, NULL, NULL);
2154 /* update the UUID to correspond to the file name */
2155 if (VBOX_SUCCESS (vrc))
2156 vrc = VDISetImageUUIDs (filePathFull, mId, NULL, NULL, NULL);
2157
2158 if (VBOX_FAILURE (vrc))
2159 return setError (E_FAIL,
2160 tr ("Could not create a differencing hard disk '%s' (%Vrc)"),
2161 filePathFull.raw(), vrc);
2162
2163 return S_OK;
2164}
2165
2166// private methods
2167/////////////////////////////////////////////////////////////////////////////
2168
2169/**
2170 * Helper to set a new file path.
2171 * Resolves a path relatively to the Virtual Box home directory.
2172 *
2173 * @note
2174 * Must be called from under the object's lock!
2175 */
2176HRESULT HVirtualDiskImage::setFilePath (const BSTR aFilePath)
2177{
2178 if (aFilePath && *aFilePath)
2179 {
2180 /* get the full file name */
2181 char filePathFull [RTPATH_MAX];
2182 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aFilePath),
2183 filePathFull, sizeof (filePathFull));
2184 if (VBOX_FAILURE (vrc))
2185 return setError (E_FAIL,
2186 tr ("Invalid image file path '%ls' (%Vrc)"), aFilePath, vrc);
2187
2188 mFilePath = aFilePath;
2189 mFilePathFull = filePathFull;
2190 }
2191 else
2192 {
2193 mFilePath.setNull();
2194 mFilePathFull.setNull();
2195 }
2196
2197 return S_OK;
2198}
2199
2200/**
2201 * Helper to query information about the VDI hard disk.
2202 *
2203 * @param aAccessError not used when NULL, otherwise see #getAccessible()
2204 *
2205 * @note Must be called from under the object's lock, only after
2206 * CHECK_BUSY_AND_READERS() succeeds.
2207 */
2208HRESULT HVirtualDiskImage::queryInformation (Bstr *aAccessError)
2209{
2210 AssertReturn (isLockedOnCurrentThread(), E_FAIL);
2211
2212 /* create a lock object to completely release it later */
2213 AutoLock alock (this);
2214
2215 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
2216
2217 ComAssertRet (mState >= Created, E_FAIL);
2218
2219 HRESULT rc = S_OK;
2220 int vrc = VINF_SUCCESS;
2221
2222 /* lazily create a semaphore */
2223 vrc = RTSemEventMultiCreate (&mStateCheckSem);
2224 ComAssertRCRet (vrc, E_FAIL);
2225
2226 /* go to Busy state to prevent any concurrent modifications
2227 * after releasing the lock below (to unblock getters before
2228 * a lengthy operation) */
2229 setBusy();
2230
2231 alock.leave();
2232
2233 /* VBoxVHDD management interface needs to be optimized: we're opening a
2234 * file three times in a raw to get three bits of information. */
2235
2236 Utf8Str filePath = mFilePathFull;
2237 Bstr errMsg;
2238
2239 do
2240 {
2241 /* check the image file */
2242 Guid id, parentId;
2243 vrc = VDICheckImage (filePath, NULL, NULL, NULL,
2244 id.ptr(), parentId.ptr(), NULL, 0);
2245
2246 if (VBOX_FAILURE (vrc))
2247 break;
2248
2249 if (!mId.isEmpty())
2250 {
2251 /* check that the actual UUID of the image matches the stored UUID */
2252 if (mId != id)
2253 {
2254 errMsg = Utf8StrFmt (
2255 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
2256 "match UUID {%Vuuid} stored in the registry"),
2257 id.ptr(), filePath.raw(), mId.ptr());
2258 break;
2259 }
2260 }
2261 else
2262 {
2263 /* assgn an UUID read from the image file */
2264 mId = id;
2265 }
2266
2267 if (mParent)
2268 {
2269 /* check parent UUID */
2270 AutoLock parentLock (mParent);
2271 if (mParent->id() != parentId)
2272 {
2273 errMsg = Utf8StrFmt (
2274 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
2275 "the hard disk image file '%s' doesn't match "
2276 "UUID {%Vuuid} stored in the registry"),
2277 parentId.raw(), mParent->toString().raw(),
2278 filePath.raw(), mParent->id().raw());
2279 break;
2280 }
2281 }
2282 else if (!parentId.isEmpty())
2283 {
2284 errMsg = Utf8StrFmt (
2285 tr ("Hard disk image '%s' is a differencing image that is linked "
2286 "to a hard disk with UUID {%Vuuid} and cannot be used "
2287 "directly as a base hard disk"),
2288 filePath.raw(), parentId.raw());
2289 break;
2290 }
2291
2292 {
2293 RTFILE file = NIL_RTFILE;
2294 vrc = RTFileOpen (&file, filePath, RTFILE_O_READ);
2295 if (VBOX_SUCCESS (vrc))
2296 {
2297 uint64_t size = 0;
2298 vrc = RTFileGetSize (file, &size);
2299 if (VBOX_SUCCESS (vrc))
2300 mActualSize = size;
2301 RTFileClose (file);
2302 }
2303 if (VBOX_FAILURE (vrc))
2304 break;
2305 }
2306
2307 if (!mParent)
2308 {
2309 /* query logical size only for non-differencing images */
2310
2311 PVDIDISK disk = VDIDiskCreate();
2312 vrc = VDIDiskOpenImage (disk, Utf8Str (mFilePathFull),
2313 VDI_OPEN_FLAGS_READONLY);
2314 if (VBOX_SUCCESS (vrc))
2315 {
2316 uint64_t size = VDIDiskGetSize (disk);
2317 /* convert to MBytes */
2318 mSize = size / 1024 / 1024;
2319 }
2320
2321 VDIDiskDestroy (disk);
2322 if (VBOX_FAILURE (vrc))
2323 break;
2324 }
2325 }
2326 while (0);
2327
2328 /* enter the lock again */
2329 alock.enter();
2330
2331 /* remove the busy flag */
2332 clearBusy();
2333
2334 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
2335 {
2336 LogWarningFunc (("'%ls' is not accessible "
2337 "(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
2338 mFilePathFull.raw(), rc, vrc, errMsg.raw()));
2339
2340 if (aAccessError)
2341 {
2342 if (!errMsg.isNull())
2343 *aAccessError = errMsg;
2344 else if (VBOX_FAILURE (vrc))
2345 *aAccessError = Utf8StrFmt (
2346 tr ("Could not access hard disk image '%ls' (%Vrc)"),
2347 mFilePathFull.raw(), vrc);
2348 }
2349
2350 /* downgrade to not accessible */
2351 mState = Created;
2352 }
2353 else
2354 {
2355 if (aAccessError)
2356 aAccessError->setNull();
2357
2358 mState = Accessible;
2359 }
2360
2361 /* inform waiters if there are any */
2362 if (mStateCheckWaiters > 0)
2363 {
2364 RTSemEventMultiSignal (mStateCheckSem);
2365 }
2366 else
2367 {
2368 /* delete the semaphore ourselves */
2369 RTSemEventMultiDestroy (mStateCheckSem);
2370 mStateCheckSem = NIL_RTSEMEVENTMULTI;
2371 }
2372
2373 return rc;
2374}
2375
2376/**
2377 * Helper to create hard disk images.
2378 *
2379 * @param aSize size in MB
2380 * @param aDynamic dynamic or fixed image
2381 * @param aProgress address of IProgress pointer to return
2382 */
2383HRESULT HVirtualDiskImage::createImage (ULONG64 aSize, BOOL aDynamic,
2384 IProgress **aProgress)
2385{
2386 AutoLock alock (this);
2387
2388 CHECK_BUSY_AND_READERS();
2389
2390 if (mState != NotCreated)
2391 return setError (E_ACCESSDENIED,
2392 tr ("Hard disk image '%ls' is already created"),
2393 mFilePathFull.raw());
2394
2395 if (!mFilePathFull)
2396 return setError (E_ACCESSDENIED,
2397 tr ("Cannot create a hard disk image using an empty (null) file path"),
2398 mFilePathFull.raw());
2399
2400 /* first ensure the directory exists */
2401 {
2402 Utf8Str imageDir = mFilePathFull;
2403 RTPathStripFilename (imageDir.mutableRaw());
2404 if (!RTDirExists (imageDir))
2405 {
2406 int vrc = RTDirCreateFullPath (imageDir, 0777);
2407 if (VBOX_FAILURE (vrc))
2408 {
2409 return setError (E_FAIL,
2410 tr ("Could not create a directory '%s' "
2411 "to store the image file (%Vrc)"),
2412 imageDir.raw(), vrc);
2413 }
2414 }
2415 }
2416
2417 /* check whether the given file exists or not */
2418 RTFILE file;
2419 int vrc = RTFileOpen (&file, Utf8Str (mFilePathFull),
2420 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
2421 if (vrc != VERR_FILE_NOT_FOUND)
2422 {
2423 if (VBOX_SUCCESS (vrc))
2424 RTFileClose (file);
2425 switch(vrc)
2426 {
2427 case VINF_SUCCESS:
2428 return setError (E_FAIL,
2429 tr ("Image file '%ls' already exists"),
2430 mFilePathFull.raw());
2431
2432 default:
2433 return setError(E_FAIL,
2434 tr ("Invalid image file path '%ls' (%Vrc)"),
2435 mFilePathFull.raw(), vrc);
2436 }
2437 }
2438
2439 /* check VDI size limits */
2440 {
2441 HRESULT rc;
2442 ULONG64 maxVDISize;
2443 ComPtr <ISystemProperties> props;
2444 rc = mVirtualBox->COMGETTER(SystemProperties) (props.asOutParam());
2445 ComAssertComRCRet (rc, E_FAIL);
2446 rc = props->COMGETTER(MaxVDISize) (&maxVDISize);
2447 ComAssertComRCRet (rc, E_FAIL);
2448
2449 if (aSize < 1 || aSize > maxVDISize)
2450 return setError (E_INVALIDARG,
2451 tr ("Invalid VDI size: %llu MB (must be in range [1, %llu] MB)"),
2452 aSize, maxVDISize);
2453 }
2454
2455 HRESULT rc;
2456
2457 /* create a project object */
2458 ComObjPtr <Progress> progress;
2459 progress.createObject();
2460 {
2461 Bstr desc = aDynamic ? tr ("Creating a dynamically expanding hard disk")
2462 : tr ("Creating a fixed-size hard disk");
2463 rc = progress->init (mVirtualBox, (IVirtualDiskImage *) this, desc,
2464 FALSE /* aCancelable */);
2465 CheckComRCReturnRC (rc);
2466 }
2467
2468 /* mark as busy (being created)
2469 * (VDI task thread will unmark it) */
2470 setBusy();
2471
2472 /* fill in VDI task data */
2473 VDITask *task = new VDITask (aDynamic ? VDITask::CreateDynamic
2474 : VDITask::CreateStatic,
2475 this, progress);
2476 task->size = aSize;
2477 task->size *= 1024 * 1024; /* convert to bytes */
2478
2479 /* create the hard disk creation thread, pass operation data */
2480 vrc = RTThreadCreate (NULL, vdiTaskThread, (void *) task, 0,
2481 RTTHREADTYPE_MAIN_HEAVY_WORKER, 0, "VDITask");
2482 ComAssertMsgRC (vrc, ("Could not create a thread (%Vrc)", vrc));
2483 if (VBOX_FAILURE (vrc))
2484 {
2485 clearBusy();
2486 delete task;
2487 rc = E_FAIL;
2488 }
2489 else
2490 {
2491 /* get one interface for the caller */
2492 progress.queryInterfaceTo (aProgress);
2493 }
2494
2495 return rc;
2496}
2497
2498/* static */
2499DECLCALLBACK(int) HVirtualDiskImage::vdiTaskThread (RTTHREAD thread, void *pvUser)
2500{
2501 VDITask *task = static_cast <VDITask *> (pvUser);
2502 AssertReturn (task, VERR_GENERAL_FAILURE);
2503
2504 LogFlow (("vdiTaskThread(): operation=%d, size=%llu\n",
2505 task->operation, task->size));
2506
2507 VDIIMAGETYPE type = (VDIIMAGETYPE) 0;
2508
2509 switch (task->operation)
2510 {
2511 case VDITask::CreateDynamic: type = VDI_IMAGE_TYPE_NORMAL; break;
2512 case VDITask::CreateStatic: type = VDI_IMAGE_TYPE_FIXED; break;
2513 case VDITask::CloneToImage: break;
2514 default: AssertFailedReturn (VERR_GENERAL_FAILURE); break;
2515 }
2516
2517 HRESULT rc = S_OK;
2518 Utf8Str errorMsg;
2519
2520 if (task->operation == VDITask::CloneToImage)
2521 {
2522 Assert (!task->vdi->id().isEmpty());
2523 /// @todo (dmik) check locks
2524 AutoLock sourceLock (task->source);
2525 rc = task->source->cloneToImage (task->vdi->id(),
2526 Utf8Str (task->vdi->filePathFull()),
2527 task->progress);
2528
2529 /* release reader added in HardDisk::CloneToImage() */
2530 task->source->releaseReader();
2531 }
2532 else
2533 {
2534 int vrc = VDICreateBaseImage (Utf8Str (task->vdi->filePathFull()),
2535 type, task->size,
2536 Utf8Str (task->vdi->mDescription),
2537 progressCallback,
2538 static_cast <Progress *> (task->progress));
2539
2540 if (VBOX_SUCCESS (vrc) && task->vdi->id())
2541 {
2542 /* we have a non-null UUID, update the created image */
2543 vrc = VDISetImageUUIDs (Utf8Str (task->vdi->filePathFull()),
2544 task->vdi->id().raw(), NULL, NULL, NULL);
2545 }
2546
2547 if (VBOX_FAILURE (vrc))
2548 {
2549 errorMsg = Utf8StrFmt (
2550 tr ("Falied to create a hard disk image '%ls' (%Vrc)"),
2551 task->vdi->filePathFull().raw(), vrc);
2552 rc = E_FAIL;
2553 }
2554 }
2555
2556 LogFlow (("vdiTaskThread(): rc=%08X\n", rc));
2557
2558 AutoLock alock (task->vdi);
2559
2560 /* clear busy set in in HardDisk::CloneToImage() or
2561 * in HVirtualDiskImage::createImage() */
2562 task->vdi->clearBusy();
2563
2564 if (SUCCEEDED (rc))
2565 {
2566 task->vdi->mState = HVirtualDiskImage::Created;
2567 /* update VDI data fields */
2568 Bstr errMsg;
2569 rc = task->vdi->queryInformation (&errMsg);
2570 /* we want to deliver the access check result to the caller
2571 * immediately, before he calls HardDisk::GetAccssible() himself. */
2572 if (SUCCEEDED (rc) && !errMsg.isNull())
2573 task->progress->notifyCompleteBstr (
2574 E_FAIL, COM_IIDOF (IVirtualDiskImage), getComponentName(),
2575 errMsg);
2576 else
2577 task->progress->notifyComplete (rc);
2578 }
2579 else
2580 {
2581 /* delete the target file so we don't have orphaned files */
2582 RTFileDelete(Utf8Str (task->vdi->filePathFull()));
2583
2584 task->vdi->mState = HVirtualDiskImage::NotCreated;
2585 /* complete the progress object */
2586 if (errorMsg)
2587 task->progress->notifyComplete (
2588 E_FAIL, COM_IIDOF (IVirtualDiskImage), getComponentName(),
2589 errorMsg);
2590 else
2591 task->progress->notifyComplete (rc);
2592 }
2593
2594 delete task;
2595
2596 return VINF_SUCCESS;
2597}
2598
2599////////////////////////////////////////////////////////////////////////////////
2600// HISCSIHardDisk class
2601////////////////////////////////////////////////////////////////////////////////
2602
2603// constructor / destructor
2604////////////////////////////////////////////////////////////////////////////////
2605
2606HRESULT HISCSIHardDisk::FinalConstruct()
2607{
2608 HRESULT rc = HardDisk::FinalConstruct();
2609 if (FAILED (rc))
2610 return rc;
2611
2612 mSize = 0;
2613 mActualSize = 0;
2614
2615 mPort = 0;
2616 mLun = 0;
2617
2618 return S_OK;
2619}
2620
2621void HISCSIHardDisk::FinalRelease()
2622{
2623 HardDisk::FinalRelease();
2624}
2625
2626// public initializer/uninitializer for internal purposes only
2627////////////////////////////////////////////////////////////////////////////////
2628
2629// public methods for internal purposes only
2630/////////////////////////////////////////////////////////////////////////////
2631
2632/**
2633 * Initializes the iSCSI hard disk object by reading its properties from
2634 * the given configuration node. The created hard disk will be marked as
2635 * registered on success.
2636 *
2637 * @param aHDNode <HardDisk> node
2638 * @param aVDINod <ISCSIHardDisk> node
2639 */
2640HRESULT HISCSIHardDisk::init (VirtualBox *aVirtualBox,
2641 CFGNODE aHDNode, CFGNODE aISCSINode)
2642{
2643 LogFlowMember (("HISCSIHardDisk::init (load)\n"));
2644
2645 AssertReturn (aHDNode && aISCSINode, E_FAIL);
2646
2647 AutoLock alock (this);
2648 ComAssertRet (!isReady(), E_UNEXPECTED);
2649
2650 mStorageType = HardDiskStorageType_ISCSIHardDisk;
2651
2652 HRESULT rc = S_OK;
2653
2654 do
2655 {
2656 rc = protectedInit (aVirtualBox, NULL);
2657 CheckComRCBreakRC (rc);
2658
2659 /* set ready to let protectedUninit() be called on failure */
2660 setReady (true);
2661
2662 /* server (required) */
2663 CFGLDRQueryBSTR (aISCSINode, "server", mServer.asOutParam());
2664 /* target (required) */
2665 CFGLDRQueryBSTR (aISCSINode, "target", mTarget.asOutParam());
2666
2667 /* port (optional) */
2668 CFGLDRQueryUInt16 (aISCSINode, "port", &mPort);
2669 /* lun (optional) */
2670 CFGLDRQueryUInt64 (aISCSINode, "lun", &mLun);
2671 /* userName (optional) */
2672 CFGLDRQueryBSTR (aISCSINode, "userName", mUserName.asOutParam());
2673 /* password (optional) */
2674 CFGLDRQueryBSTR (aISCSINode, "password", mPassword.asOutParam());
2675
2676 LogFlowMember ((" 'iscsi:%ls:%hu@%ls/%ls:%llu'\n",
2677 mServer.raw(), mPort, mUserName.raw(), mTarget.raw(),
2678 mLun));
2679
2680 /* load basic settings and children */
2681 rc = loadSettings (aHDNode);
2682 CheckComRCBreakRC (rc);
2683
2684 if (mType != HardDiskType_WritethroughHardDisk)
2685 {
2686 rc = setError (E_FAIL,
2687 tr ("Currently, non-Writethrough iSCSI hard disks are not "
2688 "allowed ('%ls')"),
2689 toString().raw());
2690 break;
2691 }
2692
2693 mRegistered = TRUE;
2694 }
2695 while (0);
2696
2697 if (FAILED (rc))
2698 uninit();
2699
2700 return rc;
2701}
2702
2703/**
2704 * Initializes the iSCSI hard disk object using default values for all
2705 * properties. The created hard disk will NOT be marked as registered.
2706 */
2707HRESULT HISCSIHardDisk::init (VirtualBox *aVirtualBox)
2708{
2709 LogFlowMember (("HISCSIHardDisk::init()\n"));
2710
2711 AutoLock alock (this);
2712 ComAssertRet (!isReady(), E_UNEXPECTED);
2713
2714 mStorageType = HardDiskStorageType_ISCSIHardDisk;
2715
2716 HRESULT rc = S_OK;
2717
2718 do
2719 {
2720 rc = protectedInit (aVirtualBox, NULL);
2721 CheckComRCBreakRC (rc);
2722
2723 /* set ready to let protectedUninit() be called on failure */
2724 setReady (true);
2725
2726 /* we have to generate a new UUID */
2727 mId.create();
2728 mType = HardDiskType_WritethroughHardDisk;
2729 mRegistered = FALSE;
2730 }
2731 while (0);
2732
2733 if (FAILED (rc))
2734 uninit();
2735
2736 return rc;
2737}
2738
2739/**
2740 * Uninitializes the instance and sets the ready flag to FALSE.
2741 * Called either from FinalRelease(), by the parent when it gets destroyed,
2742 * or by a third party when it decides this object is no more valid.
2743 */
2744void HISCSIHardDisk::uninit()
2745{
2746 LogFlowMember (("HISCSIHardDisk::uninit()\n"));
2747
2748 AutoLock alock (this);
2749 if (!isReady())
2750 return;
2751
2752 HardDisk::protectedUninit (alock);
2753}
2754
2755// IHardDisk properties
2756////////////////////////////////////////////////////////////////////////////////
2757
2758STDMETHODIMP HISCSIHardDisk::COMGETTER(Description) (BSTR *aDescription)
2759{
2760 if (!aDescription)
2761 return E_POINTER;
2762
2763 AutoLock alock (this);
2764 CHECK_READY();
2765
2766 mDescription.cloneTo (aDescription);
2767 return S_OK;
2768}
2769
2770STDMETHODIMP HISCSIHardDisk::COMSETTER(Description) (INPTR BSTR aDescription)
2771{
2772 AutoLock alock (this);
2773 CHECK_READY();
2774
2775 CHECK_BUSY_AND_READERS();
2776
2777 if (mDescription != aDescription)
2778 {
2779 mDescription = aDescription;
2780 if (mRegistered)
2781 return mVirtualBox->saveSettings();
2782 }
2783
2784 return S_OK;
2785}
2786
2787STDMETHODIMP HISCSIHardDisk::COMGETTER(Size) (ULONG64 *aSize)
2788{
2789 if (!aSize)
2790 return E_POINTER;
2791
2792 AutoLock alock (this);
2793 CHECK_READY();
2794
2795 *aSize = mSize;
2796 return S_OK;
2797}
2798
2799STDMETHODIMP HISCSIHardDisk::COMGETTER(ActualSize) (ULONG64 *aActualSize)
2800{
2801 if (!aActualSize)
2802 return E_POINTER;
2803
2804 AutoLock alock (this);
2805 CHECK_READY();
2806
2807 *aActualSize = mActualSize;
2808 return S_OK;
2809}
2810
2811// IISCSIHardDisk properties
2812////////////////////////////////////////////////////////////////////////////////
2813
2814STDMETHODIMP HISCSIHardDisk::COMGETTER(Server) (BSTR *aServer)
2815{
2816 if (!aServer)
2817 return E_POINTER;
2818
2819 AutoLock alock (this);
2820 CHECK_READY();
2821
2822 mServer.cloneTo (aServer);
2823 return S_OK;
2824}
2825
2826STDMETHODIMP HISCSIHardDisk::COMSETTER(Server) (INPTR BSTR aServer)
2827{
2828 if (!aServer || !*aServer)
2829 return E_INVALIDARG;
2830
2831 AutoLock alock (this);
2832 CHECK_READY();
2833
2834 CHECK_BUSY_AND_READERS();
2835
2836 if (mServer != aServer)
2837 {
2838 mServer = aServer;
2839 if (mRegistered)
2840 return mVirtualBox->saveSettings();
2841 }
2842
2843 return S_OK;
2844}
2845
2846STDMETHODIMP HISCSIHardDisk::COMGETTER(Port) (USHORT *aPort)
2847{
2848 if (!aPort)
2849 return E_POINTER;
2850
2851 AutoLock alock (this);
2852 CHECK_READY();
2853
2854 *aPort = mPort;
2855 return S_OK;
2856}
2857
2858STDMETHODIMP HISCSIHardDisk::COMSETTER(Port) (USHORT aPort)
2859{
2860 AutoLock alock (this);
2861 CHECK_READY();
2862
2863 CHECK_BUSY_AND_READERS();
2864
2865 if (mPort != aPort)
2866 {
2867 mPort = aPort;
2868 if (mRegistered)
2869 return mVirtualBox->saveSettings();
2870 }
2871
2872 return S_OK;
2873}
2874
2875STDMETHODIMP HISCSIHardDisk::COMGETTER(Target) (BSTR *aTarget)
2876{
2877 if (!aTarget)
2878 return E_POINTER;
2879
2880 AutoLock alock (this);
2881 CHECK_READY();
2882
2883 mTarget.cloneTo (aTarget);
2884 return S_OK;
2885}
2886
2887STDMETHODIMP HISCSIHardDisk::COMSETTER(Target) (INPTR BSTR aTarget)
2888{
2889 if (!aTarget || !*aTarget)
2890 return E_INVALIDARG;
2891
2892 AutoLock alock (this);
2893 CHECK_READY();
2894
2895 CHECK_BUSY_AND_READERS();
2896
2897 if (mTarget != aTarget)
2898 {
2899 mTarget = aTarget;
2900 if (mRegistered)
2901 return mVirtualBox->saveSettings();
2902 }
2903
2904 return S_OK;
2905}
2906
2907STDMETHODIMP HISCSIHardDisk::COMGETTER(Lun) (ULONG64 *aLun)
2908{
2909 if (!aLun)
2910 return E_POINTER;
2911
2912 AutoLock alock (this);
2913 CHECK_READY();
2914
2915 *aLun = mLun;
2916 return S_OK;
2917}
2918
2919STDMETHODIMP HISCSIHardDisk::COMSETTER(Lun) (ULONG64 aLun)
2920{
2921 AutoLock alock (this);
2922 CHECK_READY();
2923
2924 CHECK_BUSY_AND_READERS();
2925
2926 if (mLun != aLun)
2927 {
2928 mLun = aLun;
2929 if (mRegistered)
2930 return mVirtualBox->saveSettings();
2931 }
2932
2933 return S_OK;
2934}
2935
2936STDMETHODIMP HISCSIHardDisk::COMGETTER(UserName) (BSTR *aUserName)
2937{
2938 if (!aUserName)
2939 return E_POINTER;
2940
2941 AutoLock alock (this);
2942 CHECK_READY();
2943
2944 mUserName.cloneTo (aUserName);
2945 return S_OK;
2946}
2947
2948STDMETHODIMP HISCSIHardDisk::COMSETTER(UserName) (INPTR BSTR aUserName)
2949{
2950 AutoLock alock (this);
2951 CHECK_READY();
2952
2953 CHECK_BUSY_AND_READERS();
2954
2955 if (mUserName != aUserName)
2956 {
2957 mUserName = aUserName;
2958 if (mRegistered)
2959 return mVirtualBox->saveSettings();
2960 }
2961
2962 return S_OK;
2963}
2964
2965STDMETHODIMP HISCSIHardDisk::COMGETTER(Password) (BSTR *aPassword)
2966{
2967 if (!aPassword)
2968 return E_POINTER;
2969
2970 AutoLock alock (this);
2971 CHECK_READY();
2972
2973 mPassword.cloneTo (aPassword);
2974 return S_OK;
2975}
2976
2977STDMETHODIMP HISCSIHardDisk::COMSETTER(Password) (INPTR BSTR aPassword)
2978{
2979 AutoLock alock (this);
2980 CHECK_READY();
2981
2982 CHECK_BUSY_AND_READERS();
2983
2984 if (mPassword != aPassword)
2985 {
2986 mPassword = aPassword;
2987 if (mRegistered)
2988 return mVirtualBox->saveSettings();
2989 }
2990
2991 return S_OK;
2992}
2993
2994// public/protected methods for internal purposes only
2995/////////////////////////////////////////////////////////////////////////////
2996
2997/**
2998 * Attempts to mark the hard disk as registered.
2999 * Only VirtualBox can call this method.
3000 */
3001HRESULT HISCSIHardDisk::trySetRegistered (BOOL aRegistered)
3002{
3003 AutoLock alock (this);
3004 CHECK_READY();
3005
3006 if (aRegistered)
3007 {
3008 if (mServer.isEmpty() || mTarget.isEmpty())
3009 return setError (E_FAIL,
3010 tr ("iSCSI Hard disk has no server or target defined"));
3011 }
3012 else
3013 {
3014 }
3015
3016 return HardDisk::trySetRegistered (aRegistered);
3017}
3018
3019/**
3020 * Checks accessibility of this iSCSI hard disk.
3021 */
3022HRESULT HISCSIHardDisk::getAccessible (Bstr &aAccessError)
3023{
3024 AutoLock alock (this);
3025 CHECK_READY();
3026
3027 /* check the basic accessibility */
3028 HRESULT rc = getBaseAccessible (aAccessError);
3029 if (FAILED (rc) || !aAccessError.isNull())
3030 return rc;
3031
3032 return queryInformation (aAccessError);
3033}
3034
3035/**
3036 * Saves hard disk settings to the specified storage node and saves
3037 * all children to the specified hard disk node
3038 *
3039 * @param aHDNode <HardDisk>
3040 * @param aStorageNode <ISCSIHardDisk> node
3041 */
3042HRESULT HISCSIHardDisk::saveSettings (CFGNODE aHDNode, CFGNODE aStorageNode)
3043{
3044 AssertReturn (aHDNode && aStorageNode, E_FAIL);
3045
3046 AutoLock alock (this);
3047 CHECK_READY();
3048
3049 /* server (required) */
3050 CFGLDRSetBSTR (aStorageNode, "server", mServer);
3051 /* target (required) */
3052 CFGLDRSetBSTR (aStorageNode, "target", mTarget);
3053
3054 /* port (optional) */
3055 if (mPort != 0)
3056 CFGLDRSetUInt16 (aStorageNode, "port", mPort);
3057 else
3058 CFGLDRDeleteAttribute (aStorageNode, "port");
3059 /* lun (optional) */
3060 if (mLun != 0)
3061 CFGLDRSetUInt64 (aStorageNode, "lun", mLun);
3062 else
3063 CFGLDRDeleteAttribute (aStorageNode, "lun");
3064 /* userName (optional) */
3065 if (!mUserName.isNull())
3066 CFGLDRSetBSTR (aStorageNode, "userName", mUserName);
3067 else
3068 CFGLDRDeleteAttribute (aStorageNode, "userName");
3069 /* password (optional) */
3070 if (!mPassword.isNull())
3071 CFGLDRSetBSTR (aStorageNode, "password", mPassword);
3072 else
3073 CFGLDRDeleteAttribute (aStorageNode, "password");
3074
3075 /* save basic settings and children */
3076 return HardDisk::saveSettings (aHDNode);
3077}
3078
3079/**
3080 * Returns the string representation of this hard disk.
3081 * When \a aShort is false, returns the full image file path.
3082 * Otherwise, returns the image file name only.
3083 *
3084 * @param aShort if true, a short representation is returned
3085 */
3086Bstr HISCSIHardDisk::toString (bool aShort /* = false */)
3087{
3088 AutoLock alock (this);
3089
3090 Bstr str;
3091 if (!aShort)
3092 {
3093 /* the format is iscsi:[<user@>]<server>[:<port>]/<target>[:<lun>] */
3094 str = Utf8StrFmt ("iscsi:%s%ls%s/%ls%s",
3095 !mUserName.isNull() ? Utf8StrFmt ("%ls@", mUserName.raw()).raw() : "",
3096 mServer.raw(),
3097 mPort != 0 ? Utf8StrFmt (":%hu", mPort).raw() : "",
3098 mTarget.raw(),
3099 mLun != 0 ? Utf8StrFmt (":%llu", mLun).raw() : "");
3100 }
3101 else
3102 {
3103 str = Utf8StrFmt ("%ls%s",
3104 mTarget.raw(),
3105 mLun != 0 ? Utf8StrFmt (":%llu", mLun).raw() : "");
3106 }
3107
3108 return str;
3109}
3110
3111/**
3112 * Creates a clone of this hard disk by storing hard disk data in the given
3113 * VDI file name.
3114 *
3115 * @param aId UUID to assign to the created image
3116 * @param aTargetPath VDI file where the cloned image is to be to stored
3117 * @param aProgress progress object to run during operation
3118 */
3119HRESULT
3120HISCSIHardDisk::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
3121 Progress *aProgress)
3122{
3123 ComAssertMsgFailed (("Not implemented"));
3124 return E_NOTIMPL;
3125
3126// AssertReturn (isBusy() == false, E_FAIL);
3127// addReader();
3128// releaseReader();
3129}
3130
3131/**
3132 * Creates a new differencing image for this hard disk with the given
3133 * VDI file name.
3134 *
3135 * @param aId UUID to assign to the created image
3136 * @param aTargetPath VDI file where to store the created differencing image
3137 * @param aProgress progress object to run during operation
3138 * (can be NULL)
3139 */
3140HRESULT
3141HISCSIHardDisk::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
3142 Progress *aProgress)
3143{
3144 ComAssertMsgFailed (("Not implemented"));
3145 return E_NOTIMPL;
3146
3147// AssertReturn (isBusy() == false, E_FAIL);
3148// addReader();
3149// releaseReader();
3150}
3151
3152// private methods
3153/////////////////////////////////////////////////////////////////////////////
3154
3155/**
3156 * Helper to query information about the iSCSI hard disk.
3157 *
3158 * @param aAccessError see #getAccessible()
3159 * @note
3160 * Must be called from under the object's lock!
3161 */
3162HRESULT HISCSIHardDisk::queryInformation (Bstr &aAccessError)
3163{
3164 /// @todo (dmik) query info about this iSCSI disk,
3165 // set mSize and mActualSize,
3166 // or set aAccessError in case of failure
3167
3168 aAccessError.setNull();
3169 return S_OK;
3170}
3171
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