VirtualBox

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

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

Main: #3312: Restore the proper hard disk accessibility state after unlock if checked while being locked; allow to delete hard disks in Inaccessible state (backends will fail anyway). Fixes the problem of orphan diffs after reverting to the current snapshot on power off.

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