VirtualBox

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

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

Moved the template code out of cdefs.h, partly because it didn't belong there but mostly because it was at the end of the file and would screw up any attempts made by the object cache at avoid recompiling on cdefs.h changes.

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