VirtualBox

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

Last change on this file since 15424 was 15334, checked in by vboxsync, 16 years ago

Main: #3377: Implicitly detach DVD/Floppy from an unregistered machine (as opposed to hard disks, this does not have side effects).

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