VirtualBox

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

Last change on this file since 22309 was 22173, checked in by vboxsync, 15 years ago

Main: the big XML settings rework. Move XML reading/writing out of interface implementation code into separate layer so it can handle individual settings versions in the future.

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