VirtualBox

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

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

Main: Fixed: CD/DVD and Floppy not locked for reading when manually attached at VM runtime (#3504).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.3 KB
Line 
1/* $Id: DVDDriveImpl.cpp 15991 2009-01-16 14:02:20Z vboxsync $ */
2
3/** @file
4 *
5 * VirtualBox COM class implementation
6 */
7
8/*
9 * Copyright (C) 2006-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 "DVDDriveImpl.h"
25
26#include "MachineImpl.h"
27#include "HostImpl.h"
28#include "HostDVDDriveImpl.h"
29#include "VirtualBoxImpl.h"
30
31#include "Global.h"
32
33#include "Logging.h"
34
35#include <iprt/string.h>
36#include <iprt/cpputils.h>
37
38// constructor / destructor
39////////////////////////////////////////////////////////////////////////////////
40
41DEFINE_EMPTY_CTOR_DTOR (DVDDrive)
42
43HRESULT DVDDrive::FinalConstruct()
44{
45 return S_OK;
46}
47
48void DVDDrive::FinalRelease()
49{
50 uninit();
51}
52
53// public initializer/uninitializer for internal purposes only
54////////////////////////////////////////////////////////////////////////////////
55
56/**
57 * Initializes the DVD drive object.
58 *
59 * @param aParent Handle of the parent object.
60 */
61HRESULT DVDDrive::init (Machine *aParent)
62{
63 LogFlowThisFunc (("aParent=%p\n", aParent));
64
65 ComAssertRet (aParent, E_INVALIDARG);
66
67 /* Enclose the state transition NotReady->InInit->Ready */
68 AutoInitSpan autoInitSpan (this);
69 AssertReturn (autoInitSpan.isOk(), E_FAIL);
70
71 unconst (mParent) = aParent;
72 /* mPeer is left null */
73
74 m.allocate();
75
76 /* Confirm a successful initialization */
77 autoInitSpan.setSucceeded();
78
79 return S_OK;
80}
81
82/**
83 * Initializes the DVD drive object given another DVD drive object
84 * (a kind of copy constructor). This object shares data with
85 * the object passed as an argument.
86 *
87 * @note This object must be destroyed before the original object
88 * it shares data with is destroyed.
89 *
90 * @note Locks @a aThat object for reading.
91 */
92HRESULT DVDDrive::init (Machine *aParent, DVDDrive *aThat)
93{
94 LogFlowThisFunc (("aParent=%p, aThat=%p\n", aParent, aThat));
95
96 ComAssertRet (aParent && aThat, E_INVALIDARG);
97
98 /* Enclose the state transition NotReady->InInit->Ready */
99 AutoInitSpan autoInitSpan (this);
100 AssertReturn (autoInitSpan.isOk(), E_FAIL);
101
102 unconst (mParent) = aParent;
103 unconst (mPeer) = aThat;
104
105 AutoCaller thatCaller (aThat);
106 AssertComRCReturnRC (thatCaller.rc());
107
108 AutoReadLock thatLock (aThat);
109 m.share (aThat->m);
110
111 /* Confirm a successful initialization */
112 autoInitSpan.setSucceeded();
113
114 return S_OK;
115}
116
117/**
118 * Initializes the DVD drive object given another DVD drive object
119 * (a kind of copy constructor). This object makes a private copy of data
120 * of the original object passed as an argument.
121 *
122 * @note Locks @a aThat object for reading.
123 */
124HRESULT DVDDrive::initCopy (Machine *aParent, DVDDrive *aThat)
125{
126 LogFlowThisFunc (("aParent=%p, aThat=%p\n", aParent, aThat));
127
128 ComAssertRet (aParent && aThat, E_INVALIDARG);
129
130 /* Enclose the state transition NotReady->InInit->Ready */
131 AutoInitSpan autoInitSpan (this);
132 AssertReturn (autoInitSpan.isOk(), E_FAIL);
133
134 unconst (mParent) = aParent;
135 /* mPeer is left null */
136
137 AutoCaller thatCaller (aThat);
138 AssertComRCReturnRC (thatCaller.rc());
139
140 AutoReadLock thatLock (aThat);
141 m.attachCopy (aThat->m);
142
143 /* at present, this must be a snapshot machine */
144 Assert (!aParent->snapshotId().isEmpty());
145
146 if (m->state == DriveState_ImageMounted)
147 {
148 /* associate the DVD image media with the snapshot */
149 HRESULT rc = m->image->attachTo (aParent->id(),
150 aParent->snapshotId());
151 AssertComRC (rc);
152 }
153
154 /* Confirm a successful initialization */
155 autoInitSpan.setSucceeded();
156
157 return S_OK;
158}
159
160/**
161 * Uninitializes the instance and sets the ready flag to FALSE.
162 * Called either from FinalRelease() or by the parent when it gets destroyed.
163 */
164void DVDDrive::uninit()
165{
166 LogFlowThisFunc (("\n"));
167
168 /* Enclose the state transition Ready->InUninit->NotReady */
169 AutoUninitSpan autoUninitSpan (this);
170 if (autoUninitSpan.uninitDone())
171 return;
172
173 if ((mParent->type() == Machine::IsMachine ||
174 mParent->type() == Machine::IsSnapshotMachine) &&
175 m->state == DriveState_ImageMounted)
176 {
177 /* Deassociate the DVD image (only when mParent is a real Machine or a
178 * SnapshotMachine instance; SessionMachine instances
179 * refer to real Machine hard disks). This is necessary for a clean
180 * re-initialization of the VM after successfully re-checking the
181 * accessibility state. */
182 HRESULT rc = m->image->detachFrom (mParent->id(),
183 mParent->snapshotId());
184 AssertComRC (rc);
185 }
186
187 m.free();
188
189 unconst (mPeer).setNull();
190 unconst (mParent).setNull();
191}
192
193// IDVDDrive properties
194////////////////////////////////////////////////////////////////////////////////
195
196STDMETHODIMP DVDDrive::COMGETTER(State) (DriveState_T *aState)
197{
198 CheckComArgOutPointerValid(aState);
199
200 AutoCaller autoCaller (this);
201 CheckComRCReturnRC (autoCaller.rc());
202
203 AutoReadLock alock (this);
204
205 *aState = m->state;
206
207 return S_OK;
208}
209
210STDMETHODIMP DVDDrive::COMGETTER(Passthrough) (BOOL *aPassthrough)
211{
212 CheckComArgOutPointerValid(aPassthrough);
213
214 AutoCaller autoCaller (this);
215 CheckComRCReturnRC (autoCaller.rc());
216
217 AutoReadLock alock (this);
218
219 *aPassthrough = m->passthrough;
220
221 return S_OK;
222}
223
224STDMETHODIMP DVDDrive::COMSETTER(Passthrough) (BOOL aPassthrough)
225{
226 AutoCaller autoCaller (this);
227 CheckComRCReturnRC (autoCaller.rc());
228
229 /* the machine needs to be mutable */
230 Machine::AutoMutableStateDependency adep (mParent);
231 CheckComRCReturnRC (adep.rc());
232
233 AutoWriteLock alock (this);
234
235 if (m->passthrough != aPassthrough)
236 {
237 m.backup();
238 m->passthrough = aPassthrough;
239 }
240
241 return S_OK;
242}
243
244// IDVDDrive methods
245////////////////////////////////////////////////////////////////////////////////
246
247STDMETHODIMP DVDDrive::MountImage (IN_GUID aImageId)
248{
249 Guid imageId = aImageId;
250 CheckComArgExpr(aImageId, !imageId.isEmpty());
251
252 AutoCaller autoCaller (this);
253 CheckComRCReturnRC (autoCaller.rc());
254
255 /* the machine needs to be mutable */
256 Machine::AutoMutableStateDependency adep (mParent);
257 CheckComRCReturnRC (adep.rc());
258
259 AutoWriteLock alock (this);
260
261 HRESULT rc = E_FAIL;
262
263 /* Our lifetime is bound to mParent's lifetime, so we don't add caller.
264 * We also don't lock mParent since its mParent field is const. */
265
266 ComObjPtr <DVDImage2> image;
267 rc = mParent->virtualBox()->findDVDImage2 (&imageId, NULL,
268 true /* aSetError */, &image);
269
270 if (SUCCEEDED (rc))
271 {
272 if (m->state != DriveState_ImageMounted ||
273 !m->image.equalsTo (image))
274 {
275 rc = image->attachTo (mParent->id(), mParent->snapshotId());
276 if (SUCCEEDED (rc))
277 {
278 /* umount() will backup data */
279 rc = unmount();
280
281 if (SUCCEEDED (rc))
282 {
283 /* lock the image for reading if the VM is online. It will
284 * be unlocked either when unmounted from this drive or by
285 * SessionMachine::setMachineState() when the VM is
286 * terminated */
287 if (Global::IsOnline (adep.machineState()))
288 rc = image->LockRead (NULL);
289 }
290
291 if (SUCCEEDED (rc))
292 {
293 m->image = image;
294 m->state = DriveState_ImageMounted;
295
296 /* leave the lock before informing callbacks */
297 alock.unlock();
298
299 mParent->onDVDDriveChange();
300 }
301 }
302 }
303 }
304
305 return rc;
306}
307
308STDMETHODIMP DVDDrive::CaptureHostDrive (IHostDVDDrive *aHostDVDDrive)
309{
310 CheckComArgNotNull(aHostDVDDrive);
311
312 AutoCaller autoCaller (this);
313 CheckComRCReturnRC (autoCaller.rc());
314
315 /* the machine needs to be mutable */
316 Machine::AutoMutableStateDependency adep (mParent);
317 CheckComRCReturnRC (adep.rc());
318
319 AutoWriteLock alock (this);
320
321 if (m->state != DriveState_HostDriveCaptured ||
322 !m->hostDrive.equalsTo (aHostDVDDrive))
323 {
324 /* umount() will backup data */
325 HRESULT rc = unmount();
326 if (SUCCEEDED (rc))
327 {
328 m->hostDrive = aHostDVDDrive;
329 m->state = DriveState_HostDriveCaptured;
330
331 /* leave the lock before informing callbacks */
332 alock.unlock();
333
334 mParent->onDVDDriveChange();
335 }
336 }
337
338 return S_OK;
339}
340
341STDMETHODIMP DVDDrive::Unmount()
342{
343 AutoCaller autoCaller (this);
344 CheckComRCReturnRC (autoCaller.rc());
345
346 /* the machine needs to be mutable */
347 Machine::AutoMutableStateDependency adep (mParent);
348 CheckComRCReturnRC (adep.rc());
349
350 AutoWriteLock alock (this);
351
352 if (m->state != DriveState_NotMounted)
353 {
354 /* umount() will backup data */
355 HRESULT rc = unmount();
356 if (SUCCEEDED (rc))
357 {
358 m->state = DriveState_NotMounted;
359
360 /* leave the lock before informing callbacks */
361 alock.unlock();
362
363 mParent->onDVDDriveChange();
364 }
365 }
366
367 return S_OK;
368}
369
370STDMETHODIMP DVDDrive::GetImage (IDVDImage2 **aDVDImage)
371{
372 CheckComArgOutPointerValid(aDVDImage);
373
374 AutoCaller autoCaller (this);
375 CheckComRCReturnRC (autoCaller.rc());
376
377 AutoReadLock alock (this);
378
379 m->image.queryInterfaceTo (aDVDImage);
380
381 return S_OK;
382}
383
384STDMETHODIMP DVDDrive::GetHostDrive(IHostDVDDrive **aHostDrive)
385{
386 CheckComArgOutPointerValid(aHostDrive);
387
388 AutoCaller autoCaller (this);
389 CheckComRCReturnRC (autoCaller.rc());
390
391 AutoReadLock alock (this);
392
393 m->hostDrive.queryInterfaceTo (aHostDrive);
394
395 return S_OK;
396}
397
398// public methods only for internal purposes
399////////////////////////////////////////////////////////////////////////////////
400
401/**
402 * Loads settings from the given machine node. May be called once right after
403 * this object creation.
404 *
405 * @param aMachineNode <Machine> node.
406 *
407 * @note Locks this object for writing.
408 */
409HRESULT DVDDrive::loadSettings (const settings::Key &aMachineNode)
410{
411 using namespace settings;
412
413 AssertReturn (!aMachineNode.isNull(), E_FAIL);
414
415 AutoCaller autoCaller (this);
416 AssertComRCReturnRC (autoCaller.rc());
417
418 AutoWriteLock alock (this);
419
420 /* Note: we assume that the default values for attributes of optional
421 * nodes are assigned in the Data::Data() constructor and don't do it
422 * here. It implies that this method may only be called after constructing
423 * a new BIOSSettings object while all its data fields are in the default
424 * values. Exceptions are fields whose creation time defaults don't match
425 * values that should be applied when these fields are not explicitly set
426 * in the settings file (for backwards compatibility reasons). This takes
427 * place when a setting of a newly created object must default to A while
428 * the same setting of an object loaded from the old settings file must
429 * default to B. */
430
431 HRESULT rc = S_OK;
432
433 /* DVD drive (required, contains either Image or HostDrive or nothing) */
434 Key dvdDriveNode = aMachineNode.key ("DVDDrive");
435
436 /* optional, defaults to false */
437 m->passthrough = dvdDriveNode.value <bool> ("passthrough");
438
439 Key typeNode;
440
441 if (!(typeNode = dvdDriveNode.findKey ("Image")).isNull())
442 {
443 Guid uuid = typeNode.value <Guid> ("uuid");
444 rc = MountImage (uuid);
445 CheckComRCReturnRC (rc);
446 }
447 else if (!(typeNode = dvdDriveNode.findKey ("HostDrive")).isNull())
448 {
449
450 Bstr src = typeNode.stringValue ("src");
451
452 /* find the correspoding object */
453 ComObjPtr <Host> host = mParent->virtualBox()->host();
454
455 ComPtr <IHostDVDDriveCollection> coll;
456 rc = host->COMGETTER(DVDDrives) (coll.asOutParam());
457 AssertComRC (rc);
458
459 ComPtr <IHostDVDDrive> drive;
460 rc = coll->FindByName (src, drive.asOutParam());
461 if (SUCCEEDED (rc))
462 {
463 rc = CaptureHostDrive (drive);
464 CheckComRCReturnRC (rc);
465 }
466 else if (rc == E_INVALIDARG)
467 {
468 /* the host DVD drive is not currently available. we
469 * assume it will be available later and create an
470 * extra object now */
471 ComObjPtr <HostDVDDrive> hostDrive;
472 hostDrive.createObject();
473 rc = hostDrive->init (src);
474 AssertComRC (rc);
475 rc = CaptureHostDrive (hostDrive);
476 CheckComRCReturnRC (rc);
477 }
478 else
479 AssertComRC (rc);
480 }
481
482 return S_OK;
483}
484
485/**
486 * Saves settings to the given machine node.
487 *
488 * @param aMachineNode <Machine> node.
489 *
490 * @note Locks this object for reading.
491 */
492HRESULT DVDDrive::saveSettings (settings::Key &aMachineNode)
493{
494 using namespace settings;
495
496 AssertReturn (!aMachineNode.isNull(), E_FAIL);
497
498 AutoCaller autoCaller (this);
499 AssertComRCReturnRC (autoCaller.rc());
500
501 AutoReadLock alock (this);
502
503 Key node = aMachineNode.createKey ("DVDDrive");
504
505 node.setValue <bool> ("passthrough", !!m->passthrough);
506
507 switch (m->state)
508 {
509 case DriveState_ImageMounted:
510 {
511 Assert (!m->image.isNull());
512
513 Guid id;
514 HRESULT rc = m->image->COMGETTER(Id) (id.asOutParam());
515 AssertComRC (rc);
516 Assert (!id.isEmpty());
517
518 Key imageNode = node.createKey ("Image");
519 imageNode.setValue <Guid> ("uuid", id);
520 break;
521 }
522 case DriveState_HostDriveCaptured:
523 {
524 Assert (!m->hostDrive.isNull());
525
526 Bstr name;
527 HRESULT rc = m->hostDrive->COMGETTER(Name) (name.asOutParam());
528 AssertComRC (rc);
529 Assert (!name.isEmpty());
530
531 Key hostDriveNode = node.createKey ("HostDrive");
532 hostDriveNode.setValue <Bstr> ("src", name);
533 break;
534 }
535 case DriveState_NotMounted:
536 /* do nothing, i.e.leave the drive node empty */
537 break;
538 default:
539 ComAssertMsgFailedRet (("Invalid drive state: %d", m->state),
540 E_FAIL);
541 }
542
543 return S_OK;
544}
545
546/**
547 * @note Locks this object for writing.
548 */
549bool DVDDrive::rollback()
550{
551 /* sanity */
552 AutoCaller autoCaller (this);
553 AssertComRCReturn (autoCaller.rc(), false);
554
555 /* we need adep for the state check */
556 Machine::AutoAnyStateDependency adep (mParent);
557 AssertComRCReturn (adep.rc(), false);
558
559 AutoWriteLock alock (this);
560
561 bool changed = false;
562
563 if (m.isBackedUp())
564 {
565 /* we need to check all data to see whether anything will be changed
566 * after rollback */
567 changed = m.hasActualChanges();
568
569 if (changed)
570 {
571 Data *oldData = m.backedUpData();
572
573 if (!m->image.isNull() &&
574 !oldData->image.equalsTo (m->image))
575 {
576 /* detach the current image that will go away after rollback */
577 m->image->detachFrom (mParent->id(), mParent->snapshotId());
578
579 /* unlock the image for reading if the VM is online */
580 if (Global::IsOnline (adep.machineState()))
581 {
582 HRESULT rc = m->image->UnlockRead (NULL);
583 AssertComRC (rc);
584 }
585 }
586 }
587
588 m.rollback();
589 }
590
591 return changed;
592}
593
594/**
595 * @note Locks this object for writing, together with the peer object (also for
596 * writing) if there is one.
597 */
598void DVDDrive::commit()
599{
600 /* sanity */
601 AutoCaller autoCaller (this);
602 AssertComRCReturnVoid (autoCaller.rc());
603
604 /* sanity too */
605 AutoCaller peerCaller (mPeer);
606 AssertComRCReturnVoid (peerCaller.rc());
607
608 /* we need adep for the state check */
609 Machine::AutoAnyStateDependency adep (mParent);
610 AssertComRCReturnVoid (adep.rc());
611
612 /* lock both for writing since we modify both (mPeer is "master" so locked
613 * first) */
614 AutoMultiWriteLock2 alock (mPeer, this);
615
616 if (m.isBackedUp())
617 {
618 Data *oldData = m.backedUpData();
619
620 if (!oldData->image.isNull() &&
621 !oldData->image.equalsTo (m->image))
622 {
623 /* detach the old image that will go away after commit */
624 oldData->image->detachFrom (mParent->id(), mParent->snapshotId());
625
626 /* unlock the image for reading if the VM is online */
627 if (Global::IsOnline (adep.machineState()))
628 {
629 HRESULT rc = oldData->image->UnlockRead (NULL);
630 AssertComRC (rc);
631 }
632 }
633
634 m.commit();
635 if (mPeer)
636 {
637 /* attach new data to the peer and reshare it */
638 mPeer->m.attach (m);
639 }
640 }
641}
642
643/**
644 * @note Locks this object for writing, together with the peer object
645 * represented by @a aThat (locked for reading).
646 */
647void DVDDrive::copyFrom (DVDDrive *aThat)
648{
649 AssertReturnVoid (aThat != NULL);
650
651 /* sanity */
652 AutoCaller autoCaller (this);
653 AssertComRCReturnVoid (autoCaller.rc());
654
655 /* sanity too */
656 AutoCaller thatCaller (aThat);
657 AssertComRCReturnVoid (thatCaller.rc());
658
659 /* peer is not modified, lock it for reading (aThat is "master" so locked
660 * first) */
661 AutoMultiLock2 alock (aThat->rlock(), this->wlock());
662
663 /* this will back up current data */
664 m.assignCopy (aThat->m);
665}
666
667/**
668 * Helper to unmount a drive.
669 *
670 * @note Must be called from under this object's write lock.
671 */
672HRESULT DVDDrive::unmount()
673{
674 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
675
676 m.backup();
677
678 if (m->image)
679 m->image.setNull();
680 if (m->hostDrive)
681 m->hostDrive.setNull();
682
683 m->state = DriveState_NotMounted;
684
685 return S_OK;
686}
687
688// private methods
689////////////////////////////////////////////////////////////////////////////////
690
691/* 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