VirtualBox

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

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

Main: Change Utf8StrFmt to Utf8StrFmtVA, could trigger a fancy assertion in the HardDisk code because opening a non-existent VDI would not fail but succeed and give a zero UUID.

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