VirtualBox

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

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

Merged dmik/s2 branch (r25959:26751) to the trunk.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 141.2 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 (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will 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 (const settings::Key &aHDNode)
1195{
1196 using namespace settings;
1197
1198 AssertReturn (!aHDNode.isNull(), E_FAIL);
1199
1200 /* required */
1201 mId = aHDNode.value <Guid> ("uuid");
1202
1203 if (!isDifferencing())
1204 {
1205 /* type required for <HardDisk> nodes only */
1206 const char *type = aHDNode.stringValue ("type");
1207 if (strcmp (type, "normal") == 0)
1208 mType = HardDiskType_NormalHardDisk;
1209 else if (strcmp (type, "immutable") == 0)
1210 mType = HardDiskType_ImmutableHardDisk;
1211 else if (strcmp (type, "writethrough") == 0)
1212 mType = HardDiskType_WritethroughHardDisk;
1213 else
1214 ComAssertMsgFailedRet (("Invalid hard disk type '%s'\n", type),
1215 E_FAIL);
1216 }
1217 else
1218 mType = HardDiskType_NormalHardDisk;
1219
1220 HRESULT rc = mVirtualBox->registerHardDisk (this, VirtualBox::RHD_OnStartUp);
1221 CheckComRCReturnRC (rc);
1222
1223 /* load all children */
1224 Key::List children = aHDNode.keys ("DiffHardDisk");
1225 for (Key::List::const_iterator it = children.begin();
1226 it != children.end(); ++ it)
1227 {
1228 Key vdiNode = (*it).key ("VirtualDiskImage");
1229
1230 ComObjPtr <HVirtualDiskImage> vdi;
1231 vdi.createObject();
1232 rc = vdi->init (mVirtualBox, this, (*it), vdiNode);
1233 CheckComRCBreakRC (rc);
1234 }
1235
1236 return rc;
1237}
1238
1239/**
1240 * Saves the base settings of the hard disk to the given node
1241 * and saves all child hard disks as <DiffHardDisk> nodes.
1242 *
1243 * Subclasses must call this method in their saveSettings() methods
1244 * in order to be properly saved to the settings file.
1245 *
1246 * @param aHDNode <HardDisk> node when #isDifferencing() = false, or
1247 * <DiffHardDisk> node otherwise.
1248 *
1249 * @note
1250 * Must be called from under the object's lock
1251 */
1252HRESULT HardDisk::saveSettings (settings::Key &aHDNode)
1253{
1254 using namespace settings;
1255
1256 AssertReturn (!aHDNode.isNull(), E_FAIL);
1257
1258 /* uuid (required) */
1259 aHDNode.setValue <Guid> ("uuid", mId);
1260
1261 if (!isDifferencing())
1262 {
1263 /* type (required) */
1264 const char *type = NULL;
1265 switch (mType)
1266 {
1267 case HardDiskType_NormalHardDisk:
1268 type = "normal";
1269 break;
1270 case HardDiskType_ImmutableHardDisk:
1271 type = "immutable";
1272 break;
1273 case HardDiskType_WritethroughHardDisk:
1274 type = "writethrough";
1275 break;
1276 }
1277 aHDNode.setStringValue ("type", type);
1278 }
1279
1280 /* save all children */
1281 AutoLock chLock (childrenLock());
1282 for (HardDiskList::const_iterator it = children().begin();
1283 it != children().end();
1284 ++ it)
1285 {
1286 ComObjPtr <HardDisk> child = *it;
1287 AutoLock childLock (child);
1288
1289 Key hdNode = aHDNode.appendKey ("DiffHardDisk");
1290
1291 {
1292 Key vdiNode = hdNode.createKey ("VirtualDiskImage");
1293 HRESULT rc = child->saveSettings (hdNode, vdiNode);
1294 CheckComRCReturnRC (rc);
1295 }
1296 }
1297
1298 return S_OK;
1299}
1300
1301////////////////////////////////////////////////////////////////////////////////
1302// HVirtualDiskImage class
1303////////////////////////////////////////////////////////////////////////////////
1304
1305// constructor / destructor
1306////////////////////////////////////////////////////////////////////////////////
1307
1308HRESULT HVirtualDiskImage::FinalConstruct()
1309{
1310 HRESULT rc = HardDisk::FinalConstruct();
1311 if (FAILED (rc))
1312 return rc;
1313
1314 mState = NotCreated;
1315
1316 mStateCheckSem = NIL_RTSEMEVENTMULTI;
1317 mStateCheckWaiters = 0;
1318
1319 mSize = 0;
1320 mActualSize = 0;
1321
1322 return S_OK;
1323}
1324
1325void HVirtualDiskImage::FinalRelease()
1326{
1327 HardDisk::FinalRelease();
1328}
1329
1330// public initializer/uninitializer for internal purposes only
1331////////////////////////////////////////////////////////////////////////////////
1332
1333// public methods for internal purposes only
1334/////////////////////////////////////////////////////////////////////////////
1335
1336/**
1337 * Initializes the VDI hard disk object by reading its properties from
1338 * the given configuration node. The created hard disk will be marked as
1339 * registered on success.
1340 *
1341 * @param aHDNode <HardDisk> or <DiffHardDisk> node.
1342 * @param aVDINode <VirtualDiskImage> node.
1343 */
1344HRESULT HVirtualDiskImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
1345 const settings::Key &aHDNode,
1346 const settings::Key &aVDINode)
1347{
1348 using namespace settings;
1349
1350 LogFlowThisFunc (("\n"));
1351
1352 AssertReturn (!aHDNode.isNull() && !aVDINode.isNull(), E_FAIL);
1353
1354 AutoLock alock (this);
1355 ComAssertRet (!isReady(), E_UNEXPECTED);
1356
1357 mStorageType = HardDiskStorageType_VirtualDiskImage;
1358
1359 HRESULT rc = S_OK;
1360
1361 do
1362 {
1363 rc = protectedInit (aVirtualBox, aParent);
1364 CheckComRCBreakRC (rc);
1365
1366 /* set ready to let protectedUninit() be called on failure */
1367 setReady (true);
1368
1369 /* filePath (required) */
1370 Bstr filePath = aVDINode.stringValue ("filePath");
1371 rc = setFilePath (filePath);
1372 CheckComRCBreakRC (rc);
1373
1374 LogFlowThisFunc (("'%ls'\n", mFilePathFull.raw()));
1375
1376 /* load basic settings and children */
1377 rc = loadSettings (aHDNode);
1378 CheckComRCBreakRC (rc);
1379
1380 mState = Created;
1381 mRegistered = TRUE;
1382
1383 /* Don't call queryInformation() for registered hard disks to
1384 * prevent the calling thread (i.e. the VirtualBox server startup
1385 * thread) from an unexpected freeze. The vital mId property (UUID)
1386 * is read from the registry file in loadSettings(). To get the rest,
1387 * the user will have to call COMGETTER(Accessible) manually. */
1388 }
1389 while (0);
1390
1391 if (FAILED (rc))
1392 uninit();
1393
1394 return rc;
1395}
1396
1397/**
1398 * Initializes the VDI hard disk object using the given image file name.
1399 *
1400 * @param aVirtualBox VirtualBox parent.
1401 * @param aParent Parent hard disk.
1402 * @param aFilePath Path to the image file, or @c NULL to create an
1403 * image-less object.
1404 * @param aRegistered Whether to mark this disk as registered or not
1405 * (ignored when @a aFilePath is @c NULL, assuming @c FALSE)
1406 */
1407HRESULT HVirtualDiskImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
1408 const BSTR aFilePath, BOOL aRegistered /* = FALSE */)
1409{
1410 LogFlowThisFunc (("aFilePath='%ls', aRegistered=%d\n",
1411 aFilePath, aRegistered));
1412
1413 AutoLock alock (this);
1414 ComAssertRet (!isReady(), E_UNEXPECTED);
1415
1416 mStorageType = HardDiskStorageType_VirtualDiskImage;
1417
1418 HRESULT rc = S_OK;
1419
1420 do
1421 {
1422 rc = protectedInit (aVirtualBox, aParent);
1423 CheckComRCBreakRC (rc);
1424
1425 /* set ready to let protectedUninit() be called on failure */
1426 setReady (true);
1427
1428 rc = setFilePath (aFilePath);
1429 CheckComRCBreakRC (rc);
1430
1431 Assert (mId.isEmpty());
1432
1433 if (aFilePath && *aFilePath)
1434 {
1435 mRegistered = aRegistered;
1436 mState = Created;
1437
1438 /* Call queryInformation() anyway (even if it will block), because
1439 * it is the only way to get the UUID of the existing VDI and
1440 * initialize the vital mId property. */
1441 Bstr errMsg;
1442 rc = queryInformation (&errMsg);
1443 if (SUCCEEDED (rc))
1444 {
1445 /* We are constructing a new HVirtualDiskImage object. If there
1446 * is a fatal accessibility error (we cannot read image UUID),
1447 * we have to fail. We do so even on non-fatal errors as well,
1448 * because it's not worth to keep going with the inaccessible
1449 * image from the very beginning (when nothing else depends on
1450 * it yet). */
1451 if (!errMsg.isNull())
1452 rc = setErrorBstr (E_FAIL, errMsg);
1453 }
1454 }
1455 else
1456 {
1457 mRegistered = FALSE;
1458 mState = NotCreated;
1459 mId.create();
1460 }
1461 }
1462 while (0);
1463
1464 if (FAILED (rc))
1465 uninit();
1466
1467 return rc;
1468}
1469
1470/**
1471 * Uninitializes the instance and sets the ready flag to FALSE.
1472 * Called either from FinalRelease(), by the parent when it gets destroyed,
1473 * or by a third party when it decides this object is no more valid.
1474 */
1475void HVirtualDiskImage::uninit()
1476{
1477 LogFlowThisFunc (("\n"));
1478
1479 AutoLock alock (this);
1480 if (!isReady())
1481 return;
1482
1483 HardDisk::protectedUninit (alock);
1484}
1485
1486// IHardDisk properties
1487////////////////////////////////////////////////////////////////////////////////
1488
1489STDMETHODIMP HVirtualDiskImage::COMGETTER(Description) (BSTR *aDescription)
1490{
1491 if (!aDescription)
1492 return E_POINTER;
1493
1494 AutoLock alock (this);
1495 CHECK_READY();
1496
1497 mDescription.cloneTo (aDescription);
1498 return S_OK;
1499}
1500
1501STDMETHODIMP HVirtualDiskImage::COMSETTER(Description) (INPTR BSTR aDescription)
1502{
1503 AutoLock alock (this);
1504 CHECK_READY();
1505
1506 CHECK_BUSY_AND_READERS();
1507
1508 if (mState >= Created)
1509 {
1510 int vrc = VDISetImageComment (Utf8Str (mFilePathFull), Utf8Str (aDescription));
1511 if (VBOX_FAILURE (vrc))
1512 return setError (E_FAIL,
1513 tr ("Could not change the description of the VDI hard disk '%ls' "
1514 "(%Vrc)"),
1515 toString().raw(), vrc);
1516 }
1517
1518 mDescription = aDescription;
1519 return S_OK;
1520}
1521
1522STDMETHODIMP HVirtualDiskImage::COMGETTER(Size) (ULONG64 *aSize)
1523{
1524 if (!aSize)
1525 return E_POINTER;
1526
1527 AutoLock alock (this);
1528 CHECK_READY();
1529
1530 /* only a non-differencing image knows the logical size */
1531 if (isDifferencing())
1532 return root()->COMGETTER(Size) (aSize);
1533
1534 *aSize = mSize;
1535 return S_OK;
1536}
1537
1538STDMETHODIMP HVirtualDiskImage::COMGETTER(ActualSize) (ULONG64 *aActualSize)
1539{
1540 if (!aActualSize)
1541 return E_POINTER;
1542
1543 AutoLock alock (this);
1544 CHECK_READY();
1545
1546 *aActualSize = mActualSize;
1547 return S_OK;
1548}
1549
1550// IVirtualDiskImage properties
1551////////////////////////////////////////////////////////////////////////////////
1552
1553STDMETHODIMP HVirtualDiskImage::COMGETTER(FilePath) (BSTR *aFilePath)
1554{
1555 if (!aFilePath)
1556 return E_POINTER;
1557
1558 AutoLock alock (this);
1559 CHECK_READY();
1560
1561 mFilePathFull.cloneTo (aFilePath);
1562 return S_OK;
1563}
1564
1565STDMETHODIMP HVirtualDiskImage::COMSETTER(FilePath) (INPTR BSTR aFilePath)
1566{
1567 AutoLock alock (this);
1568 CHECK_READY();
1569
1570 if (mState != NotCreated)
1571 return setError (E_ACCESSDENIED,
1572 tr ("Cannot change the file path of the existing hard disk '%ls'"),
1573 toString().raw());
1574
1575 CHECK_BUSY_AND_READERS();
1576
1577 /* append the default path if only a name is given */
1578 Bstr path = aFilePath;
1579 if (aFilePath && *aFilePath)
1580 {
1581 Utf8Str fp = aFilePath;
1582 if (!RTPathHavePath (fp))
1583 {
1584 AutoReaderLock propsLock (mVirtualBox->systemProperties());
1585 path = Utf8StrFmt ("%ls%c%s",
1586 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
1587 RTPATH_DELIMITER,
1588 fp.raw());
1589 }
1590 }
1591
1592 return setFilePath (path);
1593}
1594
1595STDMETHODIMP HVirtualDiskImage::COMGETTER(Created) (BOOL *aCreated)
1596{
1597 if (!aCreated)
1598 return E_POINTER;
1599
1600 AutoLock alock (this);
1601 CHECK_READY();
1602
1603 *aCreated = mState >= Created;
1604 return S_OK;
1605}
1606
1607// IVirtualDiskImage methods
1608/////////////////////////////////////////////////////////////////////////////
1609
1610STDMETHODIMP HVirtualDiskImage::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
1611{
1612 if (!aProgress)
1613 return E_POINTER;
1614
1615 AutoLock alock (this);
1616 CHECK_READY();
1617
1618 return createImage (aSize, TRUE /* aDynamic */, aProgress);
1619}
1620
1621STDMETHODIMP HVirtualDiskImage::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
1622{
1623 if (!aProgress)
1624 return E_POINTER;
1625
1626 AutoLock alock (this);
1627 CHECK_READY();
1628
1629 return createImage (aSize, FALSE /* aDynamic */, aProgress);
1630}
1631
1632STDMETHODIMP HVirtualDiskImage::DeleteImage()
1633{
1634 AutoLock alock (this);
1635 CHECK_READY();
1636 CHECK_BUSY_AND_READERS();
1637
1638 if (mRegistered)
1639 return setError (E_ACCESSDENIED,
1640 tr ("Cannot delete an image of the registered hard disk image '%ls"),
1641 mFilePathFull.raw());
1642 if (mState == NotCreated)
1643 return setError (E_FAIL,
1644 tr ("Hard disk image has been already deleted or never created"));
1645
1646 HRESULT rc = deleteImage();
1647 CheckComRCReturnRC (rc);
1648
1649 mState = NotCreated;
1650
1651 /* reset the fields */
1652 mSize = 0;
1653 mActualSize = 0;
1654
1655 return S_OK;
1656}
1657
1658// public/protected methods for internal purposes only
1659/////////////////////////////////////////////////////////////////////////////
1660
1661/**
1662 * Attempts to mark the hard disk as registered.
1663 * Only VirtualBox can call this method.
1664 */
1665HRESULT HVirtualDiskImage::trySetRegistered (BOOL aRegistered)
1666{
1667 AutoLock alock (this);
1668 CHECK_READY();
1669
1670 if (aRegistered)
1671 {
1672 if (mState == NotCreated)
1673 return setError (E_FAIL,
1674 tr ("Image file '%ls' is not yet created for this hard disk"),
1675 mFilePathFull.raw());
1676 if (isDifferencing())
1677 return setError (E_FAIL,
1678 tr ("Hard disk '%ls' is differencing and cannot be unregistered "
1679 "explicitly"),
1680 mFilePathFull.raw());
1681 }
1682 else
1683 {
1684 ComAssertRet (mState >= Created, E_FAIL);
1685 }
1686
1687 return HardDisk::trySetRegistered (aRegistered);
1688}
1689
1690/**
1691 * Checks accessibility of this hard disk image only (w/o parents).
1692 *
1693 * @param aAccessError on output, a null string indicates the hard disk is
1694 * accessible, otherwise contains a message describing
1695 * the reason of inaccessibility.
1696 */
1697HRESULT HVirtualDiskImage::getAccessible (Bstr &aAccessError)
1698{
1699 AutoLock alock (this);
1700 CHECK_READY();
1701
1702 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
1703 {
1704 /* An accessibility check in progress on some other thread,
1705 * wait for it to finish. */
1706
1707 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL);
1708 ++ mStateCheckWaiters;
1709 alock.leave();
1710
1711 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
1712
1713 alock.enter();
1714 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
1715 -- mStateCheckWaiters;
1716 if (mStateCheckWaiters == 0)
1717 {
1718 RTSemEventMultiDestroy (mStateCheckSem);
1719 mStateCheckSem = NIL_RTSEMEVENTMULTI;
1720 }
1721
1722 AssertRCReturn (vrc, E_FAIL);
1723
1724 /* don't touch aAccessError, it has been already set */
1725 return S_OK;
1726 }
1727
1728 /* check the basic accessibility */
1729 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */);
1730 if (FAILED (rc) || !aAccessError.isNull())
1731 return rc;
1732
1733 if (mState >= Created)
1734 {
1735 return queryInformation (&aAccessError);
1736 }
1737
1738 aAccessError = Utf8StrFmt ("Hard disk image '%ls' is not yet created",
1739 mFilePathFull.raw());
1740 return S_OK;
1741}
1742
1743/**
1744 * Saves hard disk settings to the specified storage node and saves
1745 * all children to the specified hard disk node
1746 *
1747 * @param aHDNode <HardDisk> or <DiffHardDisk> node.
1748 * @param aStorageNode <VirtualDiskImage> node.
1749 */
1750HRESULT HVirtualDiskImage::saveSettings (settings::Key &aHDNode,
1751 settings::Key &aStorageNode)
1752{
1753 AssertReturn (!aHDNode.isNull() && !aStorageNode.isNull(), E_FAIL);
1754
1755 AutoLock alock (this);
1756 CHECK_READY();
1757
1758 /* filePath (required) */
1759 aStorageNode.setValue <Bstr> ("filePath", mFilePath);
1760
1761 /* save basic settings and children */
1762 return HardDisk::saveSettings (aHDNode);
1763}
1764
1765/**
1766 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
1767 * of this hard disk and updates it if necessary to reflect the new location.
1768 * Intended to be from HardDisk::updatePaths().
1769 *
1770 * @param aOldPath old path (full)
1771 * @param aNewPath new path (full)
1772 *
1773 * @note Locks this object for writing.
1774 */
1775void HVirtualDiskImage::updatePath (const char *aOldPath, const char *aNewPath)
1776{
1777 AssertReturnVoid (aOldPath);
1778 AssertReturnVoid (aNewPath);
1779
1780 AutoLock alock (this);
1781 AssertReturnVoid (isReady());
1782
1783 size_t oldPathLen = strlen (aOldPath);
1784
1785 Utf8Str path = mFilePathFull;
1786 LogFlowThisFunc (("VDI.fullPath={%s}\n", path.raw()));
1787
1788 if (RTPathStartsWith (path, aOldPath))
1789 {
1790 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
1791 path.raw() + oldPathLen);
1792 path = newPath;
1793
1794 mVirtualBox->calculateRelativePath (path, path);
1795
1796 unconst (mFilePathFull) = newPath;
1797 unconst (mFilePath) = path;
1798
1799 LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
1800 newPath.raw(), path.raw()));
1801 }
1802}
1803
1804/**
1805 * Returns the string representation of this hard disk.
1806 * When \a aShort is false, returns the full image file path.
1807 * Otherwise, returns the image file name only.
1808 *
1809 * @param aShort if true, a short representation is returned
1810 */
1811Bstr HVirtualDiskImage::toString (bool aShort /* = false */)
1812{
1813 AutoLock alock (this);
1814
1815 if (!aShort)
1816 return mFilePathFull;
1817 else
1818 {
1819 Utf8Str fname = mFilePathFull;
1820 return RTPathFilename (fname.mutableRaw());
1821 }
1822}
1823
1824/**
1825 * Creates a clone of this hard disk by storing hard disk data in the given
1826 * VDI file.
1827 *
1828 * If the operation fails, @a aDeleteTarget will be set to @c true unless the
1829 * failure happened because the target file already existed.
1830 *
1831 * @param aId UUID to assign to the created image.
1832 * @param aTargetPath VDI file where the cloned image is to be to stored.
1833 * @param aProgress progress object to run during operation.
1834 * @param aDeleteTarget Whether it is recommended to delete target on
1835 * failure or not.
1836 */
1837HRESULT
1838HVirtualDiskImage::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
1839 Progress *aProgress, bool &aDeleteTarget)
1840{
1841 /* normally, the target file should be deleted on error */
1842 aDeleteTarget = true;
1843
1844 AssertReturn (!aId.isEmpty(), E_FAIL);
1845 AssertReturn (!aTargetPath.isNull(), E_FAIL);
1846 AssertReturn (aProgress, E_FAIL);
1847
1848 AutoLock alock (this);
1849 AssertReturn (isReady(), E_FAIL);
1850
1851 AssertReturn (isBusy() == false, E_FAIL);
1852
1853 /// @todo (dmik) cloning of differencing images is not yet supported
1854 AssertReturn (mParent.isNull(), E_FAIL);
1855
1856 Utf8Str filePathFull = mFilePathFull;
1857
1858 if (mState == NotCreated)
1859 return setError (E_FAIL,
1860 tr ("Source hard disk image '%s' is not yet created"),
1861 filePathFull.raw());
1862
1863 addReader();
1864 alock.leave();
1865
1866 int vrc = VDICopyImage (aTargetPath, filePathFull, NULL,
1867 progressCallback,
1868 static_cast <Progress *> (aProgress));
1869
1870 alock.enter();
1871 releaseReader();
1872
1873 /* We don't want to delete existing user files */
1874 if (vrc == VERR_ALREADY_EXISTS)
1875 aDeleteTarget = false;
1876
1877 if (VBOX_SUCCESS (vrc))
1878 vrc = VDISetImageUUIDs (aTargetPath, aId, NULL, NULL, NULL);
1879
1880 if (VBOX_FAILURE (vrc))
1881 return setError (E_FAIL,
1882 tr ("Could not copy the hard disk image '%s' to '%s' (%Vrc)"),
1883 filePathFull.raw(), aTargetPath.raw(), vrc);
1884
1885 return S_OK;
1886}
1887
1888/**
1889 * Creates a new differencing image for this hard disk with the given
1890 * VDI file name.
1891 *
1892 * @param aId UUID to assign to the created image
1893 * @param aTargetPath VDI file where to store the created differencing image
1894 * @param aProgress progress object to run during operation
1895 * (can be NULL)
1896 */
1897HRESULT
1898HVirtualDiskImage::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
1899 Progress *aProgress)
1900{
1901 AssertReturn (!aId.isEmpty(), E_FAIL);
1902 AssertReturn (!aTargetPath.isNull(), E_FAIL);
1903
1904 AutoLock alock (this);
1905 AssertReturn (isReady(), E_FAIL);
1906
1907 AssertReturn (isBusy() == false, E_FAIL);
1908 AssertReturn (mState >= Created, E_FAIL);
1909
1910 addReader();
1911 alock.leave();
1912
1913 int vrc = VDICreateDifferenceImage (aTargetPath, Utf8Str (mFilePathFull),
1914 NULL, aProgress ? progressCallback : NULL,
1915 static_cast <Progress *> (aProgress));
1916 alock.enter();
1917 releaseReader();
1918
1919 /* update the UUID to correspond to the file name */
1920 if (VBOX_SUCCESS (vrc))
1921 vrc = VDISetImageUUIDs (aTargetPath, aId, NULL, NULL, NULL);
1922
1923 if (VBOX_FAILURE (vrc))
1924 return setError (E_FAIL,
1925 tr ("Could not create a differencing hard disk '%s' (%Vrc)"),
1926 aTargetPath.raw(), vrc);
1927
1928 return S_OK;
1929}
1930
1931/**
1932 * Copies the image file of this hard disk to a separate VDI file (with an
1933 * unique creation UUID) and creates a new hard disk object for the copied
1934 * image. The copy will be created as a child of this hard disk's parent
1935 * (so that this hard disk must be a differencing one).
1936 *
1937 * The specified progress object (if not NULL) receives the percentage
1938 * of the operation completion. However, it is responsibility of the caller to
1939 * call Progress::notifyComplete() after this method returns.
1940 *
1941 * @param aFolder folder where to create a copy (must be a full path)
1942 * @param aMachineId machine ID the new hard disk will belong to
1943 * @param aHardDisk resulting hard disk object
1944 * @param aProgress progress object to run during copy operation
1945 * (may be NULL)
1946 *
1947 * @note
1948 * Must be NOT called from under locks of other objects that need external
1949 * access dirung this method execurion!
1950 */
1951HRESULT
1952HVirtualDiskImage::cloneDiffImage (const Bstr &aFolder, const Guid &aMachineId,
1953 ComObjPtr <HVirtualDiskImage> &aHardDisk,
1954 Progress *aProgress)
1955{
1956 AssertReturn (!aFolder.isEmpty() && !aMachineId.isEmpty(),
1957 E_FAIL);
1958
1959 AutoLock alock (this);
1960 CHECK_READY();
1961
1962 AssertReturn (!mParent.isNull(), E_FAIL);
1963
1964 ComAssertRet (isBusy() == false, E_FAIL);
1965 ComAssertRet (mState >= Created, E_FAIL);
1966
1967 Guid id;
1968 id.create();
1969
1970 Utf8Str filePathTo = Utf8StrFmt ("%ls%c{%Vuuid}.vdi",
1971 aFolder.raw(), RTPATH_DELIMITER, id.ptr());
1972
1973 /* try to make the path relative to the vbox home dir */
1974 const char *filePathToRel = filePathTo;
1975 {
1976 const Utf8Str &homeDir = mVirtualBox->homeDir();
1977 if (!strncmp (filePathTo, homeDir, homeDir.length()))
1978 filePathToRel = (filePathToRel + homeDir.length() + 1);
1979 }
1980
1981 /* first ensure the directory exists */
1982 {
1983 Utf8Str dir = aFolder;
1984 if (!RTDirExists (dir))
1985 {
1986 int vrc = RTDirCreateFullPath (dir, 0777);
1987 if (VBOX_FAILURE (vrc))
1988 {
1989 return setError (E_FAIL,
1990 tr ("Could not create a directory '%s' "
1991 "to store the image file (%Vrc)"),
1992 dir.raw(), vrc);
1993 }
1994 }
1995 }
1996
1997 Utf8Str filePathFull = mFilePathFull;
1998
1999 alock.leave();
2000
2001 int vrc = VDICopyImage (filePathTo, filePathFull, NULL,
2002 progressCallback,
2003 static_cast <Progress *> (aProgress));
2004
2005 alock.enter();
2006
2007 /* get modification and parent UUIDs of this image */
2008 RTUUID modUuid, parentUuid, parentModUuid;
2009 if (VBOX_SUCCESS (vrc))
2010 vrc = VDIGetImageUUIDs (filePathFull, NULL, &modUuid,
2011 &parentUuid, &parentModUuid);
2012
2013 // update the UUID of the copy to correspond to the file name
2014 // and copy all other UUIDs from this image
2015 if (VBOX_SUCCESS (vrc))
2016 vrc = VDISetImageUUIDs (filePathTo, id, &modUuid,
2017 &parentUuid, &parentModUuid);
2018
2019 if (VBOX_FAILURE (vrc))
2020 return setError (E_FAIL,
2021 tr ("Could not copy the hard disk image '%s' to '%s' (%Vrc)"),
2022 filePathFull.raw(), filePathTo.raw(), vrc);
2023
2024 ComObjPtr <HVirtualDiskImage> vdi;
2025 vdi.createObject();
2026 HRESULT rc = vdi->init (mVirtualBox, mParent, Bstr (filePathToRel),
2027 TRUE /* aRegistered */);
2028 if (FAILED (rc))
2029 return rc;
2030
2031 /* associate the created hard disk with the given machine */
2032 vdi->setMachineId (aMachineId);
2033
2034 rc = mVirtualBox->registerHardDisk (vdi, VirtualBox::RHD_Internal);
2035 if (FAILED (rc))
2036 return rc;
2037
2038 aHardDisk = vdi;
2039
2040 return S_OK;
2041}
2042
2043/**
2044 * Merges this child image to its parent image and updates the parent UUID
2045 * of all children of this image (to point to this image's parent).
2046 * It's a responsibility of the caller to unregister and uninitialize
2047 * the merged image on success.
2048 *
2049 * This method is intended to be called on a worker thread (the operation
2050 * can be time consuming).
2051 *
2052 * The specified progress object (if not NULL) receives the percentage
2053 * of the operation completion. However, it is responsibility of the caller to
2054 * call Progress::notifyComplete() after this method returns.
2055 *
2056 * @param aProgress progress object to run during copy operation
2057 * (may be NULL)
2058 *
2059 * @note
2060 * This method expects that both this hard disk and the paret hard disk
2061 * are marked as busy using #setBusyWithChildren() prior to calling it!
2062 * Busy flags of both hard disks will be cleared by this method
2063 * on a successful return. In case of failure, #clearBusyWithChildren()
2064 * must be called on a parent.
2065 *
2066 * @note
2067 * Must be NOT called from under locks of other objects that need external
2068 * access dirung this method execurion!
2069 */
2070HRESULT HVirtualDiskImage::mergeImageToParent (Progress *aProgress)
2071{
2072 LogFlowThisFunc (("mFilePathFull='%ls'\n", mFilePathFull.raw()));
2073
2074 AutoLock alock (this);
2075 CHECK_READY();
2076
2077 AssertReturn (!mParent.isNull(), E_FAIL);
2078 AutoLock parentLock (mParent);
2079
2080 ComAssertRet (isBusy() == true, E_FAIL);
2081 ComAssertRet (mParent->isBusy() == true, E_FAIL);
2082
2083 ComAssertRet (mState >= Created && mParent->asVDI()->mState >= Created, E_FAIL);
2084
2085 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2086 ("non VDI storage types are not yet supported!"), E_FAIL);
2087
2088 parentLock.leave();
2089 alock.leave();
2090
2091 int vrc = VDIMergeImage (Utf8Str (mFilePathFull),
2092 Utf8Str (mParent->asVDI()->mFilePathFull),
2093 progressCallback,
2094 static_cast <Progress *> (aProgress));
2095 alock.enter();
2096 parentLock.enter();
2097
2098 if (VBOX_FAILURE (vrc))
2099 return setError (E_FAIL,
2100 tr ("Could not merge the hard disk image '%ls' to "
2101 "its parent image '%ls' (%Vrc)"),
2102 mFilePathFull.raw(), mParent->asVDI()->mFilePathFull.raw(), vrc);
2103
2104 {
2105 HRESULT rc = S_OK;
2106
2107 AutoLock chLock (childrenLock());
2108
2109 for (HardDiskList::const_iterator it = children().begin();
2110 it != children().end(); ++ it)
2111 {
2112 ComObjPtr <HVirtualDiskImage> child = (*it)->asVDI();
2113 AutoLock childLock (child);
2114
2115 /* reparent the child */
2116 child->mParent = mParent;
2117 if (mParent)
2118 mParent->addDependentChild (child);
2119
2120 /* change the parent UUID in the image as well */
2121 RTUUID parentUuid, parentModUuid;
2122 vrc = VDIGetImageUUIDs (Utf8Str (mParent->asVDI()->mFilePathFull),
2123 &parentUuid, &parentModUuid, NULL, NULL);
2124 if (VBOX_FAILURE (vrc))
2125 {
2126 rc = setError (E_FAIL,
2127 tr ("Could not access the hard disk image '%ls' (%Vrc)"),
2128 mParent->asVDI()->mFilePathFull.raw(), vrc);
2129 break;
2130 }
2131 ComAssertBreak (mParent->id() == Guid (parentUuid), rc = E_FAIL);
2132 vrc = VDISetImageUUIDs (Utf8Str (child->mFilePathFull),
2133 NULL, NULL, &parentUuid, &parentModUuid);
2134 if (VBOX_FAILURE (vrc))
2135 {
2136 rc = setError (E_FAIL,
2137 tr ("Could not update parent UUID of the hard disk image "
2138 "'%ls' (%Vrc)"),
2139 child->mFilePathFull.raw(), vrc);
2140 break;
2141 }
2142 }
2143
2144 if (FAILED (rc))
2145 return rc;
2146 }
2147
2148 /* detach all our children to avoid their uninit in #uninit() */
2149 removeDependentChildren();
2150
2151 mParent->clearBusy();
2152 clearBusy();
2153
2154 return S_OK;
2155}
2156
2157/**
2158 * Merges this image to all its child images, updates the parent UUID
2159 * of all children of this image (to point to this image's parent).
2160 * It's a responsibility of the caller to unregister and uninitialize
2161 * the merged image on success.
2162 *
2163 * This method is intended to be called on a worker thread (the operation
2164 * can be time consuming).
2165 *
2166 * The specified progress object (if not NULL) receives the percentage
2167 * of the operation completion. However, it is responsibility of the caller to
2168 * call Progress::notifyComplete() after this method returns.
2169 *
2170 * @param aProgress progress object to run during copy operation
2171 * (may be NULL)
2172 *
2173 * @note
2174 * This method expects that both this hard disk and all children
2175 * are marked as busy using setBusyWithChildren() prior to calling it!
2176 * Busy flags of all affected hard disks will be cleared by this method
2177 * on a successful return. In case of failure, #clearBusyWithChildren()
2178 * must be called for this hard disk.
2179 *
2180 * @note
2181 * Must be NOT called from under locks of other objects that need external
2182 * access dirung this method execurion!
2183 */
2184HRESULT HVirtualDiskImage::mergeImageToChildren (Progress *aProgress)
2185{
2186 LogFlowThisFunc (("mFilePathFull='%ls'\n", mFilePathFull.raw()));
2187
2188 AutoLock alock (this);
2189 CHECK_READY();
2190
2191 /* this must be a diff image */
2192 AssertReturn (isDifferencing(), E_FAIL);
2193
2194 ComAssertRet (isBusy() == true, E_FAIL);
2195 ComAssertRet (mState >= Created, E_FAIL);
2196
2197 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2198 ("non VDI storage types are not yet supported!"), E_FAIL);
2199
2200 {
2201 HRESULT rc = S_OK;
2202
2203 AutoLock chLock (childrenLock());
2204
2205 /* iterate over a copy since we will modify the list */
2206 HardDiskList list = children();
2207
2208 for (HardDiskList::const_iterator it = list.begin();
2209 it != list.end(); ++ it)
2210 {
2211 ComObjPtr <HardDisk> hd = *it;
2212 ComObjPtr <HVirtualDiskImage> child = hd->asVDI();
2213 AutoLock childLock (child);
2214
2215 ComAssertRet (child->isBusy() == true, E_FAIL);
2216 ComAssertBreak (child->mState >= Created, rc = E_FAIL);
2217
2218 childLock.leave();
2219 alock.leave();
2220
2221 int vrc = VDIMergeImage (Utf8Str (mFilePathFull),
2222 Utf8Str (child->mFilePathFull),
2223 progressCallback,
2224 static_cast <Progress *> (aProgress));
2225 alock.enter();
2226 childLock.enter();
2227
2228 if (VBOX_FAILURE (vrc))
2229 {
2230 rc = setError (E_FAIL,
2231 tr ("Could not merge the hard disk image '%ls' to "
2232 "its parent image '%ls' (%Vrc)"),
2233 mFilePathFull.raw(), child->mFilePathFull.raw(), vrc);
2234 break;
2235 }
2236
2237 /* reparent the child */
2238 child->mParent = mParent;
2239 if (mParent)
2240 mParent->addDependentChild (child);
2241
2242 /* change the parent UUID in the image as well */
2243 RTUUID parentUuid, parentModUuid;
2244 vrc = VDIGetImageUUIDs (Utf8Str (mParent->asVDI()->mFilePathFull),
2245 &parentUuid, &parentModUuid, NULL, NULL);
2246 if (VBOX_FAILURE (vrc))
2247 {
2248 rc = setError (E_FAIL,
2249 tr ("Could not access the hard disk image '%ls' (%Vrc)"),
2250 mParent->asVDI()->mFilePathFull.raw(), vrc);
2251 break;
2252 }
2253 ComAssertBreak (mParent->id() == Guid (parentUuid), rc = E_FAIL);
2254 vrc = VDISetImageUUIDs (Utf8Str (child->mFilePathFull),
2255 NULL, NULL, &parentUuid, &parentModUuid);
2256 if (VBOX_FAILURE (vrc))
2257 {
2258 rc = setError (E_FAIL,
2259 tr ("Could not update parent UUID of the hard disk image "
2260 "'%ls' (%Vrc)"),
2261 child->mFilePathFull.raw(), vrc);
2262 break;
2263 }
2264
2265 /* detach child to avoid its uninit in #uninit() */
2266 removeDependentChild (child);
2267
2268 /* remove the busy flag */
2269 child->clearBusy();
2270 }
2271
2272 if (FAILED (rc))
2273 return rc;
2274 }
2275
2276 clearBusy();
2277
2278 return S_OK;
2279}
2280
2281/**
2282 * Deletes and recreates the differencing hard disk image from scratch.
2283 * The file name and UUID remain the same.
2284 */
2285HRESULT HVirtualDiskImage::wipeOutImage()
2286{
2287 AutoLock alock (this);
2288 CHECK_READY();
2289
2290 AssertReturn (isDifferencing(), E_FAIL);
2291 AssertReturn (mRegistered, E_FAIL);
2292 AssertReturn (mState >= Created, E_FAIL);
2293
2294 ComAssertMsgRet (mParent->storageType() == HardDiskStorageType_VirtualDiskImage,
2295 ("non-VDI storage types are not yet supported!"), E_FAIL);
2296
2297 Utf8Str filePathFull = mFilePathFull;
2298
2299 int vrc = RTFileDelete (filePathFull);
2300 if (VBOX_FAILURE (vrc))
2301 return setError (E_FAIL,
2302 tr ("Could not delete the image file '%s' (%Vrc)"),
2303 filePathFull.raw(), vrc);
2304
2305 vrc = VDICreateDifferenceImage (filePathFull,
2306 Utf8Str (mParent->asVDI()->mFilePathFull),
2307 NULL, NULL, NULL);
2308 /* update the UUID to correspond to the file name */
2309 if (VBOX_SUCCESS (vrc))
2310 vrc = VDISetImageUUIDs (filePathFull, mId, NULL, NULL, NULL);
2311
2312 if (VBOX_FAILURE (vrc))
2313 return setError (E_FAIL,
2314 tr ("Could not create a differencing hard disk '%s' (%Vrc)"),
2315 filePathFull.raw(), vrc);
2316
2317 return S_OK;
2318}
2319
2320HRESULT HVirtualDiskImage::deleteImage (bool aIgnoreErrors /* = false */)
2321{
2322 AutoLock alock (this);
2323 CHECK_READY();
2324
2325 AssertReturn (!mRegistered, E_FAIL);
2326 AssertReturn (mState >= Created, E_FAIL);
2327
2328 int vrc = RTFileDelete (Utf8Str (mFilePathFull));
2329 if (VBOX_FAILURE (vrc) && !aIgnoreErrors)
2330 return setError (E_FAIL,
2331 tr ("Could not delete the image file '%ls' (%Vrc)"),
2332 mFilePathFull.raw(), vrc);
2333
2334 return S_OK;
2335}
2336
2337// private methods
2338/////////////////////////////////////////////////////////////////////////////
2339
2340/**
2341 * Helper to set a new file path.
2342 * Resolves a path relatively to the Virtual Box home directory.
2343 *
2344 * @note
2345 * Must be called from under the object's lock!
2346 */
2347HRESULT HVirtualDiskImage::setFilePath (const BSTR aFilePath)
2348{
2349 if (aFilePath && *aFilePath)
2350 {
2351 /* get the full file name */
2352 char filePathFull [RTPATH_MAX];
2353 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aFilePath),
2354 filePathFull, sizeof (filePathFull));
2355 if (VBOX_FAILURE (vrc))
2356 return setError (E_FAIL,
2357 tr ("Invalid image file path '%ls' (%Vrc)"), aFilePath, vrc);
2358
2359 mFilePath = aFilePath;
2360 mFilePathFull = filePathFull;
2361 }
2362 else
2363 {
2364 mFilePath.setNull();
2365 mFilePathFull.setNull();
2366 }
2367
2368 return S_OK;
2369}
2370
2371/**
2372 * Helper to query information about the VDI hard disk.
2373 *
2374 * @param aAccessError not used when NULL, otherwise see #getAccessible()
2375 *
2376 * @note Must be called from under the object's lock, only after
2377 * CHECK_BUSY_AND_READERS() succeeds.
2378 */
2379HRESULT HVirtualDiskImage::queryInformation (Bstr *aAccessError)
2380{
2381 AssertReturn (isLockedOnCurrentThread(), E_FAIL);
2382
2383 /* create a lock object to completely release it later */
2384 AutoLock alock (this);
2385
2386 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
2387
2388 ComAssertRet (mState >= Created, E_FAIL);
2389
2390 HRESULT rc = S_OK;
2391 int vrc = VINF_SUCCESS;
2392
2393 /* lazily create a semaphore */
2394 vrc = RTSemEventMultiCreate (&mStateCheckSem);
2395 ComAssertRCRet (vrc, E_FAIL);
2396
2397 /* Reference the disk to prevent any concurrent modifications
2398 * after releasing the lock below (to unblock getters before
2399 * a lengthy operation). */
2400 addReader();
2401
2402 alock.leave();
2403
2404 /* VBoxVHDD management interface needs to be optimized: we're opening a
2405 * file three times in a raw to get three bits of information. */
2406
2407 Utf8Str filePath = mFilePathFull;
2408 Bstr errMsg;
2409
2410 do
2411 {
2412 /* check the image file */
2413 Guid id, parentId;
2414 vrc = VDICheckImage (filePath, NULL, NULL, NULL,
2415 id.ptr(), parentId.ptr(), NULL, 0);
2416
2417 if (VBOX_FAILURE (vrc))
2418 break;
2419
2420 if (!mId.isEmpty())
2421 {
2422 /* check that the actual UUID of the image matches the stored UUID */
2423 if (mId != id)
2424 {
2425 errMsg = Utf8StrFmt (
2426 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
2427 "match UUID {%Vuuid} stored in the registry"),
2428 id.ptr(), filePath.raw(), mId.ptr());
2429 break;
2430 }
2431 }
2432 else
2433 {
2434 /* assgn an UUID read from the image file */
2435 mId = id;
2436 }
2437
2438 if (mParent)
2439 {
2440 /* check parent UUID */
2441 AutoLock parentLock (mParent);
2442 if (mParent->id() != parentId)
2443 {
2444 errMsg = Utf8StrFmt (
2445 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
2446 "the hard disk image file '%s' doesn't match "
2447 "UUID {%Vuuid} stored in the registry"),
2448 parentId.raw(), mParent->toString().raw(),
2449 filePath.raw(), mParent->id().raw());
2450 break;
2451 }
2452 }
2453 else if (!parentId.isEmpty())
2454 {
2455 errMsg = Utf8StrFmt (
2456 tr ("Hard disk image '%s' is a differencing image that is linked "
2457 "to a hard disk with UUID {%Vuuid} and cannot be used "
2458 "directly as a base hard disk"),
2459 filePath.raw(), parentId.raw());
2460 break;
2461 }
2462
2463 {
2464 RTFILE file = NIL_RTFILE;
2465 vrc = RTFileOpen (&file, filePath, RTFILE_O_READ);
2466 if (VBOX_SUCCESS (vrc))
2467 {
2468 uint64_t size = 0;
2469 vrc = RTFileGetSize (file, &size);
2470 if (VBOX_SUCCESS (vrc))
2471 mActualSize = size;
2472 RTFileClose (file);
2473 }
2474 if (VBOX_FAILURE (vrc))
2475 break;
2476 }
2477
2478 if (!mParent)
2479 {
2480 /* query logical size only for non-differencing images */
2481
2482 PVDIDISK disk = VDIDiskCreate();
2483 vrc = VDIDiskOpenImage (disk, Utf8Str (mFilePathFull),
2484 VDI_OPEN_FLAGS_READONLY);
2485 if (VBOX_SUCCESS (vrc))
2486 {
2487 uint64_t size = VDIDiskGetSize (disk);
2488 /* convert to MBytes */
2489 mSize = size / 1024 / 1024;
2490 }
2491
2492 VDIDiskDestroy (disk);
2493 if (VBOX_FAILURE (vrc))
2494 break;
2495 }
2496 }
2497 while (0);
2498
2499 /* enter the lock again */
2500 alock.enter();
2501
2502 /* remove the reference */
2503 releaseReader();
2504
2505 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
2506 {
2507 LogWarningFunc (("'%ls' is not accessible "
2508 "(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
2509 mFilePathFull.raw(), rc, vrc, errMsg.raw()));
2510
2511 if (aAccessError)
2512 {
2513 if (!errMsg.isNull())
2514 *aAccessError = errMsg;
2515 else if (VBOX_FAILURE (vrc))
2516 *aAccessError = Utf8StrFmt (
2517 tr ("Could not access hard disk image '%ls' (%Vrc)"),
2518 mFilePathFull.raw(), vrc);
2519 }
2520
2521 /* downgrade to not accessible */
2522 mState = Created;
2523 }
2524 else
2525 {
2526 if (aAccessError)
2527 aAccessError->setNull();
2528
2529 mState = Accessible;
2530 }
2531
2532 /* inform waiters if there are any */
2533 if (mStateCheckWaiters > 0)
2534 {
2535 RTSemEventMultiSignal (mStateCheckSem);
2536 }
2537 else
2538 {
2539 /* delete the semaphore ourselves */
2540 RTSemEventMultiDestroy (mStateCheckSem);
2541 mStateCheckSem = NIL_RTSEMEVENTMULTI;
2542 }
2543
2544 return rc;
2545}
2546
2547/**
2548 * Helper to create hard disk images.
2549 *
2550 * @param aSize size in MB
2551 * @param aDynamic dynamic or fixed image
2552 * @param aProgress address of IProgress pointer to return
2553 */
2554HRESULT HVirtualDiskImage::createImage (ULONG64 aSize, BOOL aDynamic,
2555 IProgress **aProgress)
2556{
2557 AutoLock alock (this);
2558
2559 CHECK_BUSY_AND_READERS();
2560
2561 if (mState != NotCreated)
2562 return setError (E_ACCESSDENIED,
2563 tr ("Hard disk image '%ls' is already created"),
2564 mFilePathFull.raw());
2565
2566 if (!mFilePathFull)
2567 return setError (E_ACCESSDENIED,
2568 tr ("Cannot create a hard disk image using an empty (null) file path"),
2569 mFilePathFull.raw());
2570
2571 /* first ensure the directory exists */
2572 {
2573 Utf8Str imageDir = mFilePathFull;
2574 RTPathStripFilename (imageDir.mutableRaw());
2575 if (!RTDirExists (imageDir))
2576 {
2577 int vrc = RTDirCreateFullPath (imageDir, 0777);
2578 if (VBOX_FAILURE (vrc))
2579 {
2580 return setError (E_FAIL,
2581 tr ("Could not create a directory '%s' "
2582 "to store the image file (%Vrc)"),
2583 imageDir.raw(), vrc);
2584 }
2585 }
2586 }
2587
2588 /* check whether the given file exists or not */
2589 RTFILE file;
2590 int vrc = RTFileOpen (&file, Utf8Str (mFilePathFull),
2591 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
2592 if (vrc != VERR_FILE_NOT_FOUND)
2593 {
2594 if (VBOX_SUCCESS (vrc))
2595 RTFileClose (file);
2596 switch(vrc)
2597 {
2598 case VINF_SUCCESS:
2599 return setError (E_FAIL,
2600 tr ("Image file '%ls' already exists"),
2601 mFilePathFull.raw());
2602
2603 default:
2604 return setError(E_FAIL,
2605 tr ("Invalid image file path '%ls' (%Vrc)"),
2606 mFilePathFull.raw(), vrc);
2607 }
2608 }
2609
2610 /* check VDI size limits */
2611 {
2612 HRESULT rc;
2613 ULONG64 maxVDISize;
2614 ComPtr <ISystemProperties> props;
2615 rc = mVirtualBox->COMGETTER(SystemProperties) (props.asOutParam());
2616 ComAssertComRCRet (rc, E_FAIL);
2617 rc = props->COMGETTER(MaxVDISize) (&maxVDISize);
2618 ComAssertComRCRet (rc, E_FAIL);
2619
2620 if (aSize < 1 || aSize > maxVDISize)
2621 return setError (E_INVALIDARG,
2622 tr ("Invalid VDI size: %llu MB (must be in range [1, %llu] MB)"),
2623 aSize, maxVDISize);
2624 }
2625
2626 HRESULT rc;
2627
2628 /* create a project object */
2629 ComObjPtr <Progress> progress;
2630 progress.createObject();
2631 {
2632 Bstr desc = aDynamic ? tr ("Creating a dynamically expanding hard disk")
2633 : tr ("Creating a fixed-size hard disk");
2634 rc = progress->init (mVirtualBox, (IVirtualDiskImage *) this, desc,
2635 FALSE /* aCancelable */);
2636 CheckComRCReturnRC (rc);
2637 }
2638
2639 /* mark as busy (being created)
2640 * (VDI task thread will unmark it) */
2641 setBusy();
2642
2643 /* fill in VDI task data */
2644 VDITask *task = new VDITask (aDynamic ? VDITask::CreateDynamic
2645 : VDITask::CreateStatic,
2646 this, progress);
2647 task->size = aSize;
2648 task->size *= 1024 * 1024; /* convert to bytes */
2649
2650 /* create the hard disk creation thread, pass operation data */
2651 vrc = RTThreadCreate (NULL, VDITaskThread, (void *) task, 0,
2652 RTTHREADTYPE_MAIN_HEAVY_WORKER, 0, "VDITask");
2653 ComAssertMsgRC (vrc, ("Could not create a thread (%Vrc)", vrc));
2654 if (VBOX_FAILURE (vrc))
2655 {
2656 clearBusy();
2657 delete task;
2658 rc = E_FAIL;
2659 }
2660 else
2661 {
2662 /* get one interface for the caller */
2663 progress.queryInterfaceTo (aProgress);
2664 }
2665
2666 return rc;
2667}
2668
2669/* static */
2670DECLCALLBACK(int) HVirtualDiskImage::VDITaskThread (RTTHREAD thread, void *pvUser)
2671{
2672 VDITask *task = static_cast <VDITask *> (pvUser);
2673 AssertReturn (task, VERR_GENERAL_FAILURE);
2674
2675 LogFlowFunc (("operation=%d, size=%llu\n", task->operation, task->size));
2676
2677 VDIIMAGETYPE type = (VDIIMAGETYPE) 0;
2678
2679 switch (task->operation)
2680 {
2681 case VDITask::CreateDynamic: type = VDI_IMAGE_TYPE_NORMAL; break;
2682 case VDITask::CreateStatic: type = VDI_IMAGE_TYPE_FIXED; break;
2683 case VDITask::CloneToImage: break;
2684 default: AssertFailedReturn (VERR_GENERAL_FAILURE); break;
2685 }
2686
2687 HRESULT rc = S_OK;
2688 Utf8Str errorMsg;
2689
2690 bool deleteTarget = true;
2691
2692 if (task->operation == VDITask::CloneToImage)
2693 {
2694 Assert (!task->vdi->id().isEmpty());
2695 /// @todo (dmik) check locks
2696 AutoLock sourceLock (task->source);
2697 rc = task->source->cloneToImage (task->vdi->id(),
2698 Utf8Str (task->vdi->filePathFull()),
2699 task->progress, deleteTarget);
2700
2701 /* release reader added in HardDisk::CloneToImage() */
2702 task->source->releaseReader();
2703 }
2704 else
2705 {
2706 int vrc = VDICreateBaseImage (Utf8Str (task->vdi->filePathFull()),
2707 type, task->size,
2708 Utf8Str (task->vdi->mDescription),
2709 progressCallback,
2710 static_cast <Progress *> (task->progress));
2711
2712 /* We don't want to delete existing user files */
2713 if (vrc == VERR_ALREADY_EXISTS)
2714 deleteTarget = false;
2715
2716 if (VBOX_SUCCESS (vrc) && task->vdi->id())
2717 {
2718 /* we have a non-null UUID, update the created image */
2719 vrc = VDISetImageUUIDs (Utf8Str (task->vdi->filePathFull()),
2720 task->vdi->id().raw(), NULL, NULL, NULL);
2721 }
2722
2723 if (VBOX_FAILURE (vrc))
2724 {
2725 errorMsg = Utf8StrFmt (
2726 tr ("Falied to create a hard disk image '%ls' (%Vrc)"),
2727 task->vdi->filePathFull().raw(), vrc);
2728 rc = E_FAIL;
2729 }
2730 }
2731
2732 LogFlowFunc (("rc=%08X\n", rc));
2733
2734 AutoLock alock (task->vdi);
2735
2736 /* clear busy set in in HardDisk::CloneToImage() or
2737 * in HVirtualDiskImage::createImage() */
2738 task->vdi->clearBusy();
2739
2740 if (SUCCEEDED (rc))
2741 {
2742 task->vdi->mState = HVirtualDiskImage::Created;
2743 /* update VDI data fields */
2744 Bstr errMsg;
2745 rc = task->vdi->queryInformation (&errMsg);
2746 /* we want to deliver the access check result to the caller
2747 * immediately, before he calls HardDisk::GetAccssible() himself. */
2748 if (SUCCEEDED (rc) && !errMsg.isNull())
2749 task->progress->notifyCompleteBstr (
2750 E_FAIL, COM_IIDOF (IVirtualDiskImage), getComponentName(),
2751 errMsg);
2752 else
2753 task->progress->notifyComplete (rc);
2754 }
2755 else
2756 {
2757 /* delete the target file so we don't have orphaned files */
2758 if (deleteTarget)
2759 RTFileDelete(Utf8Str (task->vdi->filePathFull()));
2760
2761 task->vdi->mState = HVirtualDiskImage::NotCreated;
2762 /* complete the progress object */
2763 if (errorMsg)
2764 task->progress->notifyComplete (
2765 E_FAIL, COM_IIDOF (IVirtualDiskImage), getComponentName(),
2766 errorMsg);
2767 else
2768 task->progress->notifyComplete (rc);
2769 }
2770
2771 delete task;
2772
2773 return VINF_SUCCESS;
2774}
2775
2776////////////////////////////////////////////////////////////////////////////////
2777// HISCSIHardDisk class
2778////////////////////////////////////////////////////////////////////////////////
2779
2780// constructor / destructor
2781////////////////////////////////////////////////////////////////////////////////
2782
2783HRESULT HISCSIHardDisk::FinalConstruct()
2784{
2785 HRESULT rc = HardDisk::FinalConstruct();
2786 if (FAILED (rc))
2787 return rc;
2788
2789 mSize = 0;
2790 mActualSize = 0;
2791
2792 mPort = 0;
2793 mLun = 0;
2794
2795 return S_OK;
2796}
2797
2798void HISCSIHardDisk::FinalRelease()
2799{
2800 HardDisk::FinalRelease();
2801}
2802
2803// public initializer/uninitializer for internal purposes only
2804////////////////////////////////////////////////////////////////////////////////
2805
2806// public methods for internal purposes only
2807/////////////////////////////////////////////////////////////////////////////
2808
2809/**
2810 * Initializes the iSCSI hard disk object by reading its properties from
2811 * the given configuration node. The created hard disk will be marked as
2812 * registered on success.
2813 *
2814 * @param aHDNode <HardDisk> node.
2815 * @param aVDINod <ISCSIHardDisk> node.
2816 */
2817HRESULT HISCSIHardDisk::init (VirtualBox *aVirtualBox,
2818 const settings::Key &aHDNode,
2819 const settings::Key &aISCSINode)
2820{
2821 using namespace settings;
2822
2823 LogFlowThisFunc (("\n"));
2824
2825 AssertReturn (!aHDNode.isNull() && !aISCSINode.isNull(), E_FAIL);
2826
2827 AutoLock alock (this);
2828 ComAssertRet (!isReady(), E_UNEXPECTED);
2829
2830 mStorageType = HardDiskStorageType_ISCSIHardDisk;
2831
2832 HRESULT rc = S_OK;
2833
2834 do
2835 {
2836 rc = protectedInit (aVirtualBox, NULL);
2837 CheckComRCBreakRC (rc);
2838
2839 /* set ready to let protectedUninit() be called on failure */
2840 setReady (true);
2841
2842 /* server (required) */
2843 mServer = aISCSINode.stringValue ("server");
2844 /* target (required) */
2845 mTarget = aISCSINode.stringValue ("target");
2846
2847 /* port (optional) */
2848 mPort = aISCSINode.value <USHORT> ("port");
2849 /* lun (optional) */
2850 mLun = aISCSINode.value <ULONG64> ("lun");
2851 /* userName (optional) */
2852 mUserName = aISCSINode.stringValue ("userName");
2853 /* password (optional) */
2854 mPassword = aISCSINode.stringValue ("password");
2855
2856 LogFlowThisFunc (("'iscsi:%ls:%hu@%ls/%ls:%llu'\n",
2857 mServer.raw(), mPort, mUserName.raw(), mTarget.raw(),
2858 mLun));
2859
2860 /* load basic settings and children */
2861 rc = loadSettings (aHDNode);
2862 CheckComRCBreakRC (rc);
2863
2864 if (mType != HardDiskType_WritethroughHardDisk)
2865 {
2866 rc = setError (E_FAIL,
2867 tr ("Currently, non-Writethrough iSCSI hard disks are not "
2868 "allowed ('%ls')"),
2869 toString().raw());
2870 break;
2871 }
2872
2873 mRegistered = TRUE;
2874 }
2875 while (0);
2876
2877 if (FAILED (rc))
2878 uninit();
2879
2880 return rc;
2881}
2882
2883/**
2884 * Initializes the iSCSI hard disk object using default values for all
2885 * properties. The created hard disk will NOT be marked as registered.
2886 */
2887HRESULT HISCSIHardDisk::init (VirtualBox *aVirtualBox)
2888{
2889 LogFlowThisFunc (("\n"));
2890
2891 AutoLock alock (this);
2892 ComAssertRet (!isReady(), E_UNEXPECTED);
2893
2894 mStorageType = HardDiskStorageType_ISCSIHardDisk;
2895
2896 HRESULT rc = S_OK;
2897
2898 do
2899 {
2900 rc = protectedInit (aVirtualBox, NULL);
2901 CheckComRCBreakRC (rc);
2902
2903 /* set ready to let protectedUninit() be called on failure */
2904 setReady (true);
2905
2906 /* we have to generate a new UUID */
2907 mId.create();
2908 /* currently, all iSCSI hard disks are writethrough */
2909 mType = HardDiskType_WritethroughHardDisk;
2910 mRegistered = FALSE;
2911 }
2912 while (0);
2913
2914 if (FAILED (rc))
2915 uninit();
2916
2917 return rc;
2918}
2919
2920/**
2921 * Uninitializes the instance and sets the ready flag to FALSE.
2922 * Called either from FinalRelease(), by the parent when it gets destroyed,
2923 * or by a third party when it decides this object is no more valid.
2924 */
2925void HISCSIHardDisk::uninit()
2926{
2927 LogFlowThisFunc (("\n"));
2928
2929 AutoLock alock (this);
2930 if (!isReady())
2931 return;
2932
2933 HardDisk::protectedUninit (alock);
2934}
2935
2936// IHardDisk properties
2937////////////////////////////////////////////////////////////////////////////////
2938
2939STDMETHODIMP HISCSIHardDisk::COMGETTER(Description) (BSTR *aDescription)
2940{
2941 if (!aDescription)
2942 return E_POINTER;
2943
2944 AutoLock alock (this);
2945 CHECK_READY();
2946
2947 mDescription.cloneTo (aDescription);
2948 return S_OK;
2949}
2950
2951STDMETHODIMP HISCSIHardDisk::COMSETTER(Description) (INPTR BSTR aDescription)
2952{
2953 AutoLock alock (this);
2954 CHECK_READY();
2955
2956 CHECK_BUSY_AND_READERS();
2957
2958 if (mDescription != aDescription)
2959 {
2960 mDescription = aDescription;
2961 if (mRegistered)
2962 return mVirtualBox->saveSettings();
2963 }
2964
2965 return S_OK;
2966}
2967
2968STDMETHODIMP HISCSIHardDisk::COMGETTER(Size) (ULONG64 *aSize)
2969{
2970 if (!aSize)
2971 return E_POINTER;
2972
2973 AutoLock alock (this);
2974 CHECK_READY();
2975
2976 *aSize = mSize;
2977 return S_OK;
2978}
2979
2980STDMETHODIMP HISCSIHardDisk::COMGETTER(ActualSize) (ULONG64 *aActualSize)
2981{
2982 if (!aActualSize)
2983 return E_POINTER;
2984
2985 AutoLock alock (this);
2986 CHECK_READY();
2987
2988 *aActualSize = mActualSize;
2989 return S_OK;
2990}
2991
2992// IISCSIHardDisk properties
2993////////////////////////////////////////////////////////////////////////////////
2994
2995STDMETHODIMP HISCSIHardDisk::COMGETTER(Server) (BSTR *aServer)
2996{
2997 if (!aServer)
2998 return E_POINTER;
2999
3000 AutoLock alock (this);
3001 CHECK_READY();
3002
3003 mServer.cloneTo (aServer);
3004 return S_OK;
3005}
3006
3007STDMETHODIMP HISCSIHardDisk::COMSETTER(Server) (INPTR BSTR aServer)
3008{
3009 if (!aServer || !*aServer)
3010 return E_INVALIDARG;
3011
3012 AutoLock alock (this);
3013 CHECK_READY();
3014
3015 CHECK_BUSY_AND_READERS();
3016
3017 if (mServer != aServer)
3018 {
3019 mServer = aServer;
3020 if (mRegistered)
3021 return mVirtualBox->saveSettings();
3022 }
3023
3024 return S_OK;
3025}
3026
3027STDMETHODIMP HISCSIHardDisk::COMGETTER(Port) (USHORT *aPort)
3028{
3029 if (!aPort)
3030 return E_POINTER;
3031
3032 AutoLock alock (this);
3033 CHECK_READY();
3034
3035 *aPort = mPort;
3036 return S_OK;
3037}
3038
3039STDMETHODIMP HISCSIHardDisk::COMSETTER(Port) (USHORT aPort)
3040{
3041 AutoLock alock (this);
3042 CHECK_READY();
3043
3044 CHECK_BUSY_AND_READERS();
3045
3046 if (mPort != aPort)
3047 {
3048 mPort = aPort;
3049 if (mRegistered)
3050 return mVirtualBox->saveSettings();
3051 }
3052
3053 return S_OK;
3054}
3055
3056STDMETHODIMP HISCSIHardDisk::COMGETTER(Target) (BSTR *aTarget)
3057{
3058 if (!aTarget)
3059 return E_POINTER;
3060
3061 AutoLock alock (this);
3062 CHECK_READY();
3063
3064 mTarget.cloneTo (aTarget);
3065 return S_OK;
3066}
3067
3068STDMETHODIMP HISCSIHardDisk::COMSETTER(Target) (INPTR BSTR aTarget)
3069{
3070 if (!aTarget || !*aTarget)
3071 return E_INVALIDARG;
3072
3073 AutoLock alock (this);
3074 CHECK_READY();
3075
3076 CHECK_BUSY_AND_READERS();
3077
3078 if (mTarget != aTarget)
3079 {
3080 mTarget = aTarget;
3081 if (mRegistered)
3082 return mVirtualBox->saveSettings();
3083 }
3084
3085 return S_OK;
3086}
3087
3088STDMETHODIMP HISCSIHardDisk::COMGETTER(Lun) (ULONG64 *aLun)
3089{
3090 if (!aLun)
3091 return E_POINTER;
3092
3093 AutoLock alock (this);
3094 CHECK_READY();
3095
3096 *aLun = mLun;
3097 return S_OK;
3098}
3099
3100STDMETHODIMP HISCSIHardDisk::COMSETTER(Lun) (ULONG64 aLun)
3101{
3102 AutoLock alock (this);
3103 CHECK_READY();
3104
3105 CHECK_BUSY_AND_READERS();
3106
3107 if (mLun != aLun)
3108 {
3109 mLun = aLun;
3110 if (mRegistered)
3111 return mVirtualBox->saveSettings();
3112 }
3113
3114 return S_OK;
3115}
3116
3117STDMETHODIMP HISCSIHardDisk::COMGETTER(UserName) (BSTR *aUserName)
3118{
3119 if (!aUserName)
3120 return E_POINTER;
3121
3122 AutoLock alock (this);
3123 CHECK_READY();
3124
3125 mUserName.cloneTo (aUserName);
3126 return S_OK;
3127}
3128
3129STDMETHODIMP HISCSIHardDisk::COMSETTER(UserName) (INPTR BSTR aUserName)
3130{
3131 AutoLock alock (this);
3132 CHECK_READY();
3133
3134 CHECK_BUSY_AND_READERS();
3135
3136 if (mUserName != aUserName)
3137 {
3138 mUserName = aUserName;
3139 if (mRegistered)
3140 return mVirtualBox->saveSettings();
3141 }
3142
3143 return S_OK;
3144}
3145
3146STDMETHODIMP HISCSIHardDisk::COMGETTER(Password) (BSTR *aPassword)
3147{
3148 if (!aPassword)
3149 return E_POINTER;
3150
3151 AutoLock alock (this);
3152 CHECK_READY();
3153
3154 mPassword.cloneTo (aPassword);
3155 return S_OK;
3156}
3157
3158STDMETHODIMP HISCSIHardDisk::COMSETTER(Password) (INPTR BSTR aPassword)
3159{
3160 AutoLock alock (this);
3161 CHECK_READY();
3162
3163 CHECK_BUSY_AND_READERS();
3164
3165 if (mPassword != aPassword)
3166 {
3167 mPassword = aPassword;
3168 if (mRegistered)
3169 return mVirtualBox->saveSettings();
3170 }
3171
3172 return S_OK;
3173}
3174
3175// public/protected methods for internal purposes only
3176/////////////////////////////////////////////////////////////////////////////
3177
3178/**
3179 * Attempts to mark the hard disk as registered.
3180 * Only VirtualBox can call this method.
3181 */
3182HRESULT HISCSIHardDisk::trySetRegistered (BOOL aRegistered)
3183{
3184 AutoLock alock (this);
3185 CHECK_READY();
3186
3187 if (aRegistered)
3188 {
3189 if (mServer.isEmpty() || mTarget.isEmpty())
3190 return setError (E_FAIL,
3191 tr ("iSCSI Hard disk has no server or target defined"));
3192 }
3193 else
3194 {
3195 }
3196
3197 return HardDisk::trySetRegistered (aRegistered);
3198}
3199
3200/**
3201 * Checks accessibility of this iSCSI hard disk.
3202 */
3203HRESULT HISCSIHardDisk::getAccessible (Bstr &aAccessError)
3204{
3205 AutoLock alock (this);
3206 CHECK_READY();
3207
3208 /* check the basic accessibility */
3209 HRESULT rc = getBaseAccessible (aAccessError);
3210 if (FAILED (rc) || !aAccessError.isNull())
3211 return rc;
3212
3213 return queryInformation (aAccessError);
3214}
3215
3216/**
3217 * Saves hard disk settings to the specified storage node and saves
3218 * all children to the specified hard disk node
3219 *
3220 * @param aHDNode <HardDisk>.
3221 * @param aStorageNode <ISCSIHardDisk> node.
3222 */
3223HRESULT HISCSIHardDisk::saveSettings (settings::Key &aHDNode,
3224 settings::Key &aStorageNode)
3225{
3226 AssertReturn (!aHDNode.isNull() && !aStorageNode.isNull(), E_FAIL);
3227
3228 AutoLock alock (this);
3229 CHECK_READY();
3230
3231 /* server (required) */
3232 aStorageNode.setValue <Bstr> ("server", mServer);
3233 /* target (required) */
3234 aStorageNode.setValue <Bstr> ("target", mTarget);
3235
3236 /* port (optional, defaults to 0) */
3237 aStorageNode.setValueOr <USHORT> ("port", mPort, 0);
3238 /* lun (optional, force 0x format to coform to XML Schema!) */
3239 aStorageNode.setValueOr <ULONG64> ("lun", mLun, 0, 16);
3240 /* userName (optional) */
3241 aStorageNode.setValueOr <Bstr> ("userName", mUserName, Bstr::Null);
3242 /* password (optional) */
3243 aStorageNode.setValueOr <Bstr> ("password", mPassword, Bstr::Null);
3244
3245 /* save basic settings and children */
3246 return HardDisk::saveSettings (aHDNode);
3247}
3248
3249/**
3250 * Returns the string representation of this hard disk.
3251 * When \a aShort is false, returns the full image file path.
3252 * Otherwise, returns the image file name only.
3253 *
3254 * @param aShort if true, a short representation is returned
3255 */
3256Bstr HISCSIHardDisk::toString (bool aShort /* = false */)
3257{
3258 AutoLock alock (this);
3259
3260 Bstr str;
3261 if (!aShort)
3262 {
3263 /* the format is iscsi:[<user@>]<server>[:<port>]/<target>[:<lun>] */
3264 str = Utf8StrFmt ("iscsi:%s%ls%s/%ls%s",
3265 !mUserName.isNull() ? Utf8StrFmt ("%ls@", mUserName.raw()).raw() : "",
3266 mServer.raw(),
3267 mPort != 0 ? Utf8StrFmt (":%hu", mPort).raw() : "",
3268 mTarget.raw(),
3269 mLun != 0 ? Utf8StrFmt (":%llu", mLun).raw() : "");
3270 }
3271 else
3272 {
3273 str = Utf8StrFmt ("%ls%s",
3274 mTarget.raw(),
3275 mLun != 0 ? Utf8StrFmt (":%llu", mLun).raw() : "");
3276 }
3277
3278 return str;
3279}
3280
3281/**
3282 * Creates a clone of this hard disk by storing hard disk data in the given
3283 * VDI file.
3284 *
3285 * If the operation fails, @a aDeleteTarget will be set to @c true unless the
3286 * failure happened because the target file already existed.
3287 *
3288 * @param aId UUID to assign to the created image.
3289 * @param aTargetPath VDI file where the cloned image is to be to stored.
3290 * @param aProgress progress object to run during operation.
3291 * @param aDeleteTarget Whether it is recommended to delete target on
3292 * failure or not.
3293 */
3294HRESULT
3295HISCSIHardDisk::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
3296 Progress *aProgress, bool &aDeleteTarget)
3297{
3298 ComAssertMsgFailed (("Not implemented"));
3299 return E_NOTIMPL;
3300
3301// AssertReturn (isBusy() == false, E_FAIL);
3302// addReader();
3303// releaseReader();
3304}
3305
3306/**
3307 * Creates a new differencing image for this hard disk with the given
3308 * VDI file name.
3309 *
3310 * @param aId UUID to assign to the created image
3311 * @param aTargetPath VDI file where to store the created differencing image
3312 * @param aProgress progress object to run during operation
3313 * (can be NULL)
3314 */
3315HRESULT
3316HISCSIHardDisk::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
3317 Progress *aProgress)
3318{
3319 ComAssertMsgFailed (("Not implemented"));
3320 return E_NOTIMPL;
3321
3322// AssertReturn (isBusy() == false, E_FAIL);
3323// addReader();
3324// releaseReader();
3325}
3326
3327// private methods
3328/////////////////////////////////////////////////////////////////////////////
3329
3330/**
3331 * Helper to query information about the iSCSI hard disk.
3332 *
3333 * @param aAccessError see #getAccessible()
3334 * @note
3335 * Must be called from under the object's lock!
3336 */
3337HRESULT HISCSIHardDisk::queryInformation (Bstr &aAccessError)
3338{
3339 /// @todo (dmik) query info about this iSCSI disk,
3340 // set mSize and mActualSize,
3341 // or set aAccessError in case of failure
3342
3343 aAccessError.setNull();
3344 return S_OK;
3345}
3346
3347////////////////////////////////////////////////////////////////////////////////
3348// HVMDKImage class
3349////////////////////////////////////////////////////////////////////////////////
3350
3351// constructor / destructor
3352////////////////////////////////////////////////////////////////////////////////
3353
3354HRESULT HVMDKImage::FinalConstruct()
3355{
3356 HRESULT rc = HardDisk::FinalConstruct();
3357 if (FAILED (rc))
3358 return rc;
3359
3360 mState = NotCreated;
3361
3362 mStateCheckSem = NIL_RTSEMEVENTMULTI;
3363 mStateCheckWaiters = 0;
3364
3365 mSize = 0;
3366 mActualSize = 0;
3367
3368 /* initialize the container */
3369 int vrc = VDCreate ("VMDK", VDError, this, &mContainer);
3370 ComAssertRCRet (vrc, E_FAIL);
3371
3372 return S_OK;
3373}
3374
3375void HVMDKImage::FinalRelease()
3376{
3377 if (mContainer != NULL)
3378 VDDestroy (mContainer);
3379
3380 HardDisk::FinalRelease();
3381}
3382
3383// public initializer/uninitializer for internal purposes only
3384////////////////////////////////////////////////////////////////////////////////
3385
3386// public methods for internal purposes only
3387/////////////////////////////////////////////////////////////////////////////
3388
3389/**
3390 * Initializes the VMDK hard disk object by reading its properties from
3391 * the given configuration node. The created hard disk will be marked as
3392 * registered on success.
3393 *
3394 * @param aHDNode <HardDisk> node.
3395 * @param aVMDKNode <VirtualDiskImage> node.
3396 */
3397HRESULT HVMDKImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
3398 const settings::Key &aHDNode,
3399 const settings::Key &aVMDKNode)
3400{
3401 using namespace settings;
3402
3403 LogFlowThisFunc (("\n"));
3404
3405 AssertReturn (!aHDNode.isNull() && !aVMDKNode.isNull(), E_FAIL);
3406
3407 AutoLock alock (this);
3408 ComAssertRet (!isReady(), E_UNEXPECTED);
3409
3410 mStorageType = HardDiskStorageType_VMDKImage;
3411
3412 HRESULT rc = S_OK;
3413
3414 do
3415 {
3416 rc = protectedInit (aVirtualBox, aParent);
3417 CheckComRCBreakRC (rc);
3418
3419 /* set ready to let protectedUninit() be called on failure */
3420 setReady (true);
3421
3422 /* filePath (required) */
3423 Bstr filePath = aVMDKNode.stringValue ("filePath");
3424 rc = setFilePath (filePath);
3425 CheckComRCBreakRC (rc);
3426
3427 LogFlowThisFunc (("'%ls'\n", mFilePathFull.raw()));
3428
3429 /* load basic settings and children */
3430 rc = loadSettings (aHDNode);
3431 CheckComRCBreakRC (rc);
3432
3433 if (mType != HardDiskType_WritethroughHardDisk)
3434 {
3435 rc = setError (E_FAIL,
3436 tr ("Currently, non-Writethrough VMDK images are not "
3437 "allowed ('%ls')"),
3438 toString().raw());
3439 break;
3440 }
3441
3442 mState = Created;
3443 mRegistered = TRUE;
3444
3445 /* Don't call queryInformation() for registered hard disks to
3446 * prevent the calling thread (i.e. the VirtualBox server startup
3447 * thread) from an unexpected freeze. The vital mId property (UUID)
3448 * is read from the registry file in loadSettings(). To get the rest,
3449 * the user will have to call COMGETTER(Accessible) manually. */
3450 }
3451 while (0);
3452
3453 if (FAILED (rc))
3454 uninit();
3455
3456 return rc;
3457}
3458
3459/**
3460 * Initializes the VMDK hard disk object using the given image file name.
3461 *
3462 * @param aVirtualBox VirtualBox parent.
3463 * @param aParent Currently, must always be @c NULL.
3464 * @param aFilePath Path to the image file, or @c NULL to create an
3465 * image-less object.
3466 * @param aRegistered Whether to mark this disk as registered or not
3467 * (ignored when @a aFilePath is @c NULL, assuming @c FALSE)
3468 */
3469HRESULT HVMDKImage::init (VirtualBox *aVirtualBox, HardDisk *aParent,
3470 const BSTR aFilePath, BOOL aRegistered /* = FALSE */)
3471{
3472 LogFlowThisFunc (("aFilePath='%ls', aRegistered=%d\n", aFilePath, aRegistered));
3473
3474 AssertReturn (aParent == NULL, E_FAIL);
3475
3476 AutoLock alock (this);
3477 ComAssertRet (!isReady(), E_UNEXPECTED);
3478
3479 mStorageType = HardDiskStorageType_VMDKImage;
3480
3481 HRESULT rc = S_OK;
3482
3483 do
3484 {
3485 rc = protectedInit (aVirtualBox, aParent);
3486 CheckComRCBreakRC (rc);
3487
3488 /* set ready to let protectedUninit() be called on failure */
3489 setReady (true);
3490
3491 rc = setFilePath (aFilePath);
3492 CheckComRCBreakRC (rc);
3493
3494 /* currently, all VMDK hard disks are writethrough */
3495 mType = HardDiskType_WritethroughHardDisk;
3496
3497 Assert (mId.isEmpty());
3498
3499 if (aFilePath && *aFilePath)
3500 {
3501 mRegistered = aRegistered;
3502 mState = Created;
3503
3504 /* Call queryInformation() anyway (even if it will block), because
3505 * it is the only way to get the UUID of the existing VDI and
3506 * initialize the vital mId property. */
3507 Bstr errMsg;
3508 rc = queryInformation (&errMsg);
3509 if (SUCCEEDED (rc))
3510 {
3511 /* We are constructing a new HVirtualDiskImage object. If there
3512 * is a fatal accessibility error (we cannot read image UUID),
3513 * we have to fail. We do so even on non-fatal errors as well,
3514 * because it's not worth to keep going with the inaccessible
3515 * image from the very beginning (when nothing else depends on
3516 * it yet). */
3517 if (!errMsg.isNull())
3518 rc = setErrorBstr (E_FAIL, errMsg);
3519 }
3520 }
3521 else
3522 {
3523 mRegistered = FALSE;
3524 mState = NotCreated;
3525 mId.create();
3526 }
3527 }
3528 while (0);
3529
3530 if (FAILED (rc))
3531 uninit();
3532
3533 return rc;
3534}
3535
3536/**
3537 * Uninitializes the instance and sets the ready flag to FALSE.
3538 * Called either from FinalRelease(), by the parent when it gets destroyed,
3539 * or by a third party when it decides this object is no more valid.
3540 */
3541void HVMDKImage::uninit()
3542{
3543 LogFlowThisFunc (("\n"));
3544
3545 AutoLock alock (this);
3546 if (!isReady())
3547 return;
3548
3549 HardDisk::protectedUninit (alock);
3550}
3551
3552// IHardDisk properties
3553////////////////////////////////////////////////////////////////////////////////
3554
3555STDMETHODIMP HVMDKImage::COMGETTER(Description) (BSTR *aDescription)
3556{
3557 if (!aDescription)
3558 return E_POINTER;
3559
3560 AutoLock alock (this);
3561 CHECK_READY();
3562
3563 mDescription.cloneTo (aDescription);
3564 return S_OK;
3565}
3566
3567STDMETHODIMP HVMDKImage::COMSETTER(Description) (INPTR BSTR aDescription)
3568{
3569 AutoLock alock (this);
3570 CHECK_READY();
3571
3572 CHECK_BUSY_AND_READERS();
3573
3574 return E_NOTIMPL;
3575
3576/// @todo (r=dmik) implement
3577//
3578// if (mState >= Created)
3579// {
3580// int vrc = VDISetImageComment (Utf8Str (mFilePathFull), Utf8Str (aDescription));
3581// if (VBOX_FAILURE (vrc))
3582// return setError (E_FAIL,
3583// tr ("Could not change the description of the VDI hard disk '%ls' "
3584// "(%Vrc)"),
3585// toString().raw(), vrc);
3586// }
3587//
3588// mDescription = aDescription;
3589// return S_OK;
3590}
3591
3592STDMETHODIMP HVMDKImage::COMGETTER(Size) (ULONG64 *aSize)
3593{
3594 if (!aSize)
3595 return E_POINTER;
3596
3597 AutoLock alock (this);
3598 CHECK_READY();
3599
3600/// @todo (r=dmik) will need this if we add suppord for differencing VMDKs
3601//
3602// /* only a non-differencing image knows the logical size */
3603// if (isDifferencing())
3604// return root()->COMGETTER(Size) (aSize);
3605
3606 *aSize = mSize;
3607 return S_OK;
3608}
3609
3610STDMETHODIMP HVMDKImage::COMGETTER(ActualSize) (ULONG64 *aActualSize)
3611{
3612 if (!aActualSize)
3613 return E_POINTER;
3614
3615 AutoLock alock (this);
3616 CHECK_READY();
3617
3618 *aActualSize = mActualSize;
3619 return S_OK;
3620}
3621
3622// IVirtualDiskImage properties
3623////////////////////////////////////////////////////////////////////////////////
3624
3625STDMETHODIMP HVMDKImage::COMGETTER(FilePath) (BSTR *aFilePath)
3626{
3627 if (!aFilePath)
3628 return E_POINTER;
3629
3630 AutoLock alock (this);
3631 CHECK_READY();
3632
3633 mFilePathFull.cloneTo (aFilePath);
3634 return S_OK;
3635}
3636
3637STDMETHODIMP HVMDKImage::COMSETTER(FilePath) (INPTR BSTR aFilePath)
3638{
3639 AutoLock alock (this);
3640 CHECK_READY();
3641
3642 if (mState != NotCreated)
3643 return setError (E_ACCESSDENIED,
3644 tr ("Cannot change the file path of the existing hard disk '%ls'"),
3645 toString().raw());
3646
3647 CHECK_BUSY_AND_READERS();
3648
3649 /* append the default path if only a name is given */
3650 Bstr path = aFilePath;
3651 if (aFilePath && *aFilePath)
3652 {
3653 Utf8Str fp = aFilePath;
3654 if (!RTPathHavePath (fp))
3655 {
3656 AutoReaderLock propsLock (mVirtualBox->systemProperties());
3657 path = Utf8StrFmt ("%ls%c%s",
3658 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
3659 RTPATH_DELIMITER,
3660 fp.raw());
3661 }
3662 }
3663
3664 return setFilePath (path);
3665}
3666
3667STDMETHODIMP HVMDKImage::COMGETTER(Created) (BOOL *aCreated)
3668{
3669 if (!aCreated)
3670 return E_POINTER;
3671
3672 AutoLock alock (this);
3673 CHECK_READY();
3674
3675 *aCreated = mState >= Created;
3676 return S_OK;
3677}
3678
3679// IVMDKImage methods
3680/////////////////////////////////////////////////////////////////////////////
3681
3682STDMETHODIMP HVMDKImage::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
3683{
3684 if (!aProgress)
3685 return E_POINTER;
3686
3687 AutoLock alock (this);
3688 CHECK_READY();
3689
3690 return createImage (aSize, TRUE /* aDynamic */, aProgress);
3691}
3692
3693STDMETHODIMP HVMDKImage::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
3694{
3695 if (!aProgress)
3696 return E_POINTER;
3697
3698 AutoLock alock (this);
3699 CHECK_READY();
3700
3701 return createImage (aSize, FALSE /* aDynamic */, aProgress);
3702}
3703
3704STDMETHODIMP HVMDKImage::DeleteImage()
3705{
3706 AutoLock alock (this);
3707 CHECK_READY();
3708 CHECK_BUSY_AND_READERS();
3709
3710 return E_NOTIMPL;
3711
3712/// @todo (r=dmik) later
3713// We will need to parse the file in order to delete all related delta and
3714// sparse images etc. We may also want to obey the .vmdk.lck file
3715// which is (as far as I understood) created when the VMware VM is
3716// running or saved etc.
3717//
3718// if (mRegistered)
3719// return setError (E_ACCESSDENIED,
3720// tr ("Cannot delete an image of the registered hard disk image '%ls"),
3721// mFilePathFull.raw());
3722// if (mState == NotCreated)
3723// return setError (E_FAIL,
3724// tr ("Hard disk image has been already deleted or never created"));
3725//
3726// int vrc = RTFileDelete (Utf8Str (mFilePathFull));
3727// if (VBOX_FAILURE (vrc))
3728// return setError (E_FAIL, tr ("Could not delete the image file '%ls' (%Vrc)"),
3729// mFilePathFull.raw(), vrc);
3730//
3731// mState = NotCreated;
3732//
3733// /* reset the fields */
3734// mSize = 0;
3735// mActualSize = 0;
3736//
3737// return S_OK;
3738}
3739
3740// public/protected methods for internal purposes only
3741/////////////////////////////////////////////////////////////////////////////
3742
3743/**
3744 * Attempts to mark the hard disk as registered.
3745 * Only VirtualBox can call this method.
3746 */
3747HRESULT HVMDKImage::trySetRegistered (BOOL aRegistered)
3748{
3749 AutoLock alock (this);
3750 CHECK_READY();
3751
3752 if (aRegistered)
3753 {
3754 if (mState == NotCreated)
3755 return setError (E_FAIL,
3756 tr ("Image file '%ls' is not yet created for this hard disk"),
3757 mFilePathFull.raw());
3758
3759/// @todo (r=dmik) will need this if we add suppord for differencing VMDKs
3760// if (isDifferencing())
3761// return setError (E_FAIL,
3762// tr ("Hard disk '%ls' is differencing and cannot be unregistered "
3763// "explicitly"),
3764// mFilePathFull.raw());
3765 }
3766 else
3767 {
3768 ComAssertRet (mState >= Created, E_FAIL);
3769 }
3770
3771 return HardDisk::trySetRegistered (aRegistered);
3772}
3773
3774/**
3775 * Checks accessibility of this hard disk image only (w/o parents).
3776 *
3777 * @param aAccessError on output, a null string indicates the hard disk is
3778 * accessible, otherwise contains a message describing
3779 * the reason of inaccessibility.
3780 */
3781HRESULT HVMDKImage::getAccessible (Bstr &aAccessError)
3782{
3783 AutoLock alock (this);
3784 CHECK_READY();
3785
3786 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
3787 {
3788 /* An accessibility check in progress on some other thread,
3789 * wait for it to finish. */
3790
3791 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL);
3792 ++ mStateCheckWaiters;
3793 alock.leave();
3794
3795 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
3796
3797 alock.enter();
3798 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
3799 -- mStateCheckWaiters;
3800 if (mStateCheckWaiters == 0)
3801 {
3802 RTSemEventMultiDestroy (mStateCheckSem);
3803 mStateCheckSem = NIL_RTSEMEVENTMULTI;
3804 }
3805
3806 AssertRCReturn (vrc, E_FAIL);
3807
3808 /* don't touch aAccessError, it has been already set */
3809 return S_OK;
3810 }
3811
3812 /* check the basic accessibility */
3813 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */);
3814 if (FAILED (rc) || !aAccessError.isNull())
3815 return rc;
3816
3817 if (mState >= Created)
3818 {
3819 return queryInformation (&aAccessError);
3820 }
3821
3822 aAccessError = Utf8StrFmt ("Hard disk image '%ls' is not yet created",
3823 mFilePathFull.raw());
3824 return S_OK;
3825}
3826
3827/**
3828 * Saves hard disk settings to the specified storage node and saves
3829 * all children to the specified hard disk node
3830 *
3831 * @param aHDNode <HardDisk> or <DiffHardDisk> node.
3832 * @param aStorageNode <VirtualDiskImage> node.
3833 */
3834HRESULT HVMDKImage::saveSettings (settings::Key &aHDNode,
3835 settings::Key &aStorageNode)
3836{
3837 AssertReturn (!aHDNode.isNull() && !aStorageNode.isNull(), E_FAIL);
3838
3839 AutoLock alock (this);
3840 CHECK_READY();
3841
3842 /* filePath (required) */
3843 aStorageNode.setValue <Bstr> ("filePath", mFilePath);
3844
3845 /* save basic settings and children */
3846 return HardDisk::saveSettings (aHDNode);
3847}
3848
3849/**
3850 * Checks if the given change of \a aOldPath to \a aNewPath affects the path
3851 * of this hard disk and updates it if necessary to reflect the new location.
3852 * Intended to be from HardDisk::updatePaths().
3853 *
3854 * @param aOldPath old path (full)
3855 * @param aNewPath new path (full)
3856 *
3857 * @note Locks this object for writing.
3858 */
3859void HVMDKImage::updatePath (const char *aOldPath, const char *aNewPath)
3860{
3861 AssertReturnVoid (aOldPath);
3862 AssertReturnVoid (aNewPath);
3863
3864 AutoLock alock (this);
3865 AssertReturnVoid (isReady());
3866
3867 size_t oldPathLen = strlen (aOldPath);
3868
3869 Utf8Str path = mFilePathFull;
3870 LogFlowThisFunc (("VMDK.fullPath={%s}\n", path.raw()));
3871
3872 if (RTPathStartsWith (path, aOldPath))
3873 {
3874 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
3875 path.raw() + oldPathLen);
3876 path = newPath;
3877
3878 mVirtualBox->calculateRelativePath (path, path);
3879
3880 unconst (mFilePathFull) = newPath;
3881 unconst (mFilePath) = path;
3882
3883 LogFlowThisFunc (("-> updated: full={%s} short={%s}\n",
3884 newPath.raw(), path.raw()));
3885 }
3886}
3887
3888/**
3889 * Returns the string representation of this hard disk.
3890 * When \a aShort is false, returns the full image file path.
3891 * Otherwise, returns the image file name only.
3892 *
3893 * @param aShort if true, a short representation is returned
3894 */
3895Bstr HVMDKImage::toString (bool aShort /* = false */)
3896{
3897 AutoLock alock (this);
3898
3899 if (!aShort)
3900 return mFilePathFull;
3901 else
3902 {
3903 Utf8Str fname = mFilePathFull;
3904 return RTPathFilename (fname.mutableRaw());
3905 }
3906}
3907
3908/**
3909 * Creates a clone of this hard disk by storing hard disk data in the given
3910 * VDI file.
3911 *
3912 * If the operation fails, @a aDeleteTarget will be set to @c true unless the
3913 * failure happened because the target file already existed.
3914 *
3915 * @param aId UUID to assign to the created image.
3916 * @param aTargetPath VDI file where the cloned image is to be to stored.
3917 * @param aProgress progress object to run during operation.
3918 * @param aDeleteTarget Whether it is recommended to delete target on
3919 * failure or not.
3920 */
3921HRESULT
3922HVMDKImage::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
3923 Progress *aProgress, bool &aDeleteTarget)
3924{
3925 ComAssertMsgFailed (("Not implemented"));
3926 return E_NOTIMPL;
3927
3928/// @todo (r=dmik) will need this if we add suppord for differencing VMDKs
3929// Use code from HVirtualDiskImage::cloneToImage as an example.
3930}
3931
3932/**
3933 * Creates a new differencing image for this hard disk with the given
3934 * VDI file name.
3935 *
3936 * @param aId UUID to assign to the created image
3937 * @param aTargetPath VDI file where to store the created differencing image
3938 * @param aProgress progress object to run during operation
3939 * (can be NULL)
3940 */
3941HRESULT
3942HVMDKImage::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
3943 Progress *aProgress)
3944{
3945 ComAssertMsgFailed (("Not implemented"));
3946 return E_NOTIMPL;
3947
3948/// @todo (r=dmik) will need this if we add suppord for differencing VMDKs
3949// Use code from HVirtualDiskImage::createDiffImage as an example.
3950}
3951
3952// private methods
3953/////////////////////////////////////////////////////////////////////////////
3954
3955/**
3956 * Helper to set a new file path.
3957 * Resolves a path relatively to the Virtual Box home directory.
3958 *
3959 * @note
3960 * Must be called from under the object's lock!
3961 */
3962HRESULT HVMDKImage::setFilePath (const BSTR aFilePath)
3963{
3964 if (aFilePath && *aFilePath)
3965 {
3966 /* get the full file name */
3967 char filePathFull [RTPATH_MAX];
3968 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aFilePath),
3969 filePathFull, sizeof (filePathFull));
3970 if (VBOX_FAILURE (vrc))
3971 return setError (E_FAIL,
3972 tr ("Invalid image file path '%ls' (%Vrc)"), aFilePath, vrc);
3973
3974 mFilePath = aFilePath;
3975 mFilePathFull = filePathFull;
3976 }
3977 else
3978 {
3979 mFilePath.setNull();
3980 mFilePathFull.setNull();
3981 }
3982
3983 return S_OK;
3984}
3985
3986/**
3987 * Helper to query information about the VDI hard disk.
3988 *
3989 * @param aAccessError not used when NULL, otherwise see #getAccessible()
3990 *
3991 * @note Must be called from under the object's lock, only after
3992 * CHECK_BUSY_AND_READERS() succeeds.
3993 */
3994HRESULT HVMDKImage::queryInformation (Bstr *aAccessError)
3995{
3996 AssertReturn (isLockedOnCurrentThread(), E_FAIL);
3997
3998 /* create a lock object to completely release it later */
3999 AutoLock alock (this);
4000
4001 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
4002
4003 ComAssertRet (mState >= Created, E_FAIL);
4004
4005 HRESULT rc = S_OK;
4006 int vrc = VINF_SUCCESS;
4007
4008 /* lazily create a semaphore */
4009 vrc = RTSemEventMultiCreate (&mStateCheckSem);
4010 ComAssertRCRet (vrc, E_FAIL);
4011
4012 /* Reference the disk to prevent any concurrent modifications
4013 * after releasing the lock below (to unblock getters before
4014 * a lengthy operation). */
4015 addReader();
4016
4017 alock.leave();
4018
4019 /* VBoxVHDD management interface needs to be optimized: we're opening a
4020 * file three times in a raw to get three bits of information. */
4021
4022 Utf8Str filePath = mFilePathFull;
4023 Bstr errMsg;
4024
4025 /* reset any previous error report from VDError() */
4026 mLastVDError.setNull();
4027
4028 do
4029 {
4030 Guid id, parentId;
4031
4032 /// @todo changed from VD_OPEN_FLAGS_READONLY to VD_OPEN_FLAGS_NORMAL,
4033 /// because otherwise registering a VMDK which so far has no UUID will
4034 /// yield a null UUID. It cannot be added to a VMDK opened readonly,
4035 /// obviously. This of course changes locking behavior, but for now
4036 /// this is acceptable. A better solution needs to be found later.
4037 vrc = VDOpen (mContainer, filePath, VD_OPEN_FLAGS_NORMAL);
4038 if (VBOX_FAILURE (vrc))
4039 break;
4040
4041 vrc = VDGetUuid (mContainer, 0, id.ptr());
4042 if (VBOX_FAILURE (vrc))
4043 break;
4044 vrc = VDGetParentUuid (mContainer, 0, parentId.ptr());
4045 if (VBOX_FAILURE (vrc))
4046 break;
4047
4048 if (!mId.isEmpty())
4049 {
4050 /* check that the actual UUID of the image matches the stored UUID */
4051 if (mId != id)
4052 {
4053 errMsg = Utf8StrFmt (
4054 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
4055 "match UUID {%Vuuid} stored in the registry"),
4056 id.ptr(), filePath.raw(), mId.ptr());
4057 break;
4058 }
4059 }
4060 else
4061 {
4062 /* assgn an UUID read from the image file */
4063 mId = id;
4064 }
4065
4066 if (mParent)
4067 {
4068 /* check parent UUID */
4069 AutoLock parentLock (mParent);
4070 if (mParent->id() != parentId)
4071 {
4072 errMsg = Utf8StrFmt (
4073 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
4074 "the hard disk image file '%s' doesn't match "
4075 "UUID {%Vuuid} stored in the registry"),
4076 parentId.raw(), mParent->toString().raw(),
4077 filePath.raw(), mParent->id().raw());
4078 break;
4079 }
4080 }
4081 else if (!parentId.isEmpty())
4082 {
4083 errMsg = Utf8StrFmt (
4084 tr ("Hard disk image '%s' is a differencing image that is linked "
4085 "to a hard disk with UUID {%Vuuid} and cannot be used "
4086 "directly as a base hard disk"),
4087 filePath.raw(), parentId.raw());
4088 break;
4089 }
4090
4091 /* get actual file size */
4092 /// @todo is there a direct method in RT?
4093 {
4094 RTFILE file = NIL_RTFILE;
4095 vrc = RTFileOpen (&file, filePath, RTFILE_O_READ);
4096 if (VBOX_SUCCESS (vrc))
4097 {
4098 uint64_t size = 0;
4099 vrc = RTFileGetSize (file, &size);
4100 if (VBOX_SUCCESS (vrc))
4101 mActualSize = size;
4102 RTFileClose (file);
4103 }
4104 if (VBOX_FAILURE (vrc))
4105 break;
4106 }
4107
4108 /* query logical size only for non-differencing images */
4109 if (!mParent)
4110 {
4111 uint64_t size = VDGetSize (mContainer);
4112 /* convert to MBytes */
4113 mSize = size / 1024 / 1024;
4114 }
4115 }
4116 while (0);
4117
4118 VDCloseAll (mContainer);
4119
4120 /* enter the lock again */
4121 alock.enter();
4122
4123 /* remove the reference */
4124 releaseReader();
4125
4126 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
4127 {
4128 LogWarningFunc (("'%ls' is not accessible "
4129 "(rc=%08X, vrc=%Vrc, errMsg='%ls', mLastVDError='%s')\n",
4130 mFilePathFull.raw(), rc, vrc, errMsg.raw(), mLastVDError.raw()));
4131
4132 if (aAccessError)
4133 {
4134 if (!errMsg.isNull())
4135 *aAccessError = errMsg;
4136 else if (!mLastVDError.isNull())
4137 *aAccessError = mLastVDError;
4138 else if (VBOX_FAILURE (vrc))
4139 *aAccessError = Utf8StrFmt (
4140 tr ("Could not access hard disk image '%ls' (%Vrc)"),
4141 mFilePathFull.raw(), vrc);
4142 }
4143
4144 /* downgrade to not accessible */
4145 mState = Created;
4146 }
4147 else
4148 {
4149 if (aAccessError)
4150 aAccessError->setNull();
4151
4152 mState = Accessible;
4153 }
4154
4155 /* inform waiters if there are any */
4156 if (mStateCheckWaiters > 0)
4157 {
4158 RTSemEventMultiSignal (mStateCheckSem);
4159 }
4160 else
4161 {
4162 /* delete the semaphore ourselves */
4163 RTSemEventMultiDestroy (mStateCheckSem);
4164 mStateCheckSem = NIL_RTSEMEVENTMULTI;
4165 }
4166
4167 /* cleanup the last error report from VDError() */
4168 mLastVDError.setNull();
4169
4170 return rc;
4171}
4172
4173/**
4174 * Helper to create hard disk images.
4175 *
4176 * @param aSize size in MB
4177 * @param aDynamic dynamic or fixed image
4178 * @param aProgress address of IProgress pointer to return
4179 */
4180HRESULT HVMDKImage::createImage (ULONG64 aSize, BOOL aDynamic,
4181 IProgress **aProgress)
4182{
4183 ComAssertMsgFailed (("Not implemented"));
4184 return E_NOTIMPL;
4185
4186/// @todo (r=dmik) later
4187// Use code from HVirtualDiskImage::createImage as an example.
4188}
4189
4190/* static */
4191DECLCALLBACK(int) HVMDKImage::VDITaskThread (RTTHREAD thread, void *pvUser)
4192{
4193 AssertMsgFailed (("Not implemented"));
4194 return VERR_GENERAL_FAILURE;
4195
4196/// @todo (r=dmik) later
4197// Use code from HVirtualDiskImage::VDITaskThread as an example.
4198}
4199
4200/* static */
4201DECLCALLBACK(void) HVMDKImage::VDError (void *pvUser, int rc, RT_SRC_POS_DECL,
4202 const char *pszFormat, va_list va)
4203{
4204 HVMDKImage *that = static_cast <HVMDKImage *> (pvUser);
4205 AssertReturnVoid (that != NULL);
4206
4207 /// @todo pass the error message to the operation initiator
4208 Utf8Str err = Utf8StrFmtVA (pszFormat, va);
4209 if (VBOX_FAILURE (rc))
4210 err = Utf8StrFmt ("%s (%Vrc)", err.raw(), rc);
4211
4212 if (that->mLastVDError.isNull())
4213 that->mLastVDError = err;
4214 else
4215 that->mLastVDError = Utf8StrFmt
4216 ("%s.\n%s", that->mLastVDError.raw(), err.raw());
4217}
4218
4219////////////////////////////////////////////////////////////////////////////////
4220// HCustomHardDisk class
4221////////////////////////////////////////////////////////////////////////////////
4222
4223// constructor / destructor
4224////////////////////////////////////////////////////////////////////////////////
4225
4226HRESULT HCustomHardDisk::FinalConstruct()
4227{
4228 HRESULT rc = HardDisk::FinalConstruct();
4229 if (FAILED (rc))
4230 return rc;
4231
4232 mState = NotCreated;
4233
4234 mStateCheckSem = NIL_RTSEMEVENTMULTI;
4235 mStateCheckWaiters = 0;
4236
4237 mSize = 0;
4238 mActualSize = 0;
4239 mContainer = NULL;
4240
4241 ComAssertRCRet (rc, E_FAIL);
4242
4243 return S_OK;
4244}
4245
4246void HCustomHardDisk::FinalRelease()
4247{
4248 if (mContainer != NULL)
4249 VDDestroy (mContainer);
4250
4251 HardDisk::FinalRelease();
4252}
4253
4254// public initializer/uninitializer for internal purposes only
4255////////////////////////////////////////////////////////////////////////////////
4256
4257// public methods for internal purposes only
4258/////////////////////////////////////////////////////////////////////////////
4259
4260/**
4261 * Initializes the custom hard disk object by reading its properties from
4262 * the given configuration node. The created hard disk will be marked as
4263 * registered on success.
4264 *
4265 * @param aHDNode <HardDisk> node.
4266 * @param aCustomNode <VirtualDiskImage> node.
4267 */
4268HRESULT HCustomHardDisk::init (VirtualBox *aVirtualBox, HardDisk *aParent,
4269 const settings::Key &aHDNode,
4270 const settings::Key &aCustomNode)
4271{
4272 using namespace settings;
4273
4274 LogFlowThisFunc (("\n"));
4275
4276 AssertReturn (!aHDNode.isNull() && !aCustomNode.isNull(), E_FAIL);
4277
4278 AutoLock alock (this);
4279 ComAssertRet (!isReady(), E_UNEXPECTED);
4280
4281 mStorageType = HardDiskStorageType_CustomHardDisk;
4282
4283 HRESULT rc = S_OK;
4284 int vrc = VINF_SUCCESS;
4285 do
4286 {
4287 rc = protectedInit (aVirtualBox, aParent);
4288 CheckComRCBreakRC (rc);
4289
4290 /* set ready to let protectedUninit() be called on failure */
4291 setReady (true);
4292
4293 /* location (required) */
4294 Bstr location = aCustomNode.stringValue ("location");
4295 rc = setLocation (location);
4296 CheckComRCBreakRC (rc);
4297
4298 LogFlowThisFunc (("'%ls'\n", mLocationFull.raw()));
4299
4300 /* format (required) */
4301 mFormat = aCustomNode.stringValue ("format");
4302
4303 /* initialize the container */
4304 vrc = VDCreate (Utf8Str (mFormat), VDError, this, &mContainer);
4305 if (VBOX_FAILURE (vrc))
4306 {
4307 AssertRC (vrc);
4308 if (mLastVDError.isEmpty())
4309 rc = setError (E_FAIL,
4310 tr ("Unknown format '%ls' of the custom "
4311 "hard disk '%ls' (%Vrc)"),
4312 mFormat.raw(), toString().raw(), vrc);
4313 else
4314 rc = setErrorBstr (E_FAIL, mLastVDError);
4315 break;
4316 }
4317
4318 /* load basic settings and children */
4319 rc = loadSettings (aHDNode);
4320 CheckComRCBreakRC (rc);
4321
4322 if (mType != HardDiskType_WritethroughHardDisk)
4323 {
4324 rc = setError (E_FAIL,
4325 tr ("Currently, non-Writethrough custom hard disks "
4326 "are not allowed ('%ls')"),
4327 toString().raw());
4328 break;
4329 }
4330
4331 mState = Created;
4332 mRegistered = TRUE;
4333
4334 /* Don't call queryInformation() for registered hard disks to
4335 * prevent the calling thread (i.e. the VirtualBox server startup
4336 * thread) from an unexpected freeze. The vital mId property (UUID)
4337 * is read from the registry file in loadSettings(). To get the rest,
4338 * the user will have to call COMGETTER(Accessible) manually. */
4339 }
4340 while (0);
4341
4342 if (FAILED (rc))
4343 uninit();
4344
4345 return rc;
4346}
4347
4348/**
4349 * Initializes the custom hard disk object using the given image file name.
4350 *
4351 * @param aVirtualBox VirtualBox parent.
4352 * @param aParent Currently, must always be @c NULL.
4353 * @param aLocation Location of the virtual disk, or @c NULL to create an
4354 * image-less object.
4355 * @param aRegistered Whether to mark this disk as registered or not
4356 * (ignored when @a aLocation is @c NULL, assuming @c FALSE)
4357 */
4358HRESULT HCustomHardDisk::init (VirtualBox *aVirtualBox, HardDisk *aParent,
4359 const BSTR aLocation, BOOL aRegistered /* = FALSE */)
4360{
4361 LogFlowThisFunc (("aLocation='%ls', aRegistered=%d\n", aLocation, aRegistered));
4362
4363 AssertReturn (aParent == NULL, E_FAIL);
4364
4365 AutoLock alock (this);
4366 ComAssertRet (!isReady(), E_UNEXPECTED);
4367
4368 mStorageType = HardDiskStorageType_CustomHardDisk;
4369
4370 HRESULT rc = S_OK;
4371
4372 do
4373 {
4374 rc = protectedInit (aVirtualBox, aParent);
4375 CheckComRCBreakRC (rc);
4376
4377 /* set ready to let protectedUninit() be called on failure */
4378 setReady (true);
4379
4380 rc = setLocation (aLocation);
4381 CheckComRCBreakRC (rc);
4382
4383 /* currently, all custom hard disks are writethrough */
4384 mType = HardDiskType_WritethroughHardDisk;
4385
4386 Assert (mId.isEmpty());
4387
4388 if (aLocation && *aLocation)
4389 {
4390 mRegistered = aRegistered;
4391 mState = Created;
4392
4393 char *pszFormat = NULL;
4394
4395 int vrc = VDGetFormat (Utf8Str (mLocation), &pszFormat);
4396 if (VBOX_FAILURE(vrc))
4397 {
4398 AssertRC (vrc);
4399 rc = setError (E_FAIL,
4400 tr ("Cannot recognize the format of the custom "
4401 "hard disk '%ls' (%Vrc)"),
4402 toString().raw(), vrc);
4403 break;
4404 }
4405
4406 /* Create the corresponding container. */
4407 vrc = VDCreate (pszFormat, VDError, this, &mContainer);
4408
4409 if (VBOX_SUCCESS(vrc))
4410 mFormat = Bstr (pszFormat);
4411
4412 RTStrFree (pszFormat);
4413
4414 /* the format has been already checked for presence at this point */
4415 ComAssertRCBreak (vrc, rc = E_FAIL);
4416
4417 /* Call queryInformation() anyway (even if it will block), because
4418 * it is the only way to get the UUID of the existing VDI and
4419 * initialize the vital mId property. */
4420 Bstr errMsg;
4421 rc = queryInformation (&errMsg);
4422 if (SUCCEEDED (rc))
4423 {
4424 /* We are constructing a new HVirtualDiskImage object. If there
4425 * is a fatal accessibility error (we cannot read image UUID),
4426 * we have to fail. We do so even on non-fatal errors as well,
4427 * because it's not worth to keep going with the inaccessible
4428 * image from the very beginning (when nothing else depends on
4429 * it yet). */
4430 if (!errMsg.isNull())
4431 rc = setErrorBstr (E_FAIL, errMsg);
4432 }
4433 }
4434 else
4435 {
4436 mRegistered = FALSE;
4437 mState = NotCreated;
4438 mId.create();
4439 }
4440 }
4441 while (0);
4442
4443 if (FAILED (rc))
4444 uninit();
4445
4446 return rc;
4447}
4448
4449/**
4450 * Uninitializes the instance and sets the ready flag to FALSE.
4451 * Called either from FinalRelease(), by the parent when it gets destroyed,
4452 * or by a third party when it decides this object is no more valid.
4453 */
4454void HCustomHardDisk::uninit()
4455{
4456 LogFlowThisFunc (("\n"));
4457
4458 AutoLock alock (this);
4459 if (!isReady())
4460 return;
4461
4462 HardDisk::protectedUninit (alock);
4463}
4464
4465// IHardDisk properties
4466////////////////////////////////////////////////////////////////////////////////
4467
4468STDMETHODIMP HCustomHardDisk::COMGETTER(Description) (BSTR *aDescription)
4469{
4470 if (!aDescription)
4471 return E_POINTER;
4472
4473 AutoLock alock (this);
4474 CHECK_READY();
4475
4476 mDescription.cloneTo (aDescription);
4477 return S_OK;
4478}
4479
4480STDMETHODIMP HCustomHardDisk::COMSETTER(Description) (INPTR BSTR aDescription)
4481{
4482 AutoLock alock (this);
4483 CHECK_READY();
4484
4485 CHECK_BUSY_AND_READERS();
4486
4487 return E_NOTIMPL;
4488}
4489
4490STDMETHODIMP HCustomHardDisk::COMGETTER(Size) (ULONG64 *aSize)
4491{
4492 if (!aSize)
4493 return E_POINTER;
4494
4495 AutoLock alock (this);
4496 CHECK_READY();
4497
4498 *aSize = mSize;
4499 return S_OK;
4500}
4501
4502STDMETHODIMP HCustomHardDisk::COMGETTER(ActualSize) (ULONG64 *aActualSize)
4503{
4504 if (!aActualSize)
4505 return E_POINTER;
4506
4507 AutoLock alock (this);
4508 CHECK_READY();
4509
4510 *aActualSize = mActualSize;
4511 return S_OK;
4512}
4513
4514// ICustomHardDisk properties
4515////////////////////////////////////////////////////////////////////////////////
4516
4517STDMETHODIMP HCustomHardDisk::COMGETTER(Location) (BSTR *aLocation)
4518{
4519 if (!aLocation)
4520 return E_POINTER;
4521
4522 AutoLock alock (this);
4523 CHECK_READY();
4524
4525 mLocationFull.cloneTo (aLocation);
4526 return S_OK;
4527}
4528
4529STDMETHODIMP HCustomHardDisk::COMSETTER(Location) (INPTR BSTR aLocation)
4530{
4531 AutoLock alock (this);
4532 CHECK_READY();
4533
4534 if (mState != NotCreated)
4535 return setError (E_ACCESSDENIED,
4536 tr ("Cannot change the file path of the existing hard disk '%ls'"),
4537 toString().raw());
4538
4539 CHECK_BUSY_AND_READERS();
4540
4541 /// @todo currently, we assume that location is always a file path for
4542 /// all custom hard disks. This is not generally correct, and needs to be
4543 /// parametrized in the VD plugin interface.
4544
4545 /* append the default path if only a name is given */
4546 Bstr path = aLocation;
4547 if (aLocation && *aLocation)
4548 {
4549 Utf8Str fp = aLocation;
4550 if (!RTPathHavePath (fp))
4551 {
4552 AutoReaderLock propsLock (mVirtualBox->systemProperties());
4553 path = Utf8StrFmt ("%ls%c%s",
4554 mVirtualBox->systemProperties()->defaultVDIFolder().raw(),
4555 RTPATH_DELIMITER,
4556 fp.raw());
4557 }
4558 }
4559
4560 return setLocation (path);
4561}
4562
4563STDMETHODIMP HCustomHardDisk::COMGETTER(Created) (BOOL *aCreated)
4564{
4565 if (!aCreated)
4566 return E_POINTER;
4567
4568 AutoLock alock (this);
4569 CHECK_READY();
4570
4571 *aCreated = mState >= Created;
4572 return S_OK;
4573}
4574
4575STDMETHODIMP HCustomHardDisk::COMGETTER(Format) (BSTR *aFormat)
4576{
4577 if (!aFormat)
4578 return E_POINTER;
4579
4580 AutoLock alock (this);
4581 CHECK_READY();
4582
4583 mFormat.cloneTo (aFormat);
4584 return S_OK;
4585}
4586
4587// ICustomHardDisk methods
4588/////////////////////////////////////////////////////////////////////////////
4589
4590STDMETHODIMP HCustomHardDisk::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress)
4591{
4592 if (!aProgress)
4593 return E_POINTER;
4594
4595 AutoLock alock (this);
4596 CHECK_READY();
4597
4598 return createImage (aSize, TRUE /* aDynamic */, aProgress);
4599}
4600
4601STDMETHODIMP HCustomHardDisk::CreateFixedImage (ULONG64 aSize, IProgress **aProgress)
4602{
4603 if (!aProgress)
4604 return E_POINTER;
4605
4606 AutoLock alock (this);
4607 CHECK_READY();
4608
4609 return createImage (aSize, FALSE /* aDynamic */, aProgress);
4610}
4611
4612STDMETHODIMP HCustomHardDisk::DeleteImage()
4613{
4614 AutoLock alock (this);
4615 CHECK_READY();
4616 CHECK_BUSY_AND_READERS();
4617
4618 return E_NOTIMPL;
4619
4620/// @todo later
4621}
4622
4623// public/protected methods for internal purposes only
4624/////////////////////////////////////////////////////////////////////////////
4625
4626/**
4627 * Attempts to mark the hard disk as registered.
4628 * Only VirtualBox can call this method.
4629 */
4630HRESULT HCustomHardDisk::trySetRegistered (BOOL aRegistered)
4631{
4632 AutoLock alock (this);
4633 CHECK_READY();
4634
4635 if (aRegistered)
4636 {
4637 if (mState == NotCreated)
4638 return setError (E_FAIL,
4639 tr ("Storage location '%ls' is not yet created for this hard disk"),
4640 mLocationFull.raw());
4641 }
4642 else
4643 {
4644 ComAssertRet (mState >= Created, E_FAIL);
4645 }
4646
4647 return HardDisk::trySetRegistered (aRegistered);
4648}
4649
4650/**
4651 * Checks accessibility of this hard disk image only (w/o parents).
4652 *
4653 * @param aAccessError on output, a null string indicates the hard disk is
4654 * accessible, otherwise contains a message describing
4655 * the reason of inaccessibility.
4656 */
4657HRESULT HCustomHardDisk::getAccessible (Bstr &aAccessError)
4658{
4659 AutoLock alock (this);
4660 CHECK_READY();
4661
4662 if (mStateCheckSem != NIL_RTSEMEVENTMULTI)
4663 {
4664 /* An accessibility check in progress on some other thread,
4665 * wait for it to finish. */
4666
4667 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL);
4668 ++ mStateCheckWaiters;
4669 alock.leave();
4670
4671 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT);
4672
4673 alock.enter();
4674 AssertReturn (mStateCheckWaiters != 0, E_FAIL);
4675 -- mStateCheckWaiters;
4676 if (mStateCheckWaiters == 0)
4677 {
4678 RTSemEventMultiDestroy (mStateCheckSem);
4679 mStateCheckSem = NIL_RTSEMEVENTMULTI;
4680 }
4681
4682 AssertRCReturn (vrc, E_FAIL);
4683
4684 /* don't touch aAccessError, it has been already set */
4685 return S_OK;
4686 }
4687
4688 /* check the basic accessibility */
4689 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */);
4690 if (FAILED (rc) || !aAccessError.isNull())
4691 return rc;
4692
4693 if (mState >= Created)
4694 {
4695 return queryInformation (&aAccessError);
4696 }
4697
4698 aAccessError = Utf8StrFmt ("Hard disk '%ls' is not yet created",
4699 mLocationFull.raw());
4700 return S_OK;
4701}
4702
4703/**
4704 * Saves hard disk settings to the specified storage node and saves
4705 * all children to the specified hard disk node
4706 *
4707 * @param aHDNode <HardDisk> or <DiffHardDisk> node.
4708 * @param aStorageNode <VirtualDiskImage> node.
4709 */
4710HRESULT HCustomHardDisk::saveSettings (settings::Key &aHDNode,
4711 settings::Key &aStorageNode)
4712{
4713 AssertReturn (!aHDNode.isNull() && !aStorageNode.isNull(), E_FAIL);
4714
4715 AutoLock alock (this);
4716 CHECK_READY();
4717
4718 /* location (required) */
4719 aStorageNode.setValue <Bstr> ("location", mLocationFull);
4720
4721 /* format (required) */
4722 aStorageNode.setValue <Bstr> ("format", mFormat);
4723
4724 /* save basic settings and children */
4725 return HardDisk::saveSettings (aHDNode);
4726}
4727
4728/**
4729 * Returns the string representation of this hard disk.
4730 * When \a aShort is false, returns the full image file path.
4731 * Otherwise, returns the image file name only.
4732 *
4733 * @param aShort if true, a short representation is returned
4734 */
4735Bstr HCustomHardDisk::toString (bool aShort /* = false */)
4736{
4737 AutoLock alock (this);
4738
4739 /// @todo currently, we assume that location is always a file path for
4740 /// all custom hard disks. This is not generally correct, and needs to be
4741 /// parametrized in the VD plugin interface.
4742
4743 if (!aShort)
4744 return mLocationFull;
4745 else
4746 {
4747 Utf8Str fname = mLocationFull;
4748 return RTPathFilename (fname.mutableRaw());
4749 }
4750}
4751
4752/**
4753 * Creates a clone of this hard disk by storing hard disk data in the given
4754 * VDI file.
4755 *
4756 * If the operation fails, @a aDeleteTarget will be set to @c true unless the
4757 * failure happened because the target file already existed.
4758 *
4759 * @param aId UUID to assign to the created image.
4760 * @param aTargetPath VDI file where the cloned image is to be to stored.
4761 * @param aProgress progress object to run during operation.
4762 * @param aDeleteTarget Whether it is recommended to delete target on
4763 * failure or not.
4764 */
4765HRESULT
4766HCustomHardDisk::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath,
4767 Progress *aProgress, bool &aDeleteTarget)
4768{
4769 ComAssertMsgFailed (("Not implemented"));
4770 return E_NOTIMPL;
4771}
4772
4773/**
4774 * Creates a new differencing image for this hard disk with the given
4775 * VDI file name.
4776 *
4777 * @param aId UUID to assign to the created image
4778 * @param aTargetPath VDI file where to store the created differencing image
4779 * @param aProgress progress object to run during operation
4780 * (can be NULL)
4781 */
4782HRESULT
4783HCustomHardDisk::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath,
4784 Progress *aProgress)
4785{
4786 ComAssertMsgFailed (("Not implemented"));
4787 return E_NOTIMPL;
4788}
4789
4790// private methods
4791/////////////////////////////////////////////////////////////////////////////
4792
4793/**
4794 * Helper to set a new location.
4795 *
4796 * @note
4797 * Must be called from under the object's lock!
4798 */
4799HRESULT HCustomHardDisk::setLocation (const BSTR aLocation)
4800{
4801 /// @todo currently, we assume that location is always a file path for
4802 /// all custom hard disks. This is not generally correct, and needs to be
4803 /// parametrized in the VD plugin interface.
4804
4805 if (aLocation && *aLocation)
4806 {
4807 /* get the full file name */
4808 char locationFull [RTPATH_MAX];
4809 int vrc = RTPathAbsEx (mVirtualBox->homeDir(), Utf8Str (aLocation),
4810 locationFull, sizeof (locationFull));
4811 if (VBOX_FAILURE (vrc))
4812 return setError (E_FAIL,
4813 tr ("Invalid hard disk location '%ls' (%Vrc)"), aLocation, vrc);
4814
4815 mLocation = aLocation;
4816 mLocationFull = locationFull;
4817 }
4818 else
4819 {
4820 mLocation.setNull();
4821 mLocationFull.setNull();
4822 }
4823
4824 return S_OK;
4825}
4826
4827/**
4828 * Helper to query information about the custom hard disk.
4829 *
4830 * @param aAccessError not used when NULL, otherwise see #getAccessible()
4831 *
4832 * @note Must be called from under the object's lock, only after
4833 * CHECK_BUSY_AND_READERS() succeeds.
4834 */
4835HRESULT HCustomHardDisk::queryInformation (Bstr *aAccessError)
4836{
4837 AssertReturn (isLockedOnCurrentThread(), E_FAIL);
4838
4839 /* create a lock object to completely release it later */
4840 AutoLock alock (this);
4841
4842 AssertReturn (mStateCheckWaiters == 0, E_FAIL);
4843
4844 ComAssertRet (mState >= Created, E_FAIL);
4845
4846 HRESULT rc = S_OK;
4847 int vrc = VINF_SUCCESS;
4848
4849 /* lazily create a semaphore */
4850 vrc = RTSemEventMultiCreate (&mStateCheckSem);
4851 ComAssertRCRet (vrc, E_FAIL);
4852
4853 /* Reference the disk to prevent any concurrent modifications
4854 * after releasing the lock below (to unblock getters before
4855 * a lengthy operation). */
4856 addReader();
4857
4858 alock.leave();
4859
4860 /* VBoxVHDD management interface needs to be optimized: we're opening a
4861 * file three times in a raw to get three bits of information. */
4862
4863 Utf8Str location = mLocationFull;
4864 Bstr errMsg;
4865
4866 /* reset any previous error report from VDError() */
4867 mLastVDError.setNull();
4868
4869 do
4870 {
4871 Guid id, parentId;
4872
4873 vrc = VDOpen (mContainer, location, VD_OPEN_FLAGS_INFO);
4874 if (VBOX_FAILURE (vrc))
4875 break;
4876
4877 vrc = VDGetUuid (mContainer, 0, id.ptr());
4878 if (VBOX_FAILURE (vrc))
4879 break;
4880 vrc = VDGetParentUuid (mContainer, 0, parentId.ptr());
4881 if (VBOX_FAILURE (vrc))
4882 break;
4883
4884 if (!mId.isEmpty())
4885 {
4886 /* check that the actual UUID of the image matches the stored UUID */
4887 if (mId != id)
4888 {
4889 errMsg = Utf8StrFmt (
4890 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't "
4891 "match UUID {%Vuuid} stored in the registry"),
4892 id.ptr(), location.raw(), mId.ptr());
4893 break;
4894 }
4895 }
4896 else
4897 {
4898 /* assgn an UUID read from the image file */
4899 mId = id;
4900 }
4901
4902 if (mParent)
4903 {
4904 /* check parent UUID */
4905 AutoLock parentLock (mParent);
4906 if (mParent->id() != parentId)
4907 {
4908 errMsg = Utf8StrFmt (
4909 tr ("UUID {%Vuuid} of the parent image '%ls' stored in "
4910 "the hard disk image file '%s' doesn't match "
4911 "UUID {%Vuuid} stored in the registry"),
4912 parentId.raw(), mParent->toString().raw(),
4913 location.raw(), mParent->id().raw());
4914 break;
4915 }
4916 }
4917 else if (!parentId.isEmpty())
4918 {
4919 errMsg = Utf8StrFmt (
4920 tr ("Hard disk image '%s' is a differencing image that is linked "
4921 "to a hard disk with UUID {%Vuuid} and cannot be used "
4922 "directly as a base hard disk"),
4923 location.raw(), parentId.raw());
4924 break;
4925 }
4926
4927 /* get actual file size */
4928 /// @todo is there a direct method in RT?
4929 {
4930 RTFILE file = NIL_RTFILE;
4931 vrc = RTFileOpen (&file, location, RTFILE_O_READ);
4932 if (VBOX_SUCCESS (vrc))
4933 {
4934 uint64_t size = 0;
4935 vrc = RTFileGetSize (file, &size);
4936 if (VBOX_SUCCESS (vrc))
4937 mActualSize = size;
4938 RTFileClose (file);
4939 }
4940 if (VBOX_FAILURE (vrc))
4941 break;
4942 }
4943
4944 /* query logical size only for non-differencing images */
4945 if (!mParent)
4946 {
4947 uint64_t size = VDGetSize (mContainer);
4948 /* convert to MBytes */
4949 mSize = size / 1024 / 1024;
4950 }
4951 }
4952 while (0);
4953
4954 VDCloseAll (mContainer);
4955
4956 /* enter the lock again */
4957 alock.enter();
4958
4959 /* remove the reference */
4960 releaseReader();
4961
4962 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull())
4963 {
4964 LogWarningFunc (("'%ls' is not accessible "
4965 "(rc=%08X, vrc=%Vrc, errMsg='%ls')\n",
4966 mLocationFull.raw(), rc, vrc, errMsg.raw()));
4967
4968 if (aAccessError)
4969 {
4970 if (!errMsg.isNull())
4971 *aAccessError = errMsg;
4972 else if (!mLastVDError.isNull())
4973 *aAccessError = mLastVDError;
4974 else if (VBOX_FAILURE (vrc))
4975 *aAccessError = Utf8StrFmt (
4976 tr ("Could not access hard disk '%ls' (%Vrc)"),
4977 mLocationFull.raw(), vrc);
4978 }
4979
4980 /* downgrade to not accessible */
4981 mState = Created;
4982 }
4983 else
4984 {
4985 if (aAccessError)
4986 aAccessError->setNull();
4987
4988 mState = Accessible;
4989 }
4990
4991 /* inform waiters if there are any */
4992 if (mStateCheckWaiters > 0)
4993 {
4994 RTSemEventMultiSignal (mStateCheckSem);
4995 }
4996 else
4997 {
4998 /* delete the semaphore ourselves */
4999 RTSemEventMultiDestroy (mStateCheckSem);
5000 mStateCheckSem = NIL_RTSEMEVENTMULTI;
5001 }
5002
5003 /* cleanup the last error report from VDError() */
5004 mLastVDError.setNull();
5005
5006 return rc;
5007}
5008
5009/**
5010 * Helper to create hard disk images.
5011 *
5012 * @param aSize size in MB
5013 * @param aDynamic dynamic or fixed image
5014 * @param aProgress address of IProgress pointer to return
5015 */
5016HRESULT HCustomHardDisk::createImage (ULONG64 aSize, BOOL aDynamic,
5017 IProgress **aProgress)
5018{
5019 ComAssertMsgFailed (("Not implemented"));
5020 return E_NOTIMPL;
5021}
5022
5023/* static */
5024DECLCALLBACK(int) HCustomHardDisk::VDITaskThread (RTTHREAD thread, void *pvUser)
5025{
5026 AssertMsgFailed (("Not implemented"));
5027 return VERR_GENERAL_FAILURE;
5028}
5029
5030/* static */
5031DECLCALLBACK(void) HCustomHardDisk::VDError (void *pvUser, int rc, RT_SRC_POS_DECL,
5032 const char *pszFormat, va_list va)
5033{
5034 HCustomHardDisk *that = static_cast <HCustomHardDisk *> (pvUser);
5035 AssertReturnVoid (that != NULL);
5036
5037 /// @todo pass the error message to the operation initiator
5038 Utf8Str err = Utf8StrFmt (pszFormat, va);
5039 if (VBOX_FAILURE (rc))
5040 err = Utf8StrFmt ("%s (%Vrc)", err.raw(), rc);
5041
5042 if (that->mLastVDError.isNull())
5043 that->mLastVDError = err;
5044 else
5045 that->mLastVDError = Utf8StrFmt
5046 ("%s.\n%s", that->mLastVDError.raw(), err.raw());
5047}
5048
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