VirtualBox

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

Last change on this file since 14683 was 14579, checked in by vboxsync, 16 years ago

Main: VirtualBoxBase::addCaller() now returns E_ACCESSDENIED. Also replaced E_UNEXPECTED with E_FAIL in all Assert* statements (for consistency).

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