VirtualBox

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

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

Main: Cleaned up the long standing const BSTR = const (OLECHAR *) on WIn32 vs (const PRunichar) * on XPCOM clash. Cleaned up BSTR/GUID macros (IN_BSTR replaces INPTR BSTR, IN_GUID replaces INPTR GUIDPARAM, OUT_GUID replaces GUIDPARAMOUT).

  • 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 15051 2008-12-05 17:20:00Z 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) (OUT_GUID aId)
54{
55 CheckComArgOutPointerValid(aId);
56
57 AutoCaller autoCaller (this);
58 CheckComRCReturnRC (autoCaller.rc());
59
60 AutoReadLock alock (this);
61
62 m.id.cloneTo (aId);
63
64 return S_OK;
65}
66
67STDMETHODIMP MediumBase::COMGETTER(Description) (BSTR *aDescription)
68{
69 CheckComArgOutPointerValid(aDescription);
70
71 AutoCaller autoCaller (this);
72 CheckComRCReturnRC (autoCaller.rc());
73
74 AutoReadLock alock (this);
75
76 m.description.cloneTo (aDescription);
77
78 return S_OK;
79}
80
81STDMETHODIMP MediumBase::COMSETTER(Description) (IN_BSTR aDescription)
82{
83 CheckComArgNotNull(aDescription);
84
85 AutoCaller autoCaller (this);
86 CheckComRCReturnRC (autoCaller.rc());
87
88 AutoWriteLock alock (this);
89
90 /// @todo update m.description and save the global registry (and local
91 /// registries of portable VMs referring to this medium), this will also
92 /// require to add the mRegistered flag to data
93
94 ReturnComNotImplemented();
95}
96
97STDMETHODIMP MediumBase::COMGETTER(State) (MediaState_T *aState)
98{
99 CheckComArgOutPointerValid(aState);
100
101 AutoCaller autoCaller (this);
102 CheckComRCReturnRC (autoCaller.rc());
103
104 /* queryInfo() locks this for writing. */
105 AutoWriteLock alock (this);
106
107 HRESULT rc = S_OK;
108
109 switch (m.state)
110 {
111 case MediaState_Created:
112 case MediaState_Inaccessible:
113 case MediaState_LockedRead:
114 case MediaState_LockedWrite:
115 {
116 rc = queryInfo();
117 break;
118 }
119 default:
120 break;
121 }
122
123 *aState = m.state;
124
125 return rc;
126}
127
128STDMETHODIMP MediumBase::COMGETTER(Location) (BSTR *aLocation)
129{
130 CheckComArgOutPointerValid(aLocation);
131
132 AutoCaller autoCaller (this);
133 CheckComRCReturnRC (autoCaller.rc());
134
135 AutoReadLock alock (this);
136
137 m.locationFull.cloneTo (aLocation);
138
139 return S_OK;
140}
141
142STDMETHODIMP MediumBase::COMSETTER(Location) (IN_BSTR aLocation)
143{
144 CheckComArgNotNull(aLocation);
145
146 AutoCaller autoCaller (this);
147 CheckComRCReturnRC (autoCaller.rc());
148
149 AutoWriteLock alock (this);
150
151 /// @todo NEWMEDIA for file names, add the default extension if no extension
152 /// is present (using the information from the VD backend which also implies
153 /// that one more parameter should be passed to setLocation() requesting
154 /// that functionality since it is only allwed when called from this method
155
156 /// @todo NEWMEDIA rename the file and set m.location on success, then save
157 /// the global registry (and local registries of portable VMs referring to
158 /// this medium), this will also require to add the mRegistered flag to data
159
160 ReturnComNotImplemented();
161}
162
163STDMETHODIMP MediumBase::COMGETTER(Name) (BSTR *aName)
164{
165 CheckComArgOutPointerValid(aName);
166
167 AutoCaller autoCaller (this);
168 CheckComRCReturnRC (autoCaller.rc());
169
170 AutoReadLock alock (this);
171
172 name().cloneTo (aName);
173
174 return S_OK;
175}
176
177STDMETHODIMP MediumBase::COMGETTER(Size) (ULONG64 *aSize)
178{
179 CheckComArgOutPointerValid(aSize);
180
181 AutoCaller autoCaller (this);
182 CheckComRCReturnRC (autoCaller.rc());
183
184 AutoReadLock alock (this);
185
186 *aSize = m.size;
187
188 return S_OK;
189}
190
191STDMETHODIMP MediumBase::COMGETTER(LastAccessError) (BSTR *aLastAccessError)
192{
193 CheckComArgOutPointerValid(aLastAccessError);
194
195 AutoCaller autoCaller (this);
196 CheckComRCReturnRC (autoCaller.rc());
197
198 AutoReadLock alock (this);
199
200 m.lastAccessError.cloneTo (aLastAccessError);
201
202 return S_OK;
203}
204
205STDMETHODIMP MediumBase::COMGETTER(MachineIds) (ComSafeGUIDArrayOut (aMachineIds))
206{
207 if (ComSafeGUIDArrayOutIsNull (aMachineIds))
208 return E_POINTER;
209
210 AutoCaller autoCaller (this);
211 CheckComRCReturnRC (autoCaller.rc());
212
213 AutoReadLock alock (this);
214
215 com::SafeGUIDArray machineIds;
216
217 if (m.backRefs.size() != 0)
218 {
219 machineIds.reset (m.backRefs.size());
220
221 size_t i = 0;
222 for (BackRefList::const_iterator it = m.backRefs.begin();
223 it != m.backRefs.end(); ++ it, ++ i)
224 {
225 machineIds [i] = it->machineId;
226 }
227 }
228
229 machineIds.detachTo (ComSafeGUIDArrayOutArg (aMachineIds));
230
231 return S_OK;
232}
233
234// IMedium methods
235////////////////////////////////////////////////////////////////////////////////
236
237STDMETHODIMP MediumBase::GetSnapshotIds (IN_GUID aMachineId,
238 ComSafeGUIDArrayOut (aSnapshotIds))
239{
240 if (Guid (aMachineId).isEmpty())
241 return E_INVALIDARG;
242 if (ComSafeGUIDArrayOutIsNull (aSnapshotIds))
243 return E_POINTER;
244
245 AutoCaller autoCaller (this);
246 CheckComRCReturnRC (autoCaller.rc());
247
248 AutoReadLock alock (this);
249
250 com::SafeGUIDArray snapshotIds;
251
252 for (BackRefList::const_iterator it = m.backRefs.begin();
253 it != m.backRefs.end(); ++ it)
254 {
255 if (it->machineId == aMachineId)
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 snapshotIds [j ++] = it->machineId;
271
272 for (BackRef::GuidList::const_iterator jt =
273 it->snapshotIds.begin();
274 jt != it->snapshotIds.end(); ++ jt, ++ j)
275 {
276 snapshotIds [j] = *jt;
277 }
278 }
279
280 break;
281 }
282 }
283
284 snapshotIds.detachTo (ComSafeGUIDArrayOutArg (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 (E_FAIL,
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 (E_FAIL,
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 (E_FAIL,
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
809 /* inform other callers if there are any */
810 if (m.queryInfoCallers > 0)
811 {
812 RTSemEventMultiSignal (m.queryInfoSem);
813 }
814 else
815 {
816 /* delete the semaphore ourselves */
817 RTSemEventMultiDestroy (m.queryInfoSem);
818 m.queryInfoSem = NIL_RTSEMEVENTMULTI;
819 }
820
821 if (tempStateSet)
822 {
823 /* Set the proper state according to the result of the check */
824 if (success)
825 {
826 m.state = MediaState_Created;
827 }
828 else
829 {
830 m.state = MediaState_Inaccessible;
831 LogWarningFunc (("'%ls' is not accessible ('%ls')\n",
832 m.locationFull.raw(), m.lastAccessError.raw()));
833 }
834 }
835 else
836 {
837 /* we're locked, use a special field to store the result */
838 m.accessibleInLock = success;
839 }
840
841 return S_OK;
842}
843
844/**
845 * Sets the extended error info according to the current media state.
846 *
847 * @note Must be called from under this object's write or read lock.
848 */
849HRESULT MediumBase::setStateError()
850{
851 HRESULT rc = E_FAIL;
852
853 switch (m.state)
854 {
855 case MediaState_NotCreated:
856 {
857 rc = setError (E_FAIL,
858 tr ("Storage for the medium '%ls' is not created"),
859 m.locationFull.raw());
860 break;
861 }
862 case MediaState_Created:
863 {
864 rc = setError (E_FAIL,
865 tr ("Storage for the medium '%ls' is already created"),
866 m.locationFull.raw());
867 break;
868 }
869 case MediaState_LockedRead:
870 {
871 rc = setError (E_FAIL,
872 tr ("Medium '%ls' is locked for reading by another task"),
873 m.locationFull.raw());
874 break;
875 }
876 case MediaState_LockedWrite:
877 {
878 rc = setError (E_FAIL,
879 tr ("Medium '%ls' is locked for writing by another task"),
880 m.locationFull.raw());
881 break;
882 }
883 case MediaState_Inaccessible:
884 {
885 /* be in sync with Console::powerUpThread() */
886 if (!m.lastAccessError.isEmpty())
887 rc = setError (E_FAIL,
888 tr ("Medium '%ls' is not accessible. %ls"),
889 m.locationFull.raw(), m.lastAccessError.raw());
890 else
891 rc = setError (E_FAIL,
892 tr ("Medium '%ls' is not accessible"),
893 m.locationFull.raw());
894 break;
895 }
896 case MediaState_Creating:
897 {
898 rc = setError (E_FAIL,
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 (E_FAIL,
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 if (SUCCEEDED (rc))
967 {
968 /* if the image file is not accessible, it's not acceptable for the
969 * newly opened media so convert this into an error */
970 if (!m.lastAccessError.isNull())
971 rc = setError (E_FAIL, Utf8Str (m.lastAccessError));
972 }
973
974 /* Confirm a successful initialization when it's the case */
975 if (SUCCEEDED (rc))
976 autoInitSpan.setSucceeded();
977
978 return rc;
979}
980
981/**
982 * Initializes the image medium object by loading its data from the given
983 * settings node.
984 *
985 * Note that it is assumed that this method is called only for registered media.
986 *
987 * @param aVirtualBox Parent VirtualBox object.
988 * @param aImageNode Either <DVDImage> or <FloppyImage> settings node.
989 */
990HRESULT ImageMediumBase::protectedInit (VirtualBox *aVirtualBox,
991 const settings::Key &aImageNode)
992{
993 AssertReturn (aVirtualBox, E_INVALIDARG);
994
995 /* Enclose the state transition NotReady->InInit->Ready */
996 AutoInitSpan autoInitSpan (this);
997 AssertReturn (autoInitSpan.isOk(), E_FAIL);
998
999 HRESULT rc = S_OK;
1000
1001 /* share parent weakly */
1002 unconst (mVirtualBox) = aVirtualBox;
1003
1004 /* register with parent early, since uninit() will unconditionally
1005 * unregister on failure */
1006 mVirtualBox->addDependentChild (this);
1007
1008 /* see below why we don't call queryInfo() (and therefore treat the medium
1009 * as inaccessible for now */
1010 m.state = MediaState_Inaccessible;
1011
1012 /* required */
1013 unconst (m.id) = aImageNode.value <Guid> ("uuid");
1014 /* required */
1015 Bstr location = aImageNode.stringValue ("location");
1016 rc = setLocation (location);
1017 CheckComRCReturnRC (rc);
1018 /* optional */
1019 {
1020 settings::Key descNode = aImageNode.findKey ("Description");
1021 if (!descNode.isNull())
1022 m.description = descNode.keyStringValue();
1023 }
1024
1025 LogFlowThisFunc (("m.location='%ls', m.id={%RTuuid}\n",
1026 m.location.raw(), m.id.raw()));
1027 LogFlowThisFunc (("m.locationFull='%ls'\n", m.locationFull.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// DVDImage2 class
1101////////////////////////////////////////////////////////////////////////////////
1102
1103DEFINE_EMPTY_CTOR_DTOR (DVDImage2)
1104
1105/**
1106 * @note Called from within this object's AutoMayUninitSpan and from under
1107 * mVirtualBox write lock.
1108 */
1109HRESULT DVDImage2::unregisterWithVirtualBox()
1110{
1111 return mVirtualBox->unregisterDVDImage (this);
1112}
1113
1114////////////////////////////////////////////////////////////////////////////////
1115// FloppyImage2 class
1116////////////////////////////////////////////////////////////////////////////////
1117
1118DEFINE_EMPTY_CTOR_DTOR (FloppyImage2)
1119
1120/**
1121 * @note Called from within this object's AutoMayUninitSpan and from under
1122 * mVirtualBox write lock.
1123 */
1124HRESULT FloppyImage2::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