VirtualBox

source: vbox/trunk/src/VBox/Main/FloppyDriveImpl.cpp@ 16482

Last change on this file since 16482 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.5 KB
Line 
1/* $Id: FloppyDriveImpl.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-2007 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 "FloppyDriveImpl.h"
25
26#include "MachineImpl.h"
27#include "HostImpl.h"
28#include "HostFloppyDriveImpl.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 (FloppyDrive)
42
43HRESULT FloppyDrive::FinalConstruct()
44{
45 return S_OK;
46}
47
48void FloppyDrive::FinalRelease()
49{
50 uninit();
51}
52
53// public initializer/uninitializer for internal purposes only
54/////////////////////////////////////////////////////////////////////////////
55
56/**
57 * Initializes the Floppy drive object.
58 *
59 * @param aParent Handle of the parent object.
60 */
61HRESULT FloppyDrive::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 Floppy drive object given another Floppy 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 FloppyDrive::init (Machine *aParent, FloppyDrive *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 guest object given another guest 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 FloppyDrive::initCopy (Machine *aParent, FloppyDrive *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 FloppyDrive::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// IFloppyDrive properties
194/////////////////////////////////////////////////////////////////////////////
195
196STDMETHODIMP FloppyDrive::COMGETTER(Enabled) (BOOL *aEnabled)
197{
198 CheckComArgOutPointerValid(aEnabled);
199
200 AutoCaller autoCaller (this);
201 CheckComRCReturnRC (autoCaller.rc());
202
203 AutoReadLock alock (this);
204
205 *aEnabled = m->enabled;
206
207 return S_OK;
208}
209
210STDMETHODIMP FloppyDrive::COMSETTER(Enabled) (BOOL aEnabled)
211{
212 LogFlowThisFunc (("aEnabled=%RTbool\n", aEnabled));
213
214 AutoCaller autoCaller (this);
215 CheckComRCReturnRC (autoCaller.rc());
216
217 /* the machine needs to be mutable */
218 Machine::AutoMutableStateDependency adep (mParent);
219 CheckComRCReturnRC (adep.rc());
220
221 AutoWriteLock alock (this);
222
223 if (m->enabled != aEnabled)
224 {
225 m.backup();
226 m->enabled = aEnabled;
227
228 /* leave the lock before informing callbacks */
229 alock.unlock();
230
231 mParent->onFloppyDriveChange();
232 }
233
234 return S_OK;
235}
236
237STDMETHODIMP FloppyDrive::COMGETTER(State) (DriveState_T *aState)
238{
239 CheckComArgOutPointerValid(aState);
240
241 AutoCaller autoCaller (this);
242 CheckComRCReturnRC (autoCaller.rc());
243
244 AutoReadLock alock (this);
245
246 *aState = m->state;
247
248 return S_OK;
249}
250
251// IFloppyDrive methods
252/////////////////////////////////////////////////////////////////////////////
253
254STDMETHODIMP FloppyDrive::MountImage (IN_GUID aImageId)
255{
256 Guid imageId = aImageId;
257 CheckComArgExpr(aImageId, !imageId.isEmpty());
258
259 AutoCaller autoCaller (this);
260 CheckComRCReturnRC (autoCaller.rc());
261
262 /* the machine needs to be mutable */
263 Machine::AutoMutableStateDependency adep (mParent);
264 CheckComRCReturnRC (adep.rc());
265
266 AutoWriteLock alock (this);
267
268 HRESULT rc = E_FAIL;
269
270 /* Our lifetime is bound to mParent's lifetime, so we don't add caller.
271 * We also don't lock mParent since its mParent field is const. */
272
273 ComObjPtr <FloppyImage2> image;
274 rc = mParent->virtualBox()->findFloppyImage2 (&imageId, NULL,
275 true /* aSetError */, &image);
276
277 if (SUCCEEDED (rc))
278 {
279 if (m->state != DriveState_ImageMounted ||
280 !m->image.equalsTo (image))
281 {
282 rc = image->attachTo (mParent->id(), mParent->snapshotId());
283 if (SUCCEEDED (rc))
284 {
285 /* umount() will backup data */
286 rc = unmount();
287
288 if (SUCCEEDED (rc))
289 {
290 /* lock the image for reading if the VM is online. It will
291 * be unlocked either when unmounted from this drive or by
292 * SessionMachine::setMachineState() when the VM is
293 * terminated */
294 if (Global::IsOnline (adep.machineState()))
295 rc = image->LockRead (NULL);
296 }
297
298 if (SUCCEEDED (rc))
299 {
300 m->image = image;
301 m->state = DriveState_ImageMounted;
302
303 /* leave the lock before informing callbacks */
304 alock.unlock();
305
306 mParent->onFloppyDriveChange();
307 }
308 }
309 }
310 }
311
312 return rc;
313}
314
315STDMETHODIMP FloppyDrive::CaptureHostDrive (IHostFloppyDrive *aHostFloppyDrive)
316{
317 CheckComArgNotNull(aHostFloppyDrive);
318
319 AutoCaller autoCaller (this);
320 CheckComRCReturnRC (autoCaller.rc());
321
322 /* the machine needs to be mutable */
323 Machine::AutoMutableStateDependency adep (mParent);
324 CheckComRCReturnRC (adep.rc());
325
326 AutoWriteLock alock (this);
327
328 if (m->state != DriveState_HostDriveCaptured ||
329 !m->hostDrive.equalsTo (aHostFloppyDrive))
330 {
331 /* umount() will backup data */
332 HRESULT rc = unmount();
333 if (SUCCEEDED (rc))
334 {
335 m->hostDrive = aHostFloppyDrive;
336 m->state = DriveState_HostDriveCaptured;
337
338 /* leave the lock before informing callbacks */
339 alock.unlock();
340
341 mParent->onFloppyDriveChange();
342 }
343 }
344
345 return S_OK;
346}
347
348STDMETHODIMP FloppyDrive::Unmount()
349{
350 AutoCaller autoCaller (this);
351 CheckComRCReturnRC (autoCaller.rc());
352
353 /* the machine needs to be mutable */
354 Machine::AutoMutableStateDependency adep (mParent);
355 CheckComRCReturnRC (adep.rc());
356
357 AutoWriteLock alock (this);
358
359 if (m->state != DriveState_NotMounted)
360 {
361 /* umount() will backup data */
362 HRESULT rc = unmount();
363 if (SUCCEEDED (rc))
364 {
365 m->state = DriveState_NotMounted;
366
367 /* leave the lock before informing callbacks */
368 alock.unlock();
369
370 mParent->onFloppyDriveChange();
371 }
372 }
373
374 return S_OK;
375}
376
377STDMETHODIMP FloppyDrive::GetImage (IFloppyImage2 **aFloppyImage)
378{
379 CheckComArgOutPointerValid(aFloppyImage);
380
381 AutoCaller autoCaller (this);
382 CheckComRCReturnRC (autoCaller.rc());
383
384 AutoReadLock alock (this);
385
386 m->image.queryInterfaceTo (aFloppyImage);
387
388 return S_OK;
389}
390
391STDMETHODIMP FloppyDrive::GetHostDrive (IHostFloppyDrive **aHostDrive)
392{
393 CheckComArgOutPointerValid(aHostDrive);
394
395 AutoCaller autoCaller (this);
396 CheckComRCReturnRC (autoCaller.rc());
397
398 AutoReadLock alock (this);
399
400 m->hostDrive.queryInterfaceTo (aHostDrive);
401
402 return S_OK;
403}
404
405// public methods only for internal purposes
406/////////////////////////////////////////////////////////////////////////////
407
408/**
409 * Loads settings from the given machine node. May be called once right after
410 * this object creation.
411 *
412 * @param aMachineNode <Machine> node.
413 *
414 * @note Locks this object for writing.
415 */
416HRESULT FloppyDrive::loadSettings (const settings::Key &aMachineNode)
417{
418 using namespace settings;
419
420 AssertReturn (!aMachineNode.isNull(), E_FAIL);
421
422 AutoCaller autoCaller (this);
423 AssertComRCReturnRC (autoCaller.rc());
424
425 AutoWriteLock alock (this);
426
427 /* Note: we assume that the default values for attributes of optional
428 * nodes are assigned in the Data::Data() constructor and don't do it
429 * here. It implies that this method may only be called after constructing
430 * a new BIOSSettings object while all its data fields are in the default
431 * values. Exceptions are fields whose creation time defaults don't match
432 * values that should be applied when these fields are not explicitly set
433 * in the settings file (for backwards compatibility reasons). This takes
434 * place when a setting of a newly created object must default to A while
435 * the same setting of an object loaded from the old settings file must
436 * default to B. */
437
438 HRESULT rc = S_OK;
439
440 /* Floppy drive (required, contains either Image or HostDrive or nothing) */
441 Key floppyDriveNode = aMachineNode.key ("FloppyDrive");
442
443 /* optional, defaults to true */
444 m->enabled = floppyDriveNode.value <bool> ("enabled");
445
446 Key typeNode;
447
448 if (!(typeNode = floppyDriveNode.findKey ("Image")).isNull())
449 {
450 Guid uuid = typeNode.value <Guid> ("uuid");
451 rc = MountImage (uuid);
452 CheckComRCReturnRC (rc);
453 }
454 else if (!(typeNode = floppyDriveNode.findKey ("HostDrive")).isNull())
455 {
456
457 Bstr src = typeNode.stringValue ("src");
458
459 /* find the correspoding object */
460 ComObjPtr <Host> host = mParent->virtualBox()->host();
461
462 ComPtr <IHostFloppyDriveCollection> coll;
463 rc = host->COMGETTER(FloppyDrives) (coll.asOutParam());
464 AssertComRC (rc);
465
466 ComPtr <IHostFloppyDrive> drive;
467 rc = coll->FindByName (src, drive.asOutParam());
468 if (SUCCEEDED (rc))
469 {
470 rc = CaptureHostDrive (drive);
471 CheckComRCReturnRC (rc);
472 }
473 else if (rc == E_INVALIDARG)
474 {
475 /* the host DVD drive is not currently available. we
476 * assume it will be available later and create an
477 * extra object now */
478 ComObjPtr <HostFloppyDrive> hostDrive;
479 hostDrive.createObject();
480 rc = hostDrive->init (src);
481 AssertComRC (rc);
482 rc = CaptureHostDrive (hostDrive);
483 CheckComRCReturnRC (rc);
484 }
485 else
486 AssertComRC (rc);
487 }
488
489 return S_OK;
490}
491
492/**
493 * Saves settings to the given machine node.
494 *
495 * @param aMachineNode <Machine> node.
496 *
497 * @note Locks this object for reading.
498 */
499HRESULT FloppyDrive::saveSettings (settings::Key &aMachineNode)
500{
501 using namespace settings;
502
503 AssertReturn (!aMachineNode.isNull(), E_FAIL);
504
505 AutoCaller autoCaller (this);
506 AssertComRCReturnRC (autoCaller.rc());
507
508 AutoReadLock alock (this);
509
510 Key node = aMachineNode.createKey ("FloppyDrive");
511
512 node.setValue <bool> ("enabled", !!m->enabled);
513
514 switch (m->state)
515 {
516 case DriveState_ImageMounted:
517 {
518 Assert (!m->image.isNull());
519
520 Guid id;
521 HRESULT rc = m->image->COMGETTER(Id) (id.asOutParam());
522 AssertComRC (rc);
523 Assert (!id.isEmpty());
524
525 Key imageNode = node.createKey ("Image");
526 imageNode.setValue <Guid> ("uuid", id);
527 break;
528 }
529 case DriveState_HostDriveCaptured:
530 {
531 Assert (!m->hostDrive.isNull());
532
533 Bstr name;
534 HRESULT rc = m->hostDrive->COMGETTER(Name) (name.asOutParam());
535 AssertComRC (rc);
536 Assert (!name.isEmpty());
537
538 Key hostDriveNode = node.createKey ("HostDrive");
539 hostDriveNode.setValue <Bstr> ("src", name);
540 break;
541 }
542 case DriveState_NotMounted:
543 /* do nothing, i.e.leave the drive node empty */
544 break;
545 default:
546 ComAssertMsgFailedRet (("Invalid drive state: %d", m->state),
547 E_FAIL);
548 }
549
550 return S_OK;
551}
552
553/**
554 * @note Locks this object for writing.
555 */
556bool FloppyDrive::rollback()
557{
558 /* sanity */
559 AutoCaller autoCaller (this);
560 AssertComRCReturn (autoCaller.rc(), false);
561
562 /* we need adep for the state check */
563 Machine::AutoAnyStateDependency adep (mParent);
564 AssertComRCReturn (adep.rc(), false);
565
566 AutoWriteLock alock (this);
567
568 bool changed = false;
569
570 if (m.isBackedUp())
571 {
572 /* we need to check all data to see whether anything will be changed
573 * after rollback */
574 changed = m.hasActualChanges();
575
576 if (changed)
577 {
578 Data *oldData = m.backedUpData();
579
580 if (!m->image.isNull() &&
581 !oldData->image.equalsTo (m->image))
582 {
583 /* detach the current image that will go away after rollback */
584 m->image->detachFrom (mParent->id(), mParent->snapshotId());
585
586 /* unlock the image for reading if the VM is online */
587 if (Global::IsOnline (adep.machineState()))
588 {
589 HRESULT rc = m->image->UnlockRead (NULL);
590 AssertComRC (rc);
591 }
592 }
593 }
594
595 m.rollback();
596 }
597
598 return changed;
599}
600
601/**
602 * @note Locks this object for writing, together with the peer object (also for
603 * writing) if there is one.
604 */
605void FloppyDrive::commit()
606{
607 /* sanity */
608 AutoCaller autoCaller (this);
609 AssertComRCReturnVoid (autoCaller.rc());
610
611 /* sanity too */
612 AutoCaller peerCaller (mPeer);
613 AssertComRCReturnVoid (peerCaller.rc());
614
615 /* we need adep for the state check */
616 Machine::AutoAnyStateDependency adep (mParent);
617 AssertComRCReturnVoid (adep.rc());
618
619 /* lock both for writing since we modify both (mPeer is "master" so locked
620 * first) */
621 AutoMultiWriteLock2 alock (mPeer, this);
622
623 if (m.isBackedUp())
624 {
625 Data *oldData = m.backedUpData();
626
627 if (!oldData->image.isNull() &&
628 !oldData->image.equalsTo (m->image))
629 {
630 /* detach the old image that will go away after commit */
631 oldData->image->detachFrom (mParent->id(), mParent->snapshotId());
632
633 /* unlock the image for reading if the VM is online */
634 if (Global::IsOnline (adep.machineState()))
635 {
636 HRESULT rc = oldData->image->UnlockRead (NULL);
637 AssertComRC (rc);
638 }
639 }
640
641 m.commit();
642 if (mPeer)
643 {
644 /* attach new data to the peer and reshare it */
645 mPeer->m.attach (m);
646 }
647 }
648}
649
650/**
651 * @note Locks this object for writing, together with the peer object (locked
652 * for reading) if there is one.
653 */
654void FloppyDrive::copyFrom (FloppyDrive *aThat)
655{
656 /* sanity */
657 AutoCaller autoCaller (this);
658 AssertComRCReturnVoid (autoCaller.rc());
659
660 /* sanity too */
661 AutoCaller thatCaller (aThat);
662 AssertComRCReturnVoid (thatCaller.rc());
663
664 /* peer is not modified, lock it for reading (aThat is "master" so locked
665 * first) */
666 AutoMultiLock2 alock (aThat->rlock(), this->wlock());
667
668 /* this will back up current data */
669 m.assignCopy (aThat->m);
670}
671
672/**
673 * Helper to unmount a drive.
674 *
675 * @note Must be called from under this object's write lock.
676 */
677HRESULT FloppyDrive::unmount()
678{
679 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
680
681 m.backup();
682
683 if (m->image)
684 m->image.setNull();
685 if (m->hostDrive)
686 m->hostDrive.setNull();
687
688 m->state = DriveState_NotMounted;
689
690 return S_OK;
691}
692
693// private methods
694/////////////////////////////////////////////////////////////////////////////
695
696/* 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