VirtualBox

source: vbox/trunk/src/VBox/Main/SnapshotImpl.cpp@ 5218

Last change on this file since 5218 was 5218, checked in by vboxsync, 17 years ago

Main: Fixed getDVDImageUsage()/getFloppyImageUsage() to a) prevent from unregistering images referred to by snapshots and b) avoid accessing Limited VMs (#2410).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.1 KB
Line 
1/** @file
2 *
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include "SnapshotImpl.h"
19
20#include "MachineImpl.h"
21#include "Logging.h"
22
23#include <iprt/path.h>
24#include <VBox/param.h>
25#include <VBox/err.h>
26
27#include <algorithm>
28
29// constructor / destructor
30////////////////////////////////////////////////////////////////////////////////
31
32Snapshot::Data::Data()
33{
34 mTimeStamp = 0;
35};
36
37Snapshot::Data::~Data()
38{
39};
40
41HRESULT Snapshot::FinalConstruct()
42{
43 LogFlowMember (("Snapshot::FinalConstruct()\n"));
44 return S_OK;
45}
46
47void Snapshot::FinalRelease()
48{
49 LogFlowMember (("Snapshot::FinalRelease()\n"));
50 uninit();
51}
52
53/**
54 * Initializes the instance
55 *
56 * @param aId id of the snapshot
57 * @param aName name of the snapshot
58 * @param aDescription name of the snapshot (NULL if no description)
59 * @param aTimeStamp timestamp of the snapshot, in ms since 1970-01-01 UTC
60 * @param aMachine machine associated with this snapshot
61 * @param aParent parent snapshot (NULL if no parent)
62 */
63HRESULT Snapshot::init (const Guid &aId, INPTR BSTR aName, INPTR BSTR aDescription,
64 LONG64 aTimeStamp, SnapshotMachine *aMachine,
65 Snapshot *aParent)
66{
67 LogFlowMember (("Snapshot::init(aParent=%p)\n", aParent));
68
69 ComAssertRet (!aId.isEmpty() && aName && aMachine, E_INVALIDARG);
70
71 AutoLock alock (this);
72 ComAssertRet (!isReady(), E_UNEXPECTED);
73
74 mParent = aParent;
75
76 mData.mId = aId;
77 mData.mName = aName;
78 mData.mDescription = aDescription;
79 mData.mTimeStamp = aTimeStamp;
80 mData.mMachine = aMachine;
81
82 if (aParent)
83 aParent->addDependentChild (this);
84
85 setReady (true);
86
87 return S_OK;
88}
89
90/**
91 * Uninitializes the instance and sets the ready flag to FALSE.
92 * Called either from FinalRelease(), by the parent when it gets destroyed,
93 * or by a third party when it decides this object is no more valid.
94 */
95void Snapshot::uninit()
96{
97 LogFlowMember (("Snapshot::uninit()\n"));
98
99 AutoLock alock (this);
100
101 LogFlowMember (("Snapshot::uninit(): isReady=%d\n", isReady()));
102 if (!isReady())
103 return;
104
105 // uninit all children
106 uninitDependentChildren();
107
108 setReady (false);
109
110 if (mParent)
111 {
112 alock.leave();
113 mParent->removeDependentChild (this);
114 alock.enter();
115 mParent.setNull();
116 }
117
118 if (mData.mMachine)
119 {
120 mData.mMachine->uninit();
121 mData.mMachine.setNull();
122 }
123}
124
125/**
126 * Discards the current snapshot by removing it from the tree of snapshots
127 * and reparenting its children.
128 * This method also calls #uninit() in case of success.
129 */
130void Snapshot::discard()
131{
132 LogFlowMember (("Snapshot::discard()\n"));
133
134 AutoLock alock (this);
135 AssertReturn (isReady(), (void) 0);
136
137 {
138 AutoLock chLock (childrenLock());
139 AssertReturn (!!mParent || children().size() <= 1, (void) 0);
140
141 for (SnapshotList::const_iterator it = children().begin();
142 it != children().end(); ++ it)
143 {
144 ComObjPtr <Snapshot> child = *it;
145 AutoLock childLock (child);
146 // reparent the child
147 child->mParent = mParent;
148 if (mParent)
149 mParent->addDependentChild (child);
150 }
151 }
152
153 // detach all our children to avoid their uninit in #uninit()
154 removeDependentChildren();
155
156 // finalize uninitialization
157 uninit();
158}
159
160// ISnapshot methods
161////////////////////////////////////////////////////////////////////////////////
162
163STDMETHODIMP Snapshot::COMGETTER(Id) (GUIDPARAMOUT aId)
164{
165 if (!aId)
166 return E_POINTER;
167
168 AutoLock alock (this);
169 CHECK_READY();
170
171 mData.mId.cloneTo (aId);
172 return S_OK;
173}
174
175STDMETHODIMP Snapshot::COMGETTER(Name) (BSTR *aName)
176{
177 if (!aName)
178 return E_POINTER;
179
180 AutoLock alock (this);
181 CHECK_READY();
182
183 mData.mName.cloneTo (aName);
184 return S_OK;
185}
186
187/**
188 * @note Locks this object for writing, then calls Machine::onSnapshotChange()
189 * (see its lock requirements).
190 */
191STDMETHODIMP Snapshot::COMSETTER(Name) (INPTR BSTR aName)
192{
193 if (!aName)
194 return E_INVALIDARG;
195
196 AutoLock alock (this);
197 CHECK_READY();
198
199 if (mData.mName != aName)
200 {
201 mData.mName = aName;
202
203 alock.leave(); /* Important! (child->parent locks are forbidden) */
204
205 return mData.mMachine->onSnapshotChange (this);
206 }
207
208 return S_OK;
209}
210
211STDMETHODIMP Snapshot::COMGETTER(Description) (BSTR *aDescription)
212{
213 if (!aDescription)
214 return E_POINTER;
215
216 AutoLock alock (this);
217 CHECK_READY();
218
219 mData.mDescription.cloneTo (aDescription);
220 return S_OK;
221}
222
223STDMETHODIMP Snapshot::COMSETTER(Description) (INPTR BSTR aDescription)
224{
225 if (!aDescription)
226 return E_INVALIDARG;
227
228 AutoLock alock (this);
229 CHECK_READY();
230
231 if (mData.mDescription != aDescription)
232 {
233 mData.mDescription = aDescription;
234
235 alock.leave(); /* Important! (child->parent locks are forbidden) */
236
237 return mData.mMachine->onSnapshotChange (this);
238 }
239
240 return S_OK;
241}
242
243STDMETHODIMP Snapshot::COMGETTER(TimeStamp) (LONG64 *aTimeStamp)
244{
245 if (!aTimeStamp)
246 return E_POINTER;
247
248 AutoLock alock (this);
249 CHECK_READY();
250
251 *aTimeStamp = mData.mTimeStamp;
252 return S_OK;
253}
254
255STDMETHODIMP Snapshot::COMGETTER(Online) (BOOL *aOnline)
256{
257 if (!aOnline)
258 return E_POINTER;
259
260 AutoLock alock (this);
261 CHECK_READY();
262
263 *aOnline = !stateFilePath().isNull();
264 return S_OK;
265}
266
267STDMETHODIMP Snapshot::COMGETTER(Machine) (IMachine **aMachine)
268{
269 if (!aMachine)
270 return E_POINTER;
271
272 AutoLock alock (this);
273 CHECK_READY();
274
275 mData.mMachine.queryInterfaceTo (aMachine);
276 return S_OK;
277}
278
279STDMETHODIMP Snapshot::COMGETTER(Parent) (ISnapshot **aParent)
280{
281 if (!aParent)
282 return E_POINTER;
283
284 AutoLock alock (this);
285 CHECK_READY();
286
287 mParent.queryInterfaceTo (aParent);
288 return S_OK;
289}
290
291STDMETHODIMP Snapshot::COMGETTER(Children) (ISnapshotCollection **aChildren)
292{
293 if (!aChildren)
294 return E_POINTER;
295
296 AutoLock alock (this);
297 CHECK_READY();
298
299 AutoLock chLock (childrenLock());
300
301 ComObjPtr <SnapshotCollection> collection;
302 collection.createObject();
303 collection->init (children());
304 collection.queryInterfaceTo (aChildren);
305
306 return S_OK;
307}
308
309// public methods only for internal purposes
310////////////////////////////////////////////////////////////////////////////////
311
312/**
313 * @note
314 * Must be called from under the object's lock!
315 */
316const Bstr &Snapshot::stateFilePath() const
317{
318 return mData.mMachine->ssData()->mStateFilePath;
319}
320
321/**
322 * Returns the number of children of this snapshot, including grand-children,
323 * etc.
324 */
325ULONG Snapshot::descendantCount()
326{
327 AutoLock alock(this);
328 AssertReturn (isReady(), 0);
329
330 AutoLock chLock (childrenLock());
331
332 ULONG count = children().size();
333
334 for (SnapshotList::const_iterator it = children().begin();
335 it != children().end(); ++ it)
336 {
337 count += (*it)->descendantCount();
338 }
339
340 return count;
341}
342
343/**
344 * Searches for a snapshot with the given ID among children, grand-children,
345 * etc. of this snapshot. This snapshot itself is also included in the search.
346 */
347ComObjPtr <Snapshot> Snapshot::findChildOrSelf (INPTR GUIDPARAM aId)
348{
349 ComObjPtr <Snapshot> child;
350
351 AutoLock alock (this);
352 AssertReturn (isReady(), child);
353
354 if (mData.mId == aId)
355 child = this;
356 else
357 {
358 AutoLock chLock (childrenLock());
359 for (SnapshotList::const_iterator it = children().begin();
360 !child && it != children().end(); ++ it)
361 {
362 child = (*it)->findChildOrSelf (aId);
363 }
364 }
365
366 return child;
367}
368
369/**
370 * Searches for a first snapshot with the given name among children,
371 * grand-children, etc. of this snapshot. This snapshot itself is also included
372 * in the search.
373 */
374ComObjPtr <Snapshot> Snapshot::findChildOrSelf (INPTR BSTR aName)
375{
376 ComObjPtr <Snapshot> child;
377 AssertReturn (aName, child);
378
379 AutoLock alock (this);
380 AssertReturn (isReady(), child);
381
382 if (mData.mName == aName)
383 child = this;
384 else
385 {
386 AutoLock chLock (childrenLock());
387 for (SnapshotList::const_iterator it = children().begin();
388 !child && it != children().end(); ++ it)
389 {
390 child = (*it)->findChildOrSelf (aName);
391 }
392 }
393
394 return child;
395}
396
397/**
398 * Returns @c true if the given DVD image is attached to this snapshot or any
399 * of its children, recursively.
400 *
401 * @param aId Image ID to check.
402 *
403 * @note Locks this object for reading.
404 */
405bool Snapshot::isDVDImageUsed (const Guid &aId)
406{
407 AutoReaderLock alock (this);
408 AssertReturn (isReady(), false);
409
410 AssertReturn (!mData.mMachine.isNull(), false);
411 AssertReturn (!mData.mMachine->dvdDrive().isNull(), false);
412
413 DVDDrive::Data *d = mData.mMachine->dvdDrive()->data().data();
414
415 if (d &&
416 d->mDriveState == DriveState_ImageMounted)
417 {
418 Guid id;
419 HRESULT rc = d->mDVDImage->COMGETTER(Id) (id.asOutParam());
420 AssertComRC (rc);
421 if (id == aId)
422 return true;
423 }
424
425 AutoReaderLock chLock (childrenLock());
426 for (SnapshotList::const_iterator it = children().begin();
427 it != children().end(); ++ it)
428 {
429 if ((*it)->isDVDImageUsed (aId))
430 return true;
431 }
432
433 return false;
434}
435
436/**
437 * Returns @c true if the given Floppy image is attached to this snapshot or any
438 * of its children, recursively.
439 *
440 * @param aId Image ID to check.
441 *
442 * @note Locks this object for reading.
443 */
444bool Snapshot::isFloppyImageUsed (const Guid &aId)
445{
446 AutoReaderLock alock (this);
447 AssertReturn (isReady(), false);
448
449 AssertReturn (!mData.mMachine.isNull(), false);
450 AssertReturn (!mData.mMachine->dvdDrive().isNull(), false);
451
452 FloppyDrive::Data *d = mData.mMachine->floppyDrive()->data().data();
453
454 if (d &&
455 d->mDriveState == DriveState_ImageMounted)
456 {
457 Guid id;
458 HRESULT rc = d->mFloppyImage->COMGETTER(Id) (id.asOutParam());
459 AssertComRC (rc);
460 if (id == aId)
461 return true;
462 }
463
464 AutoReaderLock chLock (childrenLock());
465 for (SnapshotList::const_iterator it = children().begin();
466 it != children().end(); ++ it)
467 {
468 if ((*it)->isFloppyImageUsed (aId))
469 return true;
470 }
471
472 return false;
473}
474
475/**
476 * Checks if the specified path change affects the saved state file path of
477 * this snapshot or any of its (grand-)children and updates it accordingly.
478 *
479 * Intended to be called by Machine::openConfigLoader() only.
480 *
481 * @param aOldPath old path (full)
482 * @param aNewPath new path (full)
483 *
484 * @note Locks this object + children for writing.
485 */
486void Snapshot::updateSavedStatePaths (const char *aOldPath, const char *aNewPath)
487{
488 LogFlowThisFunc (("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
489
490 AssertReturnVoid (aOldPath);
491 AssertReturnVoid (aNewPath);
492
493 AutoLock alock (this);
494 AssertReturnVoid (isReady());
495
496 Utf8Str path = mData.mMachine->ssData()->mStateFilePath;
497 LogFlowThisFunc (("Snap[%ls].statePath={%s}\n", mData.mName.raw(), path.raw()));
498
499 /* state file may be NULL (for offline snapshots) */
500 if (path && RTPathStartsWith (path, aOldPath))
501 {
502 path = Utf8StrFmt ("%s%s", aNewPath, path.raw() + strlen (aOldPath));
503 mData.mMachine->ssData()->mStateFilePath = path;
504
505 LogFlowThisFunc (("-> updated: {%s}\n", path.raw()));
506 }
507
508 AutoLock chLock (childrenLock());
509 for (SnapshotList::const_iterator it = children().begin();
510 it != children().end(); ++ it)
511 {
512 (*it)->updateSavedStatePaths (aOldPath, aNewPath);
513 }
514}
515
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