VirtualBox

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

Last change on this file since 14290 was 13580, checked in by vboxsync, 16 years ago

Ported s2 branch (r37120:38456).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.9 KB
Line 
1/* $Id: DVDDriveImpl.cpp 13580 2008-10-27 14:04:18Z 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_UNEXPECTED);
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_UNEXPECTED);
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_UNEXPECTED);
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}
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