VirtualBox

source: vbox/trunk/src/VBox/Main/MediumImpl.cpp@ 18719

Last change on this file since 18719 was 18313, checked in by vboxsync, 16 years ago

Main: a different approach to opening images read-only for import which does not assert on empty UUIDs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 30.3 KB
Line 
1/* $Id: MediumImpl.cpp 18313 2009-03-26 13:59:14Z vboxsync $ */
2
3/** @file
4 *
5 * VirtualBox COM class implementation
6 */
7
8/*
9 * Copyright (C) 2008 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24#include "MediumImpl.h"
25
26#include "VirtualBoxImpl.h"
27
28#include "Logging.h"
29
30#include <VBox/com/array.h>
31
32#include <VBox/err.h>
33#include <VBox/settings.h>
34
35#include <iprt/param.h>
36#include <iprt/path.h>
37#include <iprt/file.h>
38
39////////////////////////////////////////////////////////////////////////////////
40// MediumBase class
41////////////////////////////////////////////////////////////////////////////////
42
43// constructor / destructor
44////////////////////////////////////////////////////////////////////////////////
45
46DEFINE_EMPTY_CTOR_DTOR (MediumBase)
47
48// protected initializer/uninitializer for internal purposes only
49////////////////////////////////////////////////////////////////////////////////
50
51// IMedium properties
52////////////////////////////////////////////////////////////////////////////////
53
54STDMETHODIMP MediumBase::COMGETTER(Id) (OUT_GUID aId)
55{
56 CheckComArgOutPointerValid (aId);
57
58 AutoCaller autoCaller (this);
59 CheckComRCReturnRC (autoCaller.rc());
60
61 AutoReadLock alock (this);
62
63 m.id.cloneTo (aId);
64
65 return S_OK;
66}
67
68STDMETHODIMP MediumBase::COMGETTER(Description) (BSTR *aDescription)
69{
70 CheckComArgOutPointerValid (aDescription);
71
72 AutoCaller autoCaller (this);
73 CheckComRCReturnRC (autoCaller.rc());
74
75 AutoReadLock alock (this);
76
77 m.description.cloneTo (aDescription);
78
79 return S_OK;
80}
81
82STDMETHODIMP MediumBase::COMSETTER(Description) (IN_BSTR aDescription)
83{
84 CheckComArgNotNull (aDescription);
85
86 AutoCaller autoCaller (this);
87 CheckComRCReturnRC (autoCaller.rc());
88
89 AutoWriteLock alock (this);
90
91 /// @todo update m.description and save the global registry (and local
92 /// registries of portable VMs referring to this medium), this will also
93 /// require to add the mRegistered flag to data
94
95 ReturnComNotImplemented();
96}
97
98STDMETHODIMP MediumBase::COMGETTER(State) (MediaState_T *aState)
99{
100 CheckComArgOutPointerValid(aState);
101
102 AutoCaller autoCaller (this);
103 CheckComRCReturnRC (autoCaller.rc());
104
105 /* queryInfo() locks this for writing. */
106 AutoWriteLock alock (this);
107
108 HRESULT rc = S_OK;
109
110 switch (m.state)
111 {
112 case MediaState_Created:
113 case MediaState_Inaccessible:
114 case MediaState_LockedRead:
115 case MediaState_LockedWrite:
116 {
117 rc = queryInfo();
118 break;
119 }
120 default:
121 break;
122 }
123
124 *aState = m.state;
125
126 return rc;
127}
128
129STDMETHODIMP MediumBase::COMGETTER(Location) (BSTR *aLocation)
130{
131 CheckComArgOutPointerValid(aLocation);
132
133 AutoCaller autoCaller (this);
134 CheckComRCReturnRC (autoCaller.rc());
135
136 AutoReadLock alock (this);
137
138 m.locationFull.cloneTo (aLocation);
139
140 return S_OK;
141}
142
143STDMETHODIMP MediumBase::COMSETTER(Location) (IN_BSTR aLocation)
144{
145 CheckComArgNotNull (aLocation);
146
147 AutoCaller autoCaller (this);
148 CheckComRCReturnRC (autoCaller.rc());
149
150 AutoWriteLock alock (this);
151
152 /// @todo NEWMEDIA for file names, add the default extension if no extension
153 /// is present (using the information from the VD backend which also implies
154 /// that one more parameter should be passed to setLocation() requesting
155 /// that functionality since it is only allwed when called from this method
156
157 /// @todo NEWMEDIA rename the file and set m.location on success, then save
158 /// the global registry (and local registries of portable VMs referring to
159 /// this medium), this will also require to add the mRegistered flag to data
160
161 ReturnComNotImplemented();
162}
163
164STDMETHODIMP MediumBase::COMGETTER(Name) (BSTR *aName)
165{
166 CheckComArgOutPointerValid (aName);
167
168 AutoCaller autoCaller (this);
169 CheckComRCReturnRC (autoCaller.rc());
170
171 AutoReadLock alock (this);
172
173 name().cloneTo (aName);
174
175 return S_OK;
176}
177
178STDMETHODIMP MediumBase::COMGETTER(Size) (ULONG64 *aSize)
179{
180 CheckComArgOutPointerValid (aSize);
181
182 AutoCaller autoCaller (this);
183 CheckComRCReturnRC (autoCaller.rc());
184
185 AutoReadLock alock (this);
186
187 *aSize = m.size;
188
189 return S_OK;
190}
191
192STDMETHODIMP MediumBase::COMGETTER(LastAccessError) (BSTR *aLastAccessError)
193{
194 CheckComArgOutPointerValid (aLastAccessError);
195
196 AutoCaller autoCaller (this);
197 CheckComRCReturnRC (autoCaller.rc());
198
199 AutoReadLock alock (this);
200
201 m.lastAccessError.cloneTo (aLastAccessError);
202
203 return S_OK;
204}
205
206STDMETHODIMP MediumBase::COMGETTER(MachineIds) (ComSafeGUIDArrayOut (aMachineIds))
207{
208 if (ComSafeGUIDArrayOutIsNull (aMachineIds))
209 return E_POINTER;
210
211 AutoCaller autoCaller (this);
212 CheckComRCReturnRC (autoCaller.rc());
213
214 AutoReadLock alock (this);
215
216 com::SafeGUIDArray machineIds;
217
218 if (m.backRefs.size() != 0)
219 {
220 machineIds.reset (m.backRefs.size());
221
222 size_t i = 0;
223 for (BackRefList::const_iterator it = m.backRefs.begin();
224 it != m.backRefs.end(); ++ it, ++ i)
225 {
226 machineIds [i] = it->machineId;
227 }
228 }
229
230 machineIds.detachTo (ComSafeGUIDArrayOutArg (aMachineIds));
231
232 return S_OK;
233}
234
235// IMedium methods
236////////////////////////////////////////////////////////////////////////////////
237
238STDMETHODIMP MediumBase::GetSnapshotIds (IN_GUID aMachineId,
239 ComSafeGUIDArrayOut (aSnapshotIds))
240{
241 CheckComArgExpr (aMachineId, Guid (aMachineId).isEmpty() == false);
242 CheckComArgOutSafeArrayPointerValid (aSnapshotIds);
243
244 AutoCaller autoCaller (this);
245 CheckComRCReturnRC (autoCaller.rc());
246
247 AutoReadLock alock (this);
248
249 com::SafeGUIDArray snapshotIds;
250
251 for (BackRefList::const_iterator it = m.backRefs.begin();
252 it != m.backRefs.end(); ++ it)
253 {
254 if (it->machineId == aMachineId)
255 {
256 size_t size = it->snapshotIds.size();
257
258 /* if the medium is attached to the machine in the current state, we
259 * return its ID as the first element of the array */
260 if (it->inCurState)
261 ++ size;
262
263 if (size > 0)
264 {
265 snapshotIds.reset (size);
266
267 size_t j = 0;
268 if (it->inCurState)
269 snapshotIds [j ++] = it->machineId;
270
271 for (BackRef::GuidList::const_iterator jt =
272 it->snapshotIds.begin();
273 jt != it->snapshotIds.end(); ++ jt, ++ j)
274 {
275 snapshotIds [j] = *jt;
276 }
277 }
278
279 break;
280 }
281 }
282
283 snapshotIds.detachTo (ComSafeGUIDArrayOutArg (aSnapshotIds));
284
285 return S_OK;
286}
287
288/**
289 * @note @a aState may be NULL if the state value is not needed (only for
290 * in-process calls).
291 */
292STDMETHODIMP MediumBase::LockRead (MediaState_T *aState)
293{
294 AutoCaller autoCaller (this);
295 CheckComRCReturnRC (autoCaller.rc());
296
297 AutoWriteLock alock (this);
298
299 /* return the current state before */
300 if (aState)
301 *aState = m.state;
302
303 HRESULT rc = S_OK;
304
305 switch (m.state)
306 {
307 case MediaState_Created:
308 case MediaState_Inaccessible:
309 case MediaState_LockedRead:
310 {
311 ++ m.readers;
312
313 ComAssertMsgBreak (m.readers != 0, ("Counter overflow"),
314 rc = E_FAIL);
315
316 if (m.state == MediaState_Created)
317 m.accessibleInLock = true;
318 else if (m.state == MediaState_Inaccessible)
319 m.accessibleInLock = false;
320
321 m.state = MediaState_LockedRead;
322
323 break;
324 }
325 default:
326 {
327 rc = setStateError();
328 break;
329 }
330 }
331
332 return rc;
333}
334
335/**
336 * @note @a aState may be NULL if the state value is not needed (only for
337 * in-process calls).
338 */
339STDMETHODIMP MediumBase::UnlockRead (MediaState_T *aState)
340{
341 AutoCaller autoCaller (this);
342 CheckComRCReturnRC (autoCaller.rc());
343
344 AutoWriteLock alock (this);
345
346 HRESULT rc = S_OK;
347
348 switch (m.state)
349 {
350 case MediaState_LockedRead:
351 {
352 if (m.queryInfoSem == NIL_RTSEMEVENTMULTI)
353 {
354 Assert (m.readers != 0);
355 -- m.readers;
356
357 /* Reset the state after the last reader */
358 if (m.readers == 0)
359 {
360 if (m.accessibleInLock)
361 m.state = MediaState_Created;
362 else
363 m.state = MediaState_Inaccessible;
364 }
365
366 break;
367 }
368
369 /* otherwise, queryInfo() is in progress; fall through */
370 }
371 default:
372 {
373 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
374 tr ("Medium '%ls' is not locked for reading"),
375 m.locationFull.raw());
376 break;
377 }
378 }
379
380 /* return the current state after */
381 if (aState)
382 *aState = m.state;
383
384 return rc;
385}
386
387/**
388 * @note @a aState may be NULL if the state value is not needed (only for
389 * in-process calls).
390 */
391STDMETHODIMP MediumBase::LockWrite (MediaState_T *aState)
392{
393 AutoCaller autoCaller (this);
394 CheckComRCReturnRC (autoCaller.rc());
395
396 AutoWriteLock alock (this);
397
398 /* return the current state before */
399 if (aState)
400 *aState = m.state;
401
402 HRESULT rc = S_OK;
403
404 switch (m.state)
405 {
406 case MediaState_Created:
407 case MediaState_Inaccessible:
408 {
409 if (m.state == MediaState_Created)
410 m.accessibleInLock = true;
411 else if (m.state == MediaState_Inaccessible)
412 m.accessibleInLock = false;
413
414 m.state = MediaState_LockedWrite;
415 break;
416 }
417 default:
418 {
419 rc = setStateError();
420 break;
421 }
422 }
423
424 return rc;
425}
426
427/**
428 * @note @a aState may be NULL if the state value is not needed (only for
429 * in-process calls).
430 */
431STDMETHODIMP MediumBase::UnlockWrite (MediaState_T *aState)
432{
433 AutoCaller autoCaller (this);
434 CheckComRCReturnRC (autoCaller.rc());
435
436 AutoWriteLock alock (this);
437
438 HRESULT rc = S_OK;
439
440 switch (m.state)
441 {
442 case MediaState_LockedWrite:
443 {
444 if (m.accessibleInLock)
445 m.state = MediaState_Created;
446 else
447 m.state = MediaState_Inaccessible;
448 break;
449 }
450 default:
451 {
452 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
453 tr ("Medium '%ls' is not locked for writing"),
454 m.locationFull.raw());
455 break;
456 }
457 }
458
459 /* return the current state after */
460 if (aState)
461 *aState = m.state;
462
463 return rc;
464}
465
466STDMETHODIMP MediumBase::Close()
467{
468 AutoMayUninitSpan mayUninitSpan (this);
469 CheckComRCReturnRC (mayUninitSpan.rc());
470
471 if (mayUninitSpan.alreadyInProgress())
472 return S_OK;
473
474 /* unregisterWithVirtualBox() is assumed to always need a write mVirtualBox
475 * lock as it is intenede to modify its internal structires. Also, we want
476 * to unregister ourselves atomically after detecting that closure is
477 * possible to make sure that we don't do that after another thread has done
478 * VirtualBox::find*() but before it starts using us (provided that it holds
479 * a mVirtualBox lock of course). */
480
481 AutoWriteLock vboxLock (mVirtualBox);
482
483 bool wasCreated = true;
484
485 switch (m.state)
486 {
487 case MediaState_NotCreated:
488 wasCreated = false;
489 break;
490 case MediaState_Created:
491 case MediaState_Inaccessible:
492 break;
493 default:
494 return setStateError();
495 }
496
497 if (m.backRefs.size() != 0)
498 return setError (VBOX_E_OBJECT_IN_USE,
499 tr ("Medium '%ls' is attached to %d virtual machines"),
500 m.locationFull.raw(), m.backRefs.size());
501
502 /* perform extra media-dependent close checks */
503 HRESULT rc = canClose();
504 CheckComRCReturnRC (rc);
505
506 if (wasCreated)
507 {
508 /* remove from the list of known media before performing actual
509 * uninitialization (to keep the media registry consistent on
510 * failure to do so) */
511 rc = unregisterWithVirtualBox();
512 CheckComRCReturnRC (rc);
513 }
514
515 /* cause uninit() to happen on success */
516 mayUninitSpan.acceptUninit();
517
518 return S_OK;
519}
520
521// public methods for internal purposes only
522////////////////////////////////////////////////////////////////////////////////
523
524/**
525 * Checks if the given change of \a aOldPath to \a aNewPath affects the location
526 * of this media and updates it if necessary to reflect the new location.
527 *
528 * @param aOldPath Old path (full).
529 * @param aNewPath New path (full).
530 *
531 * @note Locks this object for writing.
532 */
533HRESULT MediumBase::updatePath (const char *aOldPath, const char *aNewPath)
534{
535 AssertReturn (aOldPath, E_FAIL);
536 AssertReturn (aNewPath, E_FAIL);
537
538 AutoCaller autoCaller (this);
539 CheckComRCReturnRC (autoCaller.rc());
540
541 AutoWriteLock alock (this);
542
543 LogFlowThisFunc (("locationFull.before='%s'\n", m.locationFull.raw()));
544
545 Utf8Str path = m.locationFull;
546
547 if (RTPathStartsWith (path, aOldPath))
548 {
549 Utf8Str newPath = Utf8StrFmt ("%s%s", aNewPath,
550 path.raw() + strlen (aOldPath));
551 path = newPath;
552
553 mVirtualBox->calculateRelativePath (path, path);
554
555 unconst (m.locationFull) = newPath;
556 unconst (m.location) = path;
557
558 LogFlowThisFunc (("locationFull.after='%s'\n", m.locationFull.raw()));
559 }
560
561 return S_OK;
562}
563
564/**
565 * Adds the given machine and optionally the snapshot to the list of the objects
566 * this image is attached to.
567 *
568 * @param aMachineId Machine ID.
569 * @param aSnapshotId Snapshot ID; when non-empty, adds a snapshot attachment.
570 */
571HRESULT MediumBase::attachTo (const Guid &aMachineId,
572 const Guid &aSnapshotId /*= Guid::Empty*/)
573{
574 AssertReturn (!aMachineId.isEmpty(), E_FAIL);
575
576 AutoCaller autoCaller (this);
577 AssertComRCReturnRC (autoCaller.rc());
578
579 AutoWriteLock alock (this);
580
581 switch (m.state)
582 {
583 case MediaState_Created:
584 case MediaState_Inaccessible:
585 case MediaState_LockedRead:
586 case MediaState_LockedWrite:
587 break;
588
589 default:
590 return setStateError();
591 }
592
593 HRESULT rc = canAttach (aMachineId, aSnapshotId);
594 CheckComRCReturnRC (rc);
595
596 BackRefList::iterator it =
597 std::find_if (m.backRefs.begin(), m.backRefs.end(),
598 BackRef::EqualsTo (aMachineId));
599 if (it == m.backRefs.end())
600 {
601 BackRef ref (aMachineId, aSnapshotId);
602 m.backRefs.push_back (ref);
603
604 return S_OK;
605 }
606
607 if (aSnapshotId.isEmpty())
608 {
609 /* sanity: no duplicate attachments */
610 AssertReturn (!it->inCurState, E_FAIL);
611 it->inCurState = true;
612
613 return S_OK;
614 }
615
616 /* sanity: no duplicate attachments */
617 BackRef::GuidList::const_iterator jt =
618 std::find (it->snapshotIds.begin(), it->snapshotIds.end(), aSnapshotId);
619 AssertReturn (jt == it->snapshotIds.end(), E_FAIL);
620
621 it->snapshotIds.push_back (aSnapshotId);
622
623 return S_OK;
624}
625
626/**
627 * Removes the given machine and optionally the snapshot from the list of the
628 * objects this image is attached to.
629 *
630 * @param aMachineId Machine ID.
631 * @param aSnapshotId Snapshot ID; when non-empty, removes the snapshot
632 * attachment.
633 */
634HRESULT MediumBase::detachFrom (const Guid &aMachineId,
635 const Guid &aSnapshotId /*= Guid::Empty*/)
636{
637 AssertReturn (!aMachineId.isEmpty(), E_FAIL);
638
639 AutoCaller autoCaller (this);
640 AssertComRCReturnRC (autoCaller.rc());
641
642 AutoWriteLock alock (this);
643
644 BackRefList::iterator it =
645 std::find_if (m.backRefs.begin(), m.backRefs.end(),
646 BackRef::EqualsTo (aMachineId));
647 AssertReturn (it != m.backRefs.end(), E_FAIL);
648
649 if (aSnapshotId.isEmpty())
650 {
651 /* remove the current state attachment */
652 it->inCurState = false;
653 }
654 else
655 {
656 /* remove the snapshot attachment */
657 BackRef::GuidList::iterator jt =
658 std::find (it->snapshotIds.begin(), it->snapshotIds.end(), aSnapshotId);
659
660 AssertReturn (jt != it->snapshotIds.end(), E_FAIL);
661 it->snapshotIds.erase (jt);
662 }
663
664 /* if the backref becomes empty, remove it */
665 if (it->inCurState == false && it->snapshotIds.size() == 0)
666 m.backRefs.erase (it);
667
668 return S_OK;
669}
670
671// protected methods
672////////////////////////////////////////////////////////////////////////////////
673
674/**
675 * Returns a short version of the location attribute.
676 *
677 * @note Must be called from under this object's read or write lock.
678 */
679Utf8Str MediumBase::name()
680{
681 Utf8Str location (m.locationFull);
682
683 Utf8Str name = RTPathFilename (location);
684 return name;
685}
686
687/**
688 * Sets the value of m.location and calculates the value of m.locationFull.
689 *
690 * @param aLocation Path to the image file (can be relative to the
691 * VirtualBox home directory).
692 *
693 * @note Must be called from under this object's write lock.
694 */
695HRESULT MediumBase::setLocation (CBSTR aLocation)
696{
697 /* get the full file name */
698 Utf8Str locationFull;
699 int vrc = mVirtualBox->calculateFullPath (Utf8Str (aLocation), locationFull);
700 if (RT_FAILURE (vrc))
701 return setError (E_FAIL,
702 tr ("Invalid image file location '%ls' (%Rrc)"),
703 aLocation, vrc);
704
705 m.location = aLocation;
706 m.locationFull = locationFull;
707
708 return S_OK;
709}
710
711/**
712 * Queries information from the image file.
713 *
714 * As a result of this call, the accessibility state and data members such as
715 * size and description will be updated with the current information.
716 *
717 * @note This method may block during a system I/O call that checks image file
718 * accessibility.
719 *
720 * @note Locks this object for writing.
721 */
722HRESULT MediumBase::queryInfo()
723{
724 AutoWriteLock alock (this);
725
726 AssertReturn (m.state == MediaState_Created ||
727 m.state == MediaState_Inaccessible ||
728 m.state == MediaState_LockedRead ||
729 m.state == MediaState_LockedWrite,
730 E_FAIL);
731
732 int vrc = VINF_SUCCESS;
733
734 /* check if a blocking queryInfo() call is in progress on some other thread,
735 * and wait for it to finish if so instead of querying data ourselves */
736 if (m.queryInfoSem != NIL_RTSEMEVENTMULTI)
737 {
738 Assert (m.state == MediaState_LockedRead);
739
740 ++ m.queryInfoCallers;
741 alock.leave();
742
743 vrc = RTSemEventMultiWait (m.queryInfoSem, RT_INDEFINITE_WAIT);
744
745 alock.enter();
746 -- m.queryInfoCallers;
747
748 if (m.queryInfoCallers == 0)
749 {
750 /* last waiting caller deletes the semaphore */
751 RTSemEventMultiDestroy (m.queryInfoSem);
752 m.queryInfoSem = NIL_RTSEMEVENTMULTI;
753 }
754
755 AssertRC (vrc);
756
757 return S_OK;
758 }
759
760 /* lazily create a semaphore for possible callers */
761 vrc = RTSemEventMultiCreate (&m.queryInfoSem);
762 ComAssertRCRet (vrc, E_FAIL);
763
764 bool tempStateSet = false;
765 if (m.state != MediaState_LockedRead &&
766 m.state != MediaState_LockedWrite)
767 {
768 /* Cause other methods to prevent any modifications before leaving the
769 * lock. Note that clients will never see this temporary state change
770 * directly since any COMGETTER(State) is (or will be) blocked until we
771 * finish and restore the actual state. This may be seen only through
772 * error messages reported by other methods. */
773 m.state = MediaState_LockedRead;
774 tempStateSet = true;
775 }
776
777 /* leave the lock before a blocking operation */
778 alock.leave();
779
780 bool success = false;
781
782 /* get image file info */
783 {
784 RTFILE file;
785 vrc = RTFileOpen (&file, Utf8Str (m.locationFull), RTFILE_O_READ);
786 if (RT_SUCCESS (vrc))
787 {
788 vrc = RTFileGetSize (file, &m.size);
789
790 RTFileClose (file);
791 }
792
793 if (RT_FAILURE (vrc))
794 {
795 m.lastAccessError = Utf8StrFmt (
796 tr ("Could not access the image file '%ls' (%Rrc)"),
797 m.locationFull.raw(), vrc);
798 }
799
800 success = (RT_SUCCESS (vrc));
801 }
802
803 alock.enter();
804
805 if (success)
806 m.lastAccessError.setNull();
807 else
808 LogWarningFunc (("'%ls' is not accessible (error='%ls', vrc=%Rrc)\n",
809 m.locationFull.raw(), m.lastAccessError.raw(), vrc));
810
811 /* inform other callers if there are any */
812 if (m.queryInfoCallers > 0)
813 {
814 RTSemEventMultiSignal (m.queryInfoSem);
815 }
816 else
817 {
818 /* delete the semaphore ourselves */
819 RTSemEventMultiDestroy (m.queryInfoSem);
820 m.queryInfoSem = NIL_RTSEMEVENTMULTI;
821 }
822
823 if (tempStateSet)
824 {
825 /* Set the proper state according to the result of the check */
826 if (success)
827 m.state = MediaState_Created;
828 else
829 m.state = MediaState_Inaccessible;
830 }
831 else
832 {
833 /* we're locked, use a special field to store the result */
834 m.accessibleInLock = success;
835 }
836
837 return S_OK;
838}
839
840/**
841 * Sets the extended error info according to the current media state.
842 *
843 * @note Must be called from under this object's write or read lock.
844 */
845HRESULT MediumBase::setStateError()
846{
847 HRESULT rc = E_FAIL;
848
849 switch (m.state)
850 {
851 case MediaState_NotCreated:
852 {
853 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
854 tr ("Storage for the medium '%ls' is not created"),
855 m.locationFull.raw());
856 break;
857 }
858 case MediaState_Created:
859 {
860 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
861 tr ("Storage for the medium '%ls' is already created"),
862 m.locationFull.raw());
863 break;
864 }
865 case MediaState_LockedRead:
866 {
867 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
868 tr ("Medium '%ls' is locked for reading by another task"),
869 m.locationFull.raw());
870 break;
871 }
872 case MediaState_LockedWrite:
873 {
874 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
875 tr ("Medium '%ls' is locked for writing by another task"),
876 m.locationFull.raw());
877 break;
878 }
879 case MediaState_Inaccessible:
880 {
881 AssertMsg (!m.lastAccessError.isEmpty(),
882 ("There must always be a reason for Inaccessible"));
883
884 /* be in sync with Console::powerUpThread() */
885 if (!m.lastAccessError.isEmpty())
886 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
887 tr ("Medium '%ls' is not accessible. %ls"),
888 m.locationFull.raw(), m.lastAccessError.raw());
889 else
890 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
891 tr ("Medium '%ls' is not accessible"),
892 m.locationFull.raw());
893 break;
894 }
895 case MediaState_Creating:
896 {
897 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
898 tr ("Storage for the medium '%ls' is being created"),
899 m.locationFull.raw(), m.lastAccessError.raw());
900 break;
901 }
902 case MediaState_Deleting:
903 {
904 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
905 tr ("Storage for the medium '%ls' is being deleted"),
906 m.locationFull.raw(), m.lastAccessError.raw());
907 break;
908 }
909 default:
910 {
911 AssertFailed();
912 break;
913 }
914 }
915
916 return rc;
917}
918
919////////////////////////////////////////////////////////////////////////////////
920// ImageMediumBase class
921////////////////////////////////////////////////////////////////////////////////
922
923/**
924 * Initializes the image medium object by opening an image file at the specified
925 * location.
926 *
927 * @param aVirtualBox Parent VirtualBox object.
928 * @param aLocation Path to the image file (can be relative to the
929 * VirtualBox home directory).
930 * @param aId UUID of the image.
931 */
932HRESULT ImageMediumBase::protectedInit (VirtualBox *aVirtualBox, CBSTR aLocation,
933 const Guid &aId)
934{
935 LogFlowThisFunc (("aLocation='%ls', aId={%RTuuid}\n", aLocation, aId.raw()));
936
937 AssertReturn (aVirtualBox, E_INVALIDARG);
938 AssertReturn (aLocation, E_INVALIDARG);
939 AssertReturn (!aId.isEmpty(), E_INVALIDARG);
940
941 /* Enclose the state transition NotReady->InInit->Ready */
942 AutoInitSpan autoInitSpan (this);
943 AssertReturn (autoInitSpan.isOk(), E_FAIL);
944
945 HRESULT rc = S_OK;
946
947 /* share parent weakly */
948 unconst (mVirtualBox) = aVirtualBox;
949
950 /* register with parent early, since uninit() will unconditionally
951 * unregister on failure */
952 mVirtualBox->addDependentChild (this);
953
954 /* there must be a storage unit */
955 m.state = MediaState_Created;
956
957 unconst (m.id) = aId;
958 rc = setLocation (aLocation);
959 CheckComRCReturnRC (rc);
960
961 LogFlowThisFunc (("m.locationFull='%ls'\n", m.locationFull.raw()));
962
963 /* get all the information about the medium from the file */
964 rc = queryInfo();
965
966 if (SUCCEEDED(rc))
967 {
968 /* if the image file is not accessible, it's not acceptable for the
969 * newly opened media so convert this into an error */
970 if (!m.lastAccessError.isNull())
971 rc = setError (E_FAIL, Utf8Str (m.lastAccessError));
972 }
973
974 /* Confirm a successful initialization when it's the case */
975 if (SUCCEEDED (rc))
976 autoInitSpan.setSucceeded();
977
978 return rc;
979}
980
981/**
982 * Initializes the image medium object by loading its data from the given
983 * settings node.
984 *
985 * Note that it is assumed that this method is called only for registered media.
986 *
987 * @param aVirtualBox Parent VirtualBox object.
988 * @param aImageNode Either <DVDImage> or <FloppyImage> settings node.
989 */
990HRESULT ImageMediumBase::protectedInit (VirtualBox *aVirtualBox,
991 const settings::Key &aImageNode)
992{
993 AssertReturn (aVirtualBox, E_INVALIDARG);
994
995 /* Enclose the state transition NotReady->InInit->Ready */
996 AutoInitSpan autoInitSpan (this);
997 AssertReturn (autoInitSpan.isOk(), E_FAIL);
998
999 HRESULT rc = S_OK;
1000
1001 /* share parent weakly */
1002 unconst (mVirtualBox) = aVirtualBox;
1003
1004 /* register with parent early, since uninit() will unconditionally
1005 * unregister on failure */
1006 mVirtualBox->addDependentChild (this);
1007
1008 /* see below why we don't call queryInfo() (and therefore treat the medium
1009 * as inaccessible for now */
1010 m.state = MediaState_Inaccessible;
1011
1012 /* required */
1013 unconst (m.id) = aImageNode.value <Guid> ("uuid");
1014 /* required */
1015 Bstr location = aImageNode.stringValue ("location");
1016 rc = setLocation (location);
1017 CheckComRCReturnRC (rc);
1018 /* optional */
1019 {
1020 settings::Key descNode = aImageNode.findKey ("Description");
1021 if (!descNode.isNull())
1022 m.description = descNode.keyStringValue();
1023 }
1024
1025 LogFlowThisFunc (("m.locationFull='%ls', m.id={%RTuuid}\n",
1026 m.locationFull.raw(), m.id.raw()));
1027
1028 /* Don't call queryInfo() for registered media to prevent the calling
1029 * thread (i.e. the VirtualBox server startup thread) from an unexpected
1030 * freeze but mark it as initially inaccessible instead. The vital UUID and
1031 * location properties are read from the registry file above; to get the
1032 * actual state and the rest of the data, the user will have to call
1033 * COMGETTER(State).*/
1034
1035 /* Confirm a successful initialization when it's the case */
1036 if (SUCCEEDED (rc))
1037 autoInitSpan.setSucceeded();
1038
1039 return rc;
1040}
1041
1042/**
1043 * Uninitializes the instance.
1044 *
1045 * Called either from FinalRelease() or by the parent when it gets destroyed.
1046 */
1047void ImageMediumBase::protectedUninit()
1048{
1049 LogFlowThisFunc (("\n"));
1050
1051 /* Enclose the state transition Ready->InUninit->NotReady */
1052 AutoUninitSpan autoUninitSpan (this);
1053 if (autoUninitSpan.uninitDone())
1054 return;
1055
1056 mVirtualBox->removeDependentChild (this);
1057
1058 unconst (mVirtualBox).setNull();
1059}
1060
1061// public methods for internal purposes only
1062////////////////////////////////////////////////////////////////////////////////
1063
1064/**
1065 * Saves image data by appending a new <Image> child node to the
1066 * given <Images> parent node.
1067 *
1068 * @param aImagesNode <Images> node.
1069 *
1070 * @note Locks this object for reading.
1071 */
1072HRESULT ImageMediumBase::saveSettings (settings::Key &aImagesNode)
1073{
1074 using namespace settings;
1075
1076 AssertReturn (!aImagesNode.isNull(), E_FAIL);
1077
1078 AutoCaller autoCaller (this);
1079 CheckComRCReturnRC (autoCaller.rc());
1080
1081 AutoReadLock alock (this);
1082
1083 Key imageNode = aImagesNode.appendKey ("Image");
1084 /* required */
1085 imageNode.setValue <Guid> ("uuid", m.id);
1086 /* required */
1087 imageNode.setValue <Bstr> ("location", m.locationFull);
1088 /* optional */
1089 if (!m.description.isNull())
1090 {
1091 Key descNode = aImagesNode.createKey ("Description");
1092 descNode.setKeyValue <Bstr> (m.description);
1093 }
1094
1095 return S_OK;
1096}
1097
1098////////////////////////////////////////////////////////////////////////////////
1099// DVDImage class
1100////////////////////////////////////////////////////////////////////////////////
1101
1102DEFINE_EMPTY_CTOR_DTOR (DVDImage)
1103
1104/**
1105 * @note Called from within this object's AutoMayUninitSpan and from under
1106 * mVirtualBox write lock.
1107 */
1108HRESULT DVDImage::unregisterWithVirtualBox()
1109{
1110 return mVirtualBox->unregisterDVDImage (this);
1111}
1112
1113////////////////////////////////////////////////////////////////////////////////
1114// FloppyImage class
1115////////////////////////////////////////////////////////////////////////////////
1116
1117DEFINE_EMPTY_CTOR_DTOR (FloppyImage)
1118
1119/**
1120 * @note Called from within this object's AutoMayUninitSpan and from under
1121 * mVirtualBox write lock.
1122 */
1123HRESULT FloppyImage::unregisterWithVirtualBox()
1124{
1125 return mVirtualBox->unregisterFloppyImage (this);
1126}
1127/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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