VirtualBox

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

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

Main: Corrected HardDisk error message text.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 86.6 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.raw(), 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 that is linked "
2265 "to a hard disk with UUID {%Vuuid} and cannot be used "
2266 "directly as a base hard disk"),
2267 filePath.raw(), parentId.raw());
2268 break;
2269 }
2270
2271 {
2272 RTFILE file = NIL_RTFILE;
2273 vrc = RTFileOpen (&file, filePath, RTFILE_O_READ);
2274 if (VBOX_SUCCESS (vrc))
2275 {
2276 uint64_t size = 0;
2277 vrc = RTFileGetSize (file, &size);
2278 if (VBOX_SUCCESS (vrc))
2279 mActualSize = size;
2280 RTFileClose (file);
2281 }
2282 if (VBOX_FAILURE (vrc))
2283 break;
2284 }
2285
2286 if (!mParent)
2287 {
2288 /* query logical size only for non-differencing images */
2289
2290 PVDIDISK disk = VDIDiskCreate();
2291 vrc = VDIDiskOpenImage (disk, Utf8Str (mFilePathFull),
2292 VDI_OPEN_FLAGS_READONLY);
2293 if (VBOX_SUCCESS (vrc))
2294 {
2295 uint64_t size = VDIDiskGetSize (disk);
2296 /* convert to MBytes */
2297 mSize = size / 1024 / 1024;
2298 }
2299
2300 VDIDiskDestroy (disk);
2301 if (VBOX_FAILURE (vrc))
2302 break;
2303 }
2304 }
2305 while (0);
2306
2307 /* enter the lock again */
2308 alock.enter();
2309
2310 /* remove the busy flag */
2311 clearBusy();
2312
2313 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
2314 {
2315 LogWarningFunc (("'%ls' is not accessible "
2316 "(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
2317 mFilePathFull.raw(), rc, vrc, errMsg.raw()));
2318
2319 if (aAccessError)
2320 {
2321 if (!errMsg.isNull())
2322 *aAccessError = errMsg;
2323 else if (VBOX_FAILURE (vrc))
2324 *aAccessError = Utf8StrFmt (
2325 tr ("Could not access hard disk image '%ls' (%Vrc)"),
2326 mFilePathFull.raw(), vrc);
2327 }
2328
2329 /* downgrade to not accessible */
2330 mState = Created;
2331 }
2332 else
2333 {
2334 if (aAccessError)
2335 aAccessError->setNull();
2336
2337 mState = Accessible;
2338 }
2339
2340 /* inform waiters if there are any */
2341 if (mStateCheckWaiters > 0)
2342 {
2343 RTSemEventMultiSignal (mStateCheckSem);
2344 }
2345 else
2346 {
2347 /* delete the semaphore ourselves */
2348 RTSemEventMultiDestroy (mStateCheckSem);
2349 mStateCheckSem = NIL_RTSEMEVENTMULTI;
2350 }
2351
2352 return rc;
2353}
2354
2355/**
2356 * Helper to create hard disk images.
2357 *
2358 * @param aSize size in MB
2359 * @param aDynamic dynamic or fixed image
2360 * @param aProgress address of IProgress pointer to return
2361 */
2362HRESULT HVirtualDiskImage::createImage (ULONG64 aSize, BOOL aDynamic,
2363 IProgress **aProgress)
2364{
2365 AutoLock alock (this);
2366
2367 CHECK_BUSY_AND_READERS();
2368
2369 if (mState != NotCreated)
2370 return setError (E_ACCESSDENIED,
2371 tr ("Hard disk image '%ls' is already created"),
2372 mFilePathFull.raw());
2373
2374 if (!mFilePathFull)
2375 return setError (E_ACCESSDENIED,
2376 tr ("Cannot create a hard disk image using an empty (null) file path"),
2377 mFilePathFull.raw());
2378
2379 /* first ensure the directory exists */
2380 {
2381 Utf8Str imageDir = mFilePathFull;
2382 RTPathStripFilename (imageDir.mutableRaw());
2383 if (!RTDirExists (imageDir))
2384 {
2385 int vrc = RTDirCreateFullPath (imageDir, 0777);
2386 if (VBOX_FAILURE (vrc))
2387 {
2388 return setError (E_FAIL,
2389 tr ("Could not create a directory '%s' "
2390 "to store the image file (%Vrc)"),
2391 imageDir.raw(), vrc);
2392 }
2393 }
2394 }
2395
2396 /* check whether the given file exists or not */
2397 RTFILE file;
2398 int vrc = RTFileOpen (&file, Utf8Str (mFilePathFull),
2399 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
2400 if (vrc != VERR_FILE_NOT_FOUND)
2401 {
2402 if (VBOX_SUCCESS (vrc))
2403 RTFileClose (file);
2404 switch(vrc)
2405 {
2406 case VINF_SUCCESS:
2407 return setError (E_FAIL,
2408 tr ("Image file '%ls' already exists"),
2409 mFilePathFull.raw());
2410
2411 default:
2412 return setError(E_FAIL,
2413 tr ("Invalid image file path '%ls' (%Vrc)"),
2414 mFilePathFull.raw(), vrc);
2415 }
2416 }
2417
2418 /* check VDI size limits */
2419 {
2420 HRESULT rc;
2421 ULONG64 maxVDISize;
2422 ComPtr <ISystemProperties> props;
2423 rc = mVirtualBox->COMGETTER(SystemProperties) (props.asOutParam());
2424 ComAssertComRCRet (rc, E_FAIL);
2425 rc = props->COMGETTER(MaxVDISize) (&maxVDISize);
2426 ComAssertComRCRet (rc, E_FAIL);
2427
2428 if (aSize < 1 || aSize > maxVDISize)
2429 return setError (E_INVALIDARG,
2430 tr ("Invalid VDI size: %llu MB (must be in range [1, %llu] MB)"),
2431 aSize, maxVDISize);
2432 }
2433
2434 HRESULT rc;
2435
2436 /* create a project object */
2437 ComObjPtr <Progress> progress;
2438 progress.createObject();
2439 {
2440 Bstr desc = aDynamic ? tr ("Creating a dynamically expanding hard disk")
2441 : tr ("Creating a fixed-size hard disk");
2442 rc = progress->init (mVirtualBox, (IVirtualDiskImage *) this, desc,
2443 FALSE /* aCancelable */);
2444 CheckComRCReturnRC (rc);
2445 }
2446
2447 /* mark as busy (being created)
2448 * (VDI task thread will unmark it) */
2449 setBusy();
2450
2451 /* fill in VDI task data */
2452 VDITask *task = new VDITask (aDynamic ? VDITask::CreateDynamic
2453 : VDITask::CreateStatic,
2454 this, progress);
2455 task->size = aSize;
2456 task->size *= 1024 * 1024; /* convert to bytes */
2457
2458 /* create the hard disk creation thread, pass operation data */
2459 vrc = RTThreadCreate (NULL, vdiTaskThread, (void *) task, 0,
2460 RTTHREADTYPE_MAIN_HEAVY_WORKER, 0, "VDITask");
2461 ComAssertMsgRC (vrc, ("Could not create a thread (%Vrc)", vrc));
2462 if (VBOX_FAILURE (vrc))
2463 {
2464 clearBusy();
2465 delete task;
2466 rc = E_FAIL;
2467 }
2468 else
2469 {
2470 /* get one interface for the caller */
2471 progress.queryInterfaceTo (aProgress);
2472 }
2473
2474 return rc;
2475}
2476
2477/* static */
2478DECLCALLBACK(int) HVirtualDiskImage::vdiTaskThread (RTTHREAD thread, void *pvUser)
2479{
2480 VDITask *task = static_cast <VDITask *> (pvUser);
2481 AssertReturn (task, VERR_GENERAL_FAILURE);
2482
2483 LogFlow (("vdiTaskThread(): operation=%d, size=%llu\n",
2484 task->operation, task->size));
2485
2486 VDIIMAGETYPE type = (VDIIMAGETYPE) 0;
2487
2488 switch (task->operation)
2489 {
2490 case VDITask::CreateDynamic: type = VDI_IMAGE_TYPE_NORMAL; break;
2491 case VDITask::CreateStatic: type = VDI_IMAGE_TYPE_FIXED; break;
2492 case VDITask::CloneToImage: break;
2493 default: AssertFailedReturn (VERR_GENERAL_FAILURE); break;
2494 }
2495
2496 HRESULT rc = S_OK;
2497 Utf8Str errorMsg;
2498
2499 if (task->operation == VDITask::CloneToImage)
2500 {
2501 Assert (!task->vdi->id().isEmpty());
2502 /// @todo (dmik) check locks
2503 AutoLock sourceLock (task->source);
2504 rc = task->source->cloneToImage (task->vdi->id(),
2505 Utf8Str (task->vdi->filePathFull()),
2506 task->progress);
2507
2508 /* release reader added in HardDisk::CloneToImage() */
2509 task->source->releaseReader();
2510 }
2511 else
2512 {
2513 int vrc = VDICreateBaseImage (Utf8Str (task->vdi->filePathFull()),
2514 type, task->size,
2515 Utf8Str (task->vdi->mDescription),
2516 progressCallback,
2517 static_cast <Progress *> (task->progress));
2518
2519 if (VBOX_SUCCESS (vrc) && task->vdi->id())
2520 {
2521 /* we have a non-null UUID, update the created image */
2522 vrc = VDISetImageUUIDs (Utf8Str (task->vdi->filePathFull()),
2523 task->vdi->id().raw(), NULL, NULL, NULL);
2524 }
2525
2526 if (VBOX_FAILURE (vrc))
2527 {
2528 errorMsg = Utf8StrFmt (
2529 tr ("Falied to create a hard disk image '%ls' (%Vrc)"),
2530 task->vdi->filePathFull().raw(), vrc);
2531 rc = E_FAIL;
2532 }
2533 }
2534
2535 LogFlow (("vdiTaskThread(): rc=%08X\n", rc));
2536
2537 AutoLock alock (task->vdi);
2538
2539 /* clear busy set in in HardDisk::CloneToImage() or
2540 * in HVirtualDiskImage::createImage() */
2541 task->vdi->clearBusy();
2542
2543 if (SUCCEEDED (rc))
2544 {
2545 task->vdi->mState = HVirtualDiskImage::Created;
2546 /* update VDI data fields */
2547 Bstr errMsg;
2548 rc = task->vdi->queryInformation (&errMsg);
2549 /* we want to deliver the access check result to the caller
2550 * immediately, before he calls HardDisk::GetAccssible() himself. */
2551 if (SUCCEEDED (rc) && !errMsg.isNull())
2552 task->progress->notifyCompleteBstr (
2553 E_FAIL, COM_IIDOF (IVirtualDiskImage), getComponentName(),
2554 errMsg);
2555 else
2556 task->progress->notifyComplete (rc);
2557 }
2558 else
2559 {
2560 /* delete the target file so we don't have orphaned files */
2561 RTFileDelete(Utf8Str (task->vdi->filePathFull()));
2562
2563 task->vdi->mState = HVirtualDiskImage::NotCreated;
2564 /* complete the progress object */
2565 if (errorMsg)
2566 task->progress->notifyComplete (
2567 E_FAIL, COM_IIDOF (IVirtualDiskImage), getComponentName(),
2568 errorMsg);
2569 else
2570 task->progress->notifyComplete (rc);
2571 }
2572
2573 delete task;
2574
2575 return VINF_SUCCESS;
2576}
2577
2578////////////////////////////////////////////////////////////////////////////////
2579// HISCSIHardDisk class
2580////////////////////////////////////////////////////////////////////////////////
2581
2582// constructor / destructor
2583////////////////////////////////////////////////////////////////////////////////
2584
2585HRESULT HISCSIHardDisk::FinalConstruct()
2586{
2587 HRESULT rc = HardDisk::FinalConstruct();
2588 if (FAILED (rc))
2589 return rc;
2590
2591 mSize = 0;
2592 mActualSize = 0;
2593
2594 mPort = 0;
2595 mLun = 0;
2596
2597 return S_OK;
2598}
2599
2600void HISCSIHardDisk::FinalRelease()
2601{
2602 HardDisk::FinalRelease();
2603}
2604
2605// public initializer/uninitializer for internal purposes only
2606////////////////////////////////////////////////////////////////////////////////
2607
2608// public methods for internal purposes only
2609/////////////////////////////////////////////////////////////////////////////
2610
2611/**
2612 * Initializes the iSCSI hard disk object by reading its properties from
2613 * the given configuration node. The created hard disk will be marked as
2614 * registered on success.
2615 *
2616 * @param aHDNode <HardDisk> node
2617 * @param aVDINod <ISCSIHardDisk> node
2618 */
2619HRESULT HISCSIHardDisk::init (VirtualBox *aVirtualBox,
2620 CFGNODE aHDNode, CFGNODE aISCSINode)
2621{
2622 LogFlowMember (("HISCSIHardDisk::init (load)\n"));
2623
2624 AssertReturn (aHDNode && aISCSINode, E_FAIL);
2625
2626 AutoLock alock (this);
2627 ComAssertRet (!isReady(), E_UNEXPECTED);
2628
2629 mStorageType = HardDiskStorageType_ISCSIHardDisk;
2630
2631 HRESULT rc = S_OK;
2632
2633 do
2634 {
2635 rc = protectedInit (aVirtualBox, NULL);
2636 CheckComRCBreakRC (rc);
2637
2638 /* set ready to let protectedUninit() be called on failure */
2639 setReady (true);
2640
2641 /* server (required) */
2642 CFGLDRQueryBSTR (aISCSINode, "server", mServer.asOutParam());
2643 /* target (required) */
2644 CFGLDRQueryBSTR (aISCSINode, "target", mTarget.asOutParam());
2645
2646 /* port (optional) */
2647 CFGLDRQueryUInt16 (aISCSINode, "port", &mPort);
2648 /* lun (optional) */
2649 CFGLDRQueryUInt64 (aISCSINode, "lun", &mLun);
2650 /* userName (optional) */
2651 CFGLDRQueryBSTR (aISCSINode, "userName", mUserName.asOutParam());
2652 /* password (optional) */
2653 CFGLDRQueryBSTR (aISCSINode, "password", mPassword.asOutParam());
2654
2655 LogFlowMember ((" 'iscsi:%ls:%hu@%ls/%ls:%llu'\n",
2656 mServer.raw(), mPort, mUserName.raw(), mTarget.raw(),
2657 mLun));
2658
2659 /* load basic settings and children */
2660 rc = loadSettings (aHDNode);
2661 CheckComRCBreakRC (rc);
2662
2663 if (mType != HardDiskType_WritethroughHardDisk)
2664 {
2665 rc = setError (E_FAIL,
2666 tr ("Currently, non-Writethrough iSCSI hard disks are not "
2667 "allowed ('%ls')"),
2668 toString().raw());
2669 break;
2670 }
2671
2672 mRegistered = TRUE;
2673 }
2674 while (0);
2675
2676 if (FAILED (rc))
2677 uninit();
2678
2679 return rc;
2680}
2681
2682/**
2683 * Initializes the iSCSI hard disk object using default values for all
2684 * properties. The created hard disk will NOT be marked as registered.
2685 */
2686HRESULT HISCSIHardDisk::init (VirtualBox *aVirtualBox)
2687{
2688 LogFlowMember (("HISCSIHardDisk::init()\n"));
2689
2690 AutoLock alock (this);
2691 ComAssertRet (!isReady(), E_UNEXPECTED);
2692
2693 mStorageType = HardDiskStorageType_ISCSIHardDisk;
2694
2695 HRESULT rc = S_OK;
2696
2697 do
2698 {
2699 rc = protectedInit (aVirtualBox, NULL);
2700 CheckComRCBreakRC (rc);
2701
2702 /* set ready to let protectedUninit() be called on failure */
2703 setReady (true);
2704
2705 /* we have to generate a new UUID */
2706 mId.create();
2707 mType = HardDiskType_WritethroughHardDisk;
2708 mRegistered = FALSE;
2709 }
2710 while (0);
2711
2712 if (FAILED (rc))
2713 uninit();
2714
2715 return rc;
2716}
2717
2718/**
2719 * Uninitializes the instance and sets the ready flag to FALSE.
2720 * Called either from FinalRelease(), by the parent when it gets destroyed,
2721 * or by a third party when it decides this object is no more valid.
2722 */
2723void HISCSIHardDisk::uninit()
2724{
2725 LogFlowMember (("HISCSIHardDisk::uninit()\n"));
2726
2727 AutoLock alock (this);
2728 if (!isReady())
2729 return;
2730
2731 HardDisk::protectedUninit (alock);
2732}
2733
2734// IHardDisk properties
2735////////////////////////////////////////////////////////////////////////////////
2736
2737STDMETHODIMP HISCSIHardDisk::COMGETTER(Description) (BSTR *aDescription)
2738{
2739 if (!aDescription)
2740 return E_POINTER;
2741
2742 AutoLock alock (this);
2743 CHECK_READY();
2744
2745 mDescription.cloneTo (aDescription);
2746 return S_OK;
2747}
2748
2749STDMETHODIMP HISCSIHardDisk::COMSETTER(Description) (INPTR BSTR aDescription)
2750{
2751 AutoLock alock (this);
2752 CHECK_READY();
2753
2754 CHECK_BUSY_AND_READERS();
2755
2756 if (mDescription != aDescription)
2757 {
2758 mDescription = aDescription;
2759 if (mRegistered)
2760 return mVirtualBox->saveSettings();
2761 }
2762
2763 return S_OK;
2764}
2765
2766STDMETHODIMP HISCSIHardDisk::COMGETTER(Size) (ULONG64 *aSize)
2767{
2768 if (!aSize)
2769 return E_POINTER;
2770
2771 AutoLock alock (this);
2772 CHECK_READY();
2773
2774 *aSize = mSize;
2775 return S_OK;
2776}
2777
2778STDMETHODIMP HISCSIHardDisk::COMGETTER(ActualSize) (ULONG64 *aActualSize)
2779{
2780 if (!aActualSize)
2781 return E_POINTER;
2782
2783 AutoLock alock (this);
2784 CHECK_READY();
2785
2786 *aActualSize = mActualSize;
2787 return S_OK;
2788}
2789
2790// IISCSIHardDisk properties
2791////////////////////////////////////////////////////////////////////////////////
2792
2793STDMETHODIMP HISCSIHardDisk::COMGETTER(Server) (BSTR *aServer)
2794{
2795 if (!aServer)
2796 return E_POINTER;
2797
2798 AutoLock alock (this);
2799 CHECK_READY();
2800
2801 mServer.cloneTo (aServer);
2802 return S_OK;
2803}
2804
2805STDMETHODIMP HISCSIHardDisk::COMSETTER(Server) (INPTR BSTR aServer)
2806{
2807 if (!aServer || !*aServer)
2808 return E_INVALIDARG;
2809
2810 AutoLock alock (this);
2811 CHECK_READY();
2812
2813 CHECK_BUSY_AND_READERS();
2814
2815 if (mServer != aServer)
2816 {
2817 mServer = aServer;
2818 if (mRegistered)
2819 return mVirtualBox->saveSettings();
2820 }
2821
2822 return S_OK;
2823}
2824
2825STDMETHODIMP HISCSIHardDisk::COMGETTER(Port) (USHORT *aPort)
2826{
2827 if (!aPort)
2828 return E_POINTER;
2829
2830 AutoLock alock (this);
2831 CHECK_READY();
2832
2833 *aPort = mPort;
2834 return S_OK;
2835}
2836
2837STDMETHODIMP HISCSIHardDisk::COMSETTER(Port) (USHORT aPort)
2838{
2839 AutoLock alock (this);
2840 CHECK_READY();
2841
2842 CHECK_BUSY_AND_READERS();
2843
2844 if (mPort != aPort)
2845 {
2846 mPort = aPort;
2847 if (mRegistered)
2848 return mVirtualBox->saveSettings();
2849 }
2850
2851 return S_OK;
2852}
2853
2854STDMETHODIMP HISCSIHardDisk::COMGETTER(Target) (BSTR *aTarget)
2855{
2856 if (!aTarget)
2857 return E_POINTER;
2858
2859 AutoLock alock (this);
2860 CHECK_READY();
2861
2862 mTarget.cloneTo (aTarget);
2863 return S_OK;
2864}
2865
2866STDMETHODIMP HISCSIHardDisk::COMSETTER(Target) (INPTR BSTR aTarget)
2867{
2868 if (!aTarget || !*aTarget)
2869 return E_INVALIDARG;
2870
2871 AutoLock alock (this);
2872 CHECK_READY();
2873
2874 CHECK_BUSY_AND_READERS();
2875
2876 if (mTarget != aTarget)
2877 {
2878 mTarget = aTarget;
2879 if (mRegistered)
2880 return mVirtualBox->saveSettings();
2881 }
2882
2883 return S_OK;
2884}
2885
2886STDMETHODIMP HISCSIHardDisk::COMGETTER(Lun) (ULONG64 *aLun)
2887{
2888 if (!aLun)
2889 return E_POINTER;
2890
2891 AutoLock alock (this);
2892 CHECK_READY();
2893
2894 *aLun = mLun;
2895 return S_OK;
2896}
2897
2898STDMETHODIMP HISCSIHardDisk::COMSETTER(Lun) (ULONG64 aLun)
2899{
2900 AutoLock alock (this);
2901 CHECK_READY();
2902
2903 CHECK_BUSY_AND_READERS();
2904
2905 if (mLun != aLun)
2906 {
2907 mLun = aLun;
2908 if (mRegistered)
2909 return mVirtualBox->saveSettings();
2910 }
2911
2912 return S_OK;
2913}
2914
2915STDMETHODIMP HISCSIHardDisk::COMGETTER(UserName) (BSTR *aUserName)
2916{
2917 if (!aUserName)
2918 return E_POINTER;
2919
2920 AutoLock alock (this);
2921 CHECK_READY();
2922
2923 mUserName.cloneTo (aUserName);
2924 return S_OK;
2925}
2926
2927STDMETHODIMP HISCSIHardDisk::COMSETTER(UserName) (INPTR BSTR aUserName)
2928{
2929 AutoLock alock (this);
2930 CHECK_READY();
2931
2932 CHECK_BUSY_AND_READERS();
2933
2934 if (mUserName != aUserName)
2935 {
2936 mUserName = aUserName;
2937 if (mRegistered)
2938 return mVirtualBox->saveSettings();
2939 }
2940
2941 return S_OK;
2942}
2943
2944STDMETHODIMP HISCSIHardDisk::COMGETTER(Password) (BSTR *aPassword)
2945{
2946 if (!aPassword)
2947 return E_POINTER;
2948
2949 AutoLock alock (this);
2950 CHECK_READY();
2951
2952 mPassword.cloneTo (aPassword);
2953 return S_OK;
2954}
2955
2956STDMETHODIMP HISCSIHardDisk::COMSETTER(Password) (INPTR BSTR aPassword)
2957{
2958 AutoLock alock (this);
2959 CHECK_READY();
2960
2961 CHECK_BUSY_AND_READERS();
2962
2963 if (mPassword != aPassword)
2964 {
2965 mPassword = aPassword;
2966 if (mRegistered)
2967 return mVirtualBox->saveSettings();
2968 }
2969
2970 return S_OK;
2971}
2972
2973// public/protected methods for internal purposes only
2974/////////////////////////////////////////////////////////////////////////////
2975
2976/**
2977 * Attempts to mark the hard disk as registered.
2978 * Only VirtualBox can call this method.
2979 */
2980HRESULT HISCSIHardDisk::trySetRegistered (BOOL aRegistered)
2981{
2982 AutoLock alock (this);
2983 CHECK_READY();
2984
2985 if (aRegistered)
2986 {
2987 if (mServer.isEmpty() || mTarget.isEmpty())
2988 return setError (E_FAIL,
2989 tr ("iSCSI Hard disk has no server or target defined"));
2990 }
2991 else
2992 {
2993 }
2994
2995 return HardDisk::trySetRegistered (aRegistered);
2996}
2997
2998/**
2999 * Checks accessibility of this iSCSI hard disk.
3000 */
3001HRESULT HISCSIHardDisk::getAccessible (Bstr &aAccessError)
3002{
3003 AutoLock alock (this);
3004 CHECK_READY();
3005
3006 /* check the basic accessibility */
3007 HRESULT rc = HardDisk::getAccessible (aAccessError);
3008 if (FAILED (rc) || !aAccessError.isNull())
3009 return rc;
3010
3011 return queryInformation (aAccessError);
3012}
3013
3014/**
3015 * Saves hard disk settings to the specified storage node and saves
3016 * all children to the specified hard disk node
3017 *
3018 * @param aHDNode <HardDisk>
3019 * @param aStorageNode <ISCSIHardDisk> node
3020 */
3021HRESULT HISCSIHardDisk::saveSettings (CFGNODE aHDNode, CFGNODE aStorageNode)
3022{
3023 AssertReturn (aHDNode && aStorageNode, E_FAIL);
3024
3025 AutoLock alock (this);
3026 CHECK_READY();
3027
3028 /* server (required) */
3029 CFGLDRSetBSTR (aStorageNode, "server", mServer);
3030 /* target (required) */
3031 CFGLDRSetBSTR (aStorageNode, "target", mTarget);
3032
3033 /* port (optional) */
3034 if (mPort != 0)
3035 CFGLDRSetUInt16 (aStorageNode, "port", mPort);
3036 else
3037 CFGLDRDeleteAttribute (aStorageNode, "port");
3038 /* lun (optional) */
3039 if (mLun != 0)
3040 CFGLDRSetUInt64 (aStorageNode, "lun", mLun);
3041 else
3042 CFGLDRDeleteAttribute (aStorageNode, "lun");
3043 /* userName (optional) */
3044 if (!mUserName.isNull())
3045 CFGLDRSetBSTR (aStorageNode, "userName", mUserName);
3046 else
3047 CFGLDRDeleteAttribute (aStorageNode, "userName");
3048 /* password (optional) */
3049 if (!mPassword.isNull())
3050 CFGLDRSetBSTR (aStorageNode, "password", mPassword);
3051 else
3052 CFGLDRDeleteAttribute (aStorageNode, "password");
3053
3054 /* save basic settings and children */
3055 return HardDisk::saveSettings (aHDNode);
3056}
3057
3058/**
3059 * Returns the string representation of this hard disk.
3060 * When \a aShort is false, returns the full image file path.
3061 * Otherwise, returns the image file name only.
3062 *
3063 * @param aShort if true, a short representation is returned
3064 */
3065Bstr HISCSIHardDisk::toString (bool aShort /* = false */)
3066{
3067 AutoLock alock (this);
3068
3069 Bstr str;
3070 if (!aShort)
3071 {
3072 /* the format is iscsi:[<user@>]<server>[:<port>]/<target>[:<lun>] */
3073 str = Utf8StrFmt ("iscsi:%s%ls%s/%ls%s",
3074 !mUserName.isNull() ? Utf8StrFmt ("%ls@", mUserName.raw()).raw() : "",
3075 mServer.raw(),
3076 mPort != 0 ? Utf8StrFmt (":%hu", mPort).raw() : "",
3077 mTarget.raw(),
3078 mLun != 0 ? Utf8StrFmt (":%llu", mLun).raw() : "");
3079 }
3080 else
3081 {
3082 str = Utf8StrFmt ("%ls%s",
3083 mTarget.raw(),
3084 mLun != 0 ? Utf8StrFmt (":%llu", mLun).raw() : "");
3085 }
3086
3087 return str;
3088}
3089
3090/**
3091 * Creates a clone of this hard disk by storing hard disk data in the given
3092 * VDI file name.
3093 *
3094 * @param aId UUID to assign to the created image
3095 * @param aTargetPath VDI file where the cloned image is to be to stored
3096 * @param aProgress progress object to run during operation
3097 */
3098HRESULT
3099HISCSIHardDisk::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
3100 Progress *aProgress)
3101{
3102 ComAssertMsgFailed (("Not implemented"));
3103 return E_NOTIMPL;
3104
3105// AssertReturn (isBusy() == false, E_FAIL);
3106// addReader();
3107// releaseReader();
3108}
3109
3110/**
3111 * Creates a new differencing image for this hard disk with the given
3112 * VDI file name.
3113 *
3114 * @param aId UUID to assign to the created image
3115 * @param aTargetPath VDI file where to store the created differencing image
3116 * @param aProgress progress object to run during operation
3117 * (can be NULL)
3118 */
3119HRESULT
3120HISCSIHardDisk::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
3121 Progress *aProgress)
3122{
3123 ComAssertMsgFailed (("Not implemented"));
3124 return E_NOTIMPL;
3125
3126// AssertReturn (isBusy() == false, E_FAIL);
3127// addReader();
3128// releaseReader();
3129}
3130
3131// private methods
3132/////////////////////////////////////////////////////////////////////////////
3133
3134/**
3135 * Helper to query information about the iSCSI hard disk.
3136 *
3137 * @param aAccessError see #getAccessible()
3138 * @note
3139 * Must be called from under the object's lock!
3140 */
3141HRESULT HISCSIHardDisk::queryInformation (Bstr &aAccessError)
3142{
3143 /// @todo (dmik) query info about this iSCSI disk,
3144 // set mSize and mActualSize,
3145 // or set aAccessError in case of failure
3146
3147 aAccessError.setNull();
3148 return S_OK;
3149}
3150
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