VirtualBox

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

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

Main: Fixed: HVirtualDiskImage::geAccessible() blocked all other methods, including simple getters [defect #1789].

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 86.0 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 if (aFilePath && *aFilePath)
1268 {
1269 mRegistered = aRegistered;
1270 mState = Created;
1271
1272 /* Call queryInformation() anyway (even if it will block), because
1273 * it is the only way to get the UUID of the existing VDI and
1274 * initialize the vital mId property. */
1275 rc = queryInformation (NULL);
1276 }
1277 else
1278 {
1279 mRegistered = FALSE;
1280 mState = NotCreated;
1281 mId.create();
1282 }
1283 }
1284 while (0);
1285
1286 if (FAILED (rc))
1287 uninit();
1288
1289 return rc;
1290}
1291
1292/**
1293 * Uninitializes the instance and sets the ready flag to FALSE.
1294 * Called either from FinalRelease(), by the parent when it gets destroyed,
1295 * or by a third party when it decides this object is no more valid.
1296 */
1297void HVirtualDiskImage::uninit()
1298{
1299 LogFlowMember (("HVirtualDiskImage::uninit()\n"));
1300
1301 AutoLock alock (this);
1302 if (!isReady())
1303 return;
1304
1305 HardDisk::protectedUninit (alock);
1306}
1307
1308// IHardDisk properties
1309////////////////////////////////////////////////////////////////////////////////
1310
1311STDMETHODIMP HVirtualDiskImage::COMGETTER(Description) (BSTR *aDescription)
1312{
1313 if (!aDescription)
1314 return E_POINTER;
1315
1316 AutoLock alock (this);
1317 CHECK_READY();
1318
1319 mDescription.cloneTo (aDescription);
1320 return S_OK;
1321}
1322
1323STDMETHODIMP HVirtualDiskImage::COMSETTER(Description) (INPTR BSTR aDescription)
1324{
1325 AutoLock alock (this);
1326 CHECK_READY();
1327
1328 CHECK_BUSY_AND_READERS();
1329
1330 if (mState >= Created)
1331 {
1332 int vrc = VDISetImageComment (Utf8Str (mFilePathFull), Utf8Str (aDescription));
1333 if (VBOX_FAILURE (vrc))
1334 return setError (E_FAIL,
1335 tr ("Could not change the description of the VDI hard disk '%ls' "
1336 "(%Vrc)"),
1337 toString().raw(), vrc);
1338 }
1339
1340 mDescription = aDescription;
1341 return S_OK;
1342}
1343
1344STDMETHODIMP HVirtualDiskImage::COMGETTER(Size) (ULONG64 *aSize)
1345{
1346 if (!aSize)
1347 return E_POINTER;
1348
1349 AutoLock alock (this);
1350 CHECK_READY();
1351
1352 /* only a non-differencing image knows the logical size */
1353 if (isDifferencing())
1354 return root()->COMGETTER(Size) (aSize);
1355
1356 *aSize = mSize;
1357 return S_OK;
1358}
1359
1360STDMETHODIMP HVirtualDiskImage::COMGETTER(ActualSize) (ULONG64 *aActualSize)
1361{
1362 if (!aActualSize)
1363 return E_POINTER;
1364
1365 AutoLock alock (this);
1366 CHECK_READY();
1367
1368 *aActualSize = mActualSize;
1369 return S_OK;
1370}
1371
1372// IVirtualDiskImage properties
1373////////////////////////////////////////////////////////////////////////////////
1374
1375STDMETHODIMP HVirtualDiskImage::COMGETTER(FilePath) (BSTR *aFilePath)
1376{
1377 if (!aFilePath)
1378 return E_POINTER;
1379
1380 AutoLock alock (this);
1381 CHECK_READY();
1382
1383 mFilePathFull.cloneTo (aFilePath);
1384 return S_OK;
1385}
1386
1387STDMETHODIMP HVirtualDiskImage::COMSETTER(FilePath) (INPTR BSTR aFilePath)
1388{
1389 AutoLock alock (this);
1390 CHECK_READY();
1391
1392 if (mState != NotCreated)
1393 return setError (E_ACCESSDENIED,
1394 tr ("Cannot change the file path of the existing VDI hard disk '%ls'"),
1395 toString().raw());
1396
1397 CHECK_BUSY_AND_READERS();
1398
1399 // append the default path if only a name is given
1400 Bstr path = aFilePath;
1401 if (aFilePath && *aFilePath)
1402 {
1403 Utf8Str fp = aFilePath;
1404 if (!RTPathHavePath (fp))
1405 {
1406 AutoReaderLock propsLock (mVirtualBox->systemProperties());
1407 path = Utf8StrFmt ("%ls%c%s",
1408 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
1409 RTPATH_DELIMITER,
1410 fp.raw());
1411 }
1412 }
1413
1414 return setFilePath (path);
1415}
1416
1417STDMETHODIMP HVirtualDiskImage::COMGETTER(Created) (BOOL *aCreated)
1418{
1419 if (!aCreated)
1420 return E_POINTER;
1421
1422 AutoLock alock (this);
1423 CHECK_READY();
1424
1425 *aCreated = mState >= Created;
1426 return S_OK;
1427}
1428
1429// IVirtualDiskImage methods
1430/////////////////////////////////////////////////////////////////////////////
1431
1432STDMETHODIMP HVirtualDiskImage::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
1433{
1434 if (!aProgress)
1435 return E_POINTER;
1436
1437 AutoLock alock (this);
1438 CHECK_READY();
1439
1440 return createImage (aSize, TRUE /* aDynamic */, aProgress);
1441}
1442
1443STDMETHODIMP HVirtualDiskImage::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
1444{
1445 if (!aProgress)
1446 return E_POINTER;
1447
1448 AutoLock alock (this);
1449 CHECK_READY();
1450
1451 return createImage (aSize, FALSE /* aDynamic */, aProgress);
1452}
1453
1454STDMETHODIMP HVirtualDiskImage::DeleteImage()
1455{
1456 AutoLock alock (this);
1457 CHECK_READY();
1458 CHECK_BUSY_AND_READERS();
1459
1460 if (mRegistered)
1461 return setError (E_ACCESSDENIED,
1462 tr ("Cannot delete an image of the registered hard disk image '%ls"),
1463 mFilePathFull.raw());
1464 if (mState == NotCreated)
1465 return setError (E_FAIL,
1466 tr ("Hard disk image has been already deleted or never created"));
1467
1468 int vrc = RTFileDelete (Utf8Str (mFilePathFull));
1469 if (VBOX_FAILURE (vrc))
1470 return setError (E_FAIL, tr ("Could not delete the image file '%ls' (%Vrc)"),
1471 mFilePathFull.raw(), vrc);
1472
1473 mState = NotCreated;
1474
1475 // reset the fields
1476 mSize = 0;
1477 mActualSize = 0;
1478
1479 return S_OK;
1480}
1481
1482// public/protected methods for internal purposes only
1483/////////////////////////////////////////////////////////////////////////////
1484
1485/**
1486 * Attempts to mark the hard disk as registered.
1487 * Only VirtualBox can call this method.
1488 */
1489HRESULT HVirtualDiskImage::trySetRegistered (BOOL aRegistered)
1490{
1491 AutoLock alock (this);
1492 CHECK_READY();
1493
1494 if (aRegistered)
1495 {
1496 if (mState == NotCreated)
1497 return setError (E_FAIL,
1498 tr ("Image file '%ls' is not yet created for this hard disk"),
1499 mFilePathFull.raw());
1500 if (isDifferencing())
1501 return setError (E_FAIL,
1502 tr ("Hard disk '%ls' is differencing and cannot be unregistered "
1503 "explicitly"),
1504 mFilePathFull.raw());
1505 }
1506 else
1507 {
1508 ComAssertRet (mState >= Created, E_FAIL);
1509 }
1510
1511 return HardDisk::trySetRegistered (aRegistered);
1512}
1513
1514/**
1515 * Checks accessibility of this hard disk image only (w/o parents).
1516 *
1517 * @param aAccessError on output, a null string indicates the hard disk is
1518 * accessible, otherwise contains a message describing
1519 * the reason of inaccessibility.
1520 */
1521HRESULT HVirtualDiskImage::getAccessible (Bstr &aAccessError)
1522{
1523 AutoLock alock (this);
1524 CHECK_READY();
1525
1526 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
1527 {
1528 /* An accessibility check in progress on some other thread,
1529 * wait for it to finish. */
1530
1531 ComAssertRet (mStateCheckWaiters != ~0, E_FAIL);
1532 ++ mStateCheckWaiters;
1533 alock.leave();
1534
1535 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
1536
1537 alock.enter();
1538 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
1539 -- mStateCheckWaiters;
1540 if (mStateCheckWaiters == 0)
1541 {
1542 RTSemEventMultiDestroy (mStateCheckSem);
1543 mStateCheckSem = NIL_RTSEMEVENTMULTI;
1544 }
1545
1546 /* don't touch aAccessError, it has been already set */
1547 return S_OK;
1548 }
1549
1550 /* check the basic accessibility */
1551 HRESULT rc = HardDisk::getAccessible (aAccessError);
1552 if (FAILED (rc) || !aAccessError.isNull())
1553 return rc;
1554
1555 if (mState >= Created)
1556 {
1557 return queryInformation (&aAccessError);
1558 /* if we fail here, this means something like UUID mismatch.
1559 * Do nothing, just return the failure (error info is already
1560 * set by queryInformation()), in hope that one of subsequent
1561 * attempts to check for acessibility will succeed */
1562 }
1563
1564 aAccessError = Utf8StrFmt ("Hard disk image '%ls' is not yet created",
1565 mFilePathFull.raw());
1566 return S_OK;
1567}
1568
1569/**
1570 * Saves hard disk settings to the specified storage node and saves
1571 * all children to the specified hard disk node
1572 *
1573 * @param aHDNode <HardDisk> or <DiffHardDisk> node
1574 * @param aStorageNode <VirtualDiskImage> node
1575 */
1576HRESULT HVirtualDiskImage::saveSettings (CFGNODE aHDNode, CFGNODE aStorageNode)
1577{
1578 AssertReturn (aHDNode && aStorageNode, E_FAIL);
1579
1580 AutoLock alock (this);
1581 CHECK_READY();
1582
1583 // filePath (required)
1584 CFGLDRSetBSTR (aStorageNode, "filePath", mFilePath);
1585
1586 // save basic settings and children
1587 return HardDisk::saveSettings (aHDNode);
1588}
1589
1590/**
1591 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
1592 * of this hard disk and updates it if necessary to reflect the new location.
1593 * Intended to be from HardDisk::updatePaths().
1594 *
1595 * @param aOldPath old path (full)
1596 * @param aNewPath new path (full)
1597 *
1598 * @note Locks this object for writing.
1599 */
1600void HVirtualDiskImage::updatePath (const char *aOldPath, const char *aNewPath)
1601{
1602 AssertReturnVoid (aOldPath);
1603 AssertReturnVoid (aNewPath);
1604
1605 AutoLock alock (this);
1606 AssertReturnVoid (isReady());
1607
1608 size_t oldPathLen = strlen (aOldPath);
1609
1610 Utf8Str path = mFilePathFull;
1611 LogFlowThisFunc (("VDI.fullPath={%s}\n", path.raw()));
1612
1613 if (RTPathStartsWith (path, aOldPath))
1614 {
1615 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
1616 path.raw() + oldPathLen);
1617 path = newPath;
1618
1619 mVirtualBox->calculateRelativePath (path, path);
1620
1621 unconst (mFilePathFull) = newPath;
1622 unconst (mFilePath) = path;
1623
1624 LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
1625 newPath.raw(), path.raw()));
1626 }
1627}
1628
1629/**
1630 * Returns the string representation of this hard disk.
1631 * When \a aShort is false, returns the full image file path.
1632 * Otherwise, returns the image file name only.
1633 *
1634 * @param aShort if true, a short representation is returned
1635 */
1636Bstr HVirtualDiskImage::toString (bool aShort /* = false */)
1637{
1638 AutoLock alock (this);
1639
1640 if (!aShort)
1641 return mFilePathFull;
1642 else
1643 {
1644 Utf8Str fname = mFilePathFull;
1645 return RTPathFilename (fname.mutableRaw());
1646 }
1647}
1648
1649/**
1650 * Creates a clone of this hard disk by storing hard disk data in the given
1651 * VDI file name.
1652 *
1653 * @param aId UUID to assign to the created image
1654 * @param aTargetPath VDI file where the cloned image is to be to stored
1655 * @param aProgress progress object to run during operation
1656 */
1657HRESULT
1658HVirtualDiskImage::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
1659 Progress *aProgress)
1660{
1661 AssertReturn (!aId.isEmpty(), E_FAIL);
1662 AssertReturn (!aTargetPath.isNull(), E_FAIL);
1663 AssertReturn (aProgress, E_FAIL);
1664
1665 AutoLock alock (this);
1666 AssertReturn (isReady(), E_FAIL);
1667
1668 AssertReturn (isBusy() == false, E_FAIL);
1669
1670 /// @todo (dmik) cloning of differencing images is not yet supported
1671 AssertReturn (mParent.isNull(), E_FAIL);
1672
1673 Utf8Str filePathFull = mFilePathFull;
1674
1675 if (mState == NotCreated)
1676 return setError (E_FAIL,
1677 tr ("Source hard disk image '%s' is not yet created"),
1678 filePathFull.raw());
1679
1680 addReader();
1681 alock.leave();
1682
1683 int vrc = VDICopyImage (aTargetPath, filePathFull, NULL,
1684 progressCallback,
1685 static_cast <Progress *> (aProgress));
1686
1687 alock.enter();
1688 releaseReader();
1689
1690 if (VBOX_SUCCESS (vrc))
1691 vrc = VDISetImageUUIDs (aTargetPath, aId, NULL, NULL, NULL);
1692
1693 if (VBOX_FAILURE (vrc))
1694 return setError (E_FAIL,
1695 tr ("Could not copy the hard disk image '%s' to '%s' (%Vrc)"),
1696 filePathFull.raw(), aTargetPath.raw(), vrc);
1697
1698 return S_OK;
1699}
1700
1701/**
1702 * Creates a new differencing image for this hard disk with the given
1703 * VDI file name.
1704 *
1705 * @param aId UUID to assign to the created image
1706 * @param aTargetPath VDI file where to store the created differencing image
1707 * @param aProgress progress object to run during operation
1708 * (can be NULL)
1709 */
1710HRESULT
1711HVirtualDiskImage::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
1712 Progress *aProgress)
1713{
1714 AssertReturn (!aId.isEmpty(), E_FAIL);
1715 AssertReturn (!aTargetPath.isNull(), E_FAIL);
1716
1717 AutoLock alock (this);
1718 AssertReturn (isReady(), E_FAIL);
1719
1720 AssertReturn (isBusy() == false, E_FAIL);
1721 AssertReturn (mState >= Created, E_FAIL);
1722
1723 addReader();
1724 alock.leave();
1725
1726 int vrc = VDICreateDifferenceImage (aTargetPath, Utf8Str (mFilePathFull),
1727 NULL, aProgress ? progressCallback : NULL,
1728 static_cast <Progress *> (aProgress));
1729 alock.enter();
1730 releaseReader();
1731
1732 /* update the UUID to correspond to the file name */
1733 if (VBOX_SUCCESS (vrc))
1734 vrc = VDISetImageUUIDs (aTargetPath, aId, NULL, NULL, NULL);
1735
1736 if (VBOX_FAILURE (vrc))
1737 return setError (E_FAIL,
1738 tr ("Could not create a differencing hard disk '%s' (%Vrc)"),
1739 aTargetPath.raw(), vrc);
1740
1741 return S_OK;
1742}
1743
1744/**
1745 * Copies the image file of this hard disk to a separate VDI file (with an
1746 * unique creation UUID) and creates a new hard disk object for the copied
1747 * image. The copy will be created as a child of this hard disk's parent
1748 * (so that this hard disk must be a differencing one).
1749 *
1750 * The specified progress object (if not NULL) receives the percentage
1751 * of the operation completion. However, it is responsibility of the caller to
1752 * call Progress::notifyComplete() after this method returns.
1753 *
1754 * @param aFolder folder where to create a copy (must be a full path)
1755 * @param aMachineId machine ID the new hard disk will belong to
1756 * @param aHardDisk resulting hard disk object
1757 * @param aProgress progress object to run during copy operation
1758 * (may be NULL)
1759 *
1760 * @note
1761 * Must be NOT called from under locks of other objects that need external
1762 * access dirung this method execurion!
1763 */
1764HRESULT
1765HVirtualDiskImage::cloneDiffImage (const Bstr &aFolder, const Guid &aMachineId,
1766 ComObjPtr <HVirtualDiskImage> &aHardDisk,
1767 Progress *aProgress)
1768{
1769 AssertReturn (!aFolder.isEmpty() && !aMachineId.isEmpty(),
1770 E_FAIL);
1771
1772 AutoLock alock (this);
1773 CHECK_READY();
1774
1775 AssertReturn (!mParent.isNull(), E_FAIL);
1776
1777 ComAssertRet (isBusy() == false, E_FAIL);
1778 ComAssertRet (mState >= Created, E_FAIL);
1779
1780 Guid id;
1781 id.create();
1782
1783 Utf8Str filePathTo = Utf8StrFmt ("%ls%c{%Vuuid}.vdi",
1784 aFolder.raw(), RTPATH_DELIMITER, id.ptr());
1785
1786 /* try to make the path relative to the vbox home dir */
1787 const char *filePathToRel = filePathTo;
1788 {
1789 const Utf8Str &homeDir = mVirtualBox->homeDir();
1790 if (!strncmp (filePathTo, homeDir, homeDir.length()))
1791 filePathToRel = (filePathToRel + homeDir.length() + 1);
1792 }
1793
1794 /* first ensure the directory exists */
1795 {
1796 Utf8Str dir = aFolder;
1797 if (!RTDirExists (dir))
1798 {
1799 int vrc = RTDirCreateFullPath (dir, 0777);
1800 if (VBOX_FAILURE (vrc))
1801 {
1802 return setError (E_FAIL,
1803 tr ("Could not create a directory '%s' "
1804 "to store the image file (%Vrc)"),
1805 dir.raw(), vrc);
1806 }
1807 }
1808 }
1809
1810 Utf8Str filePathFull = mFilePathFull;
1811
1812 alock.leave();
1813
1814 int vrc = VDICopyImage (filePathTo, filePathFull, NULL,
1815 progressCallback,
1816 static_cast <Progress *> (aProgress));
1817
1818 alock.enter();
1819
1820 /* get modification and parent UUIDs of this image */
1821 RTUUID modUuid, parentUuid, parentModUuid;
1822 if (VBOX_SUCCESS (vrc))
1823 vrc = VDIGetImageUUIDs (filePathFull, NULL, &modUuid,
1824 &parentUuid, &parentModUuid);
1825
1826 // update the UUID of the copy to correspond to the file name
1827 // and copy all other UUIDs from this image
1828 if (VBOX_SUCCESS (vrc))
1829 vrc = VDISetImageUUIDs (filePathTo, id, &modUuid,
1830 &parentUuid, &parentModUuid);
1831
1832 if (VBOX_FAILURE (vrc))
1833 return setError (E_FAIL,
1834 tr ("Could not copy the hard disk image '%s' to '%s' (%Vrc)"),
1835 filePathFull.raw(), filePathTo.raw(), vrc);
1836
1837 ComObjPtr <HVirtualDiskImage> vdi;
1838 vdi.createObject();
1839 HRESULT rc = vdi->init (mVirtualBox, mParent, Bstr (filePathToRel),
1840 TRUE /* aRegistered */);
1841 if (FAILED (rc))
1842 return rc;
1843
1844 /* associate the created hard disk with the given machine */
1845 vdi->setMachineId (aMachineId);
1846
1847 rc = mVirtualBox->registerHardDisk (vdi, VirtualBox::RHD_Internal);
1848 if (FAILED (rc))
1849 return rc;
1850
1851 aHardDisk = vdi;
1852
1853 return S_OK;
1854}
1855
1856/**
1857 * Merges this child image to its parent image and updates the parent UUID
1858 * of all children of this image (to point to this image's parent).
1859 * It's a responsibility of the caller to unregister and uninitialize
1860 * the merged image on success.
1861 *
1862 * This method is intended to be called on a worker thread (the operation
1863 * can be time consuming).
1864 *
1865 * The specified progress object (if not NULL) receives the percentage
1866 * of the operation completion. However, it is responsibility of the caller to
1867 * call Progress::notifyComplete() after this method returns.
1868 *
1869 * @param aProgress progress object to run during copy operation
1870 * (may be NULL)
1871 *
1872 * @note
1873 * This method expects that both this hard disk and the paret hard disk
1874 * are marked as busy using #setBusyWithChildren() prior to calling it!
1875 * Busy flags of both hard disks will be cleared by this method
1876 * on a successful return. In case of failure, #clearBusyWithChildren()
1877 * must be called on a parent.
1878 *
1879 * @note
1880 * Must be NOT called from under locks of other objects that need external
1881 * access dirung this method execurion!
1882 */
1883HRESULT HVirtualDiskImage::mergeImageToParent (Progress *aProgress)
1884{
1885 LogFlowMember (("HVirtualDiskImage::mergeImageToParent(): image='%ls'\n",
1886 mFilePathFull.raw()));
1887
1888 AutoLock alock (this);
1889 CHECK_READY();
1890
1891 AssertReturn (!mParent.isNull(), E_FAIL);
1892 AutoLock parentLock (mParent);
1893
1894 ComAssertRet (isBusy() == true, E_FAIL);
1895 ComAssertRet (mParent->isBusy() == true, E_FAIL);
1896
1897 ComAssertRet (mState >= Created && mParent->asVDI()->mState >= Created, E_FAIL);
1898
1899 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
1900 ("non VDI storage types are not yet supported!"), E_FAIL);
1901
1902 parentLock.leave();
1903 alock.leave();
1904
1905 int vrc = VDIMergeImage (Utf8Str (mFilePathFull),
1906 Utf8Str (mParent->asVDI()->mFilePathFull),
1907 progressCallback,
1908 static_cast <Progress *> (aProgress));
1909 alock.enter();
1910 parentLock.enter();
1911
1912 if (VBOX_FAILURE (vrc))
1913 return setError (E_FAIL,
1914 tr ("Could not merge the hard disk image '%ls' to "
1915 "its parent image '%ls' (%Vrc)"),
1916 mFilePathFull.raw(), mParent->asVDI()->mFilePathFull.raw(), vrc);
1917
1918 {
1919 HRESULT rc = S_OK;
1920
1921 AutoLock chLock (childrenLock());
1922
1923 for (HardDiskList::const_iterator it = children().begin();
1924 it != children().end(); ++ it)
1925 {
1926 ComObjPtr <HVirtualDiskImage> child = (*it)->asVDI();
1927 AutoLock childLock (child);
1928
1929 /* reparent the child */
1930 child->mParent = mParent;
1931 if (mParent)
1932 mParent->addDependentChild (child);
1933
1934 /* change the parent UUID in the image as well */
1935 RTUUID parentUuid, parentModUuid;
1936 vrc = VDIGetImageUUIDs (Utf8Str (mParent->asVDI()->mFilePathFull),
1937 &parentUuid, &parentModUuid, NULL, NULL);
1938 if (VBOX_FAILURE (vrc))
1939 {
1940 rc = setError (E_FAIL,
1941 tr ("Could not access the hard disk image '%ls' (%Vrc)"),
1942 mParent->asVDI()->mFilePathFull.raw(), vrc);
1943 break;
1944 }
1945 ComAssertBreak (mParent->id() == Guid (parentUuid), rc = E_FAIL);
1946 vrc = VDISetImageUUIDs (Utf8Str (child->mFilePathFull),
1947 NULL, NULL, &parentUuid, &parentModUuid);
1948 if (VBOX_FAILURE (vrc))
1949 {
1950 rc = setError (E_FAIL,
1951 tr ("Could not update parent UUID of the hard disk image "
1952 "'%ls' (%Vrc)"),
1953 child->mFilePathFull.raw(), vrc);
1954 break;
1955 }
1956 }
1957
1958 if (FAILED (rc))
1959 return rc;
1960 }
1961
1962 /* detach all our children to avoid their uninit in #uninit() */
1963 removeDependentChildren();
1964
1965 mParent->clearBusy();
1966 clearBusy();
1967
1968 return S_OK;
1969}
1970
1971/**
1972 * Merges this image to all its child images, updates the parent UUID
1973 * of all children of this image (to point to this image's parent).
1974 * It's a responsibility of the caller to unregister and uninitialize
1975 * the merged image on success.
1976 *
1977 * This method is intended to be called on a worker thread (the operation
1978 * can be time consuming).
1979 *
1980 * The specified progress object (if not NULL) receives the percentage
1981 * of the operation completion. However, it is responsibility of the caller to
1982 * call Progress::notifyComplete() after this method returns.
1983 *
1984 * @param aProgress progress object to run during copy operation
1985 * (may be NULL)
1986 *
1987 * @note
1988 * This method expects that both this hard disk and all children
1989 * are marked as busy using setBusyWithChildren() prior to calling it!
1990 * Busy flags of all affected hard disks will be cleared by this method
1991 * on a successful return. In case of failure, #clearBusyWithChildren()
1992 * must be called for this hard disk.
1993 *
1994 * @note
1995 * Must be NOT called from under locks of other objects that need external
1996 * access dirung this method execurion!
1997 */
1998HRESULT HVirtualDiskImage::mergeImageToChildren (Progress *aProgress)
1999{
2000 LogFlowMember (("HVirtualDiskImage::mergeImageToChildren(): image='%ls'\n",
2001 mFilePathFull.raw()));
2002
2003 AutoLock alock (this);
2004 CHECK_READY();
2005
2006 /* this must be a diff image */
2007 AssertReturn (isDifferencing(), E_FAIL);
2008
2009 ComAssertRet (isBusy() == true, E_FAIL);
2010 ComAssertRet (mState >= Created, E_FAIL);
2011
2012 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2013 ("non VDI storage types are not yet supported!"), E_FAIL);
2014
2015 {
2016 HRESULT rc = S_OK;
2017
2018 AutoLock chLock (childrenLock());
2019
2020 /* iterate over a copy since we will modify the list */
2021 HardDiskList list = children();
2022
2023 for (HardDiskList::const_iterator it = list.begin();
2024 it != list.end(); ++ it)
2025 {
2026 ComObjPtr <HardDisk> hd = *it;
2027 ComObjPtr <HVirtualDiskImage> child = hd->asVDI();
2028 AutoLock childLock (child);
2029
2030 ComAssertRet (child->isBusy() == true, E_FAIL);
2031 ComAssertBreak (child->mState >= Created, rc = E_FAIL);
2032
2033 childLock.leave();
2034 alock.leave();
2035
2036 int vrc = VDIMergeImage (Utf8Str (mFilePathFull),
2037 Utf8Str (child->mFilePathFull),
2038 progressCallback,
2039 static_cast <Progress *> (aProgress));
2040 alock.enter();
2041 childLock.enter();
2042
2043 if (VBOX_FAILURE (vrc))
2044 {
2045 rc = setError (E_FAIL,
2046 tr ("Could not merge the hard disk image '%ls' to "
2047 "its parent image '%ls' (%Vrc)"),
2048 mFilePathFull.raw(), child->mFilePathFull.raw(), vrc);
2049 break;
2050 }
2051
2052 /* reparent the child */
2053 child->mParent = mParent;
2054 if (mParent)
2055 mParent->addDependentChild (child);
2056
2057 /* change the parent UUID in the image as well */
2058 RTUUID parentUuid, parentModUuid;
2059 vrc = VDIGetImageUUIDs (Utf8Str (mParent->asVDI()->mFilePathFull),
2060 &parentUuid, &parentModUuid, NULL, NULL);
2061 if (VBOX_FAILURE (vrc))
2062 {
2063 rc = setError (E_FAIL,
2064 tr ("Could not access the hard disk image '%ls' (%Vrc)"),
2065 mParent->asVDI()->mFilePathFull.raw(), vrc);
2066 break;
2067 }
2068 ComAssertBreak (mParent->id() == Guid (parentUuid), rc = E_FAIL);
2069 vrc = VDISetImageUUIDs (Utf8Str (child->mFilePathFull),
2070 NULL, NULL, &parentUuid, &parentModUuid);
2071 if (VBOX_FAILURE (vrc))
2072 {
2073 rc = setError (E_FAIL,
2074 tr ("Could not update parent UUID of the hard disk image "
2075 "'%ls' (%Vrc)"),
2076 child->mFilePathFull.raw(), vrc);
2077 break;
2078 }
2079
2080 /* detach child to avoid its uninit in #uninit() */
2081 removeDependentChild (child);
2082
2083 /* remove the busy flag */
2084 child->clearBusy();
2085 }
2086
2087 if (FAILED (rc))
2088 return rc;
2089 }
2090
2091 clearBusy();
2092
2093 return S_OK;
2094}
2095
2096/**
2097 * Deletes and recreates the differencing hard disk image from scratch.
2098 * The file name and UUID remain the same.
2099 */
2100HRESULT HVirtualDiskImage::wipeOutImage()
2101{
2102 AutoLock alock (this);
2103 CHECK_READY();
2104
2105 AssertReturn (isDifferencing(), E_FAIL);
2106 AssertReturn (mRegistered, E_FAIL);
2107 AssertReturn (mState >= Created, E_FAIL);
2108
2109 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2110 ("non-VDI storage types are not yet supported!"), E_FAIL);
2111
2112 Utf8Str filePathFull = mFilePathFull;
2113
2114 int vrc = RTFileDelete (filePathFull);
2115 if (VBOX_FAILURE (vrc))
2116 return setError (E_FAIL,
2117 tr ("Could not delete the image file '%s' (%Vrc)"),
2118 filePathFull.raw(), vrc);
2119
2120 vrc = VDICreateDifferenceImage (filePathFull,
2121 Utf8Str (mParent->asVDI()->mFilePathFull),
2122 NULL, NULL, NULL);
2123 /* update the UUID to correspond to the file name */
2124 if (VBOX_SUCCESS (vrc))
2125 vrc = VDISetImageUUIDs (filePathFull, mId, NULL, NULL, NULL);
2126
2127 if (VBOX_FAILURE (vrc))
2128 return setError (E_FAIL,
2129 tr ("Could not create a differencing hard disk '%s' (%Vrc)"),
2130 filePathFull.raw(), vrc);
2131
2132 return S_OK;
2133}
2134
2135// private methods
2136/////////////////////////////////////////////////////////////////////////////
2137
2138/**
2139 * Helper to set a new file path.
2140 * Resolves a path relatively to the Virtual Box home directory.
2141 *
2142 * @note
2143 * Must be called from under the object's lock!
2144 */
2145HRESULT HVirtualDiskImage::setFilePath (const BSTR aFilePath)
2146{
2147 if (aFilePath && *aFilePath)
2148 {
2149 /* get the full file name */
2150 char filePathFull [RTPATH_MAX];
2151 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aFilePath),
2152 filePathFull, sizeof (filePathFull));
2153 if (VBOX_FAILURE (vrc))
2154 return setError (E_FAIL,
2155 tr ("Invalid image file path '%ls' (%Vrc)"), aFilePath, vrc);
2156
2157 mFilePath = aFilePath;
2158 mFilePathFull = filePathFull;
2159 }
2160 else
2161 {
2162 mFilePath.setNull();
2163 mFilePathFull.setNull();
2164 }
2165
2166 return S_OK;
2167}
2168
2169/**
2170 * Helper to query information about the VDI hard disk.
2171 *
2172 * @param aAccessError not used when NULL, otherwise see #getAccessible()
2173 *
2174 * @note Must be called from under the object's lock, only after
2175 * CHECK_BUSY_AND_READERS() succeeds.
2176 */
2177HRESULT HVirtualDiskImage::queryInformation (Bstr *aAccessError)
2178{
2179 AssertReturn (isLockedOnCurrentThread(), E_FAIL);
2180
2181 /* create a lock object to completely release it later */
2182 AutoLock alock (this);
2183
2184 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
2185
2186 ComAssertRet (mState >= Created, E_FAIL);
2187
2188 HRESULT rc = S_OK;
2189 int vrc = VINF_SUCCESS;
2190
2191 /* lazily create a semaphore */
2192 vrc = RTSemEventMultiCreate (&mStateCheckSem);
2193 ComAssertRCRet (vrc, E_FAIL);
2194
2195 /* go to Busy state to prevent any concurrent modifications
2196 * after releasing the lock below (to unblock getters before
2197 * a lengthy operation) */
2198 setBusy();
2199
2200 alock.leave();
2201
2202 /* VBoxVHDD management interface needs to be optimized: we're opening a
2203 * file three times in a raw to get three bits of information. */
2204
2205 Utf8Str filePath = mFilePathFull;
2206
2207 do
2208 {
2209 /* check the image file */
2210 Guid id, parentId;
2211 vrc = VDICheckImage (filePath, NULL, NULL, NULL,
2212 id.ptr(), parentId.ptr(), NULL, 0);
2213
2214 if (VBOX_FAILURE (vrc))
2215 {
2216 /* mId is empty only when constructing a HVirtualDiskImage object
2217 * from an existing file image which UUID is not known. If we can't
2218 * read it, we have to fail. */
2219 if (mId.isEmpty())
2220 rc = setError (E_FAIL,
2221 tr ("Could not open the hard disk image '%s' (%Vrc)"),
2222 filePath.raw(), vrc);
2223 break;
2224 }
2225
2226 if (!mId.isEmpty())
2227 {
2228 /* check that the actual UUID of the image matches the stored UUID */
2229 if (mId != id)
2230 {
2231 rc = setError (E_FAIL,
2232 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
2233 "match UUID {%Vuuid} stored in the registry"),
2234 id.ptr(), filePath.raw(), mId.ptr());
2235 break;
2236 }
2237 }
2238 else
2239 {
2240 /* assgn an UUID read from the image file */
2241 mId = id;
2242 }
2243
2244 if (mParent)
2245 {
2246 /* check parent UUID */
2247 AutoLock parentLock (mParent);
2248 if (mParent->id() != parentId)
2249 {
2250 rc = setError (E_FAIL,
2251 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
2252 "the hard disk image file '%s' doesn't match "
2253 "UUID {%Vuuid} stored in the registry"),
2254 parentId.ptr(), mParent->toString().raw(),
2255 filePath.raw(), mParent->id().raw());
2256 break;
2257 }
2258 }
2259 else if (!parentId.isEmpty())
2260 {
2261 rc = setError (E_FAIL,
2262 tr ("Hard disk image '%s' is a differencing image and "
2263 "cannot be opened directly"),
2264 filePath.raw());
2265 break;
2266 }
2267
2268 {
2269 RTFILE file = NIL_RTFILE;
2270 vrc = RTFileOpen (&file, filePath, RTFILE_O_READ);
2271 if (VBOX_SUCCESS (vrc))
2272 {
2273 uint64_t size = 0;
2274 vrc = RTFileGetSize (file, &size);
2275 if (VBOX_SUCCESS (vrc))
2276 mActualSize = size;
2277 RTFileClose (file);
2278 }
2279 if (VBOX_FAILURE (vrc))
2280 break;
2281 }
2282
2283 if (!mParent)
2284 {
2285 /* query logical size only for non-differencing images */
2286
2287 PVDIDISK disk = VDIDiskCreate();
2288 vrc = VDIDiskOpenImage (disk, Utf8Str (mFilePathFull),
2289 VDI_OPEN_FLAGS_READONLY);
2290 if (VBOX_SUCCESS (vrc))
2291 {
2292 uint64_t size = VDIDiskGetSize (disk);
2293 /* convert to MBytes */
2294 mSize = size / 1024 / 1024;
2295 }
2296
2297 VDIDiskDestroy (disk);
2298 if (VBOX_FAILURE (vrc))
2299 break;
2300 }
2301 }
2302 while (0);
2303
2304 /* enter the lock again */
2305 alock.enter();
2306
2307 /* remove the busy flag */
2308 clearBusy();
2309
2310 if (VBOX_FAILURE (vrc) || FAILED (rc))
2311 {
2312 Log (("HVirtualDiskImage::queryInformation(): "
2313 "WARNING: '%ls' is not accessible (%Vrc) (rc=%08X)\n",
2314 mFilePathFull.raw(), vrc, rc));
2315
2316 if (VBOX_FAILURE (vrc) && aAccessError)
2317 *aAccessError =
2318 Utf8StrFmt ("Error accessing hard disk image '%ls' (%Vrc)",
2319 mFilePathFull.raw(), vrc);
2320
2321 /* downgrade to not accessible */
2322 mState = Created;
2323 }
2324 else
2325 {
2326 if (aAccessError)
2327 aAccessError->setNull();
2328
2329 mState = Accessible;
2330 }
2331
2332 /* inform waiters if there are any */
2333 if (mStateCheckWaiters > 0)
2334 {
2335 RTSemEventMultiSignal (mStateCheckSem);
2336 }
2337 else
2338 {
2339 /* delete the semaphore ourselves */
2340 RTSemEventMultiDestroy (mStateCheckSem);
2341 mStateCheckSem = NIL_RTSEMEVENTMULTI;
2342 }
2343
2344 return rc;
2345}
2346
2347/**
2348 * Helper to create hard disk images.
2349 *
2350 * @param aSize size in MB
2351 * @param aDynamic dynamic or fixed image
2352 * @param aProgress address of IProgress pointer to return
2353 */
2354HRESULT HVirtualDiskImage::createImage (ULONG64 aSize, BOOL aDynamic,
2355 IProgress **aProgress)
2356{
2357 AutoLock alock (this);
2358
2359 CHECK_BUSY_AND_READERS();
2360
2361 if (mState != NotCreated)
2362 return setError (E_ACCESSDENIED,
2363 tr ("Hard disk image '%ls' is already created"),
2364 mFilePathFull.raw());
2365
2366 if (!mFilePathFull)
2367 return setError (E_ACCESSDENIED,
2368 tr ("Cannot create a hard disk image using an empty (null) file path"),
2369 mFilePathFull.raw());
2370
2371 /* first ensure the directory exists */
2372 {
2373 Utf8Str imageDir = mFilePathFull;
2374 RTPathStripFilename (imageDir.mutableRaw());
2375 if (!RTDirExists (imageDir))
2376 {
2377 int vrc = RTDirCreateFullPath (imageDir, 0777);
2378 if (VBOX_FAILURE (vrc))
2379 {
2380 return setError (E_FAIL,
2381 tr ("Could not create a directory '%s' "
2382 "to store the image file (%Vrc)"),
2383 imageDir.raw(), vrc);
2384 }
2385 }
2386 }
2387
2388 /* check whether the given file exists or not */
2389 RTFILE file;
2390 int vrc = RTFileOpen (&file, Utf8Str (mFilePathFull),
2391 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
2392 if (vrc != VERR_FILE_NOT_FOUND)
2393 {
2394 if (VBOX_SUCCESS (vrc))
2395 RTFileClose (file);
2396 switch(vrc)
2397 {
2398 case VINF_SUCCESS:
2399 return setError (E_FAIL,
2400 tr ("Image file '%ls' already exists"),
2401 mFilePathFull.raw());
2402
2403 default:
2404 return setError(E_FAIL,
2405 tr ("Invalid image file path '%ls' (%Vrc)"),
2406 mFilePathFull.raw(), vrc);
2407 }
2408 }
2409
2410 /* check VDI size limits */
2411 {
2412 HRESULT rc;
2413 ULONG64 maxVDISize;
2414 ComPtr <ISystemProperties> props;
2415 rc = mVirtualBox->COMGETTER(SystemProperties) (props.asOutParam());
2416 ComAssertComRCRet (rc, E_FAIL);
2417 rc = props->COMGETTER(MaxVDISize) (&maxVDISize);
2418 ComAssertComRCRet (rc, E_FAIL);
2419
2420 if (aSize < 1 || aSize > maxVDISize)
2421 return setError (E_INVALIDARG,
2422 tr ("Invalid VDI size: %llu MB (must be in range [1, %llu] MB)"),
2423 aSize, maxVDISize);
2424 }
2425
2426 HRESULT rc;
2427
2428 /* create a project object */
2429 ComObjPtr <Progress> progress;
2430 progress.createObject();
2431 {
2432 Bstr desc = aDynamic ? tr ("Creating a dynamically expanding hard disk")
2433 : tr ("Creating a fixed-size hard disk");
2434 rc = progress->init (mVirtualBox, (IVirtualDiskImage *) this, desc,
2435 FALSE /* aCancelable */);
2436 CheckComRCReturnRC (rc);
2437 }
2438
2439 /* mark as busy (being created)
2440 * (VDI task thread will unmark it) */
2441 setBusy();
2442
2443 /* fill in VDI task data */
2444 VDITask *task = new VDITask (aDynamic ? VDITask::CreateDynamic
2445 : VDITask::CreateStatic,
2446 this, progress);
2447 task->size = aSize;
2448 task->size *= 1024 * 1024; /* convert to bytes */
2449
2450 /* create the hard disk creation thread, pass operation data */
2451 vrc = RTThreadCreate (NULL, vdiTaskThread, (void *) task, 0,
2452 RTTHREADTYPE_MAIN_HEAVY_WORKER, 0, "VDITask");
2453 ComAssertMsgRC (vrc, ("Could not create a thread (%Vrc)", vrc));
2454 if (VBOX_FAILURE (vrc))
2455 {
2456 clearBusy();
2457 delete task;
2458 rc = E_FAIL;
2459 }
2460 else
2461 {
2462 /* get one interface for the caller */
2463 progress.queryInterfaceTo (aProgress);
2464 }
2465
2466 return rc;
2467}
2468
2469/* static */
2470DECLCALLBACK(int) HVirtualDiskImage::vdiTaskThread (RTTHREAD thread, void *pvUser)
2471{
2472 VDITask *task = static_cast <VDITask *> (pvUser);
2473 AssertReturn (task, VERR_GENERAL_FAILURE);
2474
2475 LogFlow (("vdiTaskThread(): operation=%d, size=%llu\n",
2476 task->operation, task->size));
2477
2478 VDIIMAGETYPE type = (VDIIMAGETYPE) 0;
2479
2480 switch (task->operation)
2481 {
2482 case VDITask::CreateDynamic: type = VDI_IMAGE_TYPE_NORMAL; break;
2483 case VDITask::CreateStatic: type = VDI_IMAGE_TYPE_FIXED; break;
2484 case VDITask::CloneToImage: break;
2485 default: AssertFailedReturn (VERR_GENERAL_FAILURE); break;
2486 }
2487
2488 HRESULT rc = S_OK;
2489 Utf8Str errorMsg;
2490
2491 if (task->operation == VDITask::CloneToImage)
2492 {
2493 Assert (!task->vdi->id().isEmpty());
2494 /// @todo (dmik) check locks
2495 AutoLock sourceLock (task->source);
2496 rc = task->source->cloneToImage (task->vdi->id(),
2497 Utf8Str (task->vdi->filePathFull()),
2498 task->progress);
2499
2500 /* release reader added in HardDisk::CloneToImage() */
2501 task->source->releaseReader();
2502 }
2503 else
2504 {
2505 int vrc = VDICreateBaseImage (Utf8Str (task->vdi->filePathFull()),
2506 type, task->size,
2507 Utf8Str (task->vdi->mDescription),
2508 progressCallback,
2509 static_cast <Progress *> (task->progress));
2510
2511 if (VBOX_SUCCESS (vrc) && task->vdi->id())
2512 {
2513 /* we have a non-null UUID, update the created image */
2514 vrc = VDISetImageUUIDs (Utf8Str (task->vdi->filePathFull()),
2515 task->vdi->id().raw(), NULL, NULL, NULL);
2516 }
2517
2518 if (VBOX_FAILURE (vrc))
2519 {
2520 errorMsg = Utf8StrFmt (
2521 tr ("Falied to create a hard disk image '%ls' (%Vrc)"),
2522 task->vdi->filePathFull().raw(), vrc);
2523 rc = E_FAIL;
2524 }
2525 }
2526
2527 LogFlow (("vdiTaskThread(): rc=%08X\n", rc));
2528
2529 AutoLock alock (task->vdi);
2530
2531 /* clear busy set in in HardDisk::CloneToImage() or
2532 * in HVirtualDiskImage::createImage() */
2533 task->vdi->clearBusy();
2534
2535 if (SUCCEEDED (rc))
2536 {
2537 task->vdi->mState = HVirtualDiskImage::Created;
2538 /* update VDI data fields */
2539 rc = task->vdi->queryInformation (NULL);
2540 /* complete the progress object */
2541 task->progress->notifyComplete (rc);
2542 }
2543 else
2544 {
2545 /* delete the target file so we don't have orphaned files */
2546 RTFileDelete(Utf8Str (task->vdi->filePathFull()));
2547
2548 task->vdi->mState = HVirtualDiskImage::NotCreated;
2549 /* complete the progress object */
2550 if (errorMsg)
2551 task->progress->notifyComplete (
2552 E_FAIL, COM_IIDOF (IVirtualDiskImage), getComponentName(),
2553 errorMsg);
2554 else
2555 task->progress->notifyComplete (rc);
2556 }
2557
2558 delete task;
2559
2560 return VINF_SUCCESS;
2561}
2562
2563////////////////////////////////////////////////////////////////////////////////
2564// HISCSIHardDisk class
2565////////////////////////////////////////////////////////////////////////////////
2566
2567// constructor / destructor
2568////////////////////////////////////////////////////////////////////////////////
2569
2570HRESULT HISCSIHardDisk::FinalConstruct()
2571{
2572 HRESULT rc = HardDisk::FinalConstruct();
2573 if (FAILED (rc))
2574 return rc;
2575
2576 mSize = 0;
2577 mActualSize = 0;
2578
2579 mPort = 0;
2580 mLun = 0;
2581
2582 return S_OK;
2583}
2584
2585void HISCSIHardDisk::FinalRelease()
2586{
2587 HardDisk::FinalRelease();
2588}
2589
2590// public initializer/uninitializer for internal purposes only
2591////////////////////////////////////////////////////////////////////////////////
2592
2593// public methods for internal purposes only
2594/////////////////////////////////////////////////////////////////////////////
2595
2596/**
2597 * Initializes the iSCSI hard disk object by reading its properties from
2598 * the given configuration node. The created hard disk will be marked as
2599 * registered on success.
2600 *
2601 * @param aHDNode <HardDisk> node
2602 * @param aVDINod <ISCSIHardDisk> node
2603 */
2604HRESULT HISCSIHardDisk::init (VirtualBox *aVirtualBox,
2605 CFGNODE aHDNode, CFGNODE aISCSINode)
2606{
2607 LogFlowMember (("HISCSIHardDisk::init (load)\n"));
2608
2609 AssertReturn (aHDNode && aISCSINode, E_FAIL);
2610
2611 AutoLock alock (this);
2612 ComAssertRet (!isReady(), E_UNEXPECTED);
2613
2614 mStorageType = HardDiskStorageType_ISCSIHardDisk;
2615
2616 HRESULT rc = S_OK;
2617
2618 do
2619 {
2620 rc = protectedInit (aVirtualBox, NULL);
2621 CheckComRCBreakRC (rc);
2622
2623 /* set ready to let protectedUninit() be called on failure */
2624 setReady (true);
2625
2626 /* server (required) */
2627 CFGLDRQueryBSTR (aISCSINode, "server", mServer.asOutParam());
2628 /* target (required) */
2629 CFGLDRQueryBSTR (aISCSINode, "target", mTarget.asOutParam());
2630
2631 /* port (optional) */
2632 CFGLDRQueryUInt16 (aISCSINode, "port", &mPort);
2633 /* lun (optional) */
2634 CFGLDRQueryUInt64 (aISCSINode, "lun", &mLun);
2635 /* userName (optional) */
2636 CFGLDRQueryBSTR (aISCSINode, "userName", mUserName.asOutParam());
2637 /* password (optional) */
2638 CFGLDRQueryBSTR (aISCSINode, "password", mPassword.asOutParam());
2639
2640 LogFlowMember ((" 'iscsi:%ls:%hu@%ls/%ls:%llu'\n",
2641 mServer.raw(), mPort, mUserName.raw(), mTarget.raw(),
2642 mLun));
2643
2644 /* load basic settings and children */
2645 rc = loadSettings (aHDNode);
2646 CheckComRCBreakRC (rc);
2647
2648 if (mType != HardDiskType_WritethroughHardDisk)
2649 {
2650 rc = setError (E_FAIL,
2651 tr ("Currently, non-Writethrough iSCSI hard disks are not "
2652 "allowed ('%ls')"),
2653 toString().raw());
2654 break;
2655 }
2656
2657 mRegistered = TRUE;
2658 }
2659 while (0);
2660
2661 if (FAILED (rc))
2662 uninit();
2663
2664 return rc;
2665}
2666
2667/**
2668 * Initializes the iSCSI hard disk object using default values for all
2669 * properties. The created hard disk will NOT be marked as registered.
2670 */
2671HRESULT HISCSIHardDisk::init (VirtualBox *aVirtualBox)
2672{
2673 LogFlowMember (("HISCSIHardDisk::init()\n"));
2674
2675 AutoLock alock (this);
2676 ComAssertRet (!isReady(), E_UNEXPECTED);
2677
2678 mStorageType = HardDiskStorageType_ISCSIHardDisk;
2679
2680 HRESULT rc = S_OK;
2681
2682 do
2683 {
2684 rc = protectedInit (aVirtualBox, NULL);
2685 CheckComRCBreakRC (rc);
2686
2687 /* set ready to let protectedUninit() be called on failure */
2688 setReady (true);
2689
2690 /* we have to generate a new UUID */
2691 mId.create();
2692 mType = HardDiskType_WritethroughHardDisk;
2693 mRegistered = FALSE;
2694 }
2695 while (0);
2696
2697 if (FAILED (rc))
2698 uninit();
2699
2700 return rc;
2701}
2702
2703/**
2704 * Uninitializes the instance and sets the ready flag to FALSE.
2705 * Called either from FinalRelease(), by the parent when it gets destroyed,
2706 * or by a third party when it decides this object is no more valid.
2707 */
2708void HISCSIHardDisk::uninit()
2709{
2710 LogFlowMember (("HISCSIHardDisk::uninit()\n"));
2711
2712 AutoLock alock (this);
2713 if (!isReady())
2714 return;
2715
2716 HardDisk::protectedUninit (alock);
2717}
2718
2719// IHardDisk properties
2720////////////////////////////////////////////////////////////////////////////////
2721
2722STDMETHODIMP HISCSIHardDisk::COMGETTER(Description) (BSTR *aDescription)
2723{
2724 if (!aDescription)
2725 return E_POINTER;
2726
2727 AutoLock alock (this);
2728 CHECK_READY();
2729
2730 mDescription.cloneTo (aDescription);
2731 return S_OK;
2732}
2733
2734STDMETHODIMP HISCSIHardDisk::COMSETTER(Description) (INPTR BSTR aDescription)
2735{
2736 AutoLock alock (this);
2737 CHECK_READY();
2738
2739 CHECK_BUSY_AND_READERS();
2740
2741 if (mDescription != aDescription)
2742 {
2743 mDescription = aDescription;
2744 if (mRegistered)
2745 return mVirtualBox->saveSettings();
2746 }
2747
2748 return S_OK;
2749}
2750
2751STDMETHODIMP HISCSIHardDisk::COMGETTER(Size) (ULONG64 *aSize)
2752{
2753 if (!aSize)
2754 return E_POINTER;
2755
2756 AutoLock alock (this);
2757 CHECK_READY();
2758
2759 *aSize = mSize;
2760 return S_OK;
2761}
2762
2763STDMETHODIMP HISCSIHardDisk::COMGETTER(ActualSize) (ULONG64 *aActualSize)
2764{
2765 if (!aActualSize)
2766 return E_POINTER;
2767
2768 AutoLock alock (this);
2769 CHECK_READY();
2770
2771 *aActualSize = mActualSize;
2772 return S_OK;
2773}
2774
2775// IISCSIHardDisk properties
2776////////////////////////////////////////////////////////////////////////////////
2777
2778STDMETHODIMP HISCSIHardDisk::COMGETTER(Server) (BSTR *aServer)
2779{
2780 if (!aServer)
2781 return E_POINTER;
2782
2783 AutoLock alock (this);
2784 CHECK_READY();
2785
2786 mServer.cloneTo (aServer);
2787 return S_OK;
2788}
2789
2790STDMETHODIMP HISCSIHardDisk::COMSETTER(Server) (INPTR BSTR aServer)
2791{
2792 if (!aServer || !*aServer)
2793 return E_INVALIDARG;
2794
2795 AutoLock alock (this);
2796 CHECK_READY();
2797
2798 CHECK_BUSY_AND_READERS();
2799
2800 if (mServer != aServer)
2801 {
2802 mServer = aServer;
2803 if (mRegistered)
2804 return mVirtualBox->saveSettings();
2805 }
2806
2807 return S_OK;
2808}
2809
2810STDMETHODIMP HISCSIHardDisk::COMGETTER(Port) (USHORT *aPort)
2811{
2812 if (!aPort)
2813 return E_POINTER;
2814
2815 AutoLock alock (this);
2816 CHECK_READY();
2817
2818 *aPort = mPort;
2819 return S_OK;
2820}
2821
2822STDMETHODIMP HISCSIHardDisk::COMSETTER(Port) (USHORT aPort)
2823{
2824 AutoLock alock (this);
2825 CHECK_READY();
2826
2827 CHECK_BUSY_AND_READERS();
2828
2829 if (mPort != aPort)
2830 {
2831 mPort = aPort;
2832 if (mRegistered)
2833 return mVirtualBox->saveSettings();
2834 }
2835
2836 return S_OK;
2837}
2838
2839STDMETHODIMP HISCSIHardDisk::COMGETTER(Target) (BSTR *aTarget)
2840{
2841 if (!aTarget)
2842 return E_POINTER;
2843
2844 AutoLock alock (this);
2845 CHECK_READY();
2846
2847 mTarget.cloneTo (aTarget);
2848 return S_OK;
2849}
2850
2851STDMETHODIMP HISCSIHardDisk::COMSETTER(Target) (INPTR BSTR aTarget)
2852{
2853 if (!aTarget || !*aTarget)
2854 return E_INVALIDARG;
2855
2856 AutoLock alock (this);
2857 CHECK_READY();
2858
2859 CHECK_BUSY_AND_READERS();
2860
2861 if (mTarget != aTarget)
2862 {
2863 mTarget = aTarget;
2864 if (mRegistered)
2865 return mVirtualBox->saveSettings();
2866 }
2867
2868 return S_OK;
2869}
2870
2871STDMETHODIMP HISCSIHardDisk::COMGETTER(Lun) (ULONG64 *aLun)
2872{
2873 if (!aLun)
2874 return E_POINTER;
2875
2876 AutoLock alock (this);
2877 CHECK_READY();
2878
2879 *aLun = mLun;
2880 return S_OK;
2881}
2882
2883STDMETHODIMP HISCSIHardDisk::COMSETTER(Lun) (ULONG64 aLun)
2884{
2885 AutoLock alock (this);
2886 CHECK_READY();
2887
2888 CHECK_BUSY_AND_READERS();
2889
2890 if (mLun != aLun)
2891 {
2892 mLun = aLun;
2893 if (mRegistered)
2894 return mVirtualBox->saveSettings();
2895 }
2896
2897 return S_OK;
2898}
2899
2900STDMETHODIMP HISCSIHardDisk::COMGETTER(UserName) (BSTR *aUserName)
2901{
2902 if (!aUserName)
2903 return E_POINTER;
2904
2905 AutoLock alock (this);
2906 CHECK_READY();
2907
2908 mUserName.cloneTo (aUserName);
2909 return S_OK;
2910}
2911
2912STDMETHODIMP HISCSIHardDisk::COMSETTER(UserName) (INPTR BSTR aUserName)
2913{
2914 AutoLock alock (this);
2915 CHECK_READY();
2916
2917 CHECK_BUSY_AND_READERS();
2918
2919 if (mUserName != aUserName)
2920 {
2921 mUserName = aUserName;
2922 if (mRegistered)
2923 return mVirtualBox->saveSettings();
2924 }
2925
2926 return S_OK;
2927}
2928
2929STDMETHODIMP HISCSIHardDisk::COMGETTER(Password) (BSTR *aPassword)
2930{
2931 if (!aPassword)
2932 return E_POINTER;
2933
2934 AutoLock alock (this);
2935 CHECK_READY();
2936
2937 mPassword.cloneTo (aPassword);
2938 return S_OK;
2939}
2940
2941STDMETHODIMP HISCSIHardDisk::COMSETTER(Password) (INPTR BSTR aPassword)
2942{
2943 AutoLock alock (this);
2944 CHECK_READY();
2945
2946 CHECK_BUSY_AND_READERS();
2947
2948 if (mPassword != aPassword)
2949 {
2950 mPassword = aPassword;
2951 if (mRegistered)
2952 return mVirtualBox->saveSettings();
2953 }
2954
2955 return S_OK;
2956}
2957
2958// public/protected methods for internal purposes only
2959/////////////////////////////////////////////////////////////////////////////
2960
2961/**
2962 * Attempts to mark the hard disk as registered.
2963 * Only VirtualBox can call this method.
2964 */
2965HRESULT HISCSIHardDisk::trySetRegistered (BOOL aRegistered)
2966{
2967 AutoLock alock (this);
2968 CHECK_READY();
2969
2970 if (aRegistered)
2971 {
2972 if (mServer.isEmpty() || mTarget.isEmpty())
2973 return setError (E_FAIL,
2974 tr ("iSCSI Hard disk has no server or target defined"));
2975 }
2976 else
2977 {
2978 }
2979
2980 return HardDisk::trySetRegistered (aRegistered);
2981}
2982
2983/**
2984 * Checks accessibility of this iSCSI hard disk.
2985 */
2986HRESULT HISCSIHardDisk::getAccessible (Bstr &aAccessError)
2987{
2988 AutoLock alock (this);
2989 CHECK_READY();
2990
2991 /* check the basic accessibility */
2992 HRESULT rc = HardDisk::getAccessible (aAccessError);
2993 if (FAILED (rc) || !aAccessError.isNull())
2994 return rc;
2995
2996 return queryInformation (aAccessError);
2997}
2998
2999/**
3000 * Saves hard disk settings to the specified storage node and saves
3001 * all children to the specified hard disk node
3002 *
3003 * @param aHDNode <HardDisk>
3004 * @param aStorageNode <ISCSIHardDisk> node
3005 */
3006HRESULT HISCSIHardDisk::saveSettings (CFGNODE aHDNode, CFGNODE aStorageNode)
3007{
3008 AssertReturn (aHDNode && aStorageNode, E_FAIL);
3009
3010 AutoLock alock (this);
3011 CHECK_READY();
3012
3013 /* server (required) */
3014 CFGLDRSetBSTR (aStorageNode, "server", mServer);
3015 /* target (required) */
3016 CFGLDRSetBSTR (aStorageNode, "target", mTarget);
3017
3018 /* port (optional) */
3019 if (mPort != 0)
3020 CFGLDRSetUInt16 (aStorageNode, "port", mPort);
3021 else
3022 CFGLDRDeleteAttribute (aStorageNode, "port");
3023 /* lun (optional) */
3024 if (mLun != 0)
3025 CFGLDRSetUInt64 (aStorageNode, "lun", mLun);
3026 else
3027 CFGLDRDeleteAttribute (aStorageNode, "lun");
3028 /* userName (optional) */
3029 if (!mUserName.isNull())
3030 CFGLDRSetBSTR (aStorageNode, "userName", mUserName);
3031 else
3032 CFGLDRDeleteAttribute (aStorageNode, "userName");
3033 /* password (optional) */
3034 if (!mPassword.isNull())
3035 CFGLDRSetBSTR (aStorageNode, "password", mPassword);
3036 else
3037 CFGLDRDeleteAttribute (aStorageNode, "password");
3038
3039 /* save basic settings and children */
3040 return HardDisk::saveSettings (aHDNode);
3041}
3042
3043/**
3044 * Returns the string representation of this hard disk.
3045 * When \a aShort is false, returns the full image file path.
3046 * Otherwise, returns the image file name only.
3047 *
3048 * @param aShort if true, a short representation is returned
3049 */
3050Bstr HISCSIHardDisk::toString (bool aShort /* = false */)
3051{
3052 AutoLock alock (this);
3053
3054 Bstr str;
3055 if (!aShort)
3056 {
3057 /* the format is iscsi:[<user@>]<server>[:<port>]/<target>[:<lun>] */
3058 str = Utf8StrFmt ("iscsi:%s%ls%s/%ls%s",
3059 !mUserName.isNull() ? Utf8StrFmt ("%ls@", mUserName.raw()).raw() : "",
3060 mServer.raw(),
3061 mPort != 0 ? Utf8StrFmt (":%hu", mPort).raw() : "",
3062 mTarget.raw(),
3063 mLun != 0 ? Utf8StrFmt (":%llu", mLun).raw() : "");
3064 }
3065 else
3066 {
3067 str = Utf8StrFmt ("%ls%s",
3068 mTarget.raw(),
3069 mLun != 0 ? Utf8StrFmt (":%llu", mLun).raw() : "");
3070 }
3071
3072 return str;
3073}
3074
3075/**
3076 * Creates a clone of this hard disk by storing hard disk data in the given
3077 * VDI file name.
3078 *
3079 * @param aId UUID to assign to the created image
3080 * @param aTargetPath VDI file where the cloned image is to be to stored
3081 * @param aProgress progress object to run during operation
3082 */
3083HRESULT
3084HISCSIHardDisk::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
3085 Progress *aProgress)
3086{
3087 ComAssertMsgFailed (("Not implemented"));
3088 return E_NOTIMPL;
3089
3090// AssertReturn (isBusy() == false, E_FAIL);
3091// addReader();
3092// releaseReader();
3093}
3094
3095/**
3096 * Creates a new differencing image for this hard disk with the given
3097 * VDI file name.
3098 *
3099 * @param aId UUID to assign to the created image
3100 * @param aTargetPath VDI file where to store the created differencing image
3101 * @param aProgress progress object to run during operation
3102 * (can be NULL)
3103 */
3104HRESULT
3105HISCSIHardDisk::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
3106 Progress *aProgress)
3107{
3108 ComAssertMsgFailed (("Not implemented"));
3109 return E_NOTIMPL;
3110
3111// AssertReturn (isBusy() == false, E_FAIL);
3112// addReader();
3113// releaseReader();
3114}
3115
3116// private methods
3117/////////////////////////////////////////////////////////////////////////////
3118
3119/**
3120 * Helper to query information about the iSCSI hard disk.
3121 *
3122 * @param aAccessError see #getAccessible()
3123 * @note
3124 * Must be called from under the object's lock!
3125 */
3126HRESULT HISCSIHardDisk::queryInformation (Bstr &aAccessError)
3127{
3128 /// @todo (dmik) query info about this iSCSI disk,
3129 // set mSize and mActualSize,
3130 // or set aAccessError in case of failure
3131
3132 aAccessError.setNull();
3133 return S_OK;
3134}
3135
Note: See TracBrowser for help on using the repository browser.

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