VirtualBox

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

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

Main: Fixed unsigned/signed warnings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 116.6 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 = 0;
1076 bool haveFirst = false;
1077 Utf8Str loc = aLocation;
1078 char *ext = RTPathExt (loc);
1079
1080 for (size_t i = 0; i < ELEMENTS (storageTypes); ++ i)
1081 {
1082 if (RTPathCompare (ext, storageTypes [i].ext) == 0)
1083 {
1084 first = i;
1085 haveFirst = true;
1086 break;
1087 }
1088 }
1089
1090 HRESULT rc = S_OK;
1091
1092 HRESULT firstRC = S_OK;
1093 com::ErrorInfoKeeper firstErr (true /* aIsNull */);
1094
1095 for (size_t i = 0; i < ELEMENTS (storageTypes); ++ i)
1096 {
1097 size_t j = !haveFirst ? i : i == 0 ? first : i == first ? 0 : i;
1098 switch (storageTypes [j].type)
1099 {
1100 case HardDiskStorageType_VirtualDiskImage:
1101 {
1102 ComObjPtr <HVirtualDiskImage> obj;
1103 obj.createObject();
1104 rc = obj->init (aVirtualBox, NULL, aLocation,
1105 FALSE /* aRegistered */);
1106 if (SUCCEEDED (rc))
1107 {
1108 hardDisk = obj;
1109 return rc;
1110 }
1111 break;
1112 }
1113 case HardDiskStorageType_VMDKImage:
1114 {
1115 ComObjPtr <HVMDKImage> obj;
1116 obj.createObject();
1117 rc = obj->init (aVirtualBox, NULL, aLocation,
1118 FALSE /* aRegistered */);
1119 if (SUCCEEDED (rc))
1120 {
1121 hardDisk = obj;
1122 return rc;
1123 }
1124 break;
1125 }
1126 default:
1127 {
1128 AssertComRCReturnRC (E_FAIL);
1129 }
1130 }
1131
1132 Assert (FAILED (rc));
1133
1134 /* remember the error of the matching class */
1135 if (haveFirst && j == first)
1136 {
1137 firstRC = rc;
1138 firstErr.fetch();
1139 }
1140 }
1141
1142 if (haveFirst)
1143 {
1144 Assert (FAILED (firstRC));
1145 /* firstErr will restore the error info upon destruction */
1146 return firstRC;
1147 }
1148
1149 /* There was no exact extension match; chances are high that an error we
1150 * got after probing is useless. Use a generic error message instead. */
1151
1152 firstErr.forget();
1153
1154 return setError (E_FAIL,
1155 tr ("Could not recognize the format of the hard disk '%ls'. "
1156 "Either the given format is not supported or hard disk data "
1157 "is corrupt"),
1158 aLocation);
1159}
1160
1161// protected methods
1162/////////////////////////////////////////////////////////////////////////////
1163
1164/**
1165 * Loads the base settings of the hard disk from the given node, registers
1166 * it and loads and registers all child hard disks as HVirtualDiskImage
1167 * instances.
1168 *
1169 * Subclasses must call this method in their init() or loadSettings() methods
1170 * *after* they load specific parts of data (at least, necessary to let
1171 * toString() function correctly), in order to be properly loaded from the
1172 * settings file and registered.
1173 *
1174 * @param aHDNode <HardDisk> node when #isDifferencing() = false, or
1175 * <DiffHardDisk> node otherwise
1176 *
1177 * @note
1178 * Must be called from under the object's lock
1179 */
1180HRESULT HardDisk::loadSettings (CFGNODE aHDNode)
1181{
1182 AssertReturn (aHDNode, E_FAIL);
1183
1184 Guid uuid; /* uuid (required) */
1185 CFGLDRQueryUUID (aHDNode, "uuid", uuid.ptr());
1186 mId = uuid;
1187
1188 if (!isDifferencing())
1189 {
1190 Bstr type; /* type (required for <HardDisk> nodes only) */
1191 CFGLDRQueryBSTR (aHDNode, "type", type.asOutParam());
1192 if (type == L"normal")
1193 mType = HardDiskType_NormalHardDisk;
1194 else if (type == L"immutable")
1195 mType = HardDiskType_ImmutableHardDisk;
1196 else if (type == L"writethrough")
1197 mType = HardDiskType_WritethroughHardDisk;
1198 else
1199 ComAssertMsgFailedRet (("Invalid hard disk type '%ls'\n", type.raw()),
1200 E_FAIL);
1201 }
1202 else
1203 mType = HardDiskType_NormalHardDisk;
1204
1205 HRESULT rc = mVirtualBox->registerHardDisk (this, VirtualBox::RHD_OnStartUp);
1206 if (FAILED (rc))
1207 return rc;
1208
1209 /* load all children */
1210 unsigned count = 0;
1211 CFGLDRCountChildren (aHDNode, "DiffHardDisk", &count);
1212 for (unsigned i = 0; i < count && SUCCEEDED (rc); ++ i)
1213 {
1214 CFGNODE hdNode = 0;
1215
1216 CFGLDRGetChildNode (aHDNode, "DiffHardDisk", i, &hdNode);
1217 ComAssertBreak (hdNode, rc = E_FAIL);
1218
1219 do
1220 {
1221 CFGNODE vdiNode = 0;
1222 CFGLDRGetChildNode (hdNode, "VirtualDiskImage", 0, &vdiNode);
1223 ComAssertBreak (vdiNode, rc = E_FAIL);
1224
1225 ComObjPtr <HVirtualDiskImage> vdi;
1226 vdi.createObject();
1227 rc = vdi->init (mVirtualBox, this, hdNode, vdiNode);
1228
1229 CFGLDRReleaseNode (vdiNode);
1230 }
1231 while (0);
1232
1233 CFGLDRReleaseNode (hdNode);
1234 }
1235
1236 return rc;
1237}
1238
1239/**
1240 * Saves the base settings of the hard disk to the given node
1241 * and saves all child hard disks as <DiffHardDisk> nodes.
1242 *
1243 * Subclasses must call this method in their saveSettings() methods
1244 * in order to be properly saved to the settings file.
1245 *
1246 * @param aHDNode <HardDisk> node when #isDifferencing() = false, or
1247 * <DiffHardDisk> node otherwise
1248 *
1249 * @note
1250 * Must be called from under the object's lock
1251 */
1252HRESULT HardDisk::saveSettings (CFGNODE aHDNode)
1253{
1254 AssertReturn (aHDNode, E_FAIL);
1255
1256 /* uuid (required) */
1257 CFGLDRSetUUID (aHDNode, "uuid", mId.ptr());
1258
1259 if (!isDifferencing())
1260 {
1261 /* type (required) */
1262 const char *type = NULL;
1263 switch (mType)
1264 {
1265 case HardDiskType_NormalHardDisk:
1266 type = "normal";
1267 break;
1268 case HardDiskType_ImmutableHardDisk:
1269 type = "immutable";
1270 break;
1271 case HardDiskType_WritethroughHardDisk:
1272 type = "writethrough";
1273 break;
1274 }
1275 CFGLDRSetString (aHDNode, "type", type);
1276 }
1277
1278 HRESULT rc = S_OK;
1279
1280 /* save all children */
1281 AutoLock chLock (childrenLock());
1282 for (HardDiskList::const_iterator it = children().begin();
1283 it != children().end() && SUCCEEDED (rc);
1284 ++ it)
1285 {
1286 ComObjPtr <HardDisk> child = *it;
1287 AutoLock childLock (child);
1288
1289 CFGNODE hdNode = 0;
1290 CFGLDRAppendChildNode (aHDNode, "DiffHardDisk", &hdNode);
1291 ComAssertBreak (hdNode, rc = E_FAIL);
1292
1293 do
1294 {
1295 CFGNODE vdiNode = 0;
1296 CFGLDRAppendChildNode (hdNode, "VirtualDiskImage", &vdiNode);
1297 ComAssertBreak (vdiNode, rc = E_FAIL);
1298
1299 rc = child->saveSettings (hdNode, vdiNode);
1300
1301 CFGLDRReleaseNode (vdiNode);
1302 }
1303 while (0);
1304
1305 CFGLDRReleaseNode (hdNode);
1306 }
1307
1308 return rc;
1309}
1310
1311////////////////////////////////////////////////////////////////////////////////
1312// HVirtualDiskImage class
1313////////////////////////////////////////////////////////////////////////////////
1314
1315// constructor / destructor
1316////////////////////////////////////////////////////////////////////////////////
1317
1318HRESULT HVirtualDiskImage::FinalConstruct()
1319{
1320 HRESULT rc = HardDisk::FinalConstruct();
1321 if (FAILED (rc))
1322 return rc;
1323
1324 mState = NotCreated;
1325
1326 mStateCheckSem = NIL_RTSEMEVENTMULTI;
1327 mStateCheckWaiters = 0;
1328
1329 mSize = 0;
1330 mActualSize = 0;
1331
1332 return S_OK;
1333}
1334
1335void HVirtualDiskImage::FinalRelease()
1336{
1337 HardDisk::FinalRelease();
1338}
1339
1340// public initializer/uninitializer for internal purposes only
1341////////////////////////////////////////////////////////////////////////////////
1342
1343// public methods for internal purposes only
1344/////////////////////////////////////////////////////////////////////////////
1345
1346/**
1347 * Initializes the VDI hard disk object by reading its properties from
1348 * the given configuration node. The created hard disk will be marked as
1349 * registered on success.
1350 *
1351 * @param aHDNode <HardDisk> node
1352 * @param aVDINode <VirtualDiskImage> node
1353 */
1354HRESULT HVirtualDiskImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
1355 CFGNODE aHDNode, CFGNODE aVDINode)
1356{
1357 LogFlowMember (("HVirtualDiskImage::init (load)\n"));
1358
1359 AssertReturn (aHDNode && aVDINode, E_FAIL);
1360
1361 AutoLock alock (this);
1362 ComAssertRet (!isReady(), E_UNEXPECTED);
1363
1364 mStorageType = HardDiskStorageType_VirtualDiskImage;
1365
1366 HRESULT rc = S_OK;
1367
1368 do
1369 {
1370 rc = protectedInit (aVirtualBox, aParent);
1371 CheckComRCBreakRC (rc);
1372
1373 /* set ready to let protectedUninit() be called on failure */
1374 setReady (true);
1375
1376 /* filePath (required) */
1377 Bstr filePath;
1378 CFGLDRQueryBSTR (aVDINode, "filePath", filePath.asOutParam());
1379
1380 rc = setFilePath (filePath);
1381 CheckComRCBreakRC (rc);
1382
1383 LogFlowMember ((" '%ls'\n", mFilePathFull.raw()));
1384
1385 /* load basic settings and children */
1386 rc = loadSettings (aHDNode);
1387 CheckComRCBreakRC (rc);
1388
1389 mState = Created;
1390 mRegistered = TRUE;
1391
1392 /* Don't call queryInformation() for registered hard disks to
1393 * prevent the calling thread (i.e. the VirtualBox server startup
1394 * thread) from an unexpected freeze. The vital mId property (UUID)
1395 * is read from the registry file in loadSettings(). To get the rest,
1396 * the user will have to call COMGETTER(Accessible) manually. */
1397 }
1398 while (0);
1399
1400 if (FAILED (rc))
1401 uninit();
1402
1403 return rc;
1404}
1405
1406/**
1407 * Initializes the VDI hard disk object using the given image file name.
1408 *
1409 * @param aVirtualBox VirtualBox parent.
1410 * @param aParent Parent hard disk.
1411 * @param aFilePath Path to the image file, or @c NULL to create an
1412 * image-less object.
1413 * @param aRegistered Whether to mark this disk as registered or not
1414 * (ignored when @a aFilePath is @c NULL, assuming @c FALSE)
1415 */
1416HRESULT HVirtualDiskImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
1417 const BSTR aFilePath, BOOL aRegistered /* = FALSE */)
1418{
1419 LogFlowMember (("HVirtualDiskImage::init (aFilePath='%ls', aRegistered=%d)\n",
1420 aFilePath, aRegistered));
1421
1422 AutoLock alock (this);
1423 ComAssertRet (!isReady(), E_UNEXPECTED);
1424
1425 mStorageType = HardDiskStorageType_VirtualDiskImage;
1426
1427 HRESULT rc = S_OK;
1428
1429 do
1430 {
1431 rc = protectedInit (aVirtualBox, aParent);
1432 CheckComRCBreakRC (rc);
1433
1434 /* set ready to let protectedUninit() be called on failure */
1435 setReady (true);
1436
1437 rc = setFilePath (aFilePath);
1438 CheckComRCBreakRC (rc);
1439
1440 Assert (mId.isEmpty());
1441
1442 if (aFilePath && *aFilePath)
1443 {
1444 mRegistered = aRegistered;
1445 mState = Created;
1446
1447 /* Call queryInformation() anyway (even if it will block), because
1448 * it is the only way to get the UUID of the existing VDI and
1449 * initialize the vital mId property. */
1450 Bstr errMsg;
1451 rc = queryInformation (&errMsg);
1452 if (SUCCEEDED (rc))
1453 {
1454 /* We are constructing a new HVirtualDiskImage object. If there
1455 * is a fatal accessibility error (we cannot read image UUID),
1456 * we have to fail. We do so even on non-fatal errors as well,
1457 * because it's not worth to keep going with the inaccessible
1458 * image from the very beginning (when nothing else depends on
1459 * it yet). */
1460 if (!errMsg.isNull())
1461 rc = setErrorBstr (E_FAIL, errMsg);
1462 }
1463 }
1464 else
1465 {
1466 mRegistered = FALSE;
1467 mState = NotCreated;
1468 mId.create();
1469 }
1470 }
1471 while (0);
1472
1473 if (FAILED (rc))
1474 uninit();
1475
1476 return rc;
1477}
1478
1479/**
1480 * Uninitializes the instance and sets the ready flag to FALSE.
1481 * Called either from FinalRelease(), by the parent when it gets destroyed,
1482 * or by a third party when it decides this object is no more valid.
1483 */
1484void HVirtualDiskImage::uninit()
1485{
1486 LogFlowMember (("HVirtualDiskImage::uninit()\n"));
1487
1488 AutoLock alock (this);
1489 if (!isReady())
1490 return;
1491
1492 HardDisk::protectedUninit (alock);
1493}
1494
1495// IHardDisk properties
1496////////////////////////////////////////////////////////////////////////////////
1497
1498STDMETHODIMP HVirtualDiskImage::COMGETTER(Description) (BSTR *aDescription)
1499{
1500 if (!aDescription)
1501 return E_POINTER;
1502
1503 AutoLock alock (this);
1504 CHECK_READY();
1505
1506 mDescription.cloneTo (aDescription);
1507 return S_OK;
1508}
1509
1510STDMETHODIMP HVirtualDiskImage::COMSETTER(Description) (INPTR BSTR aDescription)
1511{
1512 AutoLock alock (this);
1513 CHECK_READY();
1514
1515 CHECK_BUSY_AND_READERS();
1516
1517 if (mState >= Created)
1518 {
1519 int vrc = VDISetImageComment (Utf8Str (mFilePathFull), Utf8Str (aDescription));
1520 if (VBOX_FAILURE (vrc))
1521 return setError (E_FAIL,
1522 tr ("Could not change the description of the VDI hard disk '%ls' "
1523 "(%Vrc)"),
1524 toString().raw(), vrc);
1525 }
1526
1527 mDescription = aDescription;
1528 return S_OK;
1529}
1530
1531STDMETHODIMP HVirtualDiskImage::COMGETTER(Size) (ULONG64 *aSize)
1532{
1533 if (!aSize)
1534 return E_POINTER;
1535
1536 AutoLock alock (this);
1537 CHECK_READY();
1538
1539 /* only a non-differencing image knows the logical size */
1540 if (isDifferencing())
1541 return root()->COMGETTER(Size) (aSize);
1542
1543 *aSize = mSize;
1544 return S_OK;
1545}
1546
1547STDMETHODIMP HVirtualDiskImage::COMGETTER(ActualSize) (ULONG64 *aActualSize)
1548{
1549 if (!aActualSize)
1550 return E_POINTER;
1551
1552 AutoLock alock (this);
1553 CHECK_READY();
1554
1555 *aActualSize = mActualSize;
1556 return S_OK;
1557}
1558
1559// IVirtualDiskImage properties
1560////////////////////////////////////////////////////////////////////////////////
1561
1562STDMETHODIMP HVirtualDiskImage::COMGETTER(FilePath) (BSTR *aFilePath)
1563{
1564 if (!aFilePath)
1565 return E_POINTER;
1566
1567 AutoLock alock (this);
1568 CHECK_READY();
1569
1570 mFilePathFull.cloneTo (aFilePath);
1571 return S_OK;
1572}
1573
1574STDMETHODIMP HVirtualDiskImage::COMSETTER(FilePath) (INPTR BSTR aFilePath)
1575{
1576 AutoLock alock (this);
1577 CHECK_READY();
1578
1579 if (mState != NotCreated)
1580 return setError (E_ACCESSDENIED,
1581 tr ("Cannot change the file path of the existing hard disk '%ls'"),
1582 toString().raw());
1583
1584 CHECK_BUSY_AND_READERS();
1585
1586 // append the default path if only a name is given
1587 Bstr path = aFilePath;
1588 if (aFilePath && *aFilePath)
1589 {
1590 Utf8Str fp = aFilePath;
1591 if (!RTPathHavePath (fp))
1592 {
1593 AutoReaderLock propsLock (mVirtualBox->systemProperties());
1594 path = Utf8StrFmt ("%ls%c%s",
1595 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
1596 RTPATH_DELIMITER,
1597 fp.raw());
1598 }
1599 }
1600
1601 return setFilePath (path);
1602}
1603
1604STDMETHODIMP HVirtualDiskImage::COMGETTER(Created) (BOOL *aCreated)
1605{
1606 if (!aCreated)
1607 return E_POINTER;
1608
1609 AutoLock alock (this);
1610 CHECK_READY();
1611
1612 *aCreated = mState >= Created;
1613 return S_OK;
1614}
1615
1616// IVirtualDiskImage methods
1617/////////////////////////////////////////////////////////////////////////////
1618
1619STDMETHODIMP HVirtualDiskImage::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
1620{
1621 if (!aProgress)
1622 return E_POINTER;
1623
1624 AutoLock alock (this);
1625 CHECK_READY();
1626
1627 return createImage (aSize, TRUE /* aDynamic */, aProgress);
1628}
1629
1630STDMETHODIMP HVirtualDiskImage::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
1631{
1632 if (!aProgress)
1633 return E_POINTER;
1634
1635 AutoLock alock (this);
1636 CHECK_READY();
1637
1638 return createImage (aSize, FALSE /* aDynamic */, aProgress);
1639}
1640
1641STDMETHODIMP HVirtualDiskImage::DeleteImage()
1642{
1643 AutoLock alock (this);
1644 CHECK_READY();
1645 CHECK_BUSY_AND_READERS();
1646
1647 if (mRegistered)
1648 return setError (E_ACCESSDENIED,
1649 tr ("Cannot delete an image of the registered hard disk image '%ls"),
1650 mFilePathFull.raw());
1651 if (mState == NotCreated)
1652 return setError (E_FAIL,
1653 tr ("Hard disk image has been already deleted or never created"));
1654
1655 int vrc = RTFileDelete (Utf8Str (mFilePathFull));
1656 if (VBOX_FAILURE (vrc))
1657 return setError (E_FAIL, tr ("Could not delete the image file '%ls' (%Vrc)"),
1658 mFilePathFull.raw(), vrc);
1659
1660 mState = NotCreated;
1661
1662 // reset the fields
1663 mSize = 0;
1664 mActualSize = 0;
1665
1666 return S_OK;
1667}
1668
1669// public/protected methods for internal purposes only
1670/////////////////////////////////////////////////////////////////////////////
1671
1672/**
1673 * Attempts to mark the hard disk as registered.
1674 * Only VirtualBox can call this method.
1675 */
1676HRESULT HVirtualDiskImage::trySetRegistered (BOOL aRegistered)
1677{
1678 AutoLock alock (this);
1679 CHECK_READY();
1680
1681 if (aRegistered)
1682 {
1683 if (mState == NotCreated)
1684 return setError (E_FAIL,
1685 tr ("Image file '%ls' is not yet created for this hard disk"),
1686 mFilePathFull.raw());
1687 if (isDifferencing())
1688 return setError (E_FAIL,
1689 tr ("Hard disk '%ls' is differencing and cannot be unregistered "
1690 "explicitly"),
1691 mFilePathFull.raw());
1692 }
1693 else
1694 {
1695 ComAssertRet (mState >= Created, E_FAIL);
1696 }
1697
1698 return HardDisk::trySetRegistered (aRegistered);
1699}
1700
1701/**
1702 * Checks accessibility of this hard disk image only (w/o parents).
1703 *
1704 * @param aAccessError on output, a null string indicates the hard disk is
1705 * accessible, otherwise contains a message describing
1706 * the reason of inaccessibility.
1707 */
1708HRESULT HVirtualDiskImage::getAccessible (Bstr &aAccessError)
1709{
1710 AutoLock alock (this);
1711 CHECK_READY();
1712
1713 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
1714 {
1715 /* An accessibility check in progress on some other thread,
1716 * wait for it to finish. */
1717
1718 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL);
1719 ++ mStateCheckWaiters;
1720 alock.leave();
1721
1722 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
1723
1724 alock.enter();
1725 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
1726 -- mStateCheckWaiters;
1727 if (mStateCheckWaiters == 0)
1728 {
1729 RTSemEventMultiDestroy (mStateCheckSem);
1730 mStateCheckSem = NIL_RTSEMEVENTMULTI;
1731 }
1732
1733 AssertRCReturn (vrc, E_FAIL);
1734
1735 /* don't touch aAccessError, it has been already set */
1736 return S_OK;
1737 }
1738
1739 /* check the basic accessibility */
1740 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */);
1741 if (FAILED (rc) || !aAccessError.isNull())
1742 return rc;
1743
1744 if (mState >= Created)
1745 {
1746 return queryInformation (&aAccessError);
1747 }
1748
1749 aAccessError = Utf8StrFmt ("Hard disk image '%ls' is not yet created",
1750 mFilePathFull.raw());
1751 return S_OK;
1752}
1753
1754/**
1755 * Saves hard disk settings to the specified storage node and saves
1756 * all children to the specified hard disk node
1757 *
1758 * @param aHDNode <HardDisk> or <DiffHardDisk> node
1759 * @param aStorageNode <VirtualDiskImage> node
1760 */
1761HRESULT HVirtualDiskImage::saveSettings (CFGNODE aHDNode, CFGNODE aStorageNode)
1762{
1763 AssertReturn (aHDNode && aStorageNode, E_FAIL);
1764
1765 AutoLock alock (this);
1766 CHECK_READY();
1767
1768 // filePath (required)
1769 CFGLDRSetBSTR (aStorageNode, "filePath", mFilePath);
1770
1771 // save basic settings and children
1772 return HardDisk::saveSettings (aHDNode);
1773}
1774
1775/**
1776 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
1777 * of this hard disk and updates it if necessary to reflect the new location.
1778 * Intended to be from HardDisk::updatePaths().
1779 *
1780 * @param aOldPath old path (full)
1781 * @param aNewPath new path (full)
1782 *
1783 * @note Locks this object for writing.
1784 */
1785void HVirtualDiskImage::updatePath (const char *aOldPath, const char *aNewPath)
1786{
1787 AssertReturnVoid (aOldPath);
1788 AssertReturnVoid (aNewPath);
1789
1790 AutoLock alock (this);
1791 AssertReturnVoid (isReady());
1792
1793 size_t oldPathLen = strlen (aOldPath);
1794
1795 Utf8Str path = mFilePathFull;
1796 LogFlowThisFunc (("VDI.fullPath={%s}\n", path.raw()));
1797
1798 if (RTPathStartsWith (path, aOldPath))
1799 {
1800 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
1801 path.raw() + oldPathLen);
1802 path = newPath;
1803
1804 mVirtualBox->calculateRelativePath (path, path);
1805
1806 unconst (mFilePathFull) = newPath;
1807 unconst (mFilePath) = path;
1808
1809 LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
1810 newPath.raw(), path.raw()));
1811 }
1812}
1813
1814/**
1815 * Returns the string representation of this hard disk.
1816 * When \a aShort is false, returns the full image file path.
1817 * Otherwise, returns the image file name only.
1818 *
1819 * @param aShort if true, a short representation is returned
1820 */
1821Bstr HVirtualDiskImage::toString (bool aShort /* = false */)
1822{
1823 AutoLock alock (this);
1824
1825 if (!aShort)
1826 return mFilePathFull;
1827 else
1828 {
1829 Utf8Str fname = mFilePathFull;
1830 return RTPathFilename (fname.mutableRaw());
1831 }
1832}
1833
1834/**
1835 * Creates a clone of this hard disk by storing hard disk data in the given
1836 * VDI file name.
1837 *
1838 * @param aId UUID to assign to the created image
1839 * @param aTargetPath VDI file where the cloned image is to be to stored
1840 * @param aProgress progress object to run during operation
1841 */
1842HRESULT
1843HVirtualDiskImage::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
1844 Progress *aProgress)
1845{
1846 AssertReturn (!aId.isEmpty(), E_FAIL);
1847 AssertReturn (!aTargetPath.isNull(), E_FAIL);
1848 AssertReturn (aProgress, E_FAIL);
1849
1850 AutoLock alock (this);
1851 AssertReturn (isReady(), E_FAIL);
1852
1853 AssertReturn (isBusy() == false, E_FAIL);
1854
1855 /// @todo (dmik) cloning of differencing images is not yet supported
1856 AssertReturn (mParent.isNull(), E_FAIL);
1857
1858 Utf8Str filePathFull = mFilePathFull;
1859
1860 if (mState == NotCreated)
1861 return setError (E_FAIL,
1862 tr ("Source hard disk image '%s' is not yet created"),
1863 filePathFull.raw());
1864
1865 addReader();
1866 alock.leave();
1867
1868 int vrc = VDICopyImage (aTargetPath, filePathFull, NULL,
1869 progressCallback,
1870 static_cast <Progress *> (aProgress));
1871
1872 alock.enter();
1873 releaseReader();
1874
1875 if (VBOX_SUCCESS (vrc))
1876 vrc = VDISetImageUUIDs (aTargetPath, aId, NULL, NULL, NULL);
1877
1878 if (VBOX_FAILURE (vrc))
1879 return setError (E_FAIL,
1880 tr ("Could not copy the hard disk image '%s' to '%s' (%Vrc)"),
1881 filePathFull.raw(), aTargetPath.raw(), vrc);
1882
1883 return S_OK;
1884}
1885
1886/**
1887 * Creates a new differencing image for this hard disk with the given
1888 * VDI file name.
1889 *
1890 * @param aId UUID to assign to the created image
1891 * @param aTargetPath VDI file where to store the created differencing image
1892 * @param aProgress progress object to run during operation
1893 * (can be NULL)
1894 */
1895HRESULT
1896HVirtualDiskImage::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
1897 Progress *aProgress)
1898{
1899 AssertReturn (!aId.isEmpty(), E_FAIL);
1900 AssertReturn (!aTargetPath.isNull(), E_FAIL);
1901
1902 AutoLock alock (this);
1903 AssertReturn (isReady(), E_FAIL);
1904
1905 AssertReturn (isBusy() == false, E_FAIL);
1906 AssertReturn (mState >= Created, E_FAIL);
1907
1908 addReader();
1909 alock.leave();
1910
1911 int vrc = VDICreateDifferenceImage (aTargetPath, Utf8Str (mFilePathFull),
1912 NULL, aProgress ? progressCallback : NULL,
1913 static_cast <Progress *> (aProgress));
1914 alock.enter();
1915 releaseReader();
1916
1917 /* update the UUID to correspond to the file name */
1918 if (VBOX_SUCCESS (vrc))
1919 vrc = VDISetImageUUIDs (aTargetPath, aId, NULL, NULL, NULL);
1920
1921 if (VBOX_FAILURE (vrc))
1922 return setError (E_FAIL,
1923 tr ("Could not create a differencing hard disk '%s' (%Vrc)"),
1924 aTargetPath.raw(), vrc);
1925
1926 return S_OK;
1927}
1928
1929/**
1930 * Copies the image file of this hard disk to a separate VDI file (with an
1931 * unique creation UUID) and creates a new hard disk object for the copied
1932 * image. The copy will be created as a child of this hard disk's parent
1933 * (so that this hard disk must be a differencing one).
1934 *
1935 * The specified progress object (if not NULL) receives the percentage
1936 * of the operation completion. However, it is responsibility of the caller to
1937 * call Progress::notifyComplete() after this method returns.
1938 *
1939 * @param aFolder folder where to create a copy (must be a full path)
1940 * @param aMachineId machine ID the new hard disk will belong to
1941 * @param aHardDisk resulting hard disk object
1942 * @param aProgress progress object to run during copy operation
1943 * (may be NULL)
1944 *
1945 * @note
1946 * Must be NOT called from under locks of other objects that need external
1947 * access dirung this method execurion!
1948 */
1949HRESULT
1950HVirtualDiskImage::cloneDiffImage (const Bstr &aFolder, const Guid &aMachineId,
1951 ComObjPtr <HVirtualDiskImage> &aHardDisk,
1952 Progress *aProgress)
1953{
1954 AssertReturn (!aFolder.isEmpty() && !aMachineId.isEmpty(),
1955 E_FAIL);
1956
1957 AutoLock alock (this);
1958 CHECK_READY();
1959
1960 AssertReturn (!mParent.isNull(), E_FAIL);
1961
1962 ComAssertRet (isBusy() == false, E_FAIL);
1963 ComAssertRet (mState >= Created, E_FAIL);
1964
1965 Guid id;
1966 id.create();
1967
1968 Utf8Str filePathTo = Utf8StrFmt ("%ls%c{%Vuuid}.vdi",
1969 aFolder.raw(), RTPATH_DELIMITER, id.ptr());
1970
1971 /* try to make the path relative to the vbox home dir */
1972 const char *filePathToRel = filePathTo;
1973 {
1974 const Utf8Str &homeDir = mVirtualBox->homeDir();
1975 if (!strncmp (filePathTo, homeDir, homeDir.length()))
1976 filePathToRel = (filePathToRel + homeDir.length() + 1);
1977 }
1978
1979 /* first ensure the directory exists */
1980 {
1981 Utf8Str dir = aFolder;
1982 if (!RTDirExists (dir))
1983 {
1984 int vrc = RTDirCreateFullPath (dir, 0777);
1985 if (VBOX_FAILURE (vrc))
1986 {
1987 return setError (E_FAIL,
1988 tr ("Could not create a directory '%s' "
1989 "to store the image file (%Vrc)"),
1990 dir.raw(), vrc);
1991 }
1992 }
1993 }
1994
1995 Utf8Str filePathFull = mFilePathFull;
1996
1997 alock.leave();
1998
1999 int vrc = VDICopyImage (filePathTo, filePathFull, NULL,
2000 progressCallback,
2001 static_cast <Progress *> (aProgress));
2002
2003 alock.enter();
2004
2005 /* get modification and parent UUIDs of this image */
2006 RTUUID modUuid, parentUuid, parentModUuid;
2007 if (VBOX_SUCCESS (vrc))
2008 vrc = VDIGetImageUUIDs (filePathFull, NULL, &modUuid,
2009 &parentUuid, &parentModUuid);
2010
2011 // update the UUID of the copy to correspond to the file name
2012 // and copy all other UUIDs from this image
2013 if (VBOX_SUCCESS (vrc))
2014 vrc = VDISetImageUUIDs (filePathTo, id, &modUuid,
2015 &parentUuid, &parentModUuid);
2016
2017 if (VBOX_FAILURE (vrc))
2018 return setError (E_FAIL,
2019 tr ("Could not copy the hard disk image '%s' to '%s' (%Vrc)"),
2020 filePathFull.raw(), filePathTo.raw(), vrc);
2021
2022 ComObjPtr <HVirtualDiskImage> vdi;
2023 vdi.createObject();
2024 HRESULT rc = vdi->init (mVirtualBox, mParent, Bstr (filePathToRel),
2025 TRUE /* aRegistered */);
2026 if (FAILED (rc))
2027 return rc;
2028
2029 /* associate the created hard disk with the given machine */
2030 vdi->setMachineId (aMachineId);
2031
2032 rc = mVirtualBox->registerHardDisk (vdi, VirtualBox::RHD_Internal);
2033 if (FAILED (rc))
2034 return rc;
2035
2036 aHardDisk = vdi;
2037
2038 return S_OK;
2039}
2040
2041/**
2042 * Merges this child image to its parent image and updates the parent UUID
2043 * of all children of this image (to point to this image's parent).
2044 * It's a responsibility of the caller to unregister and uninitialize
2045 * the merged image on success.
2046 *
2047 * This method is intended to be called on a worker thread (the operation
2048 * can be time consuming).
2049 *
2050 * The specified progress object (if not NULL) receives the percentage
2051 * of the operation completion. However, it is responsibility of the caller to
2052 * call Progress::notifyComplete() after this method returns.
2053 *
2054 * @param aProgress progress object to run during copy operation
2055 * (may be NULL)
2056 *
2057 * @note
2058 * This method expects that both this hard disk and the paret hard disk
2059 * are marked as busy using #setBusyWithChildren() prior to calling it!
2060 * Busy flags of both hard disks will be cleared by this method
2061 * on a successful return. In case of failure, #clearBusyWithChildren()
2062 * must be called on a parent.
2063 *
2064 * @note
2065 * Must be NOT called from under locks of other objects that need external
2066 * access dirung this method execurion!
2067 */
2068HRESULT HVirtualDiskImage::mergeImageToParent (Progress *aProgress)
2069{
2070 LogFlowMember (("HVirtualDiskImage::mergeImageToParent(): image='%ls'\n",
2071 mFilePathFull.raw()));
2072
2073 AutoLock alock (this);
2074 CHECK_READY();
2075
2076 AssertReturn (!mParent.isNull(), E_FAIL);
2077 AutoLock parentLock (mParent);
2078
2079 ComAssertRet (isBusy() == true, E_FAIL);
2080 ComAssertRet (mParent->isBusy() == true, E_FAIL);
2081
2082 ComAssertRet (mState >= Created && mParent->asVDI()->mState >= Created, E_FAIL);
2083
2084 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2085 ("non VDI storage types are not yet supported!"), E_FAIL);
2086
2087 parentLock.leave();
2088 alock.leave();
2089
2090 int vrc = VDIMergeImage (Utf8Str (mFilePathFull),
2091 Utf8Str (mParent->asVDI()->mFilePathFull),
2092 progressCallback,
2093 static_cast <Progress *> (aProgress));
2094 alock.enter();
2095 parentLock.enter();
2096
2097 if (VBOX_FAILURE (vrc))
2098 return setError (E_FAIL,
2099 tr ("Could not merge the hard disk image '%ls' to "
2100 "its parent image '%ls' (%Vrc)"),
2101 mFilePathFull.raw(), mParent->asVDI()->mFilePathFull.raw(), vrc);
2102
2103 {
2104 HRESULT rc = S_OK;
2105
2106 AutoLock chLock (childrenLock());
2107
2108 for (HardDiskList::const_iterator it = children().begin();
2109 it != children().end(); ++ it)
2110 {
2111 ComObjPtr <HVirtualDiskImage> child = (*it)->asVDI();
2112 AutoLock childLock (child);
2113
2114 /* reparent the child */
2115 child->mParent = mParent;
2116 if (mParent)
2117 mParent->addDependentChild (child);
2118
2119 /* change the parent UUID in the image as well */
2120 RTUUID parentUuid, parentModUuid;
2121 vrc = VDIGetImageUUIDs (Utf8Str (mParent->asVDI()->mFilePathFull),
2122 &parentUuid, &parentModUuid, NULL, NULL);
2123 if (VBOX_FAILURE (vrc))
2124 {
2125 rc = setError (E_FAIL,
2126 tr ("Could not access the hard disk image '%ls' (%Vrc)"),
2127 mParent->asVDI()->mFilePathFull.raw(), vrc);
2128 break;
2129 }
2130 ComAssertBreak (mParent->id() == Guid (parentUuid), rc = E_FAIL);
2131 vrc = VDISetImageUUIDs (Utf8Str (child->mFilePathFull),
2132 NULL, NULL, &parentUuid, &parentModUuid);
2133 if (VBOX_FAILURE (vrc))
2134 {
2135 rc = setError (E_FAIL,
2136 tr ("Could not update parent UUID of the hard disk image "
2137 "'%ls' (%Vrc)"),
2138 child->mFilePathFull.raw(), vrc);
2139 break;
2140 }
2141 }
2142
2143 if (FAILED (rc))
2144 return rc;
2145 }
2146
2147 /* detach all our children to avoid their uninit in #uninit() */
2148 removeDependentChildren();
2149
2150 mParent->clearBusy();
2151 clearBusy();
2152
2153 return S_OK;
2154}
2155
2156/**
2157 * Merges this image to all its child images, updates the parent UUID
2158 * of all children of this image (to point to this image's parent).
2159 * It's a responsibility of the caller to unregister and uninitialize
2160 * the merged image on success.
2161 *
2162 * This method is intended to be called on a worker thread (the operation
2163 * can be time consuming).
2164 *
2165 * The specified progress object (if not NULL) receives the percentage
2166 * of the operation completion. However, it is responsibility of the caller to
2167 * call Progress::notifyComplete() after this method returns.
2168 *
2169 * @param aProgress progress object to run during copy operation
2170 * (may be NULL)
2171 *
2172 * @note
2173 * This method expects that both this hard disk and all children
2174 * are marked as busy using setBusyWithChildren() prior to calling it!
2175 * Busy flags of all affected hard disks will be cleared by this method
2176 * on a successful return. In case of failure, #clearBusyWithChildren()
2177 * must be called for this hard disk.
2178 *
2179 * @note
2180 * Must be NOT called from under locks of other objects that need external
2181 * access dirung this method execurion!
2182 */
2183HRESULT HVirtualDiskImage::mergeImageToChildren (Progress *aProgress)
2184{
2185 LogFlowMember (("HVirtualDiskImage::mergeImageToChildren(): image='%ls'\n",
2186 mFilePathFull.raw()));
2187
2188 AutoLock alock (this);
2189 CHECK_READY();
2190
2191 /* this must be a diff image */
2192 AssertReturn (isDifferencing(), E_FAIL);
2193
2194 ComAssertRet (isBusy() == true, E_FAIL);
2195 ComAssertRet (mState >= Created, E_FAIL);
2196
2197 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2198 ("non VDI storage types are not yet supported!"), E_FAIL);
2199
2200 {
2201 HRESULT rc = S_OK;
2202
2203 AutoLock chLock (childrenLock());
2204
2205 /* iterate over a copy since we will modify the list */
2206 HardDiskList list = children();
2207
2208 for (HardDiskList::const_iterator it = list.begin();
2209 it != list.end(); ++ it)
2210 {
2211 ComObjPtr <HardDisk> hd = *it;
2212 ComObjPtr <HVirtualDiskImage> child = hd->asVDI();
2213 AutoLock childLock (child);
2214
2215 ComAssertRet (child->isBusy() == true, E_FAIL);
2216 ComAssertBreak (child->mState >= Created, rc = E_FAIL);
2217
2218 childLock.leave();
2219 alock.leave();
2220
2221 int vrc = VDIMergeImage (Utf8Str (mFilePathFull),
2222 Utf8Str (child->mFilePathFull),
2223 progressCallback,
2224 static_cast <Progress *> (aProgress));
2225 alock.enter();
2226 childLock.enter();
2227
2228 if (VBOX_FAILURE (vrc))
2229 {
2230 rc = setError (E_FAIL,
2231 tr ("Could not merge the hard disk image '%ls' to "
2232 "its parent image '%ls' (%Vrc)"),
2233 mFilePathFull.raw(), child->mFilePathFull.raw(), vrc);
2234 break;
2235 }
2236
2237 /* reparent the child */
2238 child->mParent = mParent;
2239 if (mParent)
2240 mParent->addDependentChild (child);
2241
2242 /* change the parent UUID in the image as well */
2243 RTUUID parentUuid, parentModUuid;
2244 vrc = VDIGetImageUUIDs (Utf8Str (mParent->asVDI()->mFilePathFull),
2245 &parentUuid, &parentModUuid, NULL, NULL);
2246 if (VBOX_FAILURE (vrc))
2247 {
2248 rc = setError (E_FAIL,
2249 tr ("Could not access the hard disk image '%ls' (%Vrc)"),
2250 mParent->asVDI()->mFilePathFull.raw(), vrc);
2251 break;
2252 }
2253 ComAssertBreak (mParent->id() == Guid (parentUuid), rc = E_FAIL);
2254 vrc = VDISetImageUUIDs (Utf8Str (child->mFilePathFull),
2255 NULL, NULL, &parentUuid, &parentModUuid);
2256 if (VBOX_FAILURE (vrc))
2257 {
2258 rc = setError (E_FAIL,
2259 tr ("Could not update parent UUID of the hard disk image "
2260 "'%ls' (%Vrc)"),
2261 child->mFilePathFull.raw(), vrc);
2262 break;
2263 }
2264
2265 /* detach child to avoid its uninit in #uninit() */
2266 removeDependentChild (child);
2267
2268 /* remove the busy flag */
2269 child->clearBusy();
2270 }
2271
2272 if (FAILED (rc))
2273 return rc;
2274 }
2275
2276 clearBusy();
2277
2278 return S_OK;
2279}
2280
2281/**
2282 * Deletes and recreates the differencing hard disk image from scratch.
2283 * The file name and UUID remain the same.
2284 */
2285HRESULT HVirtualDiskImage::wipeOutImage()
2286{
2287 AutoLock alock (this);
2288 CHECK_READY();
2289
2290 AssertReturn (isDifferencing(), E_FAIL);
2291 AssertReturn (mRegistered, E_FAIL);
2292 AssertReturn (mState >= Created, E_FAIL);
2293
2294 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2295 ("non-VDI storage types are not yet supported!"), E_FAIL);
2296
2297 Utf8Str filePathFull = mFilePathFull;
2298
2299 int vrc = RTFileDelete (filePathFull);
2300 if (VBOX_FAILURE (vrc))
2301 return setError (E_FAIL,
2302 tr ("Could not delete the image file '%s' (%Vrc)"),
2303 filePathFull.raw(), vrc);
2304
2305 vrc = VDICreateDifferenceImage (filePathFull,
2306 Utf8Str (mParent->asVDI()->mFilePathFull),
2307 NULL, NULL, NULL);
2308 /* update the UUID to correspond to the file name */
2309 if (VBOX_SUCCESS (vrc))
2310 vrc = VDISetImageUUIDs (filePathFull, mId, NULL, NULL, NULL);
2311
2312 if (VBOX_FAILURE (vrc))
2313 return setError (E_FAIL,
2314 tr ("Could not create a differencing hard disk '%s' (%Vrc)"),
2315 filePathFull.raw(), vrc);
2316
2317 return S_OK;
2318}
2319
2320// private methods
2321/////////////////////////////////////////////////////////////////////////////
2322
2323/**
2324 * Helper to set a new file path.
2325 * Resolves a path relatively to the Virtual Box home directory.
2326 *
2327 * @note
2328 * Must be called from under the object's lock!
2329 */
2330HRESULT HVirtualDiskImage::setFilePath (const BSTR aFilePath)
2331{
2332 if (aFilePath && *aFilePath)
2333 {
2334 /* get the full file name */
2335 char filePathFull [RTPATH_MAX];
2336 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aFilePath),
2337 filePathFull, sizeof (filePathFull));
2338 if (VBOX_FAILURE (vrc))
2339 return setError (E_FAIL,
2340 tr ("Invalid image file path '%ls' (%Vrc)"), aFilePath, vrc);
2341
2342 mFilePath = aFilePath;
2343 mFilePathFull = filePathFull;
2344 }
2345 else
2346 {
2347 mFilePath.setNull();
2348 mFilePathFull.setNull();
2349 }
2350
2351 return S_OK;
2352}
2353
2354/**
2355 * Helper to query information about the VDI hard disk.
2356 *
2357 * @param aAccessError not used when NULL, otherwise see #getAccessible()
2358 *
2359 * @note Must be called from under the object's lock, only after
2360 * CHECK_BUSY_AND_READERS() succeeds.
2361 */
2362HRESULT HVirtualDiskImage::queryInformation (Bstr *aAccessError)
2363{
2364 AssertReturn (isLockedOnCurrentThread(), E_FAIL);
2365
2366 /* create a lock object to completely release it later */
2367 AutoLock alock (this);
2368
2369 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
2370
2371 ComAssertRet (mState >= Created, E_FAIL);
2372
2373 HRESULT rc = S_OK;
2374 int vrc = VINF_SUCCESS;
2375
2376 /* lazily create a semaphore */
2377 vrc = RTSemEventMultiCreate (&mStateCheckSem);
2378 ComAssertRCRet (vrc, E_FAIL);
2379
2380 /* Reference the disk to prevent any concurrent modifications
2381 * after releasing the lock below (to unblock getters before
2382 * a lengthy operation). */
2383 addReader();
2384
2385 alock.leave();
2386
2387 /* VBoxVHDD management interface needs to be optimized: we're opening a
2388 * file three times in a raw to get three bits of information. */
2389
2390 Utf8Str filePath = mFilePathFull;
2391 Bstr errMsg;
2392
2393 do
2394 {
2395 /* check the image file */
2396 Guid id, parentId;
2397 vrc = VDICheckImage (filePath, NULL, NULL, NULL,
2398 id.ptr(), parentId.ptr(), NULL, 0);
2399
2400 if (VBOX_FAILURE (vrc))
2401 break;
2402
2403 if (!mId.isEmpty())
2404 {
2405 /* check that the actual UUID of the image matches the stored UUID */
2406 if (mId != id)
2407 {
2408 errMsg = Utf8StrFmt (
2409 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
2410 "match UUID {%Vuuid} stored in the registry"),
2411 id.ptr(), filePath.raw(), mId.ptr());
2412 break;
2413 }
2414 }
2415 else
2416 {
2417 /* assgn an UUID read from the image file */
2418 mId = id;
2419 }
2420
2421 if (mParent)
2422 {
2423 /* check parent UUID */
2424 AutoLock parentLock (mParent);
2425 if (mParent->id() != parentId)
2426 {
2427 errMsg = Utf8StrFmt (
2428 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
2429 "the hard disk image file '%s' doesn't match "
2430 "UUID {%Vuuid} stored in the registry"),
2431 parentId.raw(), mParent->toString().raw(),
2432 filePath.raw(), mParent->id().raw());
2433 break;
2434 }
2435 }
2436 else if (!parentId.isEmpty())
2437 {
2438 errMsg = Utf8StrFmt (
2439 tr ("Hard disk image '%s' is a differencing image that is linked "
2440 "to a hard disk with UUID {%Vuuid} and cannot be used "
2441 "directly as a base hard disk"),
2442 filePath.raw(), parentId.raw());
2443 break;
2444 }
2445
2446 {
2447 RTFILE file = NIL_RTFILE;
2448 vrc = RTFileOpen (&file, filePath, RTFILE_O_READ);
2449 if (VBOX_SUCCESS (vrc))
2450 {
2451 uint64_t size = 0;
2452 vrc = RTFileGetSize (file, &size);
2453 if (VBOX_SUCCESS (vrc))
2454 mActualSize = size;
2455 RTFileClose (file);
2456 }
2457 if (VBOX_FAILURE (vrc))
2458 break;
2459 }
2460
2461 if (!mParent)
2462 {
2463 /* query logical size only for non-differencing images */
2464
2465 PVDIDISK disk = VDIDiskCreate();
2466 vrc = VDIDiskOpenImage (disk, Utf8Str (mFilePathFull),
2467 VDI_OPEN_FLAGS_READONLY);
2468 if (VBOX_SUCCESS (vrc))
2469 {
2470 uint64_t size = VDIDiskGetSize (disk);
2471 /* convert to MBytes */
2472 mSize = size / 1024 / 1024;
2473 }
2474
2475 VDIDiskDestroy (disk);
2476 if (VBOX_FAILURE (vrc))
2477 break;
2478 }
2479 }
2480 while (0);
2481
2482 /* enter the lock again */
2483 alock.enter();
2484
2485 /* remove the reference */
2486 releaseReader();
2487
2488 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
2489 {
2490 LogWarningFunc (("'%ls' is not accessible "
2491 "(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
2492 mFilePathFull.raw(), rc, vrc, errMsg.raw()));
2493
2494 if (aAccessError)
2495 {
2496 if (!errMsg.isNull())
2497 *aAccessError = errMsg;
2498 else if (VBOX_FAILURE (vrc))
2499 *aAccessError = Utf8StrFmt (
2500 tr ("Could not access hard disk image '%ls' (%Vrc)"),
2501 mFilePathFull.raw(), vrc);
2502 }
2503
2504 /* downgrade to not accessible */
2505 mState = Created;
2506 }
2507 else
2508 {
2509 if (aAccessError)
2510 aAccessError->setNull();
2511
2512 mState = Accessible;
2513 }
2514
2515 /* inform waiters if there are any */
2516 if (mStateCheckWaiters > 0)
2517 {
2518 RTSemEventMultiSignal (mStateCheckSem);
2519 }
2520 else
2521 {
2522 /* delete the semaphore ourselves */
2523 RTSemEventMultiDestroy (mStateCheckSem);
2524 mStateCheckSem = NIL_RTSEMEVENTMULTI;
2525 }
2526
2527 return rc;
2528}
2529
2530/**
2531 * Helper to create hard disk images.
2532 *
2533 * @param aSize size in MB
2534 * @param aDynamic dynamic or fixed image
2535 * @param aProgress address of IProgress pointer to return
2536 */
2537HRESULT HVirtualDiskImage::createImage (ULONG64 aSize, BOOL aDynamic,
2538 IProgress **aProgress)
2539{
2540 AutoLock alock (this);
2541
2542 CHECK_BUSY_AND_READERS();
2543
2544 if (mState != NotCreated)
2545 return setError (E_ACCESSDENIED,
2546 tr ("Hard disk image '%ls' is already created"),
2547 mFilePathFull.raw());
2548
2549 if (!mFilePathFull)
2550 return setError (E_ACCESSDENIED,
2551 tr ("Cannot create a hard disk image using an empty (null) file path"),
2552 mFilePathFull.raw());
2553
2554 /* first ensure the directory exists */
2555 {
2556 Utf8Str imageDir = mFilePathFull;
2557 RTPathStripFilename (imageDir.mutableRaw());
2558 if (!RTDirExists (imageDir))
2559 {
2560 int vrc = RTDirCreateFullPath (imageDir, 0777);
2561 if (VBOX_FAILURE (vrc))
2562 {
2563 return setError (E_FAIL,
2564 tr ("Could not create a directory '%s' "
2565 "to store the image file (%Vrc)"),
2566 imageDir.raw(), vrc);
2567 }
2568 }
2569 }
2570
2571 /* check whether the given file exists or not */
2572 RTFILE file;
2573 int vrc = RTFileOpen (&file, Utf8Str (mFilePathFull),
2574 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
2575 if (vrc != VERR_FILE_NOT_FOUND)
2576 {
2577 if (VBOX_SUCCESS (vrc))
2578 RTFileClose (file);
2579 switch(vrc)
2580 {
2581 case VINF_SUCCESS:
2582 return setError (E_FAIL,
2583 tr ("Image file '%ls' already exists"),
2584 mFilePathFull.raw());
2585
2586 default:
2587 return setError(E_FAIL,
2588 tr ("Invalid image file path '%ls' (%Vrc)"),
2589 mFilePathFull.raw(), vrc);
2590 }
2591 }
2592
2593 /* check VDI size limits */
2594 {
2595 HRESULT rc;
2596 ULONG64 maxVDISize;
2597 ComPtr <ISystemProperties> props;
2598 rc = mVirtualBox->COMGETTER(SystemProperties) (props.asOutParam());
2599 ComAssertComRCRet (rc, E_FAIL);
2600 rc = props->COMGETTER(MaxVDISize) (&maxVDISize);
2601 ComAssertComRCRet (rc, E_FAIL);
2602
2603 if (aSize < 1 || aSize > maxVDISize)
2604 return setError (E_INVALIDARG,
2605 tr ("Invalid VDI size: %llu MB (must be in range [1, %llu] MB)"),
2606 aSize, maxVDISize);
2607 }
2608
2609 HRESULT rc;
2610
2611 /* create a project object */
2612 ComObjPtr <Progress> progress;
2613 progress.createObject();
2614 {
2615 Bstr desc = aDynamic ? tr ("Creating a dynamically expanding hard disk")
2616 : tr ("Creating a fixed-size hard disk");
2617 rc = progress->init (mVirtualBox, (IVirtualDiskImage *) this, desc,
2618 FALSE /* aCancelable */);
2619 CheckComRCReturnRC (rc);
2620 }
2621
2622 /* mark as busy (being created)
2623 * (VDI task thread will unmark it) */
2624 setBusy();
2625
2626 /* fill in VDI task data */
2627 VDITask *task = new VDITask (aDynamic ? VDITask::CreateDynamic
2628 : VDITask::CreateStatic,
2629 this, progress);
2630 task->size = aSize;
2631 task->size *= 1024 * 1024; /* convert to bytes */
2632
2633 /* create the hard disk creation thread, pass operation data */
2634 vrc = RTThreadCreate (NULL, VDITaskThread, (void *) task, 0,
2635 RTTHREADTYPE_MAIN_HEAVY_WORKER, 0, "VDITask");
2636 ComAssertMsgRC (vrc, ("Could not create a thread (%Vrc)", vrc));
2637 if (VBOX_FAILURE (vrc))
2638 {
2639 clearBusy();
2640 delete task;
2641 rc = E_FAIL;
2642 }
2643 else
2644 {
2645 /* get one interface for the caller */
2646 progress.queryInterfaceTo (aProgress);
2647 }
2648
2649 return rc;
2650}
2651
2652/* static */
2653DECLCALLBACK(int) HVirtualDiskImage::VDITaskThread (RTTHREAD thread, void *pvUser)
2654{
2655 VDITask *task = static_cast <VDITask *> (pvUser);
2656 AssertReturn (task, VERR_GENERAL_FAILURE);
2657
2658 LogFlowFunc (("operation=%d, size=%llu\n", task->operation, task->size));
2659
2660 VDIIMAGETYPE type = (VDIIMAGETYPE) 0;
2661
2662 switch (task->operation)
2663 {
2664 case VDITask::CreateDynamic: type = VDI_IMAGE_TYPE_NORMAL; break;
2665 case VDITask::CreateStatic: type = VDI_IMAGE_TYPE_FIXED; break;
2666 case VDITask::CloneToImage: break;
2667 default: AssertFailedReturn (VERR_GENERAL_FAILURE); break;
2668 }
2669
2670 HRESULT rc = S_OK;
2671 Utf8Str errorMsg;
2672
2673 if (task->operation == VDITask::CloneToImage)
2674 {
2675 Assert (!task->vdi->id().isEmpty());
2676 /// @todo (dmik) check locks
2677 AutoLock sourceLock (task->source);
2678 rc = task->source->cloneToImage (task->vdi->id(),
2679 Utf8Str (task->vdi->filePathFull()),
2680 task->progress);
2681
2682 /* release reader added in HardDisk::CloneToImage() */
2683 task->source->releaseReader();
2684 }
2685 else
2686 {
2687 int vrc = VDICreateBaseImage (Utf8Str (task->vdi->filePathFull()),
2688 type, task->size,
2689 Utf8Str (task->vdi->mDescription),
2690 progressCallback,
2691 static_cast <Progress *> (task->progress));
2692
2693 if (VBOX_SUCCESS (vrc) && task->vdi->id())
2694 {
2695 /* we have a non-null UUID, update the created image */
2696 vrc = VDISetImageUUIDs (Utf8Str (task->vdi->filePathFull()),
2697 task->vdi->id().raw(), NULL, NULL, NULL);
2698 }
2699
2700 if (VBOX_FAILURE (vrc))
2701 {
2702 errorMsg = Utf8StrFmt (
2703 tr ("Falied to create a hard disk image '%ls' (%Vrc)"),
2704 task->vdi->filePathFull().raw(), vrc);
2705 rc = E_FAIL;
2706 }
2707 }
2708
2709 LogFlowFunc (("rc=%08X\n", rc));
2710
2711 AutoLock alock (task->vdi);
2712
2713 /* clear busy set in in HardDisk::CloneToImage() or
2714 * in HVirtualDiskImage::createImage() */
2715 task->vdi->clearBusy();
2716
2717 if (SUCCEEDED (rc))
2718 {
2719 task->vdi->mState = HVirtualDiskImage::Created;
2720 /* update VDI data fields */
2721 Bstr errMsg;
2722 rc = task->vdi->queryInformation (&errMsg);
2723 /* we want to deliver the access check result to the caller
2724 * immediately, before he calls HardDisk::GetAccssible() himself. */
2725 if (SUCCEEDED (rc) && !errMsg.isNull())
2726 task->progress->notifyCompleteBstr (
2727 E_FAIL, COM_IIDOF (IVirtualDiskImage), getComponentName(),
2728 errMsg);
2729 else
2730 task->progress->notifyComplete (rc);
2731 }
2732 else
2733 {
2734 /* delete the target file so we don't have orphaned files */
2735 RTFileDelete(Utf8Str (task->vdi->filePathFull()));
2736
2737 task->vdi->mState = HVirtualDiskImage::NotCreated;
2738 /* complete the progress object */
2739 if (errorMsg)
2740 task->progress->notifyComplete (
2741 E_FAIL, COM_IIDOF (IVirtualDiskImage), getComponentName(),
2742 errorMsg);
2743 else
2744 task->progress->notifyComplete (rc);
2745 }
2746
2747 delete task;
2748
2749 return VINF_SUCCESS;
2750}
2751
2752////////////////////////////////////////////////////////////////////////////////
2753// HISCSIHardDisk class
2754////////////////////////////////////////////////////////////////////////////////
2755
2756// constructor / destructor
2757////////////////////////////////////////////////////////////////////////////////
2758
2759HRESULT HISCSIHardDisk::FinalConstruct()
2760{
2761 HRESULT rc = HardDisk::FinalConstruct();
2762 if (FAILED (rc))
2763 return rc;
2764
2765 mSize = 0;
2766 mActualSize = 0;
2767
2768 mPort = 0;
2769 mLun = 0;
2770
2771 return S_OK;
2772}
2773
2774void HISCSIHardDisk::FinalRelease()
2775{
2776 HardDisk::FinalRelease();
2777}
2778
2779// public initializer/uninitializer for internal purposes only
2780////////////////////////////////////////////////////////////////////////////////
2781
2782// public methods for internal purposes only
2783/////////////////////////////////////////////////////////////////////////////
2784
2785/**
2786 * Initializes the iSCSI hard disk object by reading its properties from
2787 * the given configuration node. The created hard disk will be marked as
2788 * registered on success.
2789 *
2790 * @param aHDNode <HardDisk> node
2791 * @param aVDINod <ISCSIHardDisk> node
2792 */
2793HRESULT HISCSIHardDisk::init (VirtualBox *aVirtualBox,
2794 CFGNODE aHDNode, CFGNODE aISCSINode)
2795{
2796 LogFlowMember (("HISCSIHardDisk::init (load)\n"));
2797
2798 AssertReturn (aHDNode && aISCSINode, E_FAIL);
2799
2800 AutoLock alock (this);
2801 ComAssertRet (!isReady(), E_UNEXPECTED);
2802
2803 mStorageType = HardDiskStorageType_ISCSIHardDisk;
2804
2805 HRESULT rc = S_OK;
2806
2807 do
2808 {
2809 rc = protectedInit (aVirtualBox, NULL);
2810 CheckComRCBreakRC (rc);
2811
2812 /* set ready to let protectedUninit() be called on failure */
2813 setReady (true);
2814
2815 /* server (required) */
2816 CFGLDRQueryBSTR (aISCSINode, "server", mServer.asOutParam());
2817 /* target (required) */
2818 CFGLDRQueryBSTR (aISCSINode, "target", mTarget.asOutParam());
2819
2820 /* port (optional) */
2821 CFGLDRQueryUInt16 (aISCSINode, "port", &mPort);
2822 /* lun (optional) */
2823 CFGLDRQueryUInt64 (aISCSINode, "lun", &mLun);
2824 /* userName (optional) */
2825 CFGLDRQueryBSTR (aISCSINode, "userName", mUserName.asOutParam());
2826 /* password (optional) */
2827 CFGLDRQueryBSTR (aISCSINode, "password", mPassword.asOutParam());
2828
2829 LogFlowMember ((" 'iscsi:%ls:%hu@%ls/%ls:%llu'\n",
2830 mServer.raw(), mPort, mUserName.raw(), mTarget.raw(),
2831 mLun));
2832
2833 /* load basic settings and children */
2834 rc = loadSettings (aHDNode);
2835 CheckComRCBreakRC (rc);
2836
2837 if (mType != HardDiskType_WritethroughHardDisk)
2838 {
2839 rc = setError (E_FAIL,
2840 tr ("Currently, non-Writethrough iSCSI hard disks are not "
2841 "allowed ('%ls')"),
2842 toString().raw());
2843 break;
2844 }
2845
2846 mRegistered = TRUE;
2847 }
2848 while (0);
2849
2850 if (FAILED (rc))
2851 uninit();
2852
2853 return rc;
2854}
2855
2856/**
2857 * Initializes the iSCSI hard disk object using default values for all
2858 * properties. The created hard disk will NOT be marked as registered.
2859 */
2860HRESULT HISCSIHardDisk::init (VirtualBox *aVirtualBox)
2861{
2862 LogFlowMember (("HISCSIHardDisk::init()\n"));
2863
2864 AutoLock alock (this);
2865 ComAssertRet (!isReady(), E_UNEXPECTED);
2866
2867 mStorageType = HardDiskStorageType_ISCSIHardDisk;
2868
2869 HRESULT rc = S_OK;
2870
2871 do
2872 {
2873 rc = protectedInit (aVirtualBox, NULL);
2874 CheckComRCBreakRC (rc);
2875
2876 /* set ready to let protectedUninit() be called on failure */
2877 setReady (true);
2878
2879 /* we have to generate a new UUID */
2880 mId.create();
2881 /* currently, all iSCSI hard disks are writethrough */
2882 mType = HardDiskType_WritethroughHardDisk;
2883 mRegistered = FALSE;
2884 }
2885 while (0);
2886
2887 if (FAILED (rc))
2888 uninit();
2889
2890 return rc;
2891}
2892
2893/**
2894 * Uninitializes the instance and sets the ready flag to FALSE.
2895 * Called either from FinalRelease(), by the parent when it gets destroyed,
2896 * or by a third party when it decides this object is no more valid.
2897 */
2898void HISCSIHardDisk::uninit()
2899{
2900 LogFlowMember (("HISCSIHardDisk::uninit()\n"));
2901
2902 AutoLock alock (this);
2903 if (!isReady())
2904 return;
2905
2906 HardDisk::protectedUninit (alock);
2907}
2908
2909// IHardDisk properties
2910////////////////////////////////////////////////////////////////////////////////
2911
2912STDMETHODIMP HISCSIHardDisk::COMGETTER(Description) (BSTR *aDescription)
2913{
2914 if (!aDescription)
2915 return E_POINTER;
2916
2917 AutoLock alock (this);
2918 CHECK_READY();
2919
2920 mDescription.cloneTo (aDescription);
2921 return S_OK;
2922}
2923
2924STDMETHODIMP HISCSIHardDisk::COMSETTER(Description) (INPTR BSTR aDescription)
2925{
2926 AutoLock alock (this);
2927 CHECK_READY();
2928
2929 CHECK_BUSY_AND_READERS();
2930
2931 if (mDescription != aDescription)
2932 {
2933 mDescription = aDescription;
2934 if (mRegistered)
2935 return mVirtualBox->saveSettings();
2936 }
2937
2938 return S_OK;
2939}
2940
2941STDMETHODIMP HISCSIHardDisk::COMGETTER(Size) (ULONG64 *aSize)
2942{
2943 if (!aSize)
2944 return E_POINTER;
2945
2946 AutoLock alock (this);
2947 CHECK_READY();
2948
2949 *aSize = mSize;
2950 return S_OK;
2951}
2952
2953STDMETHODIMP HISCSIHardDisk::COMGETTER(ActualSize) (ULONG64 *aActualSize)
2954{
2955 if (!aActualSize)
2956 return E_POINTER;
2957
2958 AutoLock alock (this);
2959 CHECK_READY();
2960
2961 *aActualSize = mActualSize;
2962 return S_OK;
2963}
2964
2965// IISCSIHardDisk properties
2966////////////////////////////////////////////////////////////////////////////////
2967
2968STDMETHODIMP HISCSIHardDisk::COMGETTER(Server) (BSTR *aServer)
2969{
2970 if (!aServer)
2971 return E_POINTER;
2972
2973 AutoLock alock (this);
2974 CHECK_READY();
2975
2976 mServer.cloneTo (aServer);
2977 return S_OK;
2978}
2979
2980STDMETHODIMP HISCSIHardDisk::COMSETTER(Server) (INPTR BSTR aServer)
2981{
2982 if (!aServer || !*aServer)
2983 return E_INVALIDARG;
2984
2985 AutoLock alock (this);
2986 CHECK_READY();
2987
2988 CHECK_BUSY_AND_READERS();
2989
2990 if (mServer != aServer)
2991 {
2992 mServer = aServer;
2993 if (mRegistered)
2994 return mVirtualBox->saveSettings();
2995 }
2996
2997 return S_OK;
2998}
2999
3000STDMETHODIMP HISCSIHardDisk::COMGETTER(Port) (USHORT *aPort)
3001{
3002 if (!aPort)
3003 return E_POINTER;
3004
3005 AutoLock alock (this);
3006 CHECK_READY();
3007
3008 *aPort = mPort;
3009 return S_OK;
3010}
3011
3012STDMETHODIMP HISCSIHardDisk::COMSETTER(Port) (USHORT aPort)
3013{
3014 AutoLock alock (this);
3015 CHECK_READY();
3016
3017 CHECK_BUSY_AND_READERS();
3018
3019 if (mPort != aPort)
3020 {
3021 mPort = aPort;
3022 if (mRegistered)
3023 return mVirtualBox->saveSettings();
3024 }
3025
3026 return S_OK;
3027}
3028
3029STDMETHODIMP HISCSIHardDisk::COMGETTER(Target) (BSTR *aTarget)
3030{
3031 if (!aTarget)
3032 return E_POINTER;
3033
3034 AutoLock alock (this);
3035 CHECK_READY();
3036
3037 mTarget.cloneTo (aTarget);
3038 return S_OK;
3039}
3040
3041STDMETHODIMP HISCSIHardDisk::COMSETTER(Target) (INPTR BSTR aTarget)
3042{
3043 if (!aTarget || !*aTarget)
3044 return E_INVALIDARG;
3045
3046 AutoLock alock (this);
3047 CHECK_READY();
3048
3049 CHECK_BUSY_AND_READERS();
3050
3051 if (mTarget != aTarget)
3052 {
3053 mTarget = aTarget;
3054 if (mRegistered)
3055 return mVirtualBox->saveSettings();
3056 }
3057
3058 return S_OK;
3059}
3060
3061STDMETHODIMP HISCSIHardDisk::COMGETTER(Lun) (ULONG64 *aLun)
3062{
3063 if (!aLun)
3064 return E_POINTER;
3065
3066 AutoLock alock (this);
3067 CHECK_READY();
3068
3069 *aLun = mLun;
3070 return S_OK;
3071}
3072
3073STDMETHODIMP HISCSIHardDisk::COMSETTER(Lun) (ULONG64 aLun)
3074{
3075 AutoLock alock (this);
3076 CHECK_READY();
3077
3078 CHECK_BUSY_AND_READERS();
3079
3080 if (mLun != aLun)
3081 {
3082 mLun = aLun;
3083 if (mRegistered)
3084 return mVirtualBox->saveSettings();
3085 }
3086
3087 return S_OK;
3088}
3089
3090STDMETHODIMP HISCSIHardDisk::COMGETTER(UserName) (BSTR *aUserName)
3091{
3092 if (!aUserName)
3093 return E_POINTER;
3094
3095 AutoLock alock (this);
3096 CHECK_READY();
3097
3098 mUserName.cloneTo (aUserName);
3099 return S_OK;
3100}
3101
3102STDMETHODIMP HISCSIHardDisk::COMSETTER(UserName) (INPTR BSTR aUserName)
3103{
3104 AutoLock alock (this);
3105 CHECK_READY();
3106
3107 CHECK_BUSY_AND_READERS();
3108
3109 if (mUserName != aUserName)
3110 {
3111 mUserName = aUserName;
3112 if (mRegistered)
3113 return mVirtualBox->saveSettings();
3114 }
3115
3116 return S_OK;
3117}
3118
3119STDMETHODIMP HISCSIHardDisk::COMGETTER(Password) (BSTR *aPassword)
3120{
3121 if (!aPassword)
3122 return E_POINTER;
3123
3124 AutoLock alock (this);
3125 CHECK_READY();
3126
3127 mPassword.cloneTo (aPassword);
3128 return S_OK;
3129}
3130
3131STDMETHODIMP HISCSIHardDisk::COMSETTER(Password) (INPTR BSTR aPassword)
3132{
3133 AutoLock alock (this);
3134 CHECK_READY();
3135
3136 CHECK_BUSY_AND_READERS();
3137
3138 if (mPassword != aPassword)
3139 {
3140 mPassword = aPassword;
3141 if (mRegistered)
3142 return mVirtualBox->saveSettings();
3143 }
3144
3145 return S_OK;
3146}
3147
3148// public/protected methods for internal purposes only
3149/////////////////////////////////////////////////////////////////////////////
3150
3151/**
3152 * Attempts to mark the hard disk as registered.
3153 * Only VirtualBox can call this method.
3154 */
3155HRESULT HISCSIHardDisk::trySetRegistered (BOOL aRegistered)
3156{
3157 AutoLock alock (this);
3158 CHECK_READY();
3159
3160 if (aRegistered)
3161 {
3162 if (mServer.isEmpty() || mTarget.isEmpty())
3163 return setError (E_FAIL,
3164 tr ("iSCSI Hard disk has no server or target defined"));
3165 }
3166 else
3167 {
3168 }
3169
3170 return HardDisk::trySetRegistered (aRegistered);
3171}
3172
3173/**
3174 * Checks accessibility of this iSCSI hard disk.
3175 */
3176HRESULT HISCSIHardDisk::getAccessible (Bstr &aAccessError)
3177{
3178 AutoLock alock (this);
3179 CHECK_READY();
3180
3181 /* check the basic accessibility */
3182 HRESULT rc = getBaseAccessible (aAccessError);
3183 if (FAILED (rc) || !aAccessError.isNull())
3184 return rc;
3185
3186 return queryInformation (aAccessError);
3187}
3188
3189/**
3190 * Saves hard disk settings to the specified storage node and saves
3191 * all children to the specified hard disk node
3192 *
3193 * @param aHDNode <HardDisk>
3194 * @param aStorageNode <ISCSIHardDisk> node
3195 */
3196HRESULT HISCSIHardDisk::saveSettings (CFGNODE aHDNode, CFGNODE aStorageNode)
3197{
3198 AssertReturn (aHDNode && aStorageNode, E_FAIL);
3199
3200 AutoLock alock (this);
3201 CHECK_READY();
3202
3203 /* server (required) */
3204 CFGLDRSetBSTR (aStorageNode, "server", mServer);
3205 /* target (required) */
3206 CFGLDRSetBSTR (aStorageNode, "target", mTarget);
3207
3208 /* port (optional) */
3209 if (mPort != 0)
3210 CFGLDRSetUInt16 (aStorageNode, "port", mPort);
3211 else
3212 CFGLDRDeleteAttribute (aStorageNode, "port");
3213 /* lun (optional) */
3214 if (mLun != 0)
3215 CFGLDRSetUInt64 (aStorageNode, "lun", mLun);
3216 else
3217 CFGLDRDeleteAttribute (aStorageNode, "lun");
3218 /* userName (optional) */
3219 if (!mUserName.isNull())
3220 CFGLDRSetBSTR (aStorageNode, "userName", mUserName);
3221 else
3222 CFGLDRDeleteAttribute (aStorageNode, "userName");
3223 /* password (optional) */
3224 if (!mPassword.isNull())
3225 CFGLDRSetBSTR (aStorageNode, "password", mPassword);
3226 else
3227 CFGLDRDeleteAttribute (aStorageNode, "password");
3228
3229 /* save basic settings and children */
3230 return HardDisk::saveSettings (aHDNode);
3231}
3232
3233/**
3234 * Returns the string representation of this hard disk.
3235 * When \a aShort is false, returns the full image file path.
3236 * Otherwise, returns the image file name only.
3237 *
3238 * @param aShort if true, a short representation is returned
3239 */
3240Bstr HISCSIHardDisk::toString (bool aShort /* = false */)
3241{
3242 AutoLock alock (this);
3243
3244 Bstr str;
3245 if (!aShort)
3246 {
3247 /* the format is iscsi:[<user@>]<server>[:<port>]/<target>[:<lun>] */
3248 str = Utf8StrFmt ("iscsi:%s%ls%s/%ls%s",
3249 !mUserName.isNull() ? Utf8StrFmt ("%ls@", mUserName.raw()).raw() : "",
3250 mServer.raw(),
3251 mPort != 0 ? Utf8StrFmt (":%hu", mPort).raw() : "",
3252 mTarget.raw(),
3253 mLun != 0 ? Utf8StrFmt (":%llu", mLun).raw() : "");
3254 }
3255 else
3256 {
3257 str = Utf8StrFmt ("%ls%s",
3258 mTarget.raw(),
3259 mLun != 0 ? Utf8StrFmt (":%llu", mLun).raw() : "");
3260 }
3261
3262 return str;
3263}
3264
3265/**
3266 * Creates a clone of this hard disk by storing hard disk data in the given
3267 * VDI file name.
3268 *
3269 * @param aId UUID to assign to the created image
3270 * @param aTargetPath VDI file where the cloned image is to be to stored
3271 * @param aProgress progress object to run during operation
3272 */
3273HRESULT
3274HISCSIHardDisk::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
3275 Progress *aProgress)
3276{
3277 ComAssertMsgFailed (("Not implemented"));
3278 return E_NOTIMPL;
3279
3280// AssertReturn (isBusy() == false, E_FAIL);
3281// addReader();
3282// releaseReader();
3283}
3284
3285/**
3286 * Creates a new differencing image for this hard disk with the given
3287 * VDI file name.
3288 *
3289 * @param aId UUID to assign to the created image
3290 * @param aTargetPath VDI file where to store the created differencing image
3291 * @param aProgress progress object to run during operation
3292 * (can be NULL)
3293 */
3294HRESULT
3295HISCSIHardDisk::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
3296 Progress *aProgress)
3297{
3298 ComAssertMsgFailed (("Not implemented"));
3299 return E_NOTIMPL;
3300
3301// AssertReturn (isBusy() == false, E_FAIL);
3302// addReader();
3303// releaseReader();
3304}
3305
3306// private methods
3307/////////////////////////////////////////////////////////////////////////////
3308
3309/**
3310 * Helper to query information about the iSCSI hard disk.
3311 *
3312 * @param aAccessError see #getAccessible()
3313 * @note
3314 * Must be called from under the object's lock!
3315 */
3316HRESULT HISCSIHardDisk::queryInformation (Bstr &aAccessError)
3317{
3318 /// @todo (dmik) query info about this iSCSI disk,
3319 // set mSize and mActualSize,
3320 // or set aAccessError in case of failure
3321
3322 aAccessError.setNull();
3323 return S_OK;
3324}
3325
3326////////////////////////////////////////////////////////////////////////////////
3327// HVMDKImage class
3328////////////////////////////////////////////////////////////////////////////////
3329
3330// constructor / destructor
3331////////////////////////////////////////////////////////////////////////////////
3332
3333HRESULT HVMDKImage::FinalConstruct()
3334{
3335 HRESULT rc = HardDisk::FinalConstruct();
3336 if (FAILED (rc))
3337 return rc;
3338
3339 mState = NotCreated;
3340
3341 mStateCheckSem = NIL_RTSEMEVENTMULTI;
3342 mStateCheckWaiters = 0;
3343
3344 mSize = 0;
3345 mActualSize = 0;
3346
3347 /* initialize the container */
3348 int vrc = VDCreate ("VMDK", VDError, this, &mContainer);
3349 ComAssertRCRet (vrc, E_FAIL);
3350
3351 return S_OK;
3352}
3353
3354void HVMDKImage::FinalRelease()
3355{
3356 if (mContainer != NULL)
3357 VDDestroy (mContainer);
3358
3359 HardDisk::FinalRelease();
3360}
3361
3362// public initializer/uninitializer for internal purposes only
3363////////////////////////////////////////////////////////////////////////////////
3364
3365// public methods for internal purposes only
3366/////////////////////////////////////////////////////////////////////////////
3367
3368/**
3369 * Initializes the VMDK hard disk object by reading its properties from
3370 * the given configuration node. The created hard disk will be marked as
3371 * registered on success.
3372 *
3373 * @param aHDNode <HardDisk> node
3374 * @param aVMDKNode <VirtualDiskImage> node
3375 */
3376HRESULT HVMDKImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
3377 CFGNODE aHDNode, CFGNODE aVMDKNode)
3378{
3379 LogFlowMember (("HVMDKImage::init (load)\n"));
3380
3381 AssertReturn (aHDNode && aVMDKNode, E_FAIL);
3382
3383 AutoLock alock (this);
3384 ComAssertRet (!isReady(), E_UNEXPECTED);
3385
3386 mStorageType = HardDiskStorageType_VMDKImage;
3387
3388 HRESULT rc = S_OK;
3389
3390 do
3391 {
3392 rc = protectedInit (aVirtualBox, aParent);
3393 CheckComRCBreakRC (rc);
3394
3395 /* set ready to let protectedUninit() be called on failure */
3396 setReady (true);
3397
3398 /* filePath (required) */
3399 Bstr filePath;
3400 CFGLDRQueryBSTR (aVMDKNode, "filePath", filePath.asOutParam());
3401
3402 rc = setFilePath (filePath);
3403 CheckComRCBreakRC (rc);
3404
3405 LogFlowMember ((" '%ls'\n", mFilePathFull.raw()));
3406
3407 /* load basic settings and children */
3408 rc = loadSettings (aHDNode);
3409 CheckComRCBreakRC (rc);
3410
3411 if (mType != HardDiskType_WritethroughHardDisk)
3412 {
3413 rc = setError (E_FAIL,
3414 tr ("Currently, non-Writethrough VMDK images are not "
3415 "allowed ('%ls')"),
3416 toString().raw());
3417 break;
3418 }
3419
3420 mState = Created;
3421 mRegistered = TRUE;
3422
3423 /* Don't call queryInformation() for registered hard disks to
3424 * prevent the calling thread (i.e. the VirtualBox server startup
3425 * thread) from an unexpected freeze. The vital mId property (UUID)
3426 * is read from the registry file in loadSettings(). To get the rest,
3427 * the user will have to call COMGETTER(Accessible) manually. */
3428 }
3429 while (0);
3430
3431 if (FAILED (rc))
3432 uninit();
3433
3434 return rc;
3435}
3436
3437/**
3438 * Initializes the VMDK hard disk object using the given image file name.
3439 *
3440 * @param aVirtualBox VirtualBox parent.
3441 * @param aParent Currently, must always be @c NULL.
3442 * @param aFilePath Path to the image file, or @c NULL to create an
3443 * image-less object.
3444 * @param aRegistered Whether to mark this disk as registered or not
3445 * (ignored when @a aFilePath is @c NULL, assuming @c FALSE)
3446 */
3447HRESULT HVMDKImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
3448 const BSTR aFilePath, BOOL aRegistered /* = FALSE */)
3449{
3450 LogFlowMember (("HVMDKImage::init (aFilePath='%ls', aRegistered=%d)\n",
3451 aFilePath, aRegistered));
3452
3453 AssertReturn (aParent == NULL, E_FAIL);
3454
3455 AutoLock alock (this);
3456 ComAssertRet (!isReady(), E_UNEXPECTED);
3457
3458 mStorageType = HardDiskStorageType_VMDKImage;
3459
3460 HRESULT rc = S_OK;
3461
3462 do
3463 {
3464 rc = protectedInit (aVirtualBox, aParent);
3465 CheckComRCBreakRC (rc);
3466
3467 /* set ready to let protectedUninit() be called on failure */
3468 setReady (true);
3469
3470 rc = setFilePath (aFilePath);
3471 CheckComRCBreakRC (rc);
3472
3473 /* currently, all VMDK hard disks are writethrough */
3474 mType = HardDiskType_WritethroughHardDisk;
3475
3476 Assert (mId.isEmpty());
3477
3478 if (aFilePath && *aFilePath)
3479 {
3480 mRegistered = aRegistered;
3481 mState = Created;
3482
3483 /* Call queryInformation() anyway (even if it will block), because
3484 * it is the only way to get the UUID of the existing VDI and
3485 * initialize the vital mId property. */
3486 Bstr errMsg;
3487 rc = queryInformation (&errMsg);
3488 if (SUCCEEDED (rc))
3489 {
3490 /* We are constructing a new HVirtualDiskImage object. If there
3491 * is a fatal accessibility error (we cannot read image UUID),
3492 * we have to fail. We do so even on non-fatal errors as well,
3493 * because it's not worth to keep going with the inaccessible
3494 * image from the very beginning (when nothing else depends on
3495 * it yet). */
3496 if (!errMsg.isNull())
3497 rc = setErrorBstr (E_FAIL, errMsg);
3498 }
3499 }
3500 else
3501 {
3502 mRegistered = FALSE;
3503 mState = NotCreated;
3504 mId.create();
3505 }
3506 }
3507 while (0);
3508
3509 if (FAILED (rc))
3510 uninit();
3511
3512 return rc;
3513}
3514
3515/**
3516 * Uninitializes the instance and sets the ready flag to FALSE.
3517 * Called either from FinalRelease(), by the parent when it gets destroyed,
3518 * or by a third party when it decides this object is no more valid.
3519 */
3520void HVMDKImage::uninit()
3521{
3522 LogFlowMember (("HVMDKImage::uninit()\n"));
3523
3524 AutoLock alock (this);
3525 if (!isReady())
3526 return;
3527
3528 HardDisk::protectedUninit (alock);
3529}
3530
3531// IHardDisk properties
3532////////////////////////////////////////////////////////////////////////////////
3533
3534STDMETHODIMP HVMDKImage::COMGETTER(Description) (BSTR *aDescription)
3535{
3536 if (!aDescription)
3537 return E_POINTER;
3538
3539 AutoLock alock (this);
3540 CHECK_READY();
3541
3542 mDescription.cloneTo (aDescription);
3543 return S_OK;
3544}
3545
3546STDMETHODIMP HVMDKImage::COMSETTER(Description) (INPTR BSTR aDescription)
3547{
3548 AutoLock alock (this);
3549 CHECK_READY();
3550
3551 CHECK_BUSY_AND_READERS();
3552
3553 return E_NOTIMPL;
3554
3555/// @todo (r=dmik) implement
3556//
3557// if (mState >= Created)
3558// {
3559// int vrc = VDISetImageComment (Utf8Str (mFilePathFull), Utf8Str (aDescription));
3560// if (VBOX_FAILURE (vrc))
3561// return setError (E_FAIL,
3562// tr ("Could not change the description of the VDI hard disk '%ls' "
3563// "(%Vrc)"),
3564// toString().raw(), vrc);
3565// }
3566//
3567// mDescription = aDescription;
3568// return S_OK;
3569}
3570
3571STDMETHODIMP HVMDKImage::COMGETTER(Size) (ULONG64 *aSize)
3572{
3573 if (!aSize)
3574 return E_POINTER;
3575
3576 AutoLock alock (this);
3577 CHECK_READY();
3578
3579/// @todo (r=dmik) will need this if we add suppord for differencing VMDKs
3580//
3581// /* only a non-differencing image knows the logical size */
3582// if (isDifferencing())
3583// return root()->COMGETTER(Size) (aSize);
3584
3585 *aSize = mSize;
3586 return S_OK;
3587}
3588
3589STDMETHODIMP HVMDKImage::COMGETTER(ActualSize) (ULONG64 *aActualSize)
3590{
3591 if (!aActualSize)
3592 return E_POINTER;
3593
3594 AutoLock alock (this);
3595 CHECK_READY();
3596
3597 *aActualSize = mActualSize;
3598 return S_OK;
3599}
3600
3601// IVirtualDiskImage properties
3602////////////////////////////////////////////////////////////////////////////////
3603
3604STDMETHODIMP HVMDKImage::COMGETTER(FilePath) (BSTR *aFilePath)
3605{
3606 if (!aFilePath)
3607 return E_POINTER;
3608
3609 AutoLock alock (this);
3610 CHECK_READY();
3611
3612 mFilePathFull.cloneTo (aFilePath);
3613 return S_OK;
3614}
3615
3616STDMETHODIMP HVMDKImage::COMSETTER(FilePath) (INPTR BSTR aFilePath)
3617{
3618 AutoLock alock (this);
3619 CHECK_READY();
3620
3621 if (mState != NotCreated)
3622 return setError (E_ACCESSDENIED,
3623 tr ("Cannot change the file path of the existing hard disk '%ls'"),
3624 toString().raw());
3625
3626 CHECK_BUSY_AND_READERS();
3627
3628 /* append the default path if only a name is given */
3629 Bstr path = aFilePath;
3630 if (aFilePath && *aFilePath)
3631 {
3632 Utf8Str fp = aFilePath;
3633 if (!RTPathHavePath (fp))
3634 {
3635 AutoReaderLock propsLock (mVirtualBox->systemProperties());
3636 path = Utf8StrFmt ("%ls%c%s",
3637 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
3638 RTPATH_DELIMITER,
3639 fp.raw());
3640 }
3641 }
3642
3643 return setFilePath (path);
3644}
3645
3646STDMETHODIMP HVMDKImage::COMGETTER(Created) (BOOL *aCreated)
3647{
3648 if (!aCreated)
3649 return E_POINTER;
3650
3651 AutoLock alock (this);
3652 CHECK_READY();
3653
3654 *aCreated = mState >= Created;
3655 return S_OK;
3656}
3657
3658// IVMDKImage methods
3659/////////////////////////////////////////////////////////////////////////////
3660
3661STDMETHODIMP HVMDKImage::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
3662{
3663 if (!aProgress)
3664 return E_POINTER;
3665
3666 AutoLock alock (this);
3667 CHECK_READY();
3668
3669 return createImage (aSize, TRUE /* aDynamic */, aProgress);
3670}
3671
3672STDMETHODIMP HVMDKImage::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
3673{
3674 if (!aProgress)
3675 return E_POINTER;
3676
3677 AutoLock alock (this);
3678 CHECK_READY();
3679
3680 return createImage (aSize, FALSE /* aDynamic */, aProgress);
3681}
3682
3683STDMETHODIMP HVMDKImage::DeleteImage()
3684{
3685 AutoLock alock (this);
3686 CHECK_READY();
3687 CHECK_BUSY_AND_READERS();
3688
3689 return E_NOTIMPL;
3690
3691/// @todo (r=dmik) later
3692// We will need to parse the file in order to delete all related delta and
3693// sparse images etc. We may also want to obey the .vmdk.lck file
3694// which is (as far as I understood) created when the VMware VM is
3695// running or saved etc.
3696//
3697// if (mRegistered)
3698// return setError (E_ACCESSDENIED,
3699// tr ("Cannot delete an image of the registered hard disk image '%ls"),
3700// mFilePathFull.raw());
3701// if (mState == NotCreated)
3702// return setError (E_FAIL,
3703// tr ("Hard disk image has been already deleted or never created"));
3704//
3705// int vrc = RTFileDelete (Utf8Str (mFilePathFull));
3706// if (VBOX_FAILURE (vrc))
3707// return setError (E_FAIL, tr ("Could not delete the image file '%ls' (%Vrc)"),
3708// mFilePathFull.raw(), vrc);
3709//
3710// mState = NotCreated;
3711//
3712// /* reset the fields */
3713// mSize = 0;
3714// mActualSize = 0;
3715//
3716// return S_OK;
3717}
3718
3719// public/protected methods for internal purposes only
3720/////////////////////////////////////////////////////////////////////////////
3721
3722/**
3723 * Attempts to mark the hard disk as registered.
3724 * Only VirtualBox can call this method.
3725 */
3726HRESULT HVMDKImage::trySetRegistered (BOOL aRegistered)
3727{
3728 AutoLock alock (this);
3729 CHECK_READY();
3730
3731 if (aRegistered)
3732 {
3733 if (mState == NotCreated)
3734 return setError (E_FAIL,
3735 tr ("Image file '%ls' is not yet created for this hard disk"),
3736 mFilePathFull.raw());
3737
3738/// @todo (r=dmik) will need this if we add suppord for differencing VMDKs
3739// if (isDifferencing())
3740// return setError (E_FAIL,
3741// tr ("Hard disk '%ls' is differencing and cannot be unregistered "
3742// "explicitly"),
3743// mFilePathFull.raw());
3744 }
3745 else
3746 {
3747 ComAssertRet (mState >= Created, E_FAIL);
3748 }
3749
3750 return HardDisk::trySetRegistered (aRegistered);
3751}
3752
3753/**
3754 * Checks accessibility of this hard disk image only (w/o parents).
3755 *
3756 * @param aAccessError on output, a null string indicates the hard disk is
3757 * accessible, otherwise contains a message describing
3758 * the reason of inaccessibility.
3759 */
3760HRESULT HVMDKImage::getAccessible (Bstr &aAccessError)
3761{
3762 AutoLock alock (this);
3763 CHECK_READY();
3764
3765 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
3766 {
3767 /* An accessibility check in progress on some other thread,
3768 * wait for it to finish. */
3769
3770 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL);
3771 ++ mStateCheckWaiters;
3772 alock.leave();
3773
3774 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
3775
3776 alock.enter();
3777 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
3778 -- mStateCheckWaiters;
3779 if (mStateCheckWaiters == 0)
3780 {
3781 RTSemEventMultiDestroy (mStateCheckSem);
3782 mStateCheckSem = NIL_RTSEMEVENTMULTI;
3783 }
3784
3785 AssertRCReturn (vrc, E_FAIL);
3786
3787 /* don't touch aAccessError, it has been already set */
3788 return S_OK;
3789 }
3790
3791 /* check the basic accessibility */
3792 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */);
3793 if (FAILED (rc) || !aAccessError.isNull())
3794 return rc;
3795
3796 if (mState >= Created)
3797 {
3798 return queryInformation (&aAccessError);
3799 }
3800
3801 aAccessError = Utf8StrFmt ("Hard disk image '%ls' is not yet created",
3802 mFilePathFull.raw());
3803 return S_OK;
3804}
3805
3806/**
3807 * Saves hard disk settings to the specified storage node and saves
3808 * all children to the specified hard disk node
3809 *
3810 * @param aHDNode <HardDisk> or <DiffHardDisk> node
3811 * @param aStorageNode <VirtualDiskImage> node
3812 */
3813HRESULT HVMDKImage::saveSettings (CFGNODE aHDNode, CFGNODE aStorageNode)
3814{
3815 AssertReturn (aHDNode && aStorageNode, E_FAIL);
3816
3817 AutoLock alock (this);
3818 CHECK_READY();
3819
3820 /* filePath (required) */
3821 CFGLDRSetBSTR (aStorageNode, "filePath", mFilePath);
3822
3823 /* save basic settings and children */
3824 return HardDisk::saveSettings (aHDNode);
3825}
3826
3827/**
3828 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
3829 * of this hard disk and updates it if necessary to reflect the new location.
3830 * Intended to be from HardDisk::updatePaths().
3831 *
3832 * @param aOldPath old path (full)
3833 * @param aNewPath new path (full)
3834 *
3835 * @note Locks this object for writing.
3836 */
3837void HVMDKImage::updatePath (const char *aOldPath, const char *aNewPath)
3838{
3839 AssertReturnVoid (aOldPath);
3840 AssertReturnVoid (aNewPath);
3841
3842 AutoLock alock (this);
3843 AssertReturnVoid (isReady());
3844
3845 size_t oldPathLen = strlen (aOldPath);
3846
3847 Utf8Str path = mFilePathFull;
3848 LogFlowThisFunc (("VMDK.fullPath={%s}\n", path.raw()));
3849
3850 if (RTPathStartsWith (path, aOldPath))
3851 {
3852 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
3853 path.raw() + oldPathLen);
3854 path = newPath;
3855
3856 mVirtualBox->calculateRelativePath (path, path);
3857
3858 unconst (mFilePathFull) = newPath;
3859 unconst (mFilePath) = path;
3860
3861 LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
3862 newPath.raw(), path.raw()));
3863 }
3864}
3865
3866/**
3867 * Returns the string representation of this hard disk.
3868 * When \a aShort is false, returns the full image file path.
3869 * Otherwise, returns the image file name only.
3870 *
3871 * @param aShort if true, a short representation is returned
3872 */
3873Bstr HVMDKImage::toString (bool aShort /* = false */)
3874{
3875 AutoLock alock (this);
3876
3877 if (!aShort)
3878 return mFilePathFull;
3879 else
3880 {
3881 Utf8Str fname = mFilePathFull;
3882 return RTPathFilename (fname.mutableRaw());
3883 }
3884}
3885
3886/**
3887 * Creates a clone of this hard disk by storing hard disk data in the given
3888 * VDI file name.
3889 *
3890 * @param aId UUID to assign to the created image
3891 * @param aTargetPath VDI file where the cloned image is to be to stored
3892 * @param aProgress progress object to run during operation
3893 */
3894HRESULT
3895HVMDKImage::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
3896 Progress *aProgress)
3897{
3898 ComAssertMsgFailed (("Not implemented"));
3899 return E_NOTIMPL;
3900
3901/// @todo (r=dmik) will need this if we add suppord for differencing VMDKs
3902// Use code from HVirtualDiskImage::cloneToImage as an example.
3903}
3904
3905/**
3906 * Creates a new differencing image for this hard disk with the given
3907 * VDI file name.
3908 *
3909 * @param aId UUID to assign to the created image
3910 * @param aTargetPath VDI file where to store the created differencing image
3911 * @param aProgress progress object to run during operation
3912 * (can be NULL)
3913 */
3914HRESULT
3915HVMDKImage::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
3916 Progress *aProgress)
3917{
3918 ComAssertMsgFailed (("Not implemented"));
3919 return E_NOTIMPL;
3920
3921/// @todo (r=dmik) will need this if we add suppord for differencing VMDKs
3922// Use code from HVirtualDiskImage::createDiffImage as an example.
3923}
3924
3925// private methods
3926/////////////////////////////////////////////////////////////////////////////
3927
3928/**
3929 * Helper to set a new file path.
3930 * Resolves a path relatively to the Virtual Box home directory.
3931 *
3932 * @note
3933 * Must be called from under the object's lock!
3934 */
3935HRESULT HVMDKImage::setFilePath (const BSTR aFilePath)
3936{
3937 if (aFilePath && *aFilePath)
3938 {
3939 /* get the full file name */
3940 char filePathFull [RTPATH_MAX];
3941 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aFilePath),
3942 filePathFull, sizeof (filePathFull));
3943 if (VBOX_FAILURE (vrc))
3944 return setError (E_FAIL,
3945 tr ("Invalid image file path '%ls' (%Vrc)"), aFilePath, vrc);
3946
3947 mFilePath = aFilePath;
3948 mFilePathFull = filePathFull;
3949 }
3950 else
3951 {
3952 mFilePath.setNull();
3953 mFilePathFull.setNull();
3954 }
3955
3956 return S_OK;
3957}
3958
3959/**
3960 * Helper to query information about the VDI hard disk.
3961 *
3962 * @param aAccessError not used when NULL, otherwise see #getAccessible()
3963 *
3964 * @note Must be called from under the object's lock, only after
3965 * CHECK_BUSY_AND_READERS() succeeds.
3966 */
3967HRESULT HVMDKImage::queryInformation (Bstr *aAccessError)
3968{
3969 AssertReturn (isLockedOnCurrentThread(), E_FAIL);
3970
3971 /* create a lock object to completely release it later */
3972 AutoLock alock (this);
3973
3974 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
3975
3976 ComAssertRet (mState >= Created, E_FAIL);
3977
3978 HRESULT rc = S_OK;
3979 int vrc = VINF_SUCCESS;
3980
3981 /* lazily create a semaphore */
3982 vrc = RTSemEventMultiCreate (&mStateCheckSem);
3983 ComAssertRCRet (vrc, E_FAIL);
3984
3985 /* Reference the disk to prevent any concurrent modifications
3986 * after releasing the lock below (to unblock getters before
3987 * a lengthy operation). */
3988 addReader();
3989
3990 alock.leave();
3991
3992 /* VBoxVHDD management interface needs to be optimized: we're opening a
3993 * file three times in a raw to get three bits of information. */
3994
3995 Utf8Str filePath = mFilePathFull;
3996 Bstr errMsg;
3997
3998 /* reset any previous error report from VDError() */
3999 mLastVDError.setNull();
4000
4001 do
4002 {
4003 Guid id, parentId;
4004
4005 /// @todo changed from VD_OPEN_FLAGS_READONLY to VD_OPEN_FLAGS_NORMAL,
4006 /// because otherwise registering a VMDK which so far has no UUID will
4007 /// yield a null UUID. It cannot be added to a VMDK opened readonly,
4008 /// obviously. This of course changes locking behavior, but for now
4009 /// this is acceptable. A better solution needs to be found later.
4010 vrc = VDOpen (mContainer, filePath, VD_OPEN_FLAGS_NORMAL);
4011 if (VBOX_FAILURE (vrc))
4012 break;
4013
4014 vrc = VDGetUuid (mContainer, 0, id.ptr());
4015 if (VBOX_FAILURE (vrc))
4016 break;
4017 vrc = VDGetParentUuid (mContainer, 0, parentId.ptr());
4018 if (VBOX_FAILURE (vrc))
4019 break;
4020
4021 if (!mId.isEmpty())
4022 {
4023 /* check that the actual UUID of the image matches the stored UUID */
4024 if (mId != id)
4025 {
4026 errMsg = Utf8StrFmt (
4027 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
4028 "match UUID {%Vuuid} stored in the registry"),
4029 id.ptr(), filePath.raw(), mId.ptr());
4030 break;
4031 }
4032 }
4033 else
4034 {
4035 /* assgn an UUID read from the image file */
4036 mId = id;
4037 }
4038
4039 if (mParent)
4040 {
4041 /* check parent UUID */
4042 AutoLock parentLock (mParent);
4043 if (mParent->id() != parentId)
4044 {
4045 errMsg = Utf8StrFmt (
4046 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
4047 "the hard disk image file '%s' doesn't match "
4048 "UUID {%Vuuid} stored in the registry"),
4049 parentId.raw(), mParent->toString().raw(),
4050 filePath.raw(), mParent->id().raw());
4051 break;
4052 }
4053 }
4054 else if (!parentId.isEmpty())
4055 {
4056 errMsg = Utf8StrFmt (
4057 tr ("Hard disk image '%s' is a differencing image that is linked "
4058 "to a hard disk with UUID {%Vuuid} and cannot be used "
4059 "directly as a base hard disk"),
4060 filePath.raw(), parentId.raw());
4061 break;
4062 }
4063
4064 /* get actual file size */
4065 /// @todo is there a direct method in RT?
4066 {
4067 RTFILE file = NIL_RTFILE;
4068 vrc = RTFileOpen (&file, filePath, RTFILE_O_READ);
4069 if (VBOX_SUCCESS (vrc))
4070 {
4071 uint64_t size = 0;
4072 vrc = RTFileGetSize (file, &size);
4073 if (VBOX_SUCCESS (vrc))
4074 mActualSize = size;
4075 RTFileClose (file);
4076 }
4077 if (VBOX_FAILURE (vrc))
4078 break;
4079 }
4080
4081 /* query logical size only for non-differencing images */
4082 if (!mParent)
4083 {
4084 uint64_t size = VDGetSize (mContainer);
4085 /* convert to MBytes */
4086 mSize = size / 1024 / 1024;
4087 }
4088 }
4089 while (0);
4090
4091 VDCloseAll (mContainer);
4092
4093 /* enter the lock again */
4094 alock.enter();
4095
4096 /* remove the reference */
4097 releaseReader();
4098
4099 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
4100 {
4101 LogWarningFunc (("'%ls' is not accessible "
4102 "(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
4103 mFilePathFull.raw(), rc, vrc, errMsg.raw()));
4104
4105 if (aAccessError)
4106 {
4107 if (!errMsg.isNull())
4108 *aAccessError = errMsg;
4109 else if (!mLastVDError.isNull())
4110 *aAccessError = mLastVDError;
4111 else if (VBOX_FAILURE (vrc))
4112 *aAccessError = Utf8StrFmt (
4113 tr ("Could not access hard disk image '%ls' (%Vrc)"),
4114 mFilePathFull.raw(), vrc);
4115 }
4116
4117 /* downgrade to not accessible */
4118 mState = Created;
4119 }
4120 else
4121 {
4122 if (aAccessError)
4123 aAccessError->setNull();
4124
4125 mState = Accessible;
4126 }
4127
4128 /* inform waiters if there are any */
4129 if (mStateCheckWaiters > 0)
4130 {
4131 RTSemEventMultiSignal (mStateCheckSem);
4132 }
4133 else
4134 {
4135 /* delete the semaphore ourselves */
4136 RTSemEventMultiDestroy (mStateCheckSem);
4137 mStateCheckSem = NIL_RTSEMEVENTMULTI;
4138 }
4139
4140 /* cleanup the last error report from VDError() */
4141 mLastVDError.setNull();
4142
4143 return rc;
4144}
4145
4146/**
4147 * Helper to create hard disk images.
4148 *
4149 * @param aSize size in MB
4150 * @param aDynamic dynamic or fixed image
4151 * @param aProgress address of IProgress pointer to return
4152 */
4153HRESULT HVMDKImage::createImage (ULONG64 aSize, BOOL aDynamic,
4154 IProgress **aProgress)
4155{
4156 ComAssertMsgFailed (("Not implemented"));
4157 return E_NOTIMPL;
4158
4159/// @todo (r=dmik) later
4160// Use code from HVirtualDiskImage::createImage as an example.
4161}
4162
4163/* static */
4164DECLCALLBACK(int) HVMDKImage::VDITaskThread (RTTHREAD thread, void *pvUser)
4165{
4166 AssertMsgFailed (("Not implemented"));
4167 return VERR_GENERAL_FAILURE;
4168
4169/// @todo (r=dmik) later
4170// Use code from HVirtualDiskImage::VDITaskThread as an example.
4171}
4172
4173/* static */
4174DECLCALLBACK(void) HVMDKImage::VDError (void *pvUser, int rc, RT_SRC_POS_DECL,
4175 const char *pszFormat, va_list va)
4176{
4177 HVMDKImage *that = static_cast <HVMDKImage *> (pvUser);
4178 AssertReturnVoid (that != NULL);
4179
4180 /// @todo pass the error message to the operation initiator
4181 Utf8Str err = Utf8StrFmt (pszFormat, va);
4182 if (VBOX_FAILURE (rc))
4183 err = Utf8StrFmt ("%s (%Vrc)", err.raw(), rc);
4184
4185 if (that->mLastVDError.isNull())
4186 that->mLastVDError = err;
4187 else
4188 that->mLastVDError = Utf8StrFmt
4189 ("%s.\n%s", that->mLastVDError.raw(), err.raw());
4190}
4191
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use