VirtualBox

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

Last change on this file since 3317 was 3317, checked in by vboxsync, 17 years ago

Main: Improved error handling when opening hard disks using the generic IVirtualBox::openHardDisk() call (#2017).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use