VirtualBox

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

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

Main: Added basic VMDK property support via the new API (retrieving UUIDs and virtual size).

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

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