VirtualBox

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

Last change on this file since 21496 was 20977, checked in by vboxsync, 15 years ago

API: weed out NULL strings, as many clients cannot use them

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 30.6 KB
Line 
1/* $Id: MediumImpl.cpp 20977 2009-06-26 14:38:55Z 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, 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);
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 (CBSTR aLocation)
703{
704 /* get the full file name */
705 Utf8Str locationFull;
706 int vrc = mVirtualBox->calculateFullPath (Utf8Str (aLocation), locationFull);
707 if (RT_FAILURE (vrc))
708 return setError (E_FAIL,
709 tr ("Invalid image file location '%ls' (%Rrc)"),
710 aLocation, vrc);
711
712 m.location = aLocation;
713 m.locationFull = locationFull;
714
715 return S_OK;
716}
717
718/**
719 * Queries information from the image file.
720 *
721 * As a result of this call, the accessibility state and data members such as
722 * size and description will be updated with the current information.
723 *
724 * @note This method may block during a system I/O call that checks image file
725 * accessibility.
726 *
727 * @note Locks this object for writing.
728 */
729HRESULT MediumBase::queryInfo()
730{
731 AutoWriteLock alock (this);
732
733 AssertReturn (m.state == MediaState_Created ||
734 m.state == MediaState_Inaccessible ||
735 m.state == MediaState_LockedRead ||
736 m.state == MediaState_LockedWrite,
737 E_FAIL);
738
739 int vrc = VINF_SUCCESS;
740
741 /* check if a blocking queryInfo() call is in progress on some other thread,
742 * and wait for it to finish if so instead of querying data ourselves */
743 if (m.queryInfoSem != NIL_RTSEMEVENTMULTI)
744 {
745 Assert (m.state == MediaState_LockedRead);
746
747 ++ m.queryInfoCallers;
748 alock.leave();
749
750 vrc = RTSemEventMultiWait (m.queryInfoSem, RT_INDEFINITE_WAIT);
751
752 alock.enter();
753 -- m.queryInfoCallers;
754
755 if (m.queryInfoCallers == 0)
756 {
757 /* last waiting caller deletes the semaphore */
758 RTSemEventMultiDestroy (m.queryInfoSem);
759 m.queryInfoSem = NIL_RTSEMEVENTMULTI;
760 }
761
762 AssertRC (vrc);
763
764 return S_OK;
765 }
766
767 /* lazily create a semaphore for possible callers */
768 vrc = RTSemEventMultiCreate (&m.queryInfoSem);
769 ComAssertRCRet (vrc, E_FAIL);
770
771 bool tempStateSet = false;
772 if (m.state != MediaState_LockedRead &&
773 m.state != MediaState_LockedWrite)
774 {
775 /* Cause other methods to prevent any modifications before leaving the
776 * lock. Note that clients will never see this temporary state change
777 * directly since any COMGETTER(State) is (or will be) blocked until we
778 * finish and restore the actual state. This may be seen only through
779 * error messages reported by other methods. */
780 m.state = MediaState_LockedRead;
781 tempStateSet = true;
782 }
783
784 /* leave the lock before a blocking operation */
785 alock.leave();
786
787 bool success = false;
788
789 /* get image file info */
790 {
791 RTFILE file;
792 vrc = RTFileOpen (&file, Utf8Str (m.locationFull), RTFILE_O_READ);
793 if (RT_SUCCESS (vrc))
794 {
795 vrc = RTFileGetSize (file, &m.size);
796
797 RTFileClose (file);
798 }
799
800 if (RT_FAILURE (vrc))
801 {
802 m.lastAccessError = Utf8StrFmt (
803 tr ("Could not access the image file '%ls' (%Rrc)"),
804 m.locationFull.raw(), vrc);
805 }
806
807 success = (RT_SUCCESS (vrc));
808 }
809
810 alock.enter();
811
812 if (success)
813 m.lastAccessError.setNull();
814 else
815 LogWarningFunc (("'%ls' is not accessible (error='%ls', vrc=%Rrc)\n",
816 m.locationFull.raw(), m.lastAccessError.raw(), vrc));
817
818 /* inform other callers if there are any */
819 if (m.queryInfoCallers > 0)
820 {
821 RTSemEventMultiSignal (m.queryInfoSem);
822 }
823 else
824 {
825 /* delete the semaphore ourselves */
826 RTSemEventMultiDestroy (m.queryInfoSem);
827 m.queryInfoSem = NIL_RTSEMEVENTMULTI;
828 }
829
830 if (tempStateSet)
831 {
832 /* Set the proper state according to the result of the check */
833 if (success)
834 m.state = MediaState_Created;
835 else
836 m.state = MediaState_Inaccessible;
837 }
838 else
839 {
840 /* we're locked, use a special field to store the result */
841 m.accessibleInLock = success;
842 }
843
844 return S_OK;
845}
846
847/**
848 * Sets the extended error info according to the current media state.
849 *
850 * @note Must be called from under this object's write or read lock.
851 */
852HRESULT MediumBase::setStateError()
853{
854 HRESULT rc = E_FAIL;
855
856 switch (m.state)
857 {
858 case MediaState_NotCreated:
859 {
860 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
861 tr ("Storage for the medium '%ls' is not created"),
862 m.locationFull.raw());
863 break;
864 }
865 case MediaState_Created:
866 {
867 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
868 tr ("Storage for the medium '%ls' is already created"),
869 m.locationFull.raw());
870 break;
871 }
872 case MediaState_LockedRead:
873 {
874 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
875 tr ("Medium '%ls' is locked for reading by another task"),
876 m.locationFull.raw());
877 break;
878 }
879 case MediaState_LockedWrite:
880 {
881 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
882 tr ("Medium '%ls' is locked for writing by another task"),
883 m.locationFull.raw());
884 break;
885 }
886 case MediaState_Inaccessible:
887 {
888 AssertMsg (!m.lastAccessError.isEmpty(),
889 ("There must always be a reason for Inaccessible"));
890
891 /* be in sync with Console::powerUpThread() */
892 if (!m.lastAccessError.isEmpty())
893 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
894 tr ("Medium '%ls' is not accessible. %ls"),
895 m.locationFull.raw(), m.lastAccessError.raw());
896 else
897 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
898 tr ("Medium '%ls' is not accessible"),
899 m.locationFull.raw());
900 break;
901 }
902 case MediaState_Creating:
903 {
904 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
905 tr ("Storage for the medium '%ls' is being created"),
906 m.locationFull.raw(), m.lastAccessError.raw());
907 break;
908 }
909 case MediaState_Deleting:
910 {
911 rc = setError (VBOX_E_INVALID_OBJECT_STATE,
912 tr ("Storage for the medium '%ls' is being deleted"),
913 m.locationFull.raw(), m.lastAccessError.raw());
914 break;
915 }
916 default:
917 {
918 AssertFailed();
919 break;
920 }
921 }
922
923 return rc;
924}
925
926////////////////////////////////////////////////////////////////////////////////
927// ImageMediumBase class
928////////////////////////////////////////////////////////////////////////////////
929
930/**
931 * Initializes the image medium object by opening an image file at the specified
932 * location.
933 *
934 * @param aVirtualBox Parent VirtualBox object.
935 * @param aLocation Path to the image file (can be relative to the
936 * VirtualBox home directory).
937 * @param aId UUID of the image.
938 */
939HRESULT ImageMediumBase::protectedInit (VirtualBox *aVirtualBox, CBSTR aLocation,
940 const Guid &aId)
941{
942 LogFlowThisFunc (("aLocation='%ls', aId={%RTuuid}\n", aLocation, aId.raw()));
943
944 AssertReturn (aVirtualBox, E_INVALIDARG);
945 AssertReturn (aLocation, E_INVALIDARG);
946 AssertReturn (!aId.isEmpty(), E_INVALIDARG);
947
948 /* Enclose the state transition NotReady->InInit->Ready */
949 AutoInitSpan autoInitSpan (this);
950 AssertReturn (autoInitSpan.isOk(), E_FAIL);
951
952 HRESULT rc = S_OK;
953
954 /* share parent weakly */
955 unconst (mVirtualBox) = aVirtualBox;
956
957 /* register with parent early, since uninit() will unconditionally
958 * unregister on failure */
959 mVirtualBox->addDependentChild (this);
960
961 /* there must be a storage unit */
962 m.state = MediaState_Created;
963
964 unconst (m.id) = aId;
965 rc = setLocation (aLocation);
966 CheckComRCReturnRC (rc);
967
968 LogFlowThisFunc (("m.locationFull='%ls'\n", m.locationFull.raw()));
969
970 /* get all the information about the medium from the file */
971 rc = queryInfo();
972
973 if (SUCCEEDED(rc))
974 {
975 /* if the image file is not accessible, it's not acceptable for the
976 * newly opened media so convert this into an error */
977 if (!m.lastAccessError.isEmpty())
978 rc = setError (VBOX_E_FILE_ERROR, Utf8Str (m.lastAccessError));
979 }
980
981 /* Confirm a successful initialization when it's the case */
982 if (SUCCEEDED (rc))
983 autoInitSpan.setSucceeded();
984
985 return rc;
986}
987
988/**
989 * Initializes the image medium object by loading its data from the given
990 * settings node.
991 *
992 * Note that it is assumed that this method is called only for registered media.
993 *
994 * @param aVirtualBox Parent VirtualBox object.
995 * @param aImageNode Either <DVDImage> or <FloppyImage> settings node.
996 */
997HRESULT ImageMediumBase::protectedInit (VirtualBox *aVirtualBox,
998 const settings::Key &aImageNode)
999{
1000 AssertReturn (aVirtualBox, E_INVALIDARG);
1001
1002 /* Enclose the state transition NotReady->InInit->Ready */
1003 AutoInitSpan autoInitSpan (this);
1004 AssertReturn (autoInitSpan.isOk(), E_FAIL);
1005
1006 HRESULT rc = S_OK;
1007
1008 /* share parent weakly */
1009 unconst (mVirtualBox) = aVirtualBox;
1010
1011 /* register with parent early, since uninit() will unconditionally
1012 * unregister on failure */
1013 mVirtualBox->addDependentChild (this);
1014
1015 /* see below why we don't call queryInfo() (and therefore treat the medium
1016 * as inaccessible for now */
1017 m.state = MediaState_Inaccessible;
1018
1019 /* required */
1020 unconst (m.id) = aImageNode.value <Guid> ("uuid");
1021 /* required */
1022 Bstr location = aImageNode.stringValue ("location");
1023 rc = setLocation (location);
1024 CheckComRCReturnRC (rc);
1025 /* optional */
1026 {
1027 settings::Key descNode = aImageNode.findKey ("Description");
1028 if (!descNode.isNull())
1029 m.description = descNode.keyStringValue();
1030 }
1031
1032 LogFlowThisFunc (("m.locationFull='%ls', m.id={%RTuuid}\n",
1033 m.locationFull.raw(), m.id.raw()));
1034
1035 /* Don't call queryInfo() for registered media to prevent the calling
1036 * thread (i.e. the VirtualBox server startup thread) from an unexpected
1037 * freeze but mark it as initially inaccessible instead. The vital UUID and
1038 * location properties are read from the registry file above; to get the
1039 * actual state and the rest of the data, the user will have to call
1040 * COMGETTER(State).*/
1041
1042 /* Confirm a successful initialization when it's the case */
1043 if (SUCCEEDED (rc))
1044 autoInitSpan.setSucceeded();
1045
1046 return rc;
1047}
1048
1049/**
1050 * Uninitializes the instance.
1051 *
1052 * Called either from FinalRelease() or by the parent when it gets destroyed.
1053 */
1054void ImageMediumBase::protectedUninit()
1055{
1056 LogFlowThisFunc (("\n"));
1057
1058 /* Enclose the state transition Ready->InUninit->NotReady */
1059 AutoUninitSpan autoUninitSpan (this);
1060 if (autoUninitSpan.uninitDone())
1061 return;
1062
1063 mVirtualBox->removeDependentChild (this);
1064
1065 unconst (mVirtualBox).setNull();
1066}
1067
1068// public methods for internal purposes only
1069////////////////////////////////////////////////////////////////////////////////
1070
1071/**
1072 * Saves image data by appending a new <Image> child node to the
1073 * given <Images> parent node.
1074 *
1075 * @param aImagesNode <Images> node.
1076 *
1077 * @note Locks this object for reading.
1078 */
1079HRESULT ImageMediumBase::saveSettings (settings::Key &aImagesNode)
1080{
1081 using namespace settings;
1082
1083 AssertReturn (!aImagesNode.isNull(), E_FAIL);
1084
1085 AutoCaller autoCaller (this);
1086 CheckComRCReturnRC (autoCaller.rc());
1087
1088 AutoReadLock alock (this);
1089
1090 Key imageNode = aImagesNode.appendKey ("Image");
1091 /* required */
1092 imageNode.setValue <Guid> ("uuid", m.id);
1093 /* required */
1094 imageNode.setValue <Bstr> ("location", m.locationFull);
1095 /* optional */
1096 if (!m.description.isNull())
1097 {
1098 Key descNode = aImagesNode.createKey ("Description");
1099 descNode.setKeyValue <Bstr> (m.description);
1100 }
1101
1102 return S_OK;
1103}
1104
1105////////////////////////////////////////////////////////////////////////////////
1106// DVDImage class
1107////////////////////////////////////////////////////////////////////////////////
1108
1109DEFINE_EMPTY_CTOR_DTOR (DVDImage)
1110
1111/**
1112 * @note Called from within this object's AutoMayUninitSpan and from under
1113 * mVirtualBox write lock.
1114 */
1115HRESULT DVDImage::unregisterWithVirtualBox()
1116{
1117 return mVirtualBox->unregisterDVDImage (this);
1118}
1119
1120////////////////////////////////////////////////////////////////////////////////
1121// FloppyImage class
1122////////////////////////////////////////////////////////////////////////////////
1123
1124DEFINE_EMPTY_CTOR_DTOR (FloppyImage)
1125
1126/**
1127 * @note Called from within this object's AutoMayUninitSpan and from under
1128 * mVirtualBox write lock.
1129 */
1130HRESULT FloppyImage::unregisterWithVirtualBox()
1131{
1132 return mVirtualBox->unregisterFloppyImage (this);
1133}
1134/* 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