VirtualBox

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

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

Main: Prototyped IVMDKImage.

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

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