VirtualBox

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

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

Cap the VDI progress at 99% letting the main code do the final %.

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

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