VirtualBox

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

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

Added vim modelines to aid following coding guidelines, like no tabs,
similar to what is already in the xidl file.

  • 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 14772 2008-11-28 12:41:22Z 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 ReturnComNotImplemented();
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 ReturnComNotImplemented();
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/* 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