VirtualBox

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

Last change on this file since 14937 was 14772, checked in by vboxsync, 16 years ago

Added vim modelines to aid following coding guidelines, like no tabs,
similar to what is already in the xidl file.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.0 KB
Line 
1/* $Id: DVDDriveImpl.cpp 14772 2008-11-28 12:41:22Z 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 if (!aState)
197 return E_POINTER;
198
199 AutoCaller autoCaller (this);
200 CheckComRCReturnRC (autoCaller.rc());
201
202 AutoReadLock alock (this);
203
204 *aState = mData->mState;
205
206 return S_OK;
207}
208
209STDMETHODIMP DVDDrive::COMGETTER(Passthrough) (BOOL *aPassthrough)
210{
211 if (!aPassthrough)
212 return E_POINTER;
213
214 AutoCaller autoCaller (this);
215 CheckComRCReturnRC (autoCaller.rc());
216
217 AutoReadLock alock (this);
218
219 *aPassthrough = mData->mPassthrough;
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 (mData->mPassthrough != aPassthrough)
236 {
237 mData.backup();
238 mData->mPassthrough = aPassthrough;
239 }
240
241 return S_OK;
242}
243
244// IDVDDrive methods
245////////////////////////////////////////////////////////////////////////////////
246
247STDMETHODIMP DVDDrive::MountImage (INPTR GUIDPARAM aImageId)
248{
249 Guid imageId = aImageId;
250 if (imageId.isEmpty())
251 return E_INVALIDARG;
252
253 AutoCaller autoCaller (this);
254 CheckComRCReturnRC (autoCaller.rc());
255
256 /* the machine needs to be mutable */
257 Machine::AutoMutableStateDependency adep (mParent);
258 CheckComRCReturnRC (adep.rc());
259
260 AutoWriteLock alock (this);
261
262 HRESULT rc = E_FAIL;
263
264 /* Our lifetime is bound to mParent's lifetime, so we don't add caller.
265 * We also don't lock mParent since its mParent field is const. */
266
267 ComObjPtr <DVDImage2> image;
268 rc = mParent->virtualBox()->findDVDImage2 (&imageId, NULL,
269 true /* aSetError */, &image);
270
271 if (SUCCEEDED (rc))
272 {
273 if (mData->mState != DriveState_ImageMounted ||
274 !mData->mImage.equalsTo (image))
275 {
276 rc = image->attachTo (mParent->id(), mParent->snapshotId());
277 if (SUCCEEDED (rc))
278 {
279 mData.backup();
280
281 unmount();
282
283 mData->mImage = image;
284 mData->mState = DriveState_ImageMounted;
285
286 /* leave the lock before informing callbacks */
287 alock.unlock();
288
289 mParent->onDVDDriveChange();
290 }
291 }
292 }
293
294 return rc;
295}
296
297STDMETHODIMP DVDDrive::CaptureHostDrive (IHostDVDDrive *aHostDVDDrive)
298{
299 if (!aHostDVDDrive)
300 return E_INVALIDARG;
301
302 AutoCaller autoCaller (this);
303 CheckComRCReturnRC (autoCaller.rc());
304
305 /* the machine needs to be mutable */
306 Machine::AutoMutableStateDependency adep (mParent);
307 CheckComRCReturnRC (adep.rc());
308
309 AutoWriteLock alock (this);
310
311 if (mData->mState != DriveState_HostDriveCaptured ||
312 !mData->mHostDrive.equalsTo (aHostDVDDrive))
313 {
314 mData.backup();
315
316 unmount();
317
318 mData->mHostDrive = aHostDVDDrive;
319 mData->mState = DriveState_HostDriveCaptured;
320
321 /* leave the lock before informing callbacks */
322 alock.unlock();
323
324 mParent->onDVDDriveChange();
325 }
326
327 return S_OK;
328}
329
330STDMETHODIMP DVDDrive::Unmount()
331{
332 AutoCaller autoCaller (this);
333 CheckComRCReturnRC (autoCaller.rc());
334
335 /* the machine needs to be mutable */
336 Machine::AutoMutableStateDependency adep (mParent);
337 CheckComRCReturnRC (adep.rc());
338
339 AutoWriteLock alock (this);
340
341 if (mData->mState != DriveState_NotMounted)
342 {
343 mData.backup();
344
345 unmount();
346
347 mData->mState = DriveState_NotMounted;
348
349 /* leave the lock before informing callbacks */
350 alock.unlock();
351
352 mParent->onDVDDriveChange();
353 }
354
355 return S_OK;
356}
357
358STDMETHODIMP DVDDrive::GetImage (IDVDImage2 **aDVDImage)
359{
360 if (!aDVDImage)
361 return E_POINTER;
362
363 AutoCaller autoCaller (this);
364 CheckComRCReturnRC (autoCaller.rc());
365
366 AutoReadLock alock (this);
367
368 mData->mImage.queryInterfaceTo (aDVDImage);
369
370 return S_OK;
371}
372
373STDMETHODIMP DVDDrive::GetHostDrive(IHostDVDDrive **aHostDrive)
374{
375 if (!aHostDrive)
376 return E_POINTER;
377
378 AutoCaller autoCaller (this);
379 CheckComRCReturnRC (autoCaller.rc());
380
381 AutoReadLock alock (this);
382
383 mData->mHostDrive.queryInterfaceTo (aHostDrive);
384
385 return S_OK;
386}
387
388// public methods only for internal purposes
389////////////////////////////////////////////////////////////////////////////////
390
391/**
392 * Loads settings from the given machine node.
393 * May be called once right after this object creation.
394 *
395 * @param aMachineNode <Machine> node.
396 *
397 * @note Locks this object for writing.
398 */
399HRESULT DVDDrive::loadSettings (const settings::Key &aMachineNode)
400{
401 using namespace settings;
402
403 AssertReturn (!aMachineNode.isNull(), E_FAIL);
404
405 AutoCaller autoCaller (this);
406 AssertComRCReturnRC (autoCaller.rc());
407
408 AutoWriteLock alock (this);
409
410 /* Note: we assume that the default values for attributes of optional
411 * nodes are assigned in the Data::Data() constructor and don't do it
412 * here. It implies that this method may only be called after constructing
413 * a new BIOSSettings object while all its data fields are in the default
414 * values. Exceptions are fields whose creation time defaults don't match
415 * values that should be applied when these fields are not explicitly set
416 * in the settings file (for backwards compatibility reasons). This takes
417 * place when a setting of a newly created object must default to A while
418 * the same setting of an object loaded from the old settings file must
419 * default to B. */
420
421 HRESULT rc = S_OK;
422
423 /* DVD drive (required, contains either Image or HostDrive or nothing) */
424 Key dvdDriveNode = aMachineNode.key ("DVDDrive");
425
426 /* optional, defaults to false */
427 mData->mPassthrough = dvdDriveNode.value <bool> ("passthrough");
428
429 Key typeNode;
430
431 if (!(typeNode = dvdDriveNode.findKey ("Image")).isNull())
432 {
433 Guid uuid = typeNode.value <Guid> ("uuid");
434 rc = MountImage (uuid);
435 CheckComRCReturnRC (rc);
436 }
437 else if (!(typeNode = dvdDriveNode.findKey ("HostDrive")).isNull())
438 {
439
440 Bstr src = typeNode.stringValue ("src");
441
442 /* find the correspoding object */
443 ComObjPtr <Host> host = mParent->virtualBox()->host();
444
445 ComPtr <IHostDVDDriveCollection> coll;
446 rc = host->COMGETTER(DVDDrives) (coll.asOutParam());
447 AssertComRC (rc);
448
449 ComPtr <IHostDVDDrive> drive;
450 rc = coll->FindByName (src, drive.asOutParam());
451 if (SUCCEEDED (rc))
452 {
453 rc = CaptureHostDrive (drive);
454 CheckComRCReturnRC (rc);
455 }
456 else if (rc == E_INVALIDARG)
457 {
458 /* the host DVD drive is not currently available. we
459 * assume it will be available later and create an
460 * extra object now */
461 ComObjPtr <HostDVDDrive> hostDrive;
462 hostDrive.createObject();
463 rc = hostDrive->init (src);
464 AssertComRC (rc);
465 rc = CaptureHostDrive (hostDrive);
466 CheckComRCReturnRC (rc);
467 }
468 else
469 AssertComRC (rc);
470 }
471
472 return S_OK;
473}
474
475/**
476 * Saves settings to the given machine node.
477 *
478 * @param aMachineNode <Machine> node.
479 *
480 * @note Locks this object for reading.
481 */
482HRESULT DVDDrive::saveSettings (settings::Key &aMachineNode)
483{
484 using namespace settings;
485
486 AssertReturn (!aMachineNode.isNull(), E_FAIL);
487
488 AutoCaller autoCaller (this);
489 AssertComRCReturnRC (autoCaller.rc());
490
491 AutoReadLock alock (this);
492
493 Key node = aMachineNode.createKey ("DVDDrive");
494
495 node.setValue <bool> ("passthrough", !!mData->mPassthrough);
496
497 switch (mData->mState)
498 {
499 case DriveState_ImageMounted:
500 {
501 Assert (!mData->mImage.isNull());
502
503 Guid id;
504 HRESULT rc = mData->mImage->COMGETTER(Id) (id.asOutParam());
505 AssertComRC (rc);
506 Assert (!id.isEmpty());
507
508 Key imageNode = node.createKey ("Image");
509 imageNode.setValue <Guid> ("uuid", id);
510 break;
511 }
512 case DriveState_HostDriveCaptured:
513 {
514 Assert (!mData->mHostDrive.isNull());
515
516 Bstr name;
517 HRESULT rc = mData->mHostDrive->COMGETTER(Name) (name.asOutParam());
518 AssertComRC (rc);
519 Assert (!name.isEmpty());
520
521 Key hostDriveNode = node.createKey ("HostDrive");
522 hostDriveNode.setValue <Bstr> ("src", name);
523 break;
524 }
525 case DriveState_NotMounted:
526 /* do nothing, i.e.leave the drive node empty */
527 break;
528 default:
529 ComAssertMsgFailedRet (("Invalid drive state: %d\n", mData->mState),
530 E_FAIL);
531 }
532
533 return S_OK;
534}
535
536/**
537 * @note Locks this object for writing.
538 */
539bool DVDDrive::rollback()
540{
541 /* sanity */
542 AutoCaller autoCaller (this);
543 AssertComRCReturn (autoCaller.rc(), false);
544
545 AutoWriteLock alock (this);
546
547 bool changed = false;
548
549 if (mData.isBackedUp())
550 {
551 /* we need to check all data to see whether anything will be changed
552 * after rollback */
553 changed = mData.hasActualChanges();
554
555 if (changed)
556 {
557 Data *oldData = mData.backedUpData();
558
559 if (!mData->mImage.isNull() &&
560 !oldData->mImage.equalsTo (mData->mImage))
561 {
562 /* detach the current image that will go away after rollback */
563 mData->mImage->detachFrom (mParent->id(), mParent->snapshotId());
564 }
565 }
566
567 mData.rollback();
568 }
569
570 return changed;
571}
572
573/**
574 * @note Locks this object for writing, together with the peer object (also
575 * for writing) if there is one.
576 */
577void DVDDrive::commit()
578{
579 /* sanity */
580 AutoCaller autoCaller (this);
581 AssertComRCReturnVoid (autoCaller.rc());
582
583 /* sanity too */
584 AutoCaller peerCaller (mPeer);
585 AssertComRCReturnVoid (peerCaller.rc());
586
587 /* lock both for writing since we modify both (mPeer is "master" so locked
588 * first) */
589 AutoMultiWriteLock2 alock (mPeer, this);
590
591 if (mData.isBackedUp())
592 {
593 Data *oldData = mData.backedUpData();
594
595 if (!oldData->mImage.isNull() &&
596 !oldData->mImage.equalsTo (mData->mImage))
597 {
598 /* detach the old image that will go away after commit */
599 oldData->mImage->detachFrom (mParent->id(), mParent->snapshotId());
600 }
601
602 mData.commit();
603 if (mPeer)
604 {
605 /* attach new data to the peer and reshare it */
606 mPeer->mData.attach (mData);
607 }
608 }
609}
610
611/**
612 * @note Locks this object for writing, together with the peer object
613 * represented by @a aThat (locked for reading).
614 */
615void DVDDrive::copyFrom (DVDDrive *aThat)
616{
617 AssertReturnVoid (aThat != NULL);
618
619 /* sanity */
620 AutoCaller autoCaller (this);
621 AssertComRCReturnVoid (autoCaller.rc());
622
623 /* sanity too */
624 AutoCaller thatCaller (aThat);
625 AssertComRCReturnVoid (thatCaller.rc());
626
627 /* peer is not modified, lock it for reading (aThat is "master" so locked
628 * first) */
629 AutoMultiLock2 alock (aThat->rlock(), this->wlock());
630
631 /* this will back up current data */
632 mData.assignCopy (aThat->mData);
633}
634
635// private methods
636////////////////////////////////////////////////////////////////////////////////
637
638/**
639 * Helper to unmount a drive.
640 *
641 * @return COM status code
642 *
643 */
644HRESULT DVDDrive::unmount()
645{
646 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
647
648 if (mData->mImage)
649 mData->mImage.setNull();
650 if (mData->mHostDrive)
651 mData->mHostDrive.setNull();
652
653 return S_OK;
654}
655/* 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