VirtualBox

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

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

Main: Fixed: Simultaneous usage of immutable VDIs by more than one running VM was not possible.

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