VirtualBox

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

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

InnoTek -> innotek: all the headers and comments.

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

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