VirtualBox

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

Last change on this file since 20961 was 19511, checked in by vboxsync, 15 years ago

DVD/Floppy: rollback if unmount failed

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