VirtualBox

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

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

Main: #3377: Implicitly detach DVD/Floppy from an unregistered machine (as opposed to hard disks, this does not have side effects).

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