VirtualBox

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

Last change on this file since 20961 was 19239, checked in by vboxsync, 16 years ago

Main: support for using VBox from Python on Windows (still certain limitation apply, such as enum visibility)

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