VirtualBox

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

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

New VMDK code.

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