VirtualBox

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

Last change on this file since 17871 was 17255, checked in by vboxsync, 16 years ago

#3551: “Main: Replace remaining collections with safe arrays”
Replaced HostFloppyDriveCollection; tested by lelik with (GASP!) real floppy hardware.

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