VirtualBox

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

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

Main: Hard disk consistensy errors such as UUID mismatch are now also returned through the lastAccessError property, not through the HRESULT of getAccessible() (because such conditions are not fatal and can be retried later).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 86.5 KB
Line 
1/** @file
2 *
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22#include "HardDiskImpl.h"
23#include "ProgressImpl.h"
24#include "VirtualBoxImpl.h"
25#include "SystemPropertiesImpl.h"
26#include "Logging.h"
27
28#include <iprt/string.h>
29#include <iprt/thread.h>
30#include <iprt/file.h>
31#include <iprt/path.h>
32#include <iprt/dir.h>
33#include <VBox/VBoxHDD.h>
34#include <VBox/err.h>
35
36#include <algorithm>
37
38#define CHECK_BUSY() \
39 do { \
40 if (isBusy()) \
41 return setError (E_UNEXPECTED, \
42 tr ("Hard disk '%ls' is being used by another task"), \
43 toString().raw()); \
44 } while (0)
45
46#define CHECK_BUSY_AND_READERS() \
47do { \
48 if (readers() > 0 || isBusy()) \
49 return setError (E_UNEXPECTED, \
50 tr ("Hard disk '%ls' is being used by another task"), \
51 toString().raw()); \
52} while (0)
53
54/** Task structure for asynchronous VDI operations */
55struct VDITask
56{
57 enum Op { CreateDynamic, CreateStatic, CloneToImage };
58
59 VDITask (Op op, HVirtualDiskImage *i, Progress *p)
60 : operation (op)
61 , vdi (i)
62 , progress (p)
63 {}
64
65 Op operation;
66 ComObjPtr <HVirtualDiskImage> vdi;
67 ComObjPtr <Progress> progress;
68
69 /* for CreateDynamic, CreateStatic */
70 uint64_t size;
71
72 /* for CloneToImage */
73 ComObjPtr <HardDisk> source;
74};
75
76/**
77 * Progress callback handler for VDI operations.
78 *
79 * @param uPercent Completetion precentage (0-100).
80 * @param pvUser Pointer to the Progress instance.
81 */
82static DECLCALLBACK(int) progressCallback (PVM /* pVM */, unsigned uPercent, void *pvUser)
83{
84 Progress *progress = static_cast <Progress *> (pvUser);
85
86 /* update the progress object */
87 if (progress)
88 progress->notifyProgress (uPercent);
89
90 return VINF_SUCCESS;
91}
92
93////////////////////////////////////////////////////////////////////////////////
94// HardDisk class
95////////////////////////////////////////////////////////////////////////////////
96
97// constructor / destructor
98////////////////////////////////////////////////////////////////////////////////
99
100/** Shold be called by subclasses from #FinalConstruct() */
101HRESULT HardDisk::FinalConstruct()
102{
103 mRegistered = FALSE;
104
105 mStorageType = HardDiskStorageType_VirtualDiskImage;
106 mType = HardDiskType_NormalHardDisk;
107
108 mBusy = false;
109 mReaders = 0;
110
111 return S_OK;
112}
113
114/**
115 * Shold be called by subclasses from #FinalRelease().
116 * Uninitializes this object by calling #uninit() if it's not yet done.
117 */
118void HardDisk::FinalRelease()
119{
120 uninit();
121}
122
123// protected initializer/uninitializer for internal purposes only
124////////////////////////////////////////////////////////////////////////////////
125
126/**
127 * Initializes the hard disk object.
128 *
129 * Subclasses should call this or any other #init() method from their
130 * init() implementations.
131 *
132 * @note
133 * This method doesn't do |isReady()| check and doesn't call
134 * |setReady (true)| on success!
135 * @note
136 * This method must be called from under the object's lock!
137 */
138HRESULT HardDisk::protectedInit (VirtualBox *aVirtualBox, HardDisk *aParent)
139{
140 LogFlowMember (("HardDisk::protectedInit (aParent=%p)\n", aParent));
141
142 ComAssertRet (aVirtualBox, E_INVALIDARG);
143
144 mVirtualBox = aVirtualBox;
145 mParent = aParent;
146
147 if (!aParent)
148 aVirtualBox->addDependentChild (this);
149 else
150 aParent->addDependentChild (this);
151
152 return S_OK;
153}
154
155/**
156 * Uninitializes the instance.
157 * Subclasses should call this from their uninit() implementations.
158 * The readiness flag must be true on input and will be set to false
159 * on output.
160 *
161 * @param alock this object's autolock
162 *
163 * @note
164 * Using mParent and mVirtualBox members after this method returns
165 * is forbidden.
166 */
167void HardDisk::protectedUninit (AutoLock &alock)
168{
169 LogFlowMember (("HardDisk::protectedUninit()\n"));
170
171 Assert (alock.belongsTo (this));
172 Assert (isReady());
173
174 /* uninit all children */
175 uninitDependentChildren();
176
177 setReady (false);
178
179 if (mParent)
180 mParent->removeDependentChild (this);
181 else
182 {
183 alock.leave();
184 mVirtualBox->removeDependentChild (this);
185 alock.enter();
186 }
187
188 mParent.setNull();
189 mVirtualBox.setNull();
190}
191
192// IHardDisk properties
193/////////////////////////////////////////////////////////////////////////////
194
195STDMETHODIMP HardDisk::COMGETTER(Id) (GUIDPARAMOUT aId)
196{
197 if (!aId)
198 return E_POINTER;
199
200 AutoLock alock (this);
201 CHECK_READY();
202
203 mId.cloneTo (aId);
204 return S_OK;
205}
206
207STDMETHODIMP HardDisk::COMGETTER(StorageType) (HardDiskStorageType_T *aStorageType)
208{
209 if (!aStorageType)
210 return E_POINTER;
211
212 AutoLock alock (this);
213 CHECK_READY();
214
215 *aStorageType = mStorageType;
216 return S_OK;
217}
218
219STDMETHODIMP HardDisk::COMGETTER(Location) (BSTR *aLocation)
220{
221 if (!aLocation)
222 return E_POINTER;
223
224 AutoLock alock (this);
225 CHECK_READY();
226
227 toString (false /* aShort */).cloneTo (aLocation);
228 return S_OK;
229}
230
231STDMETHODIMP HardDisk::COMGETTER(Type) (HardDiskType_T *aType)
232{
233 if (!aType)
234 return E_POINTER;
235
236 AutoLock alock (this);
237 CHECK_READY();
238
239 *aType = mType;
240 return S_OK;
241}
242
243STDMETHODIMP HardDisk::COMSETTER(Type) (HardDiskType_T aType)
244{
245 AutoLock alock (this);
246 CHECK_READY();
247
248 if (mRegistered)
249 return setError (E_FAIL,
250 tr ("You cannot change the type of the registered hard disk '%ls'"),
251 toString().raw());
252
253 if (mStorageType == HardDiskStorageType_ISCSIHardDisk)
254 return setError (E_FAIL,
255 tr ("Currently, changing the type of iSCSI hard disks is not allowed"));
256
257 /// @todo (dmik) later: allow to change the type on any registered hard disk
258 // depending on whether it is attached or not, has children etc.
259 // Don't forget to save hdd configuration afterwards.
260
261 mType = aType;
262 return S_OK;
263}
264
265STDMETHODIMP HardDisk::COMGETTER(Parent) (IHardDisk **aParent)
266{
267 if (!aParent)
268 return E_POINTER;
269
270 AutoLock alock (this);
271 CHECK_READY();
272
273 mParent.queryInterfaceTo (aParent);
274 return S_OK;
275}
276
277STDMETHODIMP HardDisk::COMGETTER(Children) (IHardDiskCollection **aChildren)
278{
279 if (!aChildren)
280 return E_POINTER;
281
282 AutoLock lock(this);
283 CHECK_READY();
284
285 AutoLock chLock (childrenLock());
286
287 ComObjPtr <HardDiskCollection> collection;
288 collection.createObject();
289 collection->init (children());
290 collection.queryInterfaceTo (aChildren);
291 return S_OK;
292}
293
294STDMETHODIMP HardDisk::COMGETTER(Root) (IHardDisk **aRoot)
295{
296 if (!aRoot)
297 return E_POINTER;
298
299 AutoLock lock(this);
300 CHECK_READY();
301
302 root().queryInterfaceTo (aRoot);
303 return S_OK;
304}
305
306STDMETHODIMP HardDisk::COMGETTER(Accessible) (BOOL *aAccessible)
307{
308 if (!aAccessible)
309 return E_POINTER;
310
311 AutoLock alock (this);
312 CHECK_READY();
313
314 HRESULT rc = getAccessible (mLastAccessError);
315 if (FAILED (rc))
316 return rc;
317
318 *aAccessible = mLastAccessError.isNull();
319 return S_OK;
320}
321
322STDMETHODIMP HardDisk::COMGETTER(AllAccessible) (BOOL *aAllAccessible)
323{
324 if (!aAllAccessible)
325 return E_POINTER;
326
327 AutoLock alock (this);
328 CHECK_READY();
329
330 if (mParent)
331 {
332 HRESULT rc = S_OK;
333
334 /* check the accessibility state of all ancestors */
335 ComObjPtr <HardDisk> parent = (HardDisk *) mParent;
336 while (parent)
337 {
338 AutoLock parentLock (parent);
339 HRESULT rc = parent->getAccessible (mLastAccessError);
340 if (FAILED (rc))
341 break;
342 *aAllAccessible = mLastAccessError.isNull();
343 if (!*aAllAccessible)
344 break;
345 parent = parent->mParent;
346 }
347
348 return rc;
349 }
350
351 HRESULT rc = getAccessible (mLastAccessError);
352 if (FAILED (rc))
353 return rc;
354
355 *aAllAccessible = mLastAccessError.isNull();
356 return S_OK;
357}
358
359STDMETHODIMP HardDisk::COMGETTER(LastAccessError) (BSTR *aLastAccessError)
360{
361 if (!aLastAccessError)
362 return E_POINTER;
363
364 AutoLock alock (this);
365 CHECK_READY();
366
367 mLastAccessError.cloneTo (aLastAccessError);
368 return S_OK;
369}
370
371STDMETHODIMP HardDisk::COMGETTER(MachineId) (GUIDPARAMOUT aMachineId)
372{
373 if (!aMachineId)
374 return E_POINTER;
375
376 AutoLock alock (this);
377 CHECK_READY();
378
379 mMachineId.cloneTo (aMachineId);
380 return S_OK;
381}
382
383STDMETHODIMP HardDisk::COMGETTER(SnapshotId) (GUIDPARAMOUT aSnapshotId)
384{
385 if (!aSnapshotId)
386 return E_POINTER;
387
388 AutoLock alock (this);
389 CHECK_READY();
390
391 mSnapshotId.cloneTo (aSnapshotId);
392 return S_OK;
393}
394
395STDMETHODIMP HardDisk::CloneToImage (INPTR BSTR aFilePath,
396 IVirtualDiskImage **aImage,
397 IProgress **aProgress)
398{
399 if (!aFilePath || !(*aFilePath))
400 return E_INVALIDARG;
401 if (!aImage || !aProgress)
402 return E_POINTER;
403
404 AutoLock alock (this);
405 CHECK_READY();
406 CHECK_BUSY();
407
408 if (!mParent.isNull())
409 return setError (E_FAIL,
410 tr ("Cloning differencing VDI images is not yet supported ('%ls')"),
411 toString().raw());
412
413 HRESULT rc = S_OK;
414
415 /* create a project object */
416 ComObjPtr <Progress> progress;
417 progress.createObject();
418 rc = progress->init (mVirtualBox, (IVirtualDiskImage *) this,
419 Bstr (tr ("Creating a hard disk clone")),
420 FALSE /* aCancelable */);
421 CheckComRCReturnRC (rc);
422
423 /* create an imageless resulting object */
424 ComObjPtr <HVirtualDiskImage> image;
425 image.createObject();
426 rc = image->init (mVirtualBox, NULL, NULL);
427 CheckComRCReturnRC (rc);
428
429 /* append the default path if only a name is given */
430 Bstr path = aFilePath;
431 {
432 Utf8Str fp = aFilePath;
433 if (!RTPathHavePath (fp))
434 {
435 AutoReaderLock propsLock (mVirtualBox->systemProperties());
436 path = Utf8StrFmt ("%ls%c%s",
437 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
438 RTPATH_DELIMITER,
439 fp.raw());
440 }
441 }
442
443 /* set the desired path */
444 rc = image->setFilePath (path);
445 CheckComRCReturnRC (rc);
446
447 /* ensure the directory exists */
448 {
449 Utf8Str imageDir = image->filePath();
450 RTPathStripFilename (imageDir.mutableRaw());
451 if (!RTDirExists (imageDir))
452 {
453 int vrc = RTDirCreateFullPath (imageDir, 0777);
454 if (VBOX_FAILURE (vrc))
455 {
456 return setError (E_FAIL,
457 tr ("Could not create a directory '%s' "
458 "to store the image file (%Vrc)"),
459 imageDir.raw(), vrc);
460 }
461 }
462 }
463
464 /* mark as busy (being created)
465 * (VDI task thread will unmark it) */
466 image->setBusy();
467
468 /* fill in a VDI task data */
469 VDITask *task = new VDITask (VDITask::CloneToImage, image, progress);
470 task->source = this;
471
472 /* increase readers until finished
473 * (VDI task thread will decrease them) */
474 addReader();
475
476 /* create the hard disk creation thread, pass operation data */
477 int vrc = RTThreadCreate (NULL, HVirtualDiskImage::vdiTaskThread,
478 (void *) task, 0, RTTHREADTYPE_MAIN_HEAVY_WORKER,
479 0, "VDITask");
480 ComAssertMsgRC (vrc, ("Could not create a thread (%Vrc)", vrc));
481 if (VBOX_FAILURE (vrc))
482 {
483 releaseReader();
484 image->clearBusy();
485 delete task;
486 return E_FAIL;
487 }
488
489 /* return interfaces to the caller */
490 image.queryInterfaceTo (aImage);
491 progress.queryInterfaceTo (aProgress);
492
493 return S_OK;
494}
495
496// public methods for internal purposes only
497/////////////////////////////////////////////////////////////////////////////
498
499/**
500 * Returns the very first (grand-) parent of this hard disk or the hard
501 * disk itself, if it doesn't have a parent.
502 *
503 * @note
504 * Must be called from under the object's lock
505 */
506ComObjPtr <HardDisk> HardDisk::root() const
507{
508 ComObjPtr <HardDisk> root = const_cast <HardDisk *> (this);
509 ComObjPtr <HardDisk> parent;
510 while ((parent = root->parent()))
511 root = parent;
512
513 return root;
514}
515
516/**
517 * Attempts to mark the hard disk as registered.
518 * Must be always called by every reimplementation.
519 * Only VirtualBox can call this method.
520 *
521 * @param aRegistered true to set registered and false to set unregistered
522 */
523HRESULT HardDisk::trySetRegistered (BOOL aRegistered)
524{
525 AutoLock alock (this);
526 CHECK_READY();
527
528 if (aRegistered)
529 {
530 ComAssertRet (mMachineId.isEmpty(), E_FAIL);
531 ComAssertRet (mId && children().size() == 0, E_FAIL);
532
533 if (mRegistered)
534 return setError (E_FAIL,
535 tr ("Hard disk '%ls' is already registered"),
536 toString().raw());
537
538 CHECK_BUSY();
539 }
540 else
541 {
542 if (!mRegistered)
543 return setError (E_FAIL,
544 tr ("Hard disk '%ls' is already unregistered"),
545 toString().raw());
546
547 if (!mMachineId.isEmpty())
548 return setError (E_FAIL,
549 tr ("Hard disk '%ls' is attached to a virtual machine with UUID {%s}"),
550 toString().raw(), mMachineId.toString().raw());
551
552 if (children().size() > 0)
553 return setError (E_FAIL,
554 tr ("Hard disk '%ls' has %d differencing hard disks based on it"),
555 toString().raw(), children().size());
556
557 CHECK_BUSY_AND_READERS();
558 }
559
560 mRegistered = aRegistered;
561 return S_OK;
562}
563
564/**
565 * Checks basic accessibility of this hard disk only (w/o parents).
566 * Must be always called by every 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 != ~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 /* don't touch aAccessError, it has been already set */
1561 return S_OK;
1562 }
1563
1564 /* check the basic accessibility */
1565 HRESULT rc = HardDisk::getAccessible (aAccessError);
1566 if (FAILED (rc) || !aAccessError.isNull())
1567 return rc;
1568
1569 if (mState >= Created)
1570 {
1571 return queryInformation (&aAccessError);
1572 }
1573
1574 aAccessError = Utf8StrFmt ("Hard disk image '%ls' is not yet created",
1575 mFilePathFull.raw());
1576 return S_OK;
1577}
1578
1579/**
1580 * Saves hard disk settings to the specified storage node and saves
1581 * all children to the specified hard disk node
1582 *
1583 * @param aHDNode <HardDisk> or <DiffHardDisk> node
1584 * @param aStorageNode <VirtualDiskImage> node
1585 */
1586HRESULT HVirtualDiskImage::saveSettings (CFGNODE aHDNode, CFGNODE aStorageNode)
1587{
1588 AssertReturn (aHDNode && aStorageNode, E_FAIL);
1589
1590 AutoLock alock (this);
1591 CHECK_READY();
1592
1593 // filePath (required)
1594 CFGLDRSetBSTR (aStorageNode, "filePath", mFilePath);
1595
1596 // save basic settings and children
1597 return HardDisk::saveSettings (aHDNode);
1598}
1599
1600/**
1601 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
1602 * of this hard disk and updates it if necessary to reflect the new location.
1603 * Intended to be from HardDisk::updatePaths().
1604 *
1605 * @param aOldPath old path (full)
1606 * @param aNewPath new path (full)
1607 *
1608 * @note Locks this object for writing.
1609 */
1610void HVirtualDiskImage::updatePath (const char *aOldPath, const char *aNewPath)
1611{
1612 AssertReturnVoid (aOldPath);
1613 AssertReturnVoid (aNewPath);
1614
1615 AutoLock alock (this);
1616 AssertReturnVoid (isReady());
1617
1618 size_t oldPathLen = strlen (aOldPath);
1619
1620 Utf8Str path = mFilePathFull;
1621 LogFlowThisFunc (("VDI.fullPath={%s}\n", path.raw()));
1622
1623 if (RTPathStartsWith (path, aOldPath))
1624 {
1625 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
1626 path.raw() + oldPathLen);
1627 path = newPath;
1628
1629 mVirtualBox->calculateRelativePath (path, path);
1630
1631 unconst (mFilePathFull) = newPath;
1632 unconst (mFilePath) = path;
1633
1634 LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
1635 newPath.raw(), path.raw()));
1636 }
1637}
1638
1639/**
1640 * Returns the string representation of this hard disk.
1641 * When \a aShort is false, returns the full image file path.
1642 * Otherwise, returns the image file name only.
1643 *
1644 * @param aShort if true, a short representation is returned
1645 */
1646Bstr HVirtualDiskImage::toString (bool aShort /* = false */)
1647{
1648 AutoLock alock (this);
1649
1650 if (!aShort)
1651 return mFilePathFull;
1652 else
1653 {
1654 Utf8Str fname = mFilePathFull;
1655 return RTPathFilename (fname.mutableRaw());
1656 }
1657}
1658
1659/**
1660 * Creates a clone of this hard disk by storing hard disk data in the given
1661 * VDI file name.
1662 *
1663 * @param aId UUID to assign to the created image
1664 * @param aTargetPath VDI file where the cloned image is to be to stored
1665 * @param aProgress progress object to run during operation
1666 */
1667HRESULT
1668HVirtualDiskImage::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
1669 Progress *aProgress)
1670{
1671 AssertReturn (!aId.isEmpty(), E_FAIL);
1672 AssertReturn (!aTargetPath.isNull(), E_FAIL);
1673 AssertReturn (aProgress, E_FAIL);
1674
1675 AutoLock alock (this);
1676 AssertReturn (isReady(), E_FAIL);
1677
1678 AssertReturn (isBusy() == false, E_FAIL);
1679
1680 /// @todo (dmik) cloning of differencing images is not yet supported
1681 AssertReturn (mParent.isNull(), E_FAIL);
1682
1683 Utf8Str filePathFull = mFilePathFull;
1684
1685 if (mState == NotCreated)
1686 return setError (E_FAIL,
1687 tr ("Source hard disk image '%s' is not yet created"),
1688 filePathFull.raw());
1689
1690 addReader();
1691 alock.leave();
1692
1693 int vrc = VDICopyImage (aTargetPath, filePathFull, NULL,
1694 progressCallback,
1695 static_cast <Progress *> (aProgress));
1696
1697 alock.enter();
1698 releaseReader();
1699
1700 if (VBOX_SUCCESS (vrc))
1701 vrc = VDISetImageUUIDs (aTargetPath, aId, NULL, NULL, NULL);
1702
1703 if (VBOX_FAILURE (vrc))
1704 return setError (E_FAIL,
1705 tr ("Could not copy the hard disk image '%s' to '%s' (%Vrc)"),
1706 filePathFull.raw(), aTargetPath.raw(), vrc);
1707
1708 return S_OK;
1709}
1710
1711/**
1712 * Creates a new differencing image for this hard disk with the given
1713 * VDI file name.
1714 *
1715 * @param aId UUID to assign to the created image
1716 * @param aTargetPath VDI file where to store the created differencing image
1717 * @param aProgress progress object to run during operation
1718 * (can be NULL)
1719 */
1720HRESULT
1721HVirtualDiskImage::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
1722 Progress *aProgress)
1723{
1724 AssertReturn (!aId.isEmpty(), E_FAIL);
1725 AssertReturn (!aTargetPath.isNull(), E_FAIL);
1726
1727 AutoLock alock (this);
1728 AssertReturn (isReady(), E_FAIL);
1729
1730 AssertReturn (isBusy() == false, E_FAIL);
1731 AssertReturn (mState >= Created, E_FAIL);
1732
1733 addReader();
1734 alock.leave();
1735
1736 int vrc = VDICreateDifferenceImage (aTargetPath, Utf8Str (mFilePathFull),
1737 NULL, aProgress ? progressCallback : NULL,
1738 static_cast <Progress *> (aProgress));
1739 alock.enter();
1740 releaseReader();
1741
1742 /* update the UUID to correspond to the file name */
1743 if (VBOX_SUCCESS (vrc))
1744 vrc = VDISetImageUUIDs (aTargetPath, aId, NULL, NULL, NULL);
1745
1746 if (VBOX_FAILURE (vrc))
1747 return setError (E_FAIL,
1748 tr ("Could not create a differencing hard disk '%s' (%Vrc)"),
1749 aTargetPath.raw(), vrc);
1750
1751 return S_OK;
1752}
1753
1754/**
1755 * Copies the image file of this hard disk to a separate VDI file (with an
1756 * unique creation UUID) and creates a new hard disk object for the copied
1757 * image. The copy will be created as a child of this hard disk's parent
1758 * (so that this hard disk must be a differencing one).
1759 *
1760 * The specified progress object (if not NULL) receives the percentage
1761 * of the operation completion. However, it is responsibility of the caller to
1762 * call Progress::notifyComplete() after this method returns.
1763 *
1764 * @param aFolder folder where to create a copy (must be a full path)
1765 * @param aMachineId machine ID the new hard disk will belong to
1766 * @param aHardDisk resulting hard disk object
1767 * @param aProgress progress object to run during copy operation
1768 * (may be NULL)
1769 *
1770 * @note
1771 * Must be NOT called from under locks of other objects that need external
1772 * access dirung this method execurion!
1773 */
1774HRESULT
1775HVirtualDiskImage::cloneDiffImage (const Bstr &aFolder, const Guid &aMachineId,
1776 ComObjPtr <HVirtualDiskImage> &aHardDisk,
1777 Progress *aProgress)
1778{
1779 AssertReturn (!aFolder.isEmpty() && !aMachineId.isEmpty(),
1780 E_FAIL);
1781
1782 AutoLock alock (this);
1783 CHECK_READY();
1784
1785 AssertReturn (!mParent.isNull(), E_FAIL);
1786
1787 ComAssertRet (isBusy() == false, E_FAIL);
1788 ComAssertRet (mState >= Created, E_FAIL);
1789
1790 Guid id;
1791 id.create();
1792
1793 Utf8Str filePathTo = Utf8StrFmt ("%ls%c{%Vuuid}.vdi",
1794 aFolder.raw(), RTPATH_DELIMITER, id.ptr());
1795
1796 /* try to make the path relative to the vbox home dir */
1797 const char *filePathToRel = filePathTo;
1798 {
1799 const Utf8Str &homeDir = mVirtualBox->homeDir();
1800 if (!strncmp (filePathTo, homeDir, homeDir.length()))
1801 filePathToRel = (filePathToRel + homeDir.length() + 1);
1802 }
1803
1804 /* first ensure the directory exists */
1805 {
1806 Utf8Str dir = aFolder;
1807 if (!RTDirExists (dir))
1808 {
1809 int vrc = RTDirCreateFullPath (dir, 0777);
1810 if (VBOX_FAILURE (vrc))
1811 {
1812 return setError (E_FAIL,
1813 tr ("Could not create a directory '%s' "
1814 "to store the image file (%Vrc)"),
1815 dir.raw(), vrc);
1816 }
1817 }
1818 }
1819
1820 Utf8Str filePathFull = mFilePathFull;
1821
1822 alock.leave();
1823
1824 int vrc = VDICopyImage (filePathTo, filePathFull, NULL,
1825 progressCallback,
1826 static_cast <Progress *> (aProgress));
1827
1828 alock.enter();
1829
1830 /* get modification and parent UUIDs of this image */
1831 RTUUID modUuid, parentUuid, parentModUuid;
1832 if (VBOX_SUCCESS (vrc))
1833 vrc = VDIGetImageUUIDs (filePathFull, NULL, &modUuid,
1834 &parentUuid, &parentModUuid);
1835
1836 // update the UUID of the copy to correspond to the file name
1837 // and copy all other UUIDs from this image
1838 if (VBOX_SUCCESS (vrc))
1839 vrc = VDISetImageUUIDs (filePathTo, id, &modUuid,
1840 &parentUuid, &parentModUuid);
1841
1842 if (VBOX_FAILURE (vrc))
1843 return setError (E_FAIL,
1844 tr ("Could not copy the hard disk image '%s' to '%s' (%Vrc)"),
1845 filePathFull.raw(), filePathTo.raw(), vrc);
1846
1847 ComObjPtr <HVirtualDiskImage> vdi;
1848 vdi.createObject();
1849 HRESULT rc = vdi->init (mVirtualBox, mParent, Bstr (filePathToRel),
1850 TRUE /* aRegistered */);
1851 if (FAILED (rc))
1852 return rc;
1853
1854 /* associate the created hard disk with the given machine */
1855 vdi->setMachineId (aMachineId);
1856
1857 rc = mVirtualBox->registerHardDisk (vdi, VirtualBox::RHD_Internal);
1858 if (FAILED (rc))
1859 return rc;
1860
1861 aHardDisk = vdi;
1862
1863 return S_OK;
1864}
1865
1866/**
1867 * Merges this child image to its parent image and updates the parent UUID
1868 * of all children of this image (to point to this image's parent).
1869 * It's a responsibility of the caller to unregister and uninitialize
1870 * the merged image on success.
1871 *
1872 * This method is intended to be called on a worker thread (the operation
1873 * can be time consuming).
1874 *
1875 * The specified progress object (if not NULL) receives the percentage
1876 * of the operation completion. However, it is responsibility of the caller to
1877 * call Progress::notifyComplete() after this method returns.
1878 *
1879 * @param aProgress progress object to run during copy operation
1880 * (may be NULL)
1881 *
1882 * @note
1883 * This method expects that both this hard disk and the paret hard disk
1884 * are marked as busy using #setBusyWithChildren() prior to calling it!
1885 * Busy flags of both hard disks will be cleared by this method
1886 * on a successful return. In case of failure, #clearBusyWithChildren()
1887 * must be called on a parent.
1888 *
1889 * @note
1890 * Must be NOT called from under locks of other objects that need external
1891 * access dirung this method execurion!
1892 */
1893HRESULT HVirtualDiskImage::mergeImageToParent (Progress *aProgress)
1894{
1895 LogFlowMember (("HVirtualDiskImage::mergeImageToParent(): image='%ls'\n",
1896 mFilePathFull.raw()));
1897
1898 AutoLock alock (this);
1899 CHECK_READY();
1900
1901 AssertReturn (!mParent.isNull(), E_FAIL);
1902 AutoLock parentLock (mParent);
1903
1904 ComAssertRet (isBusy() == true, E_FAIL);
1905 ComAssertRet (mParent->isBusy() == true, E_FAIL);
1906
1907 ComAssertRet (mState >= Created && mParent->asVDI()->mState >= Created, E_FAIL);
1908
1909 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
1910 ("non VDI storage types are not yet supported!"), E_FAIL);
1911
1912 parentLock.leave();
1913 alock.leave();
1914
1915 int vrc = VDIMergeImage (Utf8Str (mFilePathFull),
1916 Utf8Str (mParent->asVDI()->mFilePathFull),
1917 progressCallback,
1918 static_cast <Progress *> (aProgress));
1919 alock.enter();
1920 parentLock.enter();
1921
1922 if (VBOX_FAILURE (vrc))
1923 return setError (E_FAIL,
1924 tr ("Could not merge the hard disk image '%ls' to "
1925 "its parent image '%ls' (%Vrc)"),
1926 mFilePathFull.raw(), mParent->asVDI()->mFilePathFull.raw(), vrc);
1927
1928 {
1929 HRESULT rc = S_OK;
1930
1931 AutoLock chLock (childrenLock());
1932
1933 for (HardDiskList::const_iterator it = children().begin();
1934 it != children().end(); ++ it)
1935 {
1936 ComObjPtr <HVirtualDiskImage> child = (*it)->asVDI();
1937 AutoLock childLock (child);
1938
1939 /* reparent the child */
1940 child->mParent = mParent;
1941 if (mParent)
1942 mParent->addDependentChild (child);
1943
1944 /* change the parent UUID in the image as well */
1945 RTUUID parentUuid, parentModUuid;
1946 vrc = VDIGetImageUUIDs (Utf8Str (mParent->asVDI()->mFilePathFull),
1947 &parentUuid, &parentModUuid, NULL, NULL);
1948 if (VBOX_FAILURE (vrc))
1949 {
1950 rc = setError (E_FAIL,
1951 tr ("Could not access the hard disk image '%ls' (%Vrc)"),
1952 mParent->asVDI()->mFilePathFull.raw(), vrc);
1953 break;
1954 }
1955 ComAssertBreak (mParent->id() == Guid (parentUuid), rc = E_FAIL);
1956 vrc = VDISetImageUUIDs (Utf8Str (child->mFilePathFull),
1957 NULL, NULL, &parentUuid, &parentModUuid);
1958 if (VBOX_FAILURE (vrc))
1959 {
1960 rc = setError (E_FAIL,
1961 tr ("Could not update parent UUID of the hard disk image "
1962 "'%ls' (%Vrc)"),
1963 child->mFilePathFull.raw(), vrc);
1964 break;
1965 }
1966 }
1967
1968 if (FAILED (rc))
1969 return rc;
1970 }
1971
1972 /* detach all our children to avoid their uninit in #uninit() */
1973 removeDependentChildren();
1974
1975 mParent->clearBusy();
1976 clearBusy();
1977
1978 return S_OK;
1979}
1980
1981/**
1982 * Merges this image to all its child images, updates the parent UUID
1983 * of all children of this image (to point to this image's parent).
1984 * It's a responsibility of the caller to unregister and uninitialize
1985 * the merged image on success.
1986 *
1987 * This method is intended to be called on a worker thread (the operation
1988 * can be time consuming).
1989 *
1990 * The specified progress object (if not NULL) receives the percentage
1991 * of the operation completion. However, it is responsibility of the caller to
1992 * call Progress::notifyComplete() after this method returns.
1993 *
1994 * @param aProgress progress object to run during copy operation
1995 * (may be NULL)
1996 *
1997 * @note
1998 * This method expects that both this hard disk and all children
1999 * are marked as busy using setBusyWithChildren() prior to calling it!
2000 * Busy flags of all affected hard disks will be cleared by this method
2001 * on a successful return. In case of failure, #clearBusyWithChildren()
2002 * must be called for this hard disk.
2003 *
2004 * @note
2005 * Must be NOT called from under locks of other objects that need external
2006 * access dirung this method execurion!
2007 */
2008HRESULT HVirtualDiskImage::mergeImageToChildren (Progress *aProgress)
2009{
2010 LogFlowMember (("HVirtualDiskImage::mergeImageToChildren(): image='%ls'\n",
2011 mFilePathFull.raw()));
2012
2013 AutoLock alock (this);
2014 CHECK_READY();
2015
2016 /* this must be a diff image */
2017 AssertReturn (isDifferencing(), E_FAIL);
2018
2019 ComAssertRet (isBusy() == true, E_FAIL);
2020 ComAssertRet (mState >= Created, E_FAIL);
2021
2022 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2023 ("non VDI storage types are not yet supported!"), E_FAIL);
2024
2025 {
2026 HRESULT rc = S_OK;
2027
2028 AutoLock chLock (childrenLock());
2029
2030 /* iterate over a copy since we will modify the list */
2031 HardDiskList list = children();
2032
2033 for (HardDiskList::const_iterator it = list.begin();
2034 it != list.end(); ++ it)
2035 {
2036 ComObjPtr <HardDisk> hd = *it;
2037 ComObjPtr <HVirtualDiskImage> child = hd->asVDI();
2038 AutoLock childLock (child);
2039
2040 ComAssertRet (child->isBusy() == true, E_FAIL);
2041 ComAssertBreak (child->mState >= Created, rc = E_FAIL);
2042
2043 childLock.leave();
2044 alock.leave();
2045
2046 int vrc = VDIMergeImage (Utf8Str (mFilePathFull),
2047 Utf8Str (child->mFilePathFull),
2048 progressCallback,
2049 static_cast <Progress *> (aProgress));
2050 alock.enter();
2051 childLock.enter();
2052
2053 if (VBOX_FAILURE (vrc))
2054 {
2055 rc = setError (E_FAIL,
2056 tr ("Could not merge the hard disk image '%ls' to "
2057 "its parent image '%ls' (%Vrc)"),
2058 mFilePathFull.raw(), child->mFilePathFull.raw(), vrc);
2059 break;
2060 }
2061
2062 /* reparent the child */
2063 child->mParent = mParent;
2064 if (mParent)
2065 mParent->addDependentChild (child);
2066
2067 /* change the parent UUID in the image as well */
2068 RTUUID parentUuid, parentModUuid;
2069 vrc = VDIGetImageUUIDs (Utf8Str (mParent->asVDI()->mFilePathFull),
2070 &parentUuid, &parentModUuid, NULL, NULL);
2071 if (VBOX_FAILURE (vrc))
2072 {
2073 rc = setError (E_FAIL,
2074 tr ("Could not access the hard disk image '%ls' (%Vrc)"),
2075 mParent->asVDI()->mFilePathFull.raw(), vrc);
2076 break;
2077 }
2078 ComAssertBreak (mParent->id() == Guid (parentUuid), rc = E_FAIL);
2079 vrc = VDISetImageUUIDs (Utf8Str (child->mFilePathFull),
2080 NULL, NULL, &parentUuid, &parentModUuid);
2081 if (VBOX_FAILURE (vrc))
2082 {
2083 rc = setError (E_FAIL,
2084 tr ("Could not update parent UUID of the hard disk image "
2085 "'%ls' (%Vrc)"),
2086 child->mFilePathFull.raw(), vrc);
2087 break;
2088 }
2089
2090 /* detach child to avoid its uninit in #uninit() */
2091 removeDependentChild (child);
2092
2093 /* remove the busy flag */
2094 child->clearBusy();
2095 }
2096
2097 if (FAILED (rc))
2098 return rc;
2099 }
2100
2101 clearBusy();
2102
2103 return S_OK;
2104}
2105
2106/**
2107 * Deletes and recreates the differencing hard disk image from scratch.
2108 * The file name and UUID remain the same.
2109 */
2110HRESULT HVirtualDiskImage::wipeOutImage()
2111{
2112 AutoLock alock (this);
2113 CHECK_READY();
2114
2115 AssertReturn (isDifferencing(), E_FAIL);
2116 AssertReturn (mRegistered, E_FAIL);
2117 AssertReturn (mState >= Created, E_FAIL);
2118
2119 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2120 ("non-VDI storage types are not yet supported!"), E_FAIL);
2121
2122 Utf8Str filePathFull = mFilePathFull;
2123
2124 int vrc = RTFileDelete (filePathFull);
2125 if (VBOX_FAILURE (vrc))
2126 return setError (E_FAIL,
2127 tr ("Could not delete the image file '%s' (%Vrc)"),
2128 filePathFull.raw(), vrc);
2129
2130 vrc = VDICreateDifferenceImage (filePathFull,
2131 Utf8Str (mParent->asVDI()->mFilePathFull),
2132 NULL, NULL, NULL);
2133 /* update the UUID to correspond to the file name */
2134 if (VBOX_SUCCESS (vrc))
2135 vrc = VDISetImageUUIDs (filePathFull, mId, NULL, NULL, NULL);
2136
2137 if (VBOX_FAILURE (vrc))
2138 return setError (E_FAIL,
2139 tr ("Could not create a differencing hard disk '%s' (%Vrc)"),
2140 filePathFull.raw(), vrc);
2141
2142 return S_OK;
2143}
2144
2145// private methods
2146/////////////////////////////////////////////////////////////////////////////
2147
2148/**
2149 * Helper to set a new file path.
2150 * Resolves a path relatively to the Virtual Box home directory.
2151 *
2152 * @note
2153 * Must be called from under the object's lock!
2154 */
2155HRESULT HVirtualDiskImage::setFilePath (const BSTR aFilePath)
2156{
2157 if (aFilePath && *aFilePath)
2158 {
2159 /* get the full file name */
2160 char filePathFull [RTPATH_MAX];
2161 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aFilePath),
2162 filePathFull, sizeof (filePathFull));
2163 if (VBOX_FAILURE (vrc))
2164 return setError (E_FAIL,
2165 tr ("Invalid image file path '%ls' (%Vrc)"), aFilePath, vrc);
2166
2167 mFilePath = aFilePath;
2168 mFilePathFull = filePathFull;
2169 }
2170 else
2171 {
2172 mFilePath.setNull();
2173 mFilePathFull.setNull();
2174 }
2175
2176 return S_OK;
2177}
2178
2179/**
2180 * Helper to query information about the VDI hard disk.
2181 *
2182 * @param aAccessError not used when NULL, otherwise see #getAccessible()
2183 *
2184 * @note Must be called from under the object's lock, only after
2185 * CHECK_BUSY_AND_READERS() succeeds.
2186 */
2187HRESULT HVirtualDiskImage::queryInformation (Bstr *aAccessError)
2188{
2189 AssertReturn (isLockedOnCurrentThread(), E_FAIL);
2190
2191 /* create a lock object to completely release it later */
2192 AutoLock alock (this);
2193
2194 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
2195
2196 ComAssertRet (mState >= Created, E_FAIL);
2197
2198 HRESULT rc = S_OK;
2199 int vrc = VINF_SUCCESS;
2200
2201 /* lazily create a semaphore */
2202 vrc = RTSemEventMultiCreate (&mStateCheckSem);
2203 ComAssertRCRet (vrc, E_FAIL);
2204
2205 /* go to Busy state to prevent any concurrent modifications
2206 * after releasing the lock below (to unblock getters before
2207 * a lengthy operation) */
2208 setBusy();
2209
2210 alock.leave();
2211
2212 /* VBoxVHDD management interface needs to be optimized: we're opening a
2213 * file three times in a raw to get three bits of information. */
2214
2215 Utf8Str filePath = mFilePathFull;
2216 Bstr errMsg;
2217
2218 do
2219 {
2220 /* check the image file */
2221 Guid id, parentId;
2222 vrc = VDICheckImage (filePath, NULL, NULL, NULL,
2223 id.ptr(), parentId.ptr(), NULL, 0);
2224
2225 if (VBOX_FAILURE (vrc))
2226 break;
2227
2228 if (!mId.isEmpty())
2229 {
2230 /* check that the actual UUID of the image matches the stored UUID */
2231 if (mId != id)
2232 {
2233 errMsg = Utf8StrFmt (
2234 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
2235 "match UUID {%Vuuid} stored in the registry"),
2236 id.ptr(), filePath.raw(), mId.ptr());
2237 break;
2238 }
2239 }
2240 else
2241 {
2242 /* assgn an UUID read from the image file */
2243 mId = id;
2244 }
2245
2246 if (mParent)
2247 {
2248 /* check parent UUID */
2249 AutoLock parentLock (mParent);
2250 if (mParent->id() != parentId)
2251 {
2252 errMsg = Utf8StrFmt (
2253 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
2254 "the hard disk image file '%s' doesn't match "
2255 "UUID {%Vuuid} stored in the registry"),
2256 parentId.ptr(), mParent->toString().raw(),
2257 filePath.raw(), mParent->id().raw());
2258 break;
2259 }
2260 }
2261 else if (!parentId.isEmpty())
2262 {
2263 errMsg = Utf8StrFmt (
2264 tr ("Hard disk image '%s' is a differencing image and "
2265 "cannot be opened directly"),
2266 filePath.raw());
2267 break;
2268 }
2269
2270 {
2271 RTFILE file = NIL_RTFILE;
2272 vrc = RTFileOpen (&file, filePath, RTFILE_O_READ);
2273 if (VBOX_SUCCESS (vrc))
2274 {
2275 uint64_t size = 0;
2276 vrc = RTFileGetSize (file, &size);
2277 if (VBOX_SUCCESS (vrc))
2278 mActualSize = size;
2279 RTFileClose (file);
2280 }
2281 if (VBOX_FAILURE (vrc))
2282 break;
2283 }
2284
2285 if (!mParent)
2286 {
2287 /* query logical size only for non-differencing images */
2288
2289 PVDIDISK disk = VDIDiskCreate();
2290 vrc = VDIDiskOpenImage (disk, Utf8Str (mFilePathFull),
2291 VDI_OPEN_FLAGS_READONLY);
2292 if (VBOX_SUCCESS (vrc))
2293 {
2294 uint64_t size = VDIDiskGetSize (disk);
2295 /* convert to MBytes */
2296 mSize = size / 1024 / 1024;
2297 }
2298
2299 VDIDiskDestroy (disk);
2300 if (VBOX_FAILURE (vrc))
2301 break;
2302 }
2303 }
2304 while (0);
2305
2306 /* enter the lock again */
2307 alock.enter();
2308
2309 /* remove the busy flag */
2310 clearBusy();
2311
2312 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
2313 {
2314 LogWarningFunc (("'%ls' is not accessible "
2315 "(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
2316 mFilePathFull.raw(), rc, vrc, errMsg.raw()));
2317
2318 if (aAccessError)
2319 {
2320 if (!errMsg.isNull())
2321 *aAccessError = errMsg;
2322 else if (VBOX_FAILURE (vrc))
2323 *aAccessError = Utf8StrFmt (
2324 tr ("Could not access hard disk image '%ls' (%Vrc)"),
2325 mFilePathFull.raw(), vrc);
2326 }
2327
2328 /* downgrade to not accessible */
2329 mState = Created;
2330 }
2331 else
2332 {
2333 if (aAccessError)
2334 aAccessError->setNull();
2335
2336 mState = Accessible;
2337 }
2338
2339 /* inform waiters if there are any */
2340 if (mStateCheckWaiters > 0)
2341 {
2342 RTSemEventMultiSignal (mStateCheckSem);
2343 }
2344 else
2345 {
2346 /* delete the semaphore ourselves */
2347 RTSemEventMultiDestroy (mStateCheckSem);
2348 mStateCheckSem = NIL_RTSEMEVENTMULTI;
2349 }
2350
2351 return rc;
2352}
2353
2354/**
2355 * Helper to create hard disk images.
2356 *
2357 * @param aSize size in MB
2358 * @param aDynamic dynamic or fixed image
2359 * @param aProgress address of IProgress pointer to return
2360 */
2361HRESULT HVirtualDiskImage::createImage (ULONG64 aSize, BOOL aDynamic,
2362 IProgress **aProgress)
2363{
2364 AutoLock alock (this);
2365
2366 CHECK_BUSY_AND_READERS();
2367
2368 if (mState != NotCreated)
2369 return setError (E_ACCESSDENIED,
2370 tr ("Hard disk image '%ls' is already created"),
2371 mFilePathFull.raw());
2372
2373 if (!mFilePathFull)
2374 return setError (E_ACCESSDENIED,
2375 tr ("Cannot create a hard disk image using an empty (null) file path"),
2376 mFilePathFull.raw());
2377
2378 /* first ensure the directory exists */
2379 {
2380 Utf8Str imageDir = mFilePathFull;
2381 RTPathStripFilename (imageDir.mutableRaw());
2382 if (!RTDirExists (imageDir))
2383 {
2384 int vrc = RTDirCreateFullPath (imageDir, 0777);
2385 if (VBOX_FAILURE (vrc))
2386 {
2387 return setError (E_FAIL,
2388 tr ("Could not create a directory '%s' "
2389 "to store the image file (%Vrc)"),
2390 imageDir.raw(), vrc);
2391 }
2392 }
2393 }
2394
2395 /* check whether the given file exists or not */
2396 RTFILE file;
2397 int vrc = RTFileOpen (&file, Utf8Str (mFilePathFull),
2398 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
2399 if (vrc != VERR_FILE_NOT_FOUND)
2400 {
2401 if (VBOX_SUCCESS (vrc))
2402 RTFileClose (file);
2403 switch(vrc)
2404 {
2405 case VINF_SUCCESS:
2406 return setError (E_FAIL,
2407 tr ("Image file '%ls' already exists"),
2408 mFilePathFull.raw());
2409
2410 default:
2411 return setError(E_FAIL,
2412 tr ("Invalid image file path '%ls' (%Vrc)"),
2413 mFilePathFull.raw(), vrc);
2414 }
2415 }
2416
2417 /* check VDI size limits */
2418 {
2419 HRESULT rc;
2420 ULONG64 maxVDISize;
2421 ComPtr <ISystemProperties> props;
2422 rc = mVirtualBox->COMGETTER(SystemProperties) (props.asOutParam());
2423 ComAssertComRCRet (rc, E_FAIL);
2424 rc = props->COMGETTER(MaxVDISize) (&maxVDISize);
2425 ComAssertComRCRet (rc, E_FAIL);
2426
2427 if (aSize < 1 || aSize > maxVDISize)
2428 return setError (E_INVALIDARG,
2429 tr ("Invalid VDI size: %llu MB (must be in range [1, %llu] MB)"),
2430 aSize, maxVDISize);
2431 }
2432
2433 HRESULT rc;
2434
2435 /* create a project object */
2436 ComObjPtr <Progress> progress;
2437 progress.createObject();
2438 {
2439 Bstr desc = aDynamic ? tr ("Creating a dynamically expanding hard disk")
2440 : tr ("Creating a fixed-size hard disk");
2441 rc = progress->init (mVirtualBox, (IVirtualDiskImage *) this, desc,
2442 FALSE /* aCancelable */);
2443 CheckComRCReturnRC (rc);
2444 }
2445
2446 /* mark as busy (being created)
2447 * (VDI task thread will unmark it) */
2448 setBusy();
2449
2450 /* fill in VDI task data */
2451 VDITask *task = new VDITask (aDynamic ? VDITask::CreateDynamic
2452 : VDITask::CreateStatic,
2453 this, progress);
2454 task->size = aSize;
2455 task->size *= 1024 * 1024; /* convert to bytes */
2456
2457 /* create the hard disk creation thread, pass operation data */
2458 vrc = RTThreadCreate (NULL, vdiTaskThread, (void *) task, 0,
2459 RTTHREADTYPE_MAIN_HEAVY_WORKER, 0, "VDITask");
2460 ComAssertMsgRC (vrc, ("Could not create a thread (%Vrc)", vrc));
2461 if (VBOX_FAILURE (vrc))
2462 {
2463 clearBusy();
2464 delete task;
2465 rc = E_FAIL;
2466 }
2467 else
2468 {
2469 /* get one interface for the caller */
2470 progress.queryInterfaceTo (aProgress);
2471 }
2472
2473 return rc;
2474}
2475
2476/* static */
2477DECLCALLBACK(int) HVirtualDiskImage::vdiTaskThread (RTTHREAD thread, void *pvUser)
2478{
2479 VDITask *task = static_cast <VDITask *> (pvUser);
2480 AssertReturn (task, VERR_GENERAL_FAILURE);
2481
2482 LogFlow (("vdiTaskThread(): operation=%d, size=%llu\n",
2483 task->operation, task->size));
2484
2485 VDIIMAGETYPE type = (VDIIMAGETYPE) 0;
2486
2487 switch (task->operation)
2488 {
2489 case VDITask::CreateDynamic: type = VDI_IMAGE_TYPE_NORMAL; break;
2490 case VDITask::CreateStatic: type = VDI_IMAGE_TYPE_FIXED; break;
2491 case VDITask::CloneToImage: break;
2492 default: AssertFailedReturn (VERR_GENERAL_FAILURE); break;
2493 }
2494
2495 HRESULT rc = S_OK;
2496 Utf8Str errorMsg;
2497
2498 if (task->operation == VDITask::CloneToImage)
2499 {
2500 Assert (!task->vdi->id().isEmpty());
2501 /// @todo (dmik) check locks
2502 AutoLock sourceLock (task->source);
2503 rc = task->source->cloneToImage (task->vdi->id(),
2504 Utf8Str (task->vdi->filePathFull()),
2505 task->progress);
2506
2507 /* release reader added in HardDisk::CloneToImage() */
2508 task->source->releaseReader();
2509 }
2510 else
2511 {
2512 int vrc = VDICreateBaseImage (Utf8Str (task->vdi->filePathFull()),
2513 type, task->size,
2514 Utf8Str (task->vdi->mDescription),
2515 progressCallback,
2516 static_cast <Progress *> (task->progress));
2517
2518 if (VBOX_SUCCESS (vrc) && task->vdi->id())
2519 {
2520 /* we have a non-null UUID, update the created image */
2521 vrc = VDISetImageUUIDs (Utf8Str (task->vdi->filePathFull()),
2522 task->vdi->id().raw(), NULL, NULL, NULL);
2523 }
2524
2525 if (VBOX_FAILURE (vrc))
2526 {
2527 errorMsg = Utf8StrFmt (
2528 tr ("Falied to create a hard disk image '%ls' (%Vrc)"),
2529 task->vdi->filePathFull().raw(), vrc);
2530 rc = E_FAIL;
2531 }
2532 }
2533
2534 LogFlow (("vdiTaskThread(): rc=%08X\n", rc));
2535
2536 AutoLock alock (task->vdi);
2537
2538 /* clear busy set in in HardDisk::CloneToImage() or
2539 * in HVirtualDiskImage::createImage() */
2540 task->vdi->clearBusy();
2541
2542 if (SUCCEEDED (rc))
2543 {
2544 task->vdi->mState = HVirtualDiskImage::Created;
2545 /* update VDI data fields */
2546 Bstr errMsg;
2547 rc = task->vdi->queryInformation (&errMsg);
2548 /* we want to deliver the access check result to the caller
2549 * immediately, before he calls HardDisk::GetAccssible() himself. */
2550 if (SUCCEEDED (rc) && !errMsg.isNull())
2551 task->progress->notifyCompleteBstr (
2552 E_FAIL, COM_IIDOF (IVirtualDiskImage), getComponentName(),
2553 errMsg);
2554 else
2555 task->progress->notifyComplete (rc);
2556 }
2557 else
2558 {
2559 /* delete the target file so we don't have orphaned files */
2560 RTFileDelete(Utf8Str (task->vdi->filePathFull()));
2561
2562 task->vdi->mState = HVirtualDiskImage::NotCreated;
2563 /* complete the progress object */
2564 if (errorMsg)
2565 task->progress->notifyComplete (
2566 E_FAIL, COM_IIDOF (IVirtualDiskImage), getComponentName(),
2567 errorMsg);
2568 else
2569 task->progress->notifyComplete (rc);
2570 }
2571
2572 delete task;
2573
2574 return VINF_SUCCESS;
2575}
2576
2577////////////////////////////////////////////////////////////////////////////////
2578// HISCSIHardDisk class
2579////////////////////////////////////////////////////////////////////////////////
2580
2581// constructor / destructor
2582////////////////////////////////////////////////////////////////////////////////
2583
2584HRESULT HISCSIHardDisk::FinalConstruct()
2585{
2586 HRESULT rc = HardDisk::FinalConstruct();
2587 if (FAILED (rc))
2588 return rc;
2589
2590 mSize = 0;
2591 mActualSize = 0;
2592
2593 mPort = 0;
2594 mLun = 0;
2595
2596 return S_OK;
2597}
2598
2599void HISCSIHardDisk::FinalRelease()
2600{
2601 HardDisk::FinalRelease();
2602}
2603
2604// public initializer/uninitializer for internal purposes only
2605////////////////////////////////////////////////////////////////////////////////
2606
2607// public methods for internal purposes only
2608/////////////////////////////////////////////////////////////////////////////
2609
2610/**
2611 * Initializes the iSCSI hard disk object by reading its properties from
2612 * the given configuration node. The created hard disk will be marked as
2613 * registered on success.
2614 *
2615 * @param aHDNode <HardDisk> node
2616 * @param aVDINod <ISCSIHardDisk> node
2617 */
2618HRESULT HISCSIHardDisk::init (VirtualBox *aVirtualBox,
2619 CFGNODE aHDNode, CFGNODE aISCSINode)
2620{
2621 LogFlowMember (("HISCSIHardDisk::init (load)\n"));
2622
2623 AssertReturn (aHDNode && aISCSINode, E_FAIL);
2624
2625 AutoLock alock (this);
2626 ComAssertRet (!isReady(), E_UNEXPECTED);
2627
2628 mStorageType = HardDiskStorageType_ISCSIHardDisk;
2629
2630 HRESULT rc = S_OK;
2631
2632 do
2633 {
2634 rc = protectedInit (aVirtualBox, NULL);
2635 CheckComRCBreakRC (rc);
2636
2637 /* set ready to let protectedUninit() be called on failure */
2638 setReady (true);
2639
2640 /* server (required) */
2641 CFGLDRQueryBSTR (aISCSINode, "server", mServer.asOutParam());
2642 /* target (required) */
2643 CFGLDRQueryBSTR (aISCSINode, "target", mTarget.asOutParam());
2644
2645 /* port (optional) */
2646 CFGLDRQueryUInt16 (aISCSINode, "port", &mPort);
2647 /* lun (optional) */
2648 CFGLDRQueryUInt64 (aISCSINode, "lun", &mLun);
2649 /* userName (optional) */
2650 CFGLDRQueryBSTR (aISCSINode, "userName", mUserName.asOutParam());
2651 /* password (optional) */
2652 CFGLDRQueryBSTR (aISCSINode, "password", mPassword.asOutParam());
2653
2654 LogFlowMember ((" 'iscsi:%ls:%hu@%ls/%ls:%llu'\n",
2655 mServer.raw(), mPort, mUserName.raw(), mTarget.raw(),
2656 mLun));
2657
2658 /* load basic settings and children */
2659 rc = loadSettings (aHDNode);
2660 CheckComRCBreakRC (rc);
2661
2662 if (mType != HardDiskType_WritethroughHardDisk)
2663 {
2664 rc = setError (E_FAIL,
2665 tr ("Currently, non-Writethrough iSCSI hard disks are not "
2666 "allowed ('%ls')"),
2667 toString().raw());
2668 break;
2669 }
2670
2671 mRegistered = TRUE;
2672 }
2673 while (0);
2674
2675 if (FAILED (rc))
2676 uninit();
2677
2678 return rc;
2679}
2680
2681/**
2682 * Initializes the iSCSI hard disk object using default values for all
2683 * properties. The created hard disk will NOT be marked as registered.
2684 */
2685HRESULT HISCSIHardDisk::init (VirtualBox *aVirtualBox)
2686{
2687 LogFlowMember (("HISCSIHardDisk::init()\n"));
2688
2689 AutoLock alock (this);
2690 ComAssertRet (!isReady(), E_UNEXPECTED);
2691
2692 mStorageType = HardDiskStorageType_ISCSIHardDisk;
2693
2694 HRESULT rc = S_OK;
2695
2696 do
2697 {
2698 rc = protectedInit (aVirtualBox, NULL);
2699 CheckComRCBreakRC (rc);
2700
2701 /* set ready to let protectedUninit() be called on failure */
2702 setReady (true);
2703
2704 /* we have to generate a new UUID */
2705 mId.create();
2706 mType = HardDiskType_WritethroughHardDisk;
2707 mRegistered = FALSE;
2708 }
2709 while (0);
2710
2711 if (FAILED (rc))
2712 uninit();
2713
2714 return rc;
2715}
2716
2717/**
2718 * Uninitializes the instance and sets the ready flag to FALSE.
2719 * Called either from FinalRelease(), by the parent when it gets destroyed,
2720 * or by a third party when it decides this object is no more valid.
2721 */
2722void HISCSIHardDisk::uninit()
2723{
2724 LogFlowMember (("HISCSIHardDisk::uninit()\n"));
2725
2726 AutoLock alock (this);
2727 if (!isReady())
2728 return;
2729
2730 HardDisk::protectedUninit (alock);
2731}
2732
2733// IHardDisk properties
2734////////////////////////////////////////////////////////////////////////////////
2735
2736STDMETHODIMP HISCSIHardDisk::COMGETTER(Description) (BSTR *aDescription)
2737{
2738 if (!aDescription)
2739 return E_POINTER;
2740
2741 AutoLock alock (this);
2742 CHECK_READY();
2743
2744 mDescription.cloneTo (aDescription);
2745 return S_OK;
2746}
2747
2748STDMETHODIMP HISCSIHardDisk::COMSETTER(Description) (INPTR BSTR aDescription)
2749{
2750 AutoLock alock (this);
2751 CHECK_READY();
2752
2753 CHECK_BUSY_AND_READERS();
2754
2755 if (mDescription != aDescription)
2756 {
2757 mDescription = aDescription;
2758 if (mRegistered)
2759 return mVirtualBox->saveSettings();
2760 }
2761
2762 return S_OK;
2763}
2764
2765STDMETHODIMP HISCSIHardDisk::COMGETTER(Size) (ULONG64 *aSize)
2766{
2767 if (!aSize)
2768 return E_POINTER;
2769
2770 AutoLock alock (this);
2771 CHECK_READY();
2772
2773 *aSize = mSize;
2774 return S_OK;
2775}
2776
2777STDMETHODIMP HISCSIHardDisk::COMGETTER(ActualSize) (ULONG64 *aActualSize)
2778{
2779 if (!aActualSize)
2780 return E_POINTER;
2781
2782 AutoLock alock (this);
2783 CHECK_READY();
2784
2785 *aActualSize = mActualSize;
2786 return S_OK;
2787}
2788
2789// IISCSIHardDisk properties
2790////////////////////////////////////////////////////////////////////////////////
2791
2792STDMETHODIMP HISCSIHardDisk::COMGETTER(Server) (BSTR *aServer)
2793{
2794 if (!aServer)
2795 return E_POINTER;
2796
2797 AutoLock alock (this);
2798 CHECK_READY();
2799
2800 mServer.cloneTo (aServer);
2801 return S_OK;
2802}
2803
2804STDMETHODIMP HISCSIHardDisk::COMSETTER(Server) (INPTR BSTR aServer)
2805{
2806 if (!aServer || !*aServer)
2807 return E_INVALIDARG;
2808
2809 AutoLock alock (this);
2810 CHECK_READY();
2811
2812 CHECK_BUSY_AND_READERS();
2813
2814 if (mServer != aServer)
2815 {
2816 mServer = aServer;
2817 if (mRegistered)
2818 return mVirtualBox->saveSettings();
2819 }
2820
2821 return S_OK;
2822}
2823
2824STDMETHODIMP HISCSIHardDisk::COMGETTER(Port) (USHORT *aPort)
2825{
2826 if (!aPort)
2827 return E_POINTER;
2828
2829 AutoLock alock (this);
2830 CHECK_READY();
2831
2832 *aPort = mPort;
2833 return S_OK;
2834}
2835
2836STDMETHODIMP HISCSIHardDisk::COMSETTER(Port) (USHORT aPort)
2837{
2838 AutoLock alock (this);
2839 CHECK_READY();
2840
2841 CHECK_BUSY_AND_READERS();
2842
2843 if (mPort != aPort)
2844 {
2845 mPort = aPort;
2846 if (mRegistered)
2847 return mVirtualBox->saveSettings();
2848 }
2849
2850 return S_OK;
2851}
2852
2853STDMETHODIMP HISCSIHardDisk::COMGETTER(Target) (BSTR *aTarget)
2854{
2855 if (!aTarget)
2856 return E_POINTER;
2857
2858 AutoLock alock (this);
2859 CHECK_READY();
2860
2861 mTarget.cloneTo (aTarget);
2862 return S_OK;
2863}
2864
2865STDMETHODIMP HISCSIHardDisk::COMSETTER(Target) (INPTR BSTR aTarget)
2866{
2867 if (!aTarget || !*aTarget)
2868 return E_INVALIDARG;
2869
2870 AutoLock alock (this);
2871 CHECK_READY();
2872
2873 CHECK_BUSY_AND_READERS();
2874
2875 if (mTarget != aTarget)
2876 {
2877 mTarget = aTarget;
2878 if (mRegistered)
2879 return mVirtualBox->saveSettings();
2880 }
2881
2882 return S_OK;
2883}
2884
2885STDMETHODIMP HISCSIHardDisk::COMGETTER(Lun) (ULONG64 *aLun)
2886{
2887 if (!aLun)
2888 return E_POINTER;
2889
2890 AutoLock alock (this);
2891 CHECK_READY();
2892
2893 *aLun = mLun;
2894 return S_OK;
2895}
2896
2897STDMETHODIMP HISCSIHardDisk::COMSETTER(Lun) (ULONG64 aLun)
2898{
2899 AutoLock alock (this);
2900 CHECK_READY();
2901
2902 CHECK_BUSY_AND_READERS();
2903
2904 if (mLun != aLun)
2905 {
2906 mLun = aLun;
2907 if (mRegistered)
2908 return mVirtualBox->saveSettings();
2909 }
2910
2911 return S_OK;
2912}
2913
2914STDMETHODIMP HISCSIHardDisk::COMGETTER(UserName) (BSTR *aUserName)
2915{
2916 if (!aUserName)
2917 return E_POINTER;
2918
2919 AutoLock alock (this);
2920 CHECK_READY();
2921
2922 mUserName.cloneTo (aUserName);
2923 return S_OK;
2924}
2925
2926STDMETHODIMP HISCSIHardDisk::COMSETTER(UserName) (INPTR BSTR aUserName)
2927{
2928 AutoLock alock (this);
2929 CHECK_READY();
2930
2931 CHECK_BUSY_AND_READERS();
2932
2933 if (mUserName != aUserName)
2934 {
2935 mUserName = aUserName;
2936 if (mRegistered)
2937 return mVirtualBox->saveSettings();
2938 }
2939
2940 return S_OK;
2941}
2942
2943STDMETHODIMP HISCSIHardDisk::COMGETTER(Password) (BSTR *aPassword)
2944{
2945 if (!aPassword)
2946 return E_POINTER;
2947
2948 AutoLock alock (this);
2949 CHECK_READY();
2950
2951 mPassword.cloneTo (aPassword);
2952 return S_OK;
2953}
2954
2955STDMETHODIMP HISCSIHardDisk::COMSETTER(Password) (INPTR BSTR aPassword)
2956{
2957 AutoLock alock (this);
2958 CHECK_READY();
2959
2960 CHECK_BUSY_AND_READERS();
2961
2962 if (mPassword != aPassword)
2963 {
2964 mPassword = aPassword;
2965 if (mRegistered)
2966 return mVirtualBox->saveSettings();
2967 }
2968
2969 return S_OK;
2970}
2971
2972// public/protected methods for internal purposes only
2973/////////////////////////////////////////////////////////////////////////////
2974
2975/**
2976 * Attempts to mark the hard disk as registered.
2977 * Only VirtualBox can call this method.
2978 */
2979HRESULT HISCSIHardDisk::trySetRegistered (BOOL aRegistered)
2980{
2981 AutoLock alock (this);
2982 CHECK_READY();
2983
2984 if (aRegistered)
2985 {
2986 if (mServer.isEmpty() || mTarget.isEmpty())
2987 return setError (E_FAIL,
2988 tr ("iSCSI Hard disk has no server or target defined"));
2989 }
2990 else
2991 {
2992 }
2993
2994 return HardDisk::trySetRegistered (aRegistered);
2995}
2996
2997/**
2998 * Checks accessibility of this iSCSI hard disk.
2999 */
3000HRESULT HISCSIHardDisk::getAccessible (Bstr &aAccessError)
3001{
3002 AutoLock alock (this);
3003 CHECK_READY();
3004
3005 /* check the basic accessibility */
3006 HRESULT rc = HardDisk::getAccessible (aAccessError);
3007 if (FAILED (rc) || !aAccessError.isNull())
3008 return rc;
3009
3010 return queryInformation (aAccessError);
3011}
3012
3013/**
3014 * Saves hard disk settings to the specified storage node and saves
3015 * all children to the specified hard disk node
3016 *
3017 * @param aHDNode <HardDisk>
3018 * @param aStorageNode <ISCSIHardDisk> node
3019 */
3020HRESULT HISCSIHardDisk::saveSettings (CFGNODE aHDNode, CFGNODE aStorageNode)
3021{
3022 AssertReturn (aHDNode && aStorageNode, E_FAIL);
3023
3024 AutoLock alock (this);
3025 CHECK_READY();
3026
3027 /* server (required) */
3028 CFGLDRSetBSTR (aStorageNode, "server", mServer);
3029 /* target (required) */
3030 CFGLDRSetBSTR (aStorageNode, "target", mTarget);
3031
3032 /* port (optional) */
3033 if (mPort != 0)
3034 CFGLDRSetUInt16 (aStorageNode, "port", mPort);
3035 else
3036 CFGLDRDeleteAttribute (aStorageNode, "port");
3037 /* lun (optional) */
3038 if (mLun != 0)
3039 CFGLDRSetUInt64 (aStorageNode, "lun", mLun);
3040 else
3041 CFGLDRDeleteAttribute (aStorageNode, "lun");
3042 /* userName (optional) */
3043 if (!mUserName.isNull())
3044 CFGLDRSetBSTR (aStorageNode, "userName", mUserName);
3045 else
3046 CFGLDRDeleteAttribute (aStorageNode, "userName");
3047 /* password (optional) */
3048 if (!mPassword.isNull())
3049 CFGLDRSetBSTR (aStorageNode, "password", mPassword);
3050 else
3051 CFGLDRDeleteAttribute (aStorageNode, "password");
3052
3053 /* save basic settings and children */
3054 return HardDisk::saveSettings (aHDNode);
3055}
3056
3057/**
3058 * Returns the string representation of this hard disk.
3059 * When \a aShort is false, returns the full image file path.
3060 * Otherwise, returns the image file name only.
3061 *
3062 * @param aShort if true, a short representation is returned
3063 */
3064Bstr HISCSIHardDisk::toString (bool aShort /* = false */)
3065{
3066 AutoLock alock (this);
3067
3068 Bstr str;
3069 if (!aShort)
3070 {
3071 /* the format is iscsi:[<user@>]<server>[:<port>]/<target>[:<lun>] */
3072 str = Utf8StrFmt ("iscsi:%s%ls%s/%ls%s",
3073 !mUserName.isNull() ? Utf8StrFmt ("%ls@", mUserName.raw()).raw() : "",
3074 mServer.raw(),
3075 mPort != 0 ? Utf8StrFmt (":%hu", mPort).raw() : "",
3076 mTarget.raw(),
3077 mLun != 0 ? Utf8StrFmt (":%llu", mLun).raw() : "");
3078 }
3079 else
3080 {
3081 str = Utf8StrFmt ("%ls%s",
3082 mTarget.raw(),
3083 mLun != 0 ? Utf8StrFmt (":%llu", mLun).raw() : "");
3084 }
3085
3086 return str;
3087}
3088
3089/**
3090 * Creates a clone of this hard disk by storing hard disk data in the given
3091 * VDI file name.
3092 *
3093 * @param aId UUID to assign to the created image
3094 * @param aTargetPath VDI file where the cloned image is to be to stored
3095 * @param aProgress progress object to run during operation
3096 */
3097HRESULT
3098HISCSIHardDisk::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
3099 Progress *aProgress)
3100{
3101 ComAssertMsgFailed (("Not implemented"));
3102 return E_NOTIMPL;
3103
3104// AssertReturn (isBusy() == false, E_FAIL);
3105// addReader();
3106// releaseReader();
3107}
3108
3109/**
3110 * Creates a new differencing image for this hard disk with the given
3111 * VDI file name.
3112 *
3113 * @param aId UUID to assign to the created image
3114 * @param aTargetPath VDI file where to store the created differencing image
3115 * @param aProgress progress object to run during operation
3116 * (can be NULL)
3117 */
3118HRESULT
3119HISCSIHardDisk::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
3120 Progress *aProgress)
3121{
3122 ComAssertMsgFailed (("Not implemented"));
3123 return E_NOTIMPL;
3124
3125// AssertReturn (isBusy() == false, E_FAIL);
3126// addReader();
3127// releaseReader();
3128}
3129
3130// private methods
3131/////////////////////////////////////////////////////////////////////////////
3132
3133/**
3134 * Helper to query information about the iSCSI hard disk.
3135 *
3136 * @param aAccessError see #getAccessible()
3137 * @note
3138 * Must be called from under the object's lock!
3139 */
3140HRESULT HISCSIHardDisk::queryInformation (Bstr &aAccessError)
3141{
3142 /// @todo (dmik) query info about this iSCSI disk,
3143 // set mSize and mActualSize,
3144 // or set aAccessError in case of failure
3145
3146 aAccessError.setNull();
3147 return S_OK;
3148}
3149
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