VirtualBox

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

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

Main: Fixed GCC warning.

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