[1] | 1 | /** @file
|
---|
| 2 | *
|
---|
[24279] | 3 | * COM class implementation for Snapshot and SnapshotMachine.
|
---|
[1] | 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[8155] | 7 | * Copyright (C) 2006-2007 Sun Microsystems, Inc.
|
---|
[1] | 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
|
---|
[5999] | 12 | * General Public License (GPL) as published by the Free Software
|
---|
| 13 | * Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
| 14 | * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
| 15 | * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
[8155] | 16 | *
|
---|
| 17 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
|
---|
| 18 | * Clara, CA 95054 USA or visit http://www.sun.com if you need
|
---|
| 19 | * additional information or have any questions.
|
---|
[1] | 20 | */
|
---|
| 21 |
|
---|
| 22 | #include "SnapshotImpl.h"
|
---|
| 23 |
|
---|
| 24 | #include "MachineImpl.h"
|
---|
[25200] | 25 | #include "MediumImpl.h"
|
---|
[24279] | 26 | #include "Global.h"
|
---|
| 27 |
|
---|
| 28 | // @todo these three includes are required for about one or two lines, try
|
---|
| 29 | // to remove them and put that code in shared code in MachineImplcpp
|
---|
| 30 | #include "SharedFolderImpl.h"
|
---|
| 31 | #include "USBControllerImpl.h"
|
---|
| 32 | #include "VirtualBoxImpl.h"
|
---|
| 33 |
|
---|
[1] | 34 | #include "Logging.h"
|
---|
| 35 |
|
---|
| 36 | #include <iprt/path.h>
|
---|
| 37 | #include <VBox/param.h>
|
---|
| 38 | #include <VBox/err.h>
|
---|
| 39 |
|
---|
[21823] | 40 | #include <VBox/settings.h>
|
---|
| 41 |
|
---|
[1] | 42 | ////////////////////////////////////////////////////////////////////////////////
|
---|
[23223] | 43 | //
|
---|
[24279] | 44 | // Globals
|
---|
| 45 | //
|
---|
| 46 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 47 |
|
---|
| 48 | /**
|
---|
| 49 | * Progress callback handler for lengthy operations
|
---|
| 50 | * (corresponds to the FNRTPROGRESS typedef).
|
---|
| 51 | *
|
---|
| 52 | * @param uPercentage Completetion precentage (0-100).
|
---|
| 53 | * @param pvUser Pointer to the Progress instance.
|
---|
| 54 | */
|
---|
| 55 | static DECLCALLBACK(int) progressCallback(unsigned uPercentage, void *pvUser)
|
---|
| 56 | {
|
---|
[24556] | 57 | IProgress *progress = static_cast<IProgress*>(pvUser);
|
---|
[24279] | 58 |
|
---|
| 59 | /* update the progress object */
|
---|
| 60 | if (progress)
|
---|
| 61 | progress->SetCurrentOperationProgress(uPercentage);
|
---|
| 62 |
|
---|
| 63 | return VINF_SUCCESS;
|
---|
| 64 | }
|
---|
| 65 |
|
---|
| 66 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 67 | //
|
---|
[23257] | 68 | // Snapshot private data definition
|
---|
[23223] | 69 | //
|
---|
| 70 | ////////////////////////////////////////////////////////////////////////////////
|
---|
[1] | 71 |
|
---|
[21835] | 72 | typedef std::list< ComObjPtr<Snapshot> > SnapshotsList;
|
---|
[21823] | 73 |
|
---|
| 74 | struct Snapshot::Data
|
---|
[1] | 75 | {
|
---|
[21823] | 76 | Data()
|
---|
| 77 | {
|
---|
| 78 | RTTimeSpecSetMilli(&timeStamp, 0);
|
---|
| 79 | };
|
---|
[1] | 80 |
|
---|
[21823] | 81 | ~Data()
|
---|
| 82 | {}
|
---|
| 83 |
|
---|
[22173] | 84 | Guid uuid;
|
---|
| 85 | Utf8Str strName;
|
---|
| 86 | Utf8Str strDescription;
|
---|
[21823] | 87 | RTTIMESPEC timeStamp;
|
---|
| 88 | ComObjPtr<SnapshotMachine> pMachine;
|
---|
| 89 |
|
---|
[25152] | 90 | /** weak VirtualBox parent */
|
---|
| 91 | const ComObjPtr<VirtualBox, ComWeakRef> pVirtualBox;
|
---|
| 92 |
|
---|
| 93 | // pParent and llChildren are protected by Machine::snapshotsTreeLockHandle()
|
---|
| 94 | ComObjPtr<Snapshot> pParent;
|
---|
| 95 | SnapshotsList llChildren;
|
---|
[1] | 96 | };
|
---|
| 97 |
|
---|
[21823] | 98 | ////////////////////////////////////////////////////////////////////////////////
|
---|
[23223] | 99 | //
|
---|
| 100 | // Constructor / destructor
|
---|
| 101 | //
|
---|
| 102 | ////////////////////////////////////////////////////////////////////////////////
|
---|
[21823] | 103 |
|
---|
[1] | 104 | HRESULT Snapshot::FinalConstruct()
|
---|
| 105 | {
|
---|
| 106 | LogFlowMember (("Snapshot::FinalConstruct()\n"));
|
---|
| 107 | return S_OK;
|
---|
| 108 | }
|
---|
| 109 |
|
---|
| 110 | void Snapshot::FinalRelease()
|
---|
| 111 | {
|
---|
| 112 | LogFlowMember (("Snapshot::FinalRelease()\n"));
|
---|
| 113 | uninit();
|
---|
| 114 | }
|
---|
| 115 |
|
---|
| 116 | /**
|
---|
| 117 | * Initializes the instance
|
---|
| 118 | *
|
---|
| 119 | * @param aId id of the snapshot
|
---|
| 120 | * @param aName name of the snapshot
|
---|
| 121 | * @param aDescription name of the snapshot (NULL if no description)
|
---|
| 122 | * @param aTimeStamp timestamp of the snapshot, in ms since 1970-01-01 UTC
|
---|
| 123 | * @param aMachine machine associated with this snapshot
|
---|
| 124 | * @param aParent parent snapshot (NULL if no parent)
|
---|
| 125 | */
|
---|
[21823] | 126 | HRESULT Snapshot::init(VirtualBox *aVirtualBox,
|
---|
| 127 | const Guid &aId,
|
---|
[22173] | 128 | const Utf8Str &aName,
|
---|
| 129 | const Utf8Str &aDescription,
|
---|
| 130 | const RTTIMESPEC &aTimeStamp,
|
---|
[21823] | 131 | SnapshotMachine *aMachine,
|
---|
| 132 | Snapshot *aParent)
|
---|
[1] | 133 | {
|
---|
[22173] | 134 | LogFlowMember(("Snapshot::init(uuid: %s, aParent->uuid=%s)\n", aId.toString().c_str(), (aParent) ? aParent->m->uuid.toString().c_str() : ""));
|
---|
[1] | 135 |
|
---|
[22173] | 136 | ComAssertRet (!aId.isEmpty() && !aName.isEmpty() && aMachine, E_INVALIDARG);
|
---|
[1] | 137 |
|
---|
[21823] | 138 | /* Enclose the state transition NotReady->InInit->Ready */
|
---|
| 139 | AutoInitSpan autoInitSpan(this);
|
---|
| 140 | AssertReturn(autoInitSpan.isOk(), E_FAIL);
|
---|
[1] | 141 |
|
---|
[21823] | 142 | m = new Data;
|
---|
| 143 |
|
---|
| 144 | /* share parent weakly */
|
---|
[25152] | 145 | unconst(m->pVirtualBox) = aVirtualBox;
|
---|
[21823] | 146 |
|
---|
[25152] | 147 | m->pParent = aParent;
|
---|
[1] | 148 |
|
---|
[22173] | 149 | m->uuid = aId;
|
---|
| 150 | m->strName = aName;
|
---|
| 151 | m->strDescription = aDescription;
|
---|
[21823] | 152 | m->timeStamp = aTimeStamp;
|
---|
| 153 | m->pMachine = aMachine;
|
---|
[1] | 154 |
|
---|
| 155 | if (aParent)
|
---|
[21823] | 156 | aParent->m->llChildren.push_back(this);
|
---|
[1] | 157 |
|
---|
[21823] | 158 | /* Confirm a successful initialization when it's the case */
|
---|
| 159 | autoInitSpan.setSucceeded();
|
---|
[1] | 160 |
|
---|
| 161 | return S_OK;
|
---|
| 162 | }
|
---|
| 163 |
|
---|
| 164 | /**
|
---|
| 165 | * Uninitializes the instance and sets the ready flag to FALSE.
|
---|
| 166 | * Called either from FinalRelease(), by the parent when it gets destroyed,
|
---|
| 167 | * or by a third party when it decides this object is no more valid.
|
---|
| 168 | */
|
---|
| 169 | void Snapshot::uninit()
|
---|
| 170 | {
|
---|
| 171 | LogFlowMember (("Snapshot::uninit()\n"));
|
---|
| 172 |
|
---|
[21823] | 173 | /* Enclose the state transition Ready->InUninit->NotReady */
|
---|
[21878] | 174 | AutoUninitSpan autoUninitSpan(this);
|
---|
[21823] | 175 | if (autoUninitSpan.uninitDone())
|
---|
[21686] | 176 | return;
|
---|
| 177 |
|
---|
[1] | 178 | // uninit all children
|
---|
[21823] | 179 | SnapshotsList::iterator it;
|
---|
| 180 | for (it = m->llChildren.begin();
|
---|
| 181 | it != m->llChildren.end();
|
---|
[21824] | 182 | ++it)
|
---|
[21823] | 183 | {
|
---|
| 184 | Snapshot *pChild = *it;
|
---|
[25152] | 185 | pChild->m->pParent.setNull();
|
---|
[21823] | 186 | pChild->uninit();
|
---|
| 187 | }
|
---|
| 188 | m->llChildren.clear(); // this unsets all the ComPtrs and probably calls delete
|
---|
[1] | 189 |
|
---|
[25152] | 190 | if (m->pParent)
|
---|
| 191 | deparent();
|
---|
[21823] | 192 |
|
---|
| 193 | if (m->pMachine)
|
---|
[1] | 194 | {
|
---|
[21823] | 195 | m->pMachine->uninit();
|
---|
| 196 | m->pMachine.setNull();
|
---|
[1] | 197 | }
|
---|
[21835] | 198 |
|
---|
| 199 | delete m;
|
---|
| 200 | m = NULL;
|
---|
[1] | 201 | }
|
---|
| 202 |
|
---|
| 203 | /**
|
---|
| 204 | * Discards the current snapshot by removing it from the tree of snapshots
|
---|
| 205 | * and reparenting its children.
|
---|
[21823] | 206 | *
|
---|
| 207 | * After this, the caller must call uninit() on the snapshot. We can't call
|
---|
| 208 | * that from here because if we do, the AutoUninitSpan waits forever for
|
---|
| 209 | * the number of callers to become 0 (it is 1 because of the AutoCaller in here).
|
---|
| 210 | *
|
---|
| 211 | * NOTE: this does NOT lock the snapshot, it is assumed that the caller has
|
---|
[21835] | 212 | * locked a) the machine and b) the snapshots tree in write mode!
|
---|
[1] | 213 | */
|
---|
[21823] | 214 | void Snapshot::beginDiscard()
|
---|
[1] | 215 | {
|
---|
[21823] | 216 | AutoCaller autoCaller(this);
|
---|
| 217 | if (FAILED(autoCaller.rc()))
|
---|
| 218 | return;
|
---|
[1] | 219 |
|
---|
[21823] | 220 | /* for now, the snapshot must have only one child when discarded,
|
---|
| 221 | * or no children at all */
|
---|
| 222 | AssertReturnVoid(m->llChildren.size() <= 1);
|
---|
[1] | 223 |
|
---|
[25152] | 224 | ComObjPtr<Snapshot> parentSnapshot = m->pParent;
|
---|
[21823] | 225 |
|
---|
| 226 | /// @todo (dmik):
|
---|
| 227 | // when we introduce clones later, discarding the snapshot
|
---|
| 228 | // will affect the current and first snapshots of clones, if they are
|
---|
| 229 | // direct children of this snapshot. So we will need to lock machines
|
---|
| 230 | // associated with child snapshots as well and update mCurrentSnapshot
|
---|
| 231 | // and/or mFirstSnapshot fields.
|
---|
| 232 |
|
---|
| 233 | if (this == m->pMachine->mData->mCurrentSnapshot)
|
---|
[1] | 234 | {
|
---|
[21823] | 235 | m->pMachine->mData->mCurrentSnapshot = parentSnapshot;
|
---|
[1] | 236 |
|
---|
[21823] | 237 | /* we've changed the base of the current state so mark it as
|
---|
| 238 | * modified as it no longer guaranteed to be its copy */
|
---|
| 239 | m->pMachine->mData->mCurrentStateModified = TRUE;
|
---|
| 240 | }
|
---|
| 241 |
|
---|
| 242 | if (this == m->pMachine->mData->mFirstSnapshot)
|
---|
| 243 | {
|
---|
| 244 | if (m->llChildren.size() == 1)
|
---|
[1] | 245 | {
|
---|
[21835] | 246 | ComObjPtr<Snapshot> childSnapshot = m->llChildren.front();
|
---|
[21823] | 247 | m->pMachine->mData->mFirstSnapshot = childSnapshot;
|
---|
[1] | 248 | }
|
---|
[21823] | 249 | else
|
---|
| 250 | m->pMachine->mData->mFirstSnapshot.setNull();
|
---|
[1] | 251 | }
|
---|
| 252 |
|
---|
[21823] | 253 | // reparent our children
|
---|
| 254 | for (SnapshotsList::const_iterator it = m->llChildren.begin();
|
---|
| 255 | it != m->llChildren.end();
|
---|
| 256 | ++it)
|
---|
| 257 | {
|
---|
[21835] | 258 | ComObjPtr<Snapshot> child = *it;
|
---|
[25310] | 259 | AutoWriteLock childLock(child COMMA_LOCKVAL_SRC_POS);
|
---|
[1] | 260 |
|
---|
[25152] | 261 | child->m->pParent = m->pParent;
|
---|
| 262 | if (m->pParent)
|
---|
| 263 | m->pParent->m->llChildren.push_back(child);
|
---|
[21823] | 264 | }
|
---|
| 265 |
|
---|
| 266 | // clear our own children list (since we reparented the children)
|
---|
| 267 | m->llChildren.clear();
|
---|
[1] | 268 | }
|
---|
| 269 |
|
---|
[25152] | 270 | /**
|
---|
| 271 | * Internal helper that removes "this" from the list of children of its
|
---|
| 272 | * parent. Used in uninit() and other places when reparenting is necessary.
|
---|
| 273 | *
|
---|
| 274 | * The caller must hold the snapshots tree lock!
|
---|
| 275 | */
|
---|
| 276 | void Snapshot::deparent()
|
---|
| 277 | {
|
---|
| 278 | SnapshotsList &llParent = m->pParent->m->llChildren;
|
---|
| 279 | for (SnapshotsList::iterator it = llParent.begin();
|
---|
| 280 | it != llParent.end();
|
---|
| 281 | ++it)
|
---|
| 282 | {
|
---|
| 283 | Snapshot *pParentsChild = *it;
|
---|
| 284 | if (this == pParentsChild)
|
---|
| 285 | {
|
---|
| 286 | llParent.erase(it);
|
---|
| 287 | break;
|
---|
| 288 | }
|
---|
| 289 | }
|
---|
| 290 |
|
---|
| 291 | m->pParent.setNull();
|
---|
| 292 | }
|
---|
| 293 |
|
---|
[1] | 294 | ////////////////////////////////////////////////////////////////////////////////
|
---|
[23223] | 295 | //
|
---|
| 296 | // ISnapshot public methods
|
---|
| 297 | //
|
---|
| 298 | ////////////////////////////////////////////////////////////////////////////////
|
---|
[1] | 299 |
|
---|
[19239] | 300 | STDMETHODIMP Snapshot::COMGETTER(Id) (BSTR *aId)
|
---|
[1] | 301 | {
|
---|
[14972] | 302 | CheckComArgOutPointerValid(aId);
|
---|
[1] | 303 |
|
---|
[21823] | 304 | AutoCaller autoCaller(this);
|
---|
[25149] | 305 | if (FAILED(autoCaller.rc())) return autoCaller.rc();
|
---|
[1] | 306 |
|
---|
[25310] | 307 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[21823] | 308 |
|
---|
[22173] | 309 | m->uuid.toUtf16().cloneTo(aId);
|
---|
[1] | 310 | return S_OK;
|
---|
| 311 | }
|
---|
| 312 |
|
---|
| 313 | STDMETHODIMP Snapshot::COMGETTER(Name) (BSTR *aName)
|
---|
| 314 | {
|
---|
[14972] | 315 | CheckComArgOutPointerValid(aName);
|
---|
[1] | 316 |
|
---|
[21823] | 317 | AutoCaller autoCaller(this);
|
---|
[25149] | 318 | if (FAILED(autoCaller.rc())) return autoCaller.rc();
|
---|
[1] | 319 |
|
---|
[25310] | 320 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[21823] | 321 |
|
---|
[22173] | 322 | m->strName.cloneTo(aName);
|
---|
[1] | 323 | return S_OK;
|
---|
| 324 | }
|
---|
| 325 |
|
---|
| 326 | /**
|
---|
| 327 | * @note Locks this object for writing, then calls Machine::onSnapshotChange()
|
---|
| 328 | * (see its lock requirements).
|
---|
| 329 | */
|
---|
[22173] | 330 | STDMETHODIMP Snapshot::COMSETTER(Name)(IN_BSTR aName)
|
---|
[1] | 331 | {
|
---|
[14972] | 332 | CheckComArgNotNull(aName);
|
---|
[1] | 333 |
|
---|
[21823] | 334 | AutoCaller autoCaller(this);
|
---|
[25149] | 335 | if (FAILED(autoCaller.rc())) return autoCaller.rc();
|
---|
[1] | 336 |
|
---|
[22173] | 337 | Utf8Str strName(aName);
|
---|
| 338 |
|
---|
[25310] | 339 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[21823] | 340 |
|
---|
[22173] | 341 | if (m->strName != strName)
|
---|
[1] | 342 | {
|
---|
[22173] | 343 | m->strName = strName;
|
---|
[1] | 344 |
|
---|
| 345 | alock.leave(); /* Important! (child->parent locks are forbidden) */
|
---|
| 346 |
|
---|
[21823] | 347 | return m->pMachine->onSnapshotChange(this);
|
---|
[1] | 348 | }
|
---|
| 349 |
|
---|
| 350 | return S_OK;
|
---|
| 351 | }
|
---|
| 352 |
|
---|
| 353 | STDMETHODIMP Snapshot::COMGETTER(Description) (BSTR *aDescription)
|
---|
| 354 | {
|
---|
[14972] | 355 | CheckComArgOutPointerValid(aDescription);
|
---|
[1] | 356 |
|
---|
[21823] | 357 | AutoCaller autoCaller(this);
|
---|
[25149] | 358 | if (FAILED(autoCaller.rc())) return autoCaller.rc();
|
---|
[1] | 359 |
|
---|
[25310] | 360 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[21823] | 361 |
|
---|
[22173] | 362 | m->strDescription.cloneTo(aDescription);
|
---|
[1] | 363 | return S_OK;
|
---|
| 364 | }
|
---|
| 365 |
|
---|
[15051] | 366 | STDMETHODIMP Snapshot::COMSETTER(Description) (IN_BSTR aDescription)
|
---|
[1] | 367 | {
|
---|
[14972] | 368 | CheckComArgNotNull(aDescription);
|
---|
[1] | 369 |
|
---|
[21823] | 370 | AutoCaller autoCaller(this);
|
---|
[25149] | 371 | if (FAILED(autoCaller.rc())) return autoCaller.rc();
|
---|
[1] | 372 |
|
---|
[22173] | 373 | Utf8Str strDescription(aDescription);
|
---|
| 374 |
|
---|
[25310] | 375 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[21823] | 376 |
|
---|
[22173] | 377 | if (m->strDescription != strDescription)
|
---|
[1] | 378 | {
|
---|
[22173] | 379 | m->strDescription = strDescription;
|
---|
[1] | 380 |
|
---|
| 381 | alock.leave(); /* Important! (child->parent locks are forbidden) */
|
---|
| 382 |
|
---|
[21823] | 383 | return m->pMachine->onSnapshotChange(this);
|
---|
[1] | 384 | }
|
---|
| 385 |
|
---|
| 386 | return S_OK;
|
---|
| 387 | }
|
---|
| 388 |
|
---|
| 389 | STDMETHODIMP Snapshot::COMGETTER(TimeStamp) (LONG64 *aTimeStamp)
|
---|
| 390 | {
|
---|
[14972] | 391 | CheckComArgOutPointerValid(aTimeStamp);
|
---|
[1] | 392 |
|
---|
[21823] | 393 | AutoCaller autoCaller(this);
|
---|
[25149] | 394 | if (FAILED(autoCaller.rc())) return autoCaller.rc();
|
---|
[1] | 395 |
|
---|
[25310] | 396 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[21823] | 397 |
|
---|
| 398 | *aTimeStamp = RTTimeSpecGetMilli(&m->timeStamp);
|
---|
[1] | 399 | return S_OK;
|
---|
| 400 | }
|
---|
| 401 |
|
---|
[22624] | 402 | STDMETHODIMP Snapshot::COMGETTER(Online)(BOOL *aOnline)
|
---|
[1] | 403 | {
|
---|
[14972] | 404 | CheckComArgOutPointerValid(aOnline);
|
---|
[1] | 405 |
|
---|
[21823] | 406 | AutoCaller autoCaller(this);
|
---|
[25149] | 407 | if (FAILED(autoCaller.rc())) return autoCaller.rc();
|
---|
[1] | 408 |
|
---|
[25310] | 409 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[21823] | 410 |
|
---|
[24196] | 411 | *aOnline = !stateFilePath().isEmpty();
|
---|
[1] | 412 | return S_OK;
|
---|
| 413 | }
|
---|
| 414 |
|
---|
| 415 | STDMETHODIMP Snapshot::COMGETTER(Machine) (IMachine **aMachine)
|
---|
| 416 | {
|
---|
[14972] | 417 | CheckComArgOutPointerValid(aMachine);
|
---|
[1] | 418 |
|
---|
[21823] | 419 | AutoCaller autoCaller(this);
|
---|
[25149] | 420 | if (FAILED(autoCaller.rc())) return autoCaller.rc();
|
---|
[1] | 421 |
|
---|
[25310] | 422 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[21823] | 423 |
|
---|
| 424 | m->pMachine.queryInterfaceTo(aMachine);
|
---|
[1] | 425 | return S_OK;
|
---|
| 426 | }
|
---|
| 427 |
|
---|
| 428 | STDMETHODIMP Snapshot::COMGETTER(Parent) (ISnapshot **aParent)
|
---|
| 429 | {
|
---|
[14972] | 430 | CheckComArgOutPointerValid(aParent);
|
---|
[1] | 431 |
|
---|
[21823] | 432 | AutoCaller autoCaller(this);
|
---|
[25149] | 433 | if (FAILED(autoCaller.rc())) return autoCaller.rc();
|
---|
[1] | 434 |
|
---|
[25310] | 435 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[21823] | 436 |
|
---|
[25152] | 437 | m->pParent.queryInterfaceTo(aParent);
|
---|
[1] | 438 | return S_OK;
|
---|
| 439 | }
|
---|
| 440 |
|
---|
[21878] | 441 | STDMETHODIMP Snapshot::COMGETTER(Children) (ComSafeArrayOut(ISnapshot *, aChildren))
|
---|
[1] | 442 | {
|
---|
[17260] | 443 | CheckComArgOutSafeArrayPointerValid(aChildren);
|
---|
[1] | 444 |
|
---|
[21823] | 445 | AutoCaller autoCaller(this);
|
---|
[25149] | 446 | if (FAILED(autoCaller.rc())) return autoCaller.rc();
|
---|
[1] | 447 |
|
---|
[25310] | 448 | AutoReadLock alock(m->pMachine->snapshotsTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
|
---|
| 449 | AutoReadLock block(this->lockHandle() COMMA_LOCKVAL_SRC_POS);
|
---|
[1] | 450 |
|
---|
[21823] | 451 | SafeIfaceArray<ISnapshot> collection(m->llChildren);
|
---|
| 452 | collection.detachTo(ComSafeArrayOutArg(aChildren));
|
---|
[1] | 453 |
|
---|
| 454 | return S_OK;
|
---|
| 455 | }
|
---|
| 456 |
|
---|
| 457 | ////////////////////////////////////////////////////////////////////////////////
|
---|
[24279] | 458 | //
|
---|
| 459 | // Snapshot public internal methods
|
---|
| 460 | //
|
---|
| 461 | ////////////////////////////////////////////////////////////////////////////////
|
---|
[1] | 462 |
|
---|
| 463 | /**
|
---|
[25152] | 464 | * Returns the parent snapshot or NULL if there's none. Must have caller + locking!
|
---|
| 465 | * @return
|
---|
| 466 | */
|
---|
| 467 | const ComObjPtr<Snapshot>& Snapshot::getParent() const
|
---|
| 468 | {
|
---|
| 469 | return m->pParent;
|
---|
| 470 | }
|
---|
| 471 |
|
---|
| 472 | /**
|
---|
[1] | 473 | * @note
|
---|
| 474 | * Must be called from under the object's lock!
|
---|
| 475 | */
|
---|
[24196] | 476 | const Utf8Str& Snapshot::stateFilePath() const
|
---|
[1] | 477 | {
|
---|
[21823] | 478 | return m->pMachine->mSSData->mStateFilePath;
|
---|
[1] | 479 | }
|
---|
| 480 |
|
---|
[21835] | 481 | /**
|
---|
| 482 | * Returns the number of direct child snapshots, without grandchildren.
|
---|
| 483 | * Does not recurse.
|
---|
| 484 | * @return
|
---|
| 485 | */
|
---|
[21826] | 486 | ULONG Snapshot::getChildrenCount()
|
---|
[1] | 487 | {
|
---|
[21823] | 488 | AutoCaller autoCaller(this);
|
---|
| 489 | AssertComRC(autoCaller.rc());
|
---|
[1] | 490 |
|
---|
[25310] | 491 | AutoReadLock treeLock(m->pMachine->snapshotsTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
|
---|
[21826] | 492 | return (ULONG)m->llChildren.size();
|
---|
| 493 | }
|
---|
| 494 |
|
---|
[21835] | 495 | /**
|
---|
| 496 | * Implementation method for getAllChildrenCount() so we request the
|
---|
| 497 | * tree lock only once before recursing. Don't call directly.
|
---|
| 498 | * @return
|
---|
| 499 | */
|
---|
[21826] | 500 | ULONG Snapshot::getAllChildrenCountImpl()
|
---|
| 501 | {
|
---|
| 502 | AutoCaller autoCaller(this);
|
---|
| 503 | AssertComRC(autoCaller.rc());
|
---|
| 504 |
|
---|
[21823] | 505 | ULONG count = (ULONG)m->llChildren.size();
|
---|
| 506 | for (SnapshotsList::const_iterator it = m->llChildren.begin();
|
---|
| 507 | it != m->llChildren.end();
|
---|
| 508 | ++it)
|
---|
[1] | 509 | {
|
---|
[21826] | 510 | count += (*it)->getAllChildrenCountImpl();
|
---|
[1] | 511 | }
|
---|
| 512 |
|
---|
| 513 | return count;
|
---|
| 514 | }
|
---|
| 515 |
|
---|
[21835] | 516 | /**
|
---|
| 517 | * Returns the number of child snapshots including all grandchildren.
|
---|
| 518 | * Recurses into the snapshots tree.
|
---|
| 519 | * @return
|
---|
| 520 | */
|
---|
[21826] | 521 | ULONG Snapshot::getAllChildrenCount()
|
---|
| 522 | {
|
---|
| 523 | AutoCaller autoCaller(this);
|
---|
| 524 | AssertComRC(autoCaller.rc());
|
---|
| 525 |
|
---|
[25310] | 526 | AutoReadLock treeLock(m->pMachine->snapshotsTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
|
---|
[21826] | 527 | return getAllChildrenCountImpl();
|
---|
| 528 | }
|
---|
| 529 |
|
---|
[21835] | 530 | /**
|
---|
| 531 | * Returns the SnapshotMachine that this snapshot belongs to.
|
---|
| 532 | * Caller must hold the snapshot's object lock!
|
---|
| 533 | * @return
|
---|
| 534 | */
|
---|
[25152] | 535 | const ComObjPtr<SnapshotMachine>& Snapshot::getSnapshotMachine() const
|
---|
[21823] | 536 | {
|
---|
[25152] | 537 | return m->pMachine;
|
---|
[21823] | 538 | }
|
---|
| 539 |
|
---|
[21835] | 540 | /**
|
---|
| 541 | * Returns the UUID of this snapshot.
|
---|
| 542 | * Caller must hold the snapshot's object lock!
|
---|
| 543 | * @return
|
---|
| 544 | */
|
---|
[21823] | 545 | Guid Snapshot::getId() const
|
---|
| 546 | {
|
---|
[22173] | 547 | return m->uuid;
|
---|
[21823] | 548 | }
|
---|
| 549 |
|
---|
[21835] | 550 | /**
|
---|
| 551 | * Returns the name of this snapshot.
|
---|
| 552 | * Caller must hold the snapshot's object lock!
|
---|
| 553 | * @return
|
---|
| 554 | */
|
---|
[22173] | 555 | const Utf8Str& Snapshot::getName() const
|
---|
[21823] | 556 | {
|
---|
[22173] | 557 | return m->strName;
|
---|
[21823] | 558 | }
|
---|
| 559 |
|
---|
[21835] | 560 | /**
|
---|
| 561 | * Returns the time stamp of this snapshot.
|
---|
| 562 | * Caller must hold the snapshot's object lock!
|
---|
| 563 | * @return
|
---|
| 564 | */
|
---|
[21823] | 565 | RTTIMESPEC Snapshot::getTimeStamp() const
|
---|
| 566 | {
|
---|
| 567 | return m->timeStamp;
|
---|
| 568 | }
|
---|
| 569 |
|
---|
[1] | 570 | /**
|
---|
| 571 | * Searches for a snapshot with the given ID among children, grand-children,
|
---|
| 572 | * etc. of this snapshot. This snapshot itself is also included in the search.
|
---|
[21835] | 573 | * Caller must hold the snapshots tree lock!
|
---|
[1] | 574 | */
|
---|
[21823] | 575 | ComObjPtr<Snapshot> Snapshot::findChildOrSelf(IN_GUID aId)
|
---|
[1] | 576 | {
|
---|
[21823] | 577 | ComObjPtr<Snapshot> child;
|
---|
[1] | 578 |
|
---|
[21823] | 579 | AutoCaller autoCaller(this);
|
---|
| 580 | AssertComRC(autoCaller.rc());
|
---|
[1] | 581 |
|
---|
[25310] | 582 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[21823] | 583 |
|
---|
[22173] | 584 | if (m->uuid == aId)
|
---|
[1] | 585 | child = this;
|
---|
| 586 | else
|
---|
| 587 | {
|
---|
[25279] | 588 | alock.release();
|
---|
[21823] | 589 | for (SnapshotsList::const_iterator it = m->llChildren.begin();
|
---|
| 590 | it != m->llChildren.end();
|
---|
| 591 | ++it)
|
---|
[1] | 592 | {
|
---|
[21823] | 593 | if ((child = (*it)->findChildOrSelf(aId)))
|
---|
| 594 | break;
|
---|
[1] | 595 | }
|
---|
| 596 | }
|
---|
| 597 |
|
---|
| 598 | return child;
|
---|
| 599 | }
|
---|
| 600 |
|
---|
| 601 | /**
|
---|
| 602 | * Searches for a first snapshot with the given name among children,
|
---|
| 603 | * grand-children, etc. of this snapshot. This snapshot itself is also included
|
---|
| 604 | * in the search.
|
---|
[21835] | 605 | * Caller must hold the snapshots tree lock!
|
---|
[1] | 606 | */
|
---|
[22173] | 607 | ComObjPtr<Snapshot> Snapshot::findChildOrSelf(const Utf8Str &aName)
|
---|
[1] | 608 | {
|
---|
[21878] | 609 | ComObjPtr<Snapshot> child;
|
---|
[22173] | 610 | AssertReturn(!aName.isEmpty(), child);
|
---|
[1] | 611 |
|
---|
[21823] | 612 | AutoCaller autoCaller(this);
|
---|
| 613 | AssertComRC(autoCaller.rc());
|
---|
[1] | 614 |
|
---|
[25310] | 615 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[21823] | 616 |
|
---|
[22173] | 617 | if (m->strName == aName)
|
---|
[1] | 618 | child = this;
|
---|
| 619 | else
|
---|
| 620 | {
|
---|
[25279] | 621 | alock.release();
|
---|
[21823] | 622 | for (SnapshotsList::const_iterator it = m->llChildren.begin();
|
---|
| 623 | it != m->llChildren.end();
|
---|
| 624 | ++it)
|
---|
[1] | 625 | {
|
---|
[21823] | 626 | if ((child = (*it)->findChildOrSelf(aName)))
|
---|
| 627 | break;
|
---|
[1] | 628 | }
|
---|
| 629 | }
|
---|
| 630 |
|
---|
| 631 | return child;
|
---|
| 632 | }
|
---|
| 633 |
|
---|
| 634 | /**
|
---|
[21823] | 635 | * Internal implementation for Snapshot::updateSavedStatePaths (below).
|
---|
| 636 | * @param aOldPath
|
---|
| 637 | * @param aNewPath
|
---|
| 638 | */
|
---|
| 639 | void Snapshot::updateSavedStatePathsImpl(const char *aOldPath, const char *aNewPath)
|
---|
| 640 | {
|
---|
[25310] | 641 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[21823] | 642 |
|
---|
[24196] | 643 | const Utf8Str &path = m->pMachine->mSSData->mStateFilePath;
|
---|
[22778] | 644 | LogFlowThisFunc(("Snap[%s].statePath={%s}\n", m->strName.c_str(), path.c_str()));
|
---|
[21823] | 645 |
|
---|
| 646 | /* state file may be NULL (for offline snapshots) */
|
---|
| 647 | if ( path.length()
|
---|
[22173] | 648 | && RTPathStartsWith(path.c_str(), aOldPath)
|
---|
[21823] | 649 | )
|
---|
| 650 | {
|
---|
[24196] | 651 | m->pMachine->mSSData->mStateFilePath = Utf8StrFmt("%s%s", aNewPath, path.raw() + strlen(aOldPath));
|
---|
[21823] | 652 |
|
---|
| 653 | LogFlowThisFunc(("-> updated: {%s}\n", path.raw()));
|
---|
| 654 | }
|
---|
| 655 |
|
---|
| 656 | for (SnapshotsList::const_iterator it = m->llChildren.begin();
|
---|
| 657 | it != m->llChildren.end();
|
---|
| 658 | ++it)
|
---|
| 659 | {
|
---|
[22778] | 660 | Snapshot *pChild = *it;
|
---|
| 661 | pChild->updateSavedStatePathsImpl(aOldPath, aNewPath);
|
---|
[21823] | 662 | }
|
---|
| 663 | }
|
---|
| 664 |
|
---|
| 665 | /**
|
---|
[1] | 666 | * Checks if the specified path change affects the saved state file path of
|
---|
| 667 | * this snapshot or any of its (grand-)children and updates it accordingly.
|
---|
| 668 | *
|
---|
| 669 | * Intended to be called by Machine::openConfigLoader() only.
|
---|
| 670 | *
|
---|
| 671 | * @param aOldPath old path (full)
|
---|
| 672 | * @param aNewPath new path (full)
|
---|
| 673 | *
|
---|
| 674 | * @note Locks this object + children for writing.
|
---|
| 675 | */
|
---|
[21823] | 676 | void Snapshot::updateSavedStatePaths(const char *aOldPath, const char *aNewPath)
|
---|
[1] | 677 | {
|
---|
[21823] | 678 | LogFlowThisFunc(("aOldPath={%s} aNewPath={%s}\n", aOldPath, aNewPath));
|
---|
[1] | 679 |
|
---|
[21823] | 680 | AssertReturnVoid(aOldPath);
|
---|
| 681 | AssertReturnVoid(aNewPath);
|
---|
[1] | 682 |
|
---|
[21823] | 683 | AutoCaller autoCaller(this);
|
---|
| 684 | AssertComRC(autoCaller.rc());
|
---|
[1] | 685 |
|
---|
[25310] | 686 | AutoWriteLock chLock(m->pMachine->snapshotsTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
|
---|
[21823] | 687 | // call the implementation under the tree lock
|
---|
| 688 | updateSavedStatePathsImpl(aOldPath, aNewPath);
|
---|
| 689 | }
|
---|
[1] | 690 |
|
---|
[21823] | 691 | /**
|
---|
| 692 | * Internal implementation for Snapshot::saveSnapshot (below).
|
---|
| 693 | * @param aNode
|
---|
| 694 | * @param aAttrsOnly
|
---|
| 695 | * @return
|
---|
| 696 | */
|
---|
[22173] | 697 | HRESULT Snapshot::saveSnapshotImpl(settings::Snapshot &data, bool aAttrsOnly)
|
---|
[21823] | 698 | {
|
---|
[25310] | 699 | AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[21835] | 700 |
|
---|
[22173] | 701 | data.uuid = m->uuid;
|
---|
| 702 | data.strName = m->strName;
|
---|
| 703 | data.timestamp = m->timeStamp;
|
---|
| 704 | data.strDescription = m->strDescription;
|
---|
[21823] | 705 |
|
---|
| 706 | if (aAttrsOnly)
|
---|
| 707 | return S_OK;
|
---|
| 708 |
|
---|
| 709 | /* stateFile (optional) */
|
---|
[24196] | 710 | if (!stateFilePath().isEmpty())
|
---|
[21823] | 711 | /* try to make the file name relative to the settings file dir */
|
---|
[24196] | 712 | m->pMachine->calculateRelativePath(stateFilePath(), data.strStateFile);
|
---|
[22173] | 713 | else
|
---|
| 714 | data.strStateFile.setNull();
|
---|
[1] | 715 |
|
---|
[22173] | 716 | HRESULT rc = m->pMachine->saveHardware(data.hardware);
|
---|
[25149] | 717 | if (FAILED(rc)) return rc;
|
---|
[21823] | 718 |
|
---|
[22173] | 719 | rc = m->pMachine->saveStorageControllers(data.storage);
|
---|
[25149] | 720 | if (FAILED(rc)) return rc;
|
---|
[21823] | 721 |
|
---|
[25279] | 722 | alock.release();
|
---|
[21835] | 723 |
|
---|
[22173] | 724 | data.llChildSnapshots.clear();
|
---|
| 725 |
|
---|
[21823] | 726 | if (m->llChildren.size())
|
---|
| 727 | {
|
---|
| 728 | for (SnapshotsList::const_iterator it = m->llChildren.begin();
|
---|
| 729 | it != m->llChildren.end();
|
---|
| 730 | ++it)
|
---|
| 731 | {
|
---|
[22173] | 732 | settings::Snapshot snap;
|
---|
| 733 | rc = (*it)->saveSnapshotImpl(snap, aAttrsOnly);
|
---|
[25149] | 734 | if (FAILED(rc)) return rc;
|
---|
[22173] | 735 |
|
---|
| 736 | data.llChildSnapshots.push_back(snap);
|
---|
[21823] | 737 | }
|
---|
| 738 | }
|
---|
| 739 |
|
---|
| 740 | return S_OK;
|
---|
[1] | 741 | }
|
---|
| 742 |
|
---|
[21823] | 743 | /**
|
---|
| 744 | * Saves the given snapshot and all its children (unless \a aAttrsOnly is true).
|
---|
| 745 | * It is assumed that the given node is empty (unless \a aAttrsOnly is true).
|
---|
| 746 | *
|
---|
| 747 | * @param aNode <Snapshot> node to save the snapshot to.
|
---|
| 748 | * @param aSnapshot Snapshot to save.
|
---|
| 749 | * @param aAttrsOnly If true, only updatge user-changeable attrs.
|
---|
| 750 | */
|
---|
[22173] | 751 | HRESULT Snapshot::saveSnapshot(settings::Snapshot &data, bool aAttrsOnly)
|
---|
[21823] | 752 | {
|
---|
[25310] | 753 | AutoWriteLock listLock(m->pMachine->snapshotsTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
|
---|
[21823] | 754 |
|
---|
[22173] | 755 | return saveSnapshotImpl(data, aAttrsOnly);
|
---|
[21823] | 756 | }
|
---|
| 757 |
|
---|
[24279] | 758 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 759 | //
|
---|
| 760 | // SnapshotMachine implementation
|
---|
| 761 | //
|
---|
| 762 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 763 |
|
---|
| 764 | DEFINE_EMPTY_CTOR_DTOR (SnapshotMachine)
|
---|
| 765 |
|
---|
| 766 | HRESULT SnapshotMachine::FinalConstruct()
|
---|
| 767 | {
|
---|
| 768 | LogFlowThisFunc(("\n"));
|
---|
| 769 |
|
---|
| 770 | /* set the proper type to indicate we're the SnapshotMachine instance */
|
---|
| 771 | unconst(mType) = IsSnapshotMachine;
|
---|
| 772 |
|
---|
| 773 | return S_OK;
|
---|
| 774 | }
|
---|
| 775 |
|
---|
| 776 | void SnapshotMachine::FinalRelease()
|
---|
| 777 | {
|
---|
| 778 | LogFlowThisFunc(("\n"));
|
---|
| 779 |
|
---|
| 780 | uninit();
|
---|
| 781 | }
|
---|
| 782 |
|
---|
| 783 | /**
|
---|
| 784 | * Initializes the SnapshotMachine object when taking a snapshot.
|
---|
| 785 | *
|
---|
| 786 | * @param aSessionMachine machine to take a snapshot from
|
---|
| 787 | * @param aSnapshotId snapshot ID of this snapshot machine
|
---|
| 788 | * @param aStateFilePath file where the execution state will be later saved
|
---|
| 789 | * (or NULL for the offline snapshot)
|
---|
| 790 | *
|
---|
| 791 | * @note The aSessionMachine must be locked for writing.
|
---|
| 792 | */
|
---|
| 793 | HRESULT SnapshotMachine::init(SessionMachine *aSessionMachine,
|
---|
| 794 | IN_GUID aSnapshotId,
|
---|
| 795 | const Utf8Str &aStateFilePath)
|
---|
| 796 | {
|
---|
| 797 | LogFlowThisFuncEnter();
|
---|
| 798 | LogFlowThisFunc(("mName={%ls}\n", aSessionMachine->mUserData->mName.raw()));
|
---|
| 799 |
|
---|
| 800 | AssertReturn(aSessionMachine && !Guid (aSnapshotId).isEmpty(), E_INVALIDARG);
|
---|
| 801 |
|
---|
| 802 | /* Enclose the state transition NotReady->InInit->Ready */
|
---|
| 803 | AutoInitSpan autoInitSpan(this);
|
---|
| 804 | AssertReturn(autoInitSpan.isOk(), E_FAIL);
|
---|
| 805 |
|
---|
| 806 | AssertReturn(aSessionMachine->isWriteLockOnCurrentThread(), E_FAIL);
|
---|
| 807 |
|
---|
| 808 | mSnapshotId = aSnapshotId;
|
---|
| 809 |
|
---|
| 810 | /* memorize the primary Machine instance (i.e. not SessionMachine!) */
|
---|
| 811 | unconst(mPeer) = aSessionMachine->mPeer;
|
---|
| 812 | /* share the parent pointer */
|
---|
| 813 | unconst(mParent) = mPeer->mParent;
|
---|
| 814 |
|
---|
| 815 | /* take the pointer to Data to share */
|
---|
| 816 | mData.share (mPeer->mData);
|
---|
| 817 |
|
---|
| 818 | /* take the pointer to UserData to share (our UserData must always be the
|
---|
| 819 | * same as Machine's data) */
|
---|
| 820 | mUserData.share (mPeer->mUserData);
|
---|
| 821 | /* make a private copy of all other data (recent changes from SessionMachine) */
|
---|
| 822 | mHWData.attachCopy (aSessionMachine->mHWData);
|
---|
| 823 | mMediaData.attachCopy(aSessionMachine->mMediaData);
|
---|
| 824 |
|
---|
| 825 | /* SSData is always unique for SnapshotMachine */
|
---|
| 826 | mSSData.allocate();
|
---|
| 827 | mSSData->mStateFilePath = aStateFilePath;
|
---|
| 828 |
|
---|
| 829 | HRESULT rc = S_OK;
|
---|
| 830 |
|
---|
| 831 | /* create copies of all shared folders (mHWData after attiching a copy
|
---|
| 832 | * contains just references to original objects) */
|
---|
| 833 | for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
|
---|
| 834 | it != mHWData->mSharedFolders.end();
|
---|
| 835 | ++it)
|
---|
| 836 | {
|
---|
| 837 | ComObjPtr<SharedFolder> folder;
|
---|
| 838 | folder.createObject();
|
---|
| 839 | rc = folder->initCopy (this, *it);
|
---|
[25149] | 840 | if (FAILED(rc)) return rc;
|
---|
[24279] | 841 | *it = folder;
|
---|
| 842 | }
|
---|
| 843 |
|
---|
| 844 | /* associate hard disks with the snapshot
|
---|
| 845 | * (Machine::uninitDataAndChildObjects() will deassociate at destruction) */
|
---|
| 846 | for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
|
---|
| 847 | it != mMediaData->mAttachments.end();
|
---|
| 848 | ++it)
|
---|
| 849 | {
|
---|
| 850 | MediumAttachment *pAtt = *it;
|
---|
[24989] | 851 | Medium *pMedium = pAtt->getMedium();
|
---|
[24279] | 852 | if (pMedium) // can be NULL for non-harddisk
|
---|
| 853 | {
|
---|
| 854 | rc = pMedium->attachTo(mData->mUuid, mSnapshotId);
|
---|
| 855 | AssertComRC(rc);
|
---|
| 856 | }
|
---|
| 857 | }
|
---|
| 858 |
|
---|
| 859 | /* create copies of all storage controllers (mStorageControllerData
|
---|
| 860 | * after attaching a copy contains just references to original objects) */
|
---|
| 861 | mStorageControllers.allocate();
|
---|
| 862 | for (StorageControllerList::const_iterator
|
---|
| 863 | it = aSessionMachine->mStorageControllers->begin();
|
---|
| 864 | it != aSessionMachine->mStorageControllers->end();
|
---|
| 865 | ++it)
|
---|
| 866 | {
|
---|
| 867 | ComObjPtr<StorageController> ctrl;
|
---|
| 868 | ctrl.createObject();
|
---|
| 869 | ctrl->initCopy (this, *it);
|
---|
| 870 | mStorageControllers->push_back(ctrl);
|
---|
| 871 | }
|
---|
| 872 |
|
---|
| 873 | /* create all other child objects that will be immutable private copies */
|
---|
| 874 |
|
---|
| 875 | unconst(mBIOSSettings).createObject();
|
---|
| 876 | mBIOSSettings->initCopy (this, mPeer->mBIOSSettings);
|
---|
| 877 |
|
---|
| 878 | #ifdef VBOX_WITH_VRDP
|
---|
| 879 | unconst(mVRDPServer).createObject();
|
---|
| 880 | mVRDPServer->initCopy (this, mPeer->mVRDPServer);
|
---|
| 881 | #endif
|
---|
| 882 |
|
---|
| 883 | unconst(mAudioAdapter).createObject();
|
---|
| 884 | mAudioAdapter->initCopy (this, mPeer->mAudioAdapter);
|
---|
| 885 |
|
---|
| 886 | unconst(mUSBController).createObject();
|
---|
[25194] | 887 | mUSBController->init(this, mPeer->mUSBController);
|
---|
[24279] | 888 |
|
---|
[25194] | 889 | for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot++)
|
---|
[24279] | 890 | {
|
---|
[25194] | 891 | unconst(mNetworkAdapters[slot]).createObject();
|
---|
| 892 | mNetworkAdapters[slot]->initCopy (this, mPeer->mNetworkAdapters [slot]);
|
---|
[24279] | 893 | }
|
---|
| 894 |
|
---|
[25194] | 895 | for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot++)
|
---|
[24279] | 896 | {
|
---|
| 897 | unconst(mSerialPorts [slot]).createObject();
|
---|
[25194] | 898 | mSerialPorts[slot]->initCopy (this, mPeer->mSerialPorts[slot]);
|
---|
[24279] | 899 | }
|
---|
| 900 |
|
---|
[25194] | 901 | for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot++)
|
---|
[24279] | 902 | {
|
---|
[25194] | 903 | unconst(mParallelPorts[slot]).createObject();
|
---|
| 904 | mParallelPorts[slot]->initCopy (this, mPeer->mParallelPorts[slot]);
|
---|
[24279] | 905 | }
|
---|
| 906 |
|
---|
| 907 | /* Confirm a successful initialization when it's the case */
|
---|
| 908 | autoInitSpan.setSucceeded();
|
---|
| 909 |
|
---|
| 910 | LogFlowThisFuncLeave();
|
---|
| 911 | return S_OK;
|
---|
| 912 | }
|
---|
| 913 |
|
---|
| 914 | /**
|
---|
| 915 | * Initializes the SnapshotMachine object when loading from the settings file.
|
---|
| 916 | *
|
---|
| 917 | * @param aMachine machine the snapshot belngs to
|
---|
| 918 | * @param aHWNode <Hardware> node
|
---|
| 919 | * @param aHDAsNode <HardDiskAttachments> node
|
---|
| 920 | * @param aSnapshotId snapshot ID of this snapshot machine
|
---|
| 921 | * @param aStateFilePath file where the execution state is saved
|
---|
| 922 | * (or NULL for the offline snapshot)
|
---|
| 923 | *
|
---|
| 924 | * @note Doesn't lock anything.
|
---|
| 925 | */
|
---|
| 926 | HRESULT SnapshotMachine::init(Machine *aMachine,
|
---|
| 927 | const settings::Hardware &hardware,
|
---|
| 928 | const settings::Storage &storage,
|
---|
| 929 | IN_GUID aSnapshotId,
|
---|
| 930 | const Utf8Str &aStateFilePath)
|
---|
| 931 | {
|
---|
| 932 | LogFlowThisFuncEnter();
|
---|
| 933 | LogFlowThisFunc(("mName={%ls}\n", aMachine->mUserData->mName.raw()));
|
---|
| 934 |
|
---|
| 935 | AssertReturn(aMachine && !Guid(aSnapshotId).isEmpty(), E_INVALIDARG);
|
---|
| 936 |
|
---|
| 937 | /* Enclose the state transition NotReady->InInit->Ready */
|
---|
| 938 | AutoInitSpan autoInitSpan(this);
|
---|
| 939 | AssertReturn(autoInitSpan.isOk(), E_FAIL);
|
---|
| 940 |
|
---|
| 941 | /* Don't need to lock aMachine when VirtualBox is starting up */
|
---|
| 942 |
|
---|
| 943 | mSnapshotId = aSnapshotId;
|
---|
| 944 |
|
---|
| 945 | /* memorize the primary Machine instance */
|
---|
| 946 | unconst(mPeer) = aMachine;
|
---|
| 947 | /* share the parent pointer */
|
---|
| 948 | unconst(mParent) = mPeer->mParent;
|
---|
| 949 |
|
---|
| 950 | /* take the pointer to Data to share */
|
---|
| 951 | mData.share (mPeer->mData);
|
---|
| 952 | /*
|
---|
| 953 | * take the pointer to UserData to share
|
---|
| 954 | * (our UserData must always be the same as Machine's data)
|
---|
| 955 | */
|
---|
| 956 | mUserData.share (mPeer->mUserData);
|
---|
| 957 | /* allocate private copies of all other data (will be loaded from settings) */
|
---|
| 958 | mHWData.allocate();
|
---|
| 959 | mMediaData.allocate();
|
---|
| 960 | mStorageControllers.allocate();
|
---|
| 961 |
|
---|
| 962 | /* SSData is always unique for SnapshotMachine */
|
---|
| 963 | mSSData.allocate();
|
---|
| 964 | mSSData->mStateFilePath = aStateFilePath;
|
---|
| 965 |
|
---|
| 966 | /* create all other child objects that will be immutable private copies */
|
---|
| 967 |
|
---|
| 968 | unconst(mBIOSSettings).createObject();
|
---|
| 969 | mBIOSSettings->init (this);
|
---|
| 970 |
|
---|
| 971 | #ifdef VBOX_WITH_VRDP
|
---|
| 972 | unconst(mVRDPServer).createObject();
|
---|
| 973 | mVRDPServer->init (this);
|
---|
| 974 | #endif
|
---|
| 975 |
|
---|
| 976 | unconst(mAudioAdapter).createObject();
|
---|
| 977 | mAudioAdapter->init (this);
|
---|
| 978 |
|
---|
| 979 | unconst(mUSBController).createObject();
|
---|
| 980 | mUSBController->init (this);
|
---|
| 981 |
|
---|
| 982 | for (ULONG slot = 0; slot < RT_ELEMENTS (mNetworkAdapters); slot ++)
|
---|
| 983 | {
|
---|
| 984 | unconst(mNetworkAdapters [slot]).createObject();
|
---|
| 985 | mNetworkAdapters [slot]->init (this, slot);
|
---|
| 986 | }
|
---|
| 987 |
|
---|
| 988 | for (ULONG slot = 0; slot < RT_ELEMENTS (mSerialPorts); slot ++)
|
---|
| 989 | {
|
---|
| 990 | unconst(mSerialPorts [slot]).createObject();
|
---|
| 991 | mSerialPorts [slot]->init (this, slot);
|
---|
| 992 | }
|
---|
| 993 |
|
---|
| 994 | for (ULONG slot = 0; slot < RT_ELEMENTS (mParallelPorts); slot ++)
|
---|
| 995 | {
|
---|
| 996 | unconst(mParallelPorts [slot]).createObject();
|
---|
| 997 | mParallelPorts [slot]->init (this, slot);
|
---|
| 998 | }
|
---|
| 999 |
|
---|
| 1000 | /* load hardware and harddisk settings */
|
---|
| 1001 |
|
---|
| 1002 | HRESULT rc = loadHardware(hardware);
|
---|
| 1003 | if (SUCCEEDED(rc))
|
---|
| 1004 | rc = loadStorageControllers(storage, true /* aRegistered */, &mSnapshotId);
|
---|
| 1005 |
|
---|
| 1006 | if (SUCCEEDED(rc))
|
---|
| 1007 | /* commit all changes made during the initialization */
|
---|
| 1008 | commit();
|
---|
| 1009 |
|
---|
| 1010 | /* Confirm a successful initialization when it's the case */
|
---|
| 1011 | if (SUCCEEDED(rc))
|
---|
| 1012 | autoInitSpan.setSucceeded();
|
---|
| 1013 |
|
---|
| 1014 | LogFlowThisFuncLeave();
|
---|
| 1015 | return rc;
|
---|
| 1016 | }
|
---|
| 1017 |
|
---|
| 1018 | /**
|
---|
| 1019 | * Uninitializes this SnapshotMachine object.
|
---|
| 1020 | */
|
---|
| 1021 | void SnapshotMachine::uninit()
|
---|
| 1022 | {
|
---|
| 1023 | LogFlowThisFuncEnter();
|
---|
| 1024 |
|
---|
| 1025 | /* Enclose the state transition Ready->InUninit->NotReady */
|
---|
| 1026 | AutoUninitSpan autoUninitSpan(this);
|
---|
| 1027 | if (autoUninitSpan.uninitDone())
|
---|
| 1028 | return;
|
---|
| 1029 |
|
---|
| 1030 | uninitDataAndChildObjects();
|
---|
| 1031 |
|
---|
| 1032 | /* free the essential data structure last */
|
---|
| 1033 | mData.free();
|
---|
| 1034 |
|
---|
| 1035 | unconst(mParent).setNull();
|
---|
| 1036 | unconst(mPeer).setNull();
|
---|
| 1037 |
|
---|
| 1038 | LogFlowThisFuncLeave();
|
---|
| 1039 | }
|
---|
| 1040 |
|
---|
| 1041 | /**
|
---|
| 1042 | * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
|
---|
| 1043 | * with the primary Machine instance (mPeer).
|
---|
| 1044 | */
|
---|
| 1045 | RWLockHandle *SnapshotMachine::lockHandle() const
|
---|
| 1046 | {
|
---|
| 1047 | AssertReturn(!mPeer.isNull(), NULL);
|
---|
| 1048 | return mPeer->lockHandle();
|
---|
| 1049 | }
|
---|
| 1050 |
|
---|
| 1051 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 1052 | //
|
---|
| 1053 | // SnapshotMachine public internal methods
|
---|
| 1054 | //
|
---|
| 1055 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 1056 |
|
---|
| 1057 | /**
|
---|
| 1058 | * Called by the snapshot object associated with this SnapshotMachine when
|
---|
| 1059 | * snapshot data such as name or description is changed.
|
---|
| 1060 | *
|
---|
| 1061 | * @note Locks this object for writing.
|
---|
| 1062 | */
|
---|
| 1063 | HRESULT SnapshotMachine::onSnapshotChange (Snapshot *aSnapshot)
|
---|
| 1064 | {
|
---|
[25310] | 1065 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[24279] | 1066 |
|
---|
| 1067 | // mPeer->saveAllSnapshots(); @todo
|
---|
| 1068 |
|
---|
| 1069 | /* inform callbacks */
|
---|
| 1070 | mParent->onSnapshotChange(mData->mUuid, aSnapshot->getId());
|
---|
| 1071 |
|
---|
| 1072 | return S_OK;
|
---|
| 1073 | }
|
---|
| 1074 |
|
---|
| 1075 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 1076 | //
|
---|
| 1077 | // SessionMachine task records
|
---|
| 1078 | //
|
---|
| 1079 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 1080 |
|
---|
[24298] | 1081 | /**
|
---|
[24299] | 1082 | * Abstract base class for SessionMachine::RestoreSnapshotTask and
|
---|
| 1083 | * SessionMachine::DeleteSnapshotTask. This is necessary since
|
---|
[24298] | 1084 | * RTThreadCreate cannot call a method as its thread function, so
|
---|
| 1085 | * instead we have it call the static SessionMachine::taskHandler,
|
---|
| 1086 | * which can then call the handler() method in here (implemented
|
---|
| 1087 | * by the children).
|
---|
| 1088 | */
|
---|
| 1089 | struct SessionMachine::SnapshotTask
|
---|
[24279] | 1090 | {
|
---|
[24298] | 1091 | SnapshotTask(SessionMachine *m,
|
---|
| 1092 | Progress *p,
|
---|
| 1093 | Snapshot *s)
|
---|
| 1094 | : pMachine(m),
|
---|
| 1095 | pProgress(p),
|
---|
| 1096 | machineStateBackup(m->mData->mMachineState), // save the current machine state
|
---|
| 1097 | pSnapshot(s)
|
---|
[24279] | 1098 | {}
|
---|
| 1099 |
|
---|
[24298] | 1100 | void modifyBackedUpState(MachineState_T s)
|
---|
[24279] | 1101 | {
|
---|
[24298] | 1102 | *const_cast<MachineState_T*>(&machineStateBackup) = s;
|
---|
[24279] | 1103 | }
|
---|
| 1104 |
|
---|
| 1105 | virtual void handler() = 0;
|
---|
| 1106 |
|
---|
[24298] | 1107 | ComObjPtr<SessionMachine> pMachine;
|
---|
| 1108 | ComObjPtr<Progress> pProgress;
|
---|
| 1109 | const MachineState_T machineStateBackup;
|
---|
| 1110 | ComObjPtr<Snapshot> pSnapshot;
|
---|
[24279] | 1111 | };
|
---|
| 1112 |
|
---|
[24299] | 1113 | /** Restore snapshot state task */
|
---|
| 1114 | struct SessionMachine::RestoreSnapshotTask
|
---|
| 1115 | : public SessionMachine::SnapshotTask
|
---|
| 1116 | {
|
---|
| 1117 | RestoreSnapshotTask(SessionMachine *m,
|
---|
| 1118 | Progress *p,
|
---|
| 1119 | Snapshot *s,
|
---|
| 1120 | ULONG ulStateFileSizeMB)
|
---|
| 1121 | : SnapshotTask(m, p, s),
|
---|
| 1122 | m_ulStateFileSizeMB(ulStateFileSizeMB)
|
---|
| 1123 | {}
|
---|
| 1124 |
|
---|
| 1125 | void handler()
|
---|
| 1126 | {
|
---|
| 1127 | pMachine->restoreSnapshotHandler(*this);
|
---|
| 1128 | }
|
---|
| 1129 |
|
---|
| 1130 | ULONG m_ulStateFileSizeMB;
|
---|
| 1131 | };
|
---|
| 1132 |
|
---|
[24279] | 1133 | /** Discard snapshot task */
|
---|
| 1134 | struct SessionMachine::DeleteSnapshotTask
|
---|
[24298] | 1135 | : public SessionMachine::SnapshotTask
|
---|
[24279] | 1136 | {
|
---|
[24298] | 1137 | DeleteSnapshotTask(SessionMachine *m,
|
---|
| 1138 | Progress *p,
|
---|
| 1139 | Snapshot *s)
|
---|
| 1140 | : SnapshotTask(m, p, s)
|
---|
[24279] | 1141 | {}
|
---|
| 1142 |
|
---|
| 1143 | void handler()
|
---|
| 1144 | {
|
---|
[24298] | 1145 | pMachine->deleteSnapshotHandler(*this);
|
---|
[24279] | 1146 | }
|
---|
| 1147 |
|
---|
[24298] | 1148 | private:
|
---|
| 1149 | DeleteSnapshotTask(const SnapshotTask &task)
|
---|
| 1150 | : SnapshotTask(task)
|
---|
| 1151 | {}
|
---|
[24279] | 1152 | };
|
---|
| 1153 |
|
---|
[24299] | 1154 | /**
|
---|
| 1155 | * Static SessionMachine method that can get passed to RTThreadCreate to
|
---|
| 1156 | * have a thread started for a SnapshotTask. See SnapshotTask above.
|
---|
| 1157 | *
|
---|
| 1158 | * This calls either RestoreSnapshotTask::handler() or DeleteSnapshotTask::handler().
|
---|
| 1159 | */
|
---|
| 1160 |
|
---|
| 1161 | /* static */ DECLCALLBACK(int) SessionMachine::taskHandler(RTTHREAD /* thread */, void *pvUser)
|
---|
[24279] | 1162 | {
|
---|
[24299] | 1163 | AssertReturn(pvUser, VERR_INVALID_POINTER);
|
---|
[24279] | 1164 |
|
---|
[24299] | 1165 | SnapshotTask *task = static_cast<SnapshotTask*>(pvUser);
|
---|
| 1166 | task->handler();
|
---|
[24279] | 1167 |
|
---|
[24299] | 1168 | // it's our responsibility to delete the task
|
---|
| 1169 | delete task;
|
---|
[24279] | 1170 |
|
---|
[24299] | 1171 | return 0;
|
---|
| 1172 | }
|
---|
| 1173 |
|
---|
[24279] | 1174 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 1175 | //
|
---|
[24299] | 1176 | // TakeSnapshot methods (SessionMachine and related tasks)
|
---|
[24279] | 1177 | //
|
---|
| 1178 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 1179 |
|
---|
| 1180 | /**
|
---|
[24299] | 1181 | * Implementation for IInternalMachineControl::beginTakingSnapshot().
|
---|
| 1182 | *
|
---|
| 1183 | * Gets called indirectly from Console::TakeSnapshot, which creates a
|
---|
| 1184 | * progress object in the client and then starts a thread
|
---|
| 1185 | * (Console::fntTakeSnapshotWorker) which then calls this.
|
---|
| 1186 | *
|
---|
| 1187 | * In other words, the asynchronous work for taking snapshots takes place
|
---|
| 1188 | * on the _client_ (in the Console). This is different from restoring
|
---|
| 1189 | * or deleting snapshots, which start threads on the server.
|
---|
| 1190 | *
|
---|
| 1191 | * This does the server-side work of taking a snapshot: it creates diffencing
|
---|
| 1192 | * images for all hard disks attached to the machine and then creates a
|
---|
| 1193 | * Snapshot object with a corresponding SnapshotMachine to save the VM settings.
|
---|
| 1194 | *
|
---|
| 1195 | * The client's fntTakeSnapshotWorker() blocks while this takes place.
|
---|
| 1196 | * After this returns successfully, fntTakeSnapshotWorker() will begin
|
---|
| 1197 | * saving the machine state to the snapshot object and reconfigure the
|
---|
| 1198 | * hard disks.
|
---|
| 1199 | *
|
---|
| 1200 | * When the console is done, it calls SessionMachine::EndTakingSnapshot().
|
---|
| 1201 | *
|
---|
[24279] | 1202 | * @note Locks mParent + this object for writing.
|
---|
[24299] | 1203 | *
|
---|
| 1204 | * @param aInitiator in: The console on which Console::TakeSnapshot was called.
|
---|
| 1205 | * @param aName in: The name for the new snapshot.
|
---|
| 1206 | * @param aDescription in: A description for the new snapshot.
|
---|
| 1207 | * @param aConsoleProgress in: The console's (client's) progress object.
|
---|
| 1208 | * @param fTakingSnapshotOnline in: True if an online snapshot is being taken (i.e. machine is running).
|
---|
| 1209 | * @param aStateFilePath out: name of file in snapshots folder to which the console should write the VM state.
|
---|
| 1210 | * @return
|
---|
[24279] | 1211 | */
|
---|
| 1212 | STDMETHODIMP SessionMachine::BeginTakingSnapshot(IConsole *aInitiator,
|
---|
| 1213 | IN_BSTR aName,
|
---|
| 1214 | IN_BSTR aDescription,
|
---|
| 1215 | IProgress *aConsoleProgress,
|
---|
| 1216 | BOOL fTakingSnapshotOnline,
|
---|
| 1217 | BSTR *aStateFilePath)
|
---|
| 1218 | {
|
---|
| 1219 | LogFlowThisFuncEnter();
|
---|
| 1220 |
|
---|
| 1221 | AssertReturn(aInitiator && aName, E_INVALIDARG);
|
---|
| 1222 | AssertReturn(aStateFilePath, E_POINTER);
|
---|
| 1223 |
|
---|
[24920] | 1224 | LogFlowThisFunc(("aName='%ls' fTakingSnapshotOnline=%RTbool\n", aName, fTakingSnapshotOnline));
|
---|
[24279] | 1225 |
|
---|
| 1226 | AutoCaller autoCaller(this);
|
---|
| 1227 | AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
|
---|
| 1228 |
|
---|
| 1229 | /* saveSettings() needs mParent lock */
|
---|
[25310] | 1230 | AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
|
---|
[24279] | 1231 |
|
---|
| 1232 | AssertReturn( !Global::IsOnlineOrTransient(mData->mMachineState)
|
---|
| 1233 | || mData->mMachineState == MachineState_Running
|
---|
| 1234 | || mData->mMachineState == MachineState_Paused, E_FAIL);
|
---|
| 1235 | AssertReturn(mSnapshotData.mLastState == MachineState_Null, E_FAIL);
|
---|
| 1236 | AssertReturn(mSnapshotData.mSnapshot.isNull(), E_FAIL);
|
---|
| 1237 |
|
---|
| 1238 | if ( !fTakingSnapshotOnline
|
---|
| 1239 | && mData->mMachineState != MachineState_Saved
|
---|
| 1240 | )
|
---|
| 1241 | {
|
---|
| 1242 | /* save all current settings to ensure current changes are committed and
|
---|
| 1243 | * hard disks are fixed up */
|
---|
| 1244 | HRESULT rc = saveSettings();
|
---|
[25149] | 1245 | if (FAILED(rc)) return rc;
|
---|
[24279] | 1246 | }
|
---|
| 1247 |
|
---|
| 1248 | /* create an ID for the snapshot */
|
---|
| 1249 | Guid snapshotId;
|
---|
| 1250 | snapshotId.create();
|
---|
| 1251 |
|
---|
| 1252 | Utf8Str strStateFilePath;
|
---|
| 1253 | /* stateFilePath is null when the machine is not online nor saved */
|
---|
| 1254 | if ( fTakingSnapshotOnline
|
---|
| 1255 | || mData->mMachineState == MachineState_Saved)
|
---|
| 1256 | {
|
---|
| 1257 | strStateFilePath = Utf8StrFmt("%ls%c{%RTuuid}.sav",
|
---|
| 1258 | mUserData->mSnapshotFolderFull.raw(),
|
---|
| 1259 | RTPATH_DELIMITER,
|
---|
| 1260 | snapshotId.ptr());
|
---|
| 1261 | /* ensure the directory for the saved state file exists */
|
---|
| 1262 | HRESULT rc = VirtualBox::ensureFilePathExists(strStateFilePath);
|
---|
[25149] | 1263 | if (FAILED(rc)) return rc;
|
---|
[24279] | 1264 | }
|
---|
| 1265 |
|
---|
| 1266 | /* create a snapshot machine object */
|
---|
| 1267 | ComObjPtr<SnapshotMachine> snapshotMachine;
|
---|
| 1268 | snapshotMachine.createObject();
|
---|
| 1269 | HRESULT rc = snapshotMachine->init(this, snapshotId, strStateFilePath);
|
---|
| 1270 | AssertComRCReturn(rc, rc);
|
---|
| 1271 |
|
---|
| 1272 | /* create a snapshot object */
|
---|
| 1273 | RTTIMESPEC time;
|
---|
| 1274 | ComObjPtr<Snapshot> pSnapshot;
|
---|
| 1275 | pSnapshot.createObject();
|
---|
| 1276 | rc = pSnapshot->init(mParent,
|
---|
| 1277 | snapshotId,
|
---|
| 1278 | aName,
|
---|
| 1279 | aDescription,
|
---|
| 1280 | *RTTimeNow(&time),
|
---|
| 1281 | snapshotMachine,
|
---|
| 1282 | mData->mCurrentSnapshot);
|
---|
| 1283 | AssertComRCReturnRC(rc);
|
---|
| 1284 |
|
---|
| 1285 | /* fill in the snapshot data */
|
---|
| 1286 | mSnapshotData.mLastState = mData->mMachineState;
|
---|
| 1287 | mSnapshotData.mSnapshot = pSnapshot;
|
---|
| 1288 |
|
---|
| 1289 | try
|
---|
| 1290 | {
|
---|
| 1291 | LogFlowThisFunc(("Creating differencing hard disks (online=%d)...\n",
|
---|
[24920] | 1292 | fTakingSnapshotOnline));
|
---|
[24279] | 1293 |
|
---|
| 1294 | // backup the media data so we can recover if things goes wrong along the day;
|
---|
| 1295 | // the matching commit() is in fixupMedia() during endSnapshot()
|
---|
| 1296 | mMediaData.backup();
|
---|
| 1297 |
|
---|
[24301] | 1298 | /* Console::fntTakeSnapshotWorker and friends expects this. */
|
---|
| 1299 | if (mSnapshotData.mLastState == MachineState_Running)
|
---|
| 1300 | setMachineState(MachineState_LiveSnapshotting);
|
---|
| 1301 | else
|
---|
| 1302 | setMachineState(MachineState_Saving); /** @todo Confusing! Saving is used for both online and offline snapshots. */
|
---|
[24279] | 1303 |
|
---|
| 1304 | /* create new differencing hard disks and attach them to this machine */
|
---|
| 1305 | rc = createImplicitDiffs(mUserData->mSnapshotFolderFull,
|
---|
| 1306 | aConsoleProgress,
|
---|
| 1307 | 1, // operation weight; must be the same as in Console::TakeSnapshot()
|
---|
| 1308 | !!fTakingSnapshotOnline);
|
---|
[24920] | 1309 | if (FAILED(rc))
|
---|
| 1310 | throw rc;
|
---|
[24279] | 1311 |
|
---|
[24920] | 1312 | if (mSnapshotData.mLastState == MachineState_Saved)
|
---|
[24279] | 1313 | {
|
---|
| 1314 | Utf8Str stateFrom = mSSData->mStateFilePath;
|
---|
| 1315 | Utf8Str stateTo = mSnapshotData.mSnapshot->stateFilePath();
|
---|
| 1316 |
|
---|
| 1317 | LogFlowThisFunc(("Copying the execution state from '%s' to '%s'...\n",
|
---|
[24920] | 1318 | stateFrom.raw(), stateTo.raw()));
|
---|
[24279] | 1319 |
|
---|
| 1320 | aConsoleProgress->SetNextOperation(Bstr(tr("Copying the execution state")),
|
---|
| 1321 | 1); // weight
|
---|
| 1322 |
|
---|
| 1323 | /* Leave the lock before a lengthy operation (mMachineState is
|
---|
| 1324 | * MachineState_Saving here) */
|
---|
| 1325 | alock.leave();
|
---|
| 1326 |
|
---|
| 1327 | /* copy the state file */
|
---|
| 1328 | int vrc = RTFileCopyEx(stateFrom.c_str(),
|
---|
| 1329 | stateTo.c_str(),
|
---|
| 1330 | 0,
|
---|
| 1331 | progressCallback,
|
---|
[24556] | 1332 | aConsoleProgress);
|
---|
[24279] | 1333 | alock.enter();
|
---|
| 1334 |
|
---|
| 1335 | if (RT_FAILURE(vrc))
|
---|
[24920] | 1336 | {
|
---|
| 1337 | /** @todo r=bird: Delete stateTo when appropriate. */
|
---|
[24279] | 1338 | throw setError(E_FAIL,
|
---|
| 1339 | tr("Could not copy the state file '%s' to '%s' (%Rrc)"),
|
---|
| 1340 | stateFrom.raw(),
|
---|
| 1341 | stateTo.raw(),
|
---|
| 1342 | vrc);
|
---|
[24920] | 1343 | }
|
---|
[24279] | 1344 | }
|
---|
| 1345 | }
|
---|
| 1346 | catch (HRESULT hrc)
|
---|
| 1347 | {
|
---|
[24920] | 1348 | LogThisFunc(("Caught %Rhrc [%s]\n", hrc, Global::stringifyMachineState(mData->mMachineState) ));
|
---|
| 1349 | if ( mSnapshotData.mLastState != mData->mMachineState
|
---|
| 1350 | && ( mSnapshotData.mLastState == MachineState_Running
|
---|
| 1351 | ? mData->mMachineState == MachineState_LiveSnapshotting
|
---|
| 1352 | : mData->mMachineState == MachineState_Saving)
|
---|
| 1353 | )
|
---|
| 1354 | setMachineState(mSnapshotData.mLastState);
|
---|
| 1355 |
|
---|
[24279] | 1356 | pSnapshot->uninit();
|
---|
| 1357 | pSnapshot.setNull();
|
---|
[24920] | 1358 | mSnapshotData.mLastState = MachineState_Null;
|
---|
| 1359 | mSnapshotData.mSnapshot.setNull();
|
---|
| 1360 |
|
---|
[24279] | 1361 | rc = hrc;
|
---|
| 1362 | }
|
---|
| 1363 |
|
---|
[24920] | 1364 | if (fTakingSnapshotOnline && SUCCEEDED(rc))
|
---|
[24279] | 1365 | strStateFilePath.cloneTo(aStateFilePath);
|
---|
| 1366 | else
|
---|
| 1367 | *aStateFilePath = NULL;
|
---|
| 1368 |
|
---|
[24920] | 1369 | LogFlowThisFunc(("LEAVE - %Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
|
---|
[24279] | 1370 | return rc;
|
---|
| 1371 | }
|
---|
| 1372 |
|
---|
| 1373 | /**
|
---|
[24299] | 1374 | * Implementation for IInternalMachineControl::beginTakingSnapshot().
|
---|
| 1375 | *
|
---|
| 1376 | * Called by the Console when it's done saving the VM state into the snapshot
|
---|
| 1377 | * (if online) and reconfiguring the hard disks. See BeginTakingSnapshot() above.
|
---|
| 1378 | *
|
---|
| 1379 | * This also gets called if the console part of snapshotting failed after the
|
---|
| 1380 | * BeginTakingSnapshot() call, to clean up the server side.
|
---|
| 1381 | *
|
---|
[24279] | 1382 | * @note Locks this object for writing.
|
---|
[24299] | 1383 | *
|
---|
| 1384 | * @param aSuccess Whether Console was successful with the client-side snapshot things.
|
---|
| 1385 | * @return
|
---|
[24279] | 1386 | */
|
---|
| 1387 | STDMETHODIMP SessionMachine::EndTakingSnapshot(BOOL aSuccess)
|
---|
| 1388 | {
|
---|
| 1389 | LogFlowThisFunc(("\n"));
|
---|
| 1390 |
|
---|
| 1391 | AutoCaller autoCaller(this);
|
---|
| 1392 | AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
|
---|
| 1393 |
|
---|
[25310] | 1394 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[24279] | 1395 |
|
---|
[24301] | 1396 | AssertReturn( !aSuccess
|
---|
| 1397 | || ( ( mData->mMachineState == MachineState_Saving
|
---|
| 1398 | || mData->mMachineState == MachineState_LiveSnapshotting)
|
---|
| 1399 | && mSnapshotData.mLastState != MachineState_Null
|
---|
| 1400 | && !mSnapshotData.mSnapshot.isNull()
|
---|
| 1401 | )
|
---|
| 1402 | , E_FAIL);
|
---|
[24279] | 1403 |
|
---|
| 1404 | /*
|
---|
| 1405 | * Restore the state we had when BeginTakingSnapshot() was called,
|
---|
| 1406 | * Console::fntTakeSnapshotWorker restores its local copy when we return.
|
---|
[24920] | 1407 | * If the state was Running, then let Console::fntTakeSnapshotWorker do it
|
---|
| 1408 | * all to avoid races.
|
---|
[24279] | 1409 | */
|
---|
| 1410 | if ( mData->mMachineState != mSnapshotData.mLastState
|
---|
| 1411 | && mSnapshotData.mLastState != MachineState_Running)
|
---|
| 1412 | setMachineState(mSnapshotData.mLastState);
|
---|
| 1413 |
|
---|
| 1414 | return endTakingSnapshot(aSuccess);
|
---|
| 1415 | }
|
---|
| 1416 |
|
---|
| 1417 | /**
|
---|
[24299] | 1418 | * Internal helper method to finalize taking a snapshot. Gets called from
|
---|
| 1419 | * SessionMachine::EndTakingSnapshot() to finalize the server-side
|
---|
| 1420 | * parts of snapshotting.
|
---|
| 1421 | *
|
---|
| 1422 | * This also gets called from SessionMachine::uninit() if an untaken
|
---|
| 1423 | * snapshot needs cleaning up.
|
---|
| 1424 | *
|
---|
| 1425 | * Expected to be called after completing *all* the tasks related to
|
---|
| 1426 | * taking the snapshot, either successfully or unsuccessfilly.
|
---|
| 1427 | *
|
---|
| 1428 | * @param aSuccess TRUE if the snapshot has been taken successfully.
|
---|
| 1429 | *
|
---|
| 1430 | * @note Locks this objects for writing.
|
---|
[24279] | 1431 | */
|
---|
[24299] | 1432 | HRESULT SessionMachine::endTakingSnapshot(BOOL aSuccess)
|
---|
[24279] | 1433 | {
|
---|
| 1434 | LogFlowThisFuncEnter();
|
---|
| 1435 |
|
---|
| 1436 | AutoCaller autoCaller(this);
|
---|
| 1437 | AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
|
---|
| 1438 |
|
---|
[25310] | 1439 | AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
|
---|
[24299] | 1440 | // saveSettings needs VirtualBox lock
|
---|
[24279] | 1441 |
|
---|
[24299] | 1442 | AssertReturn(!mSnapshotData.mSnapshot.isNull(), E_FAIL);
|
---|
[24279] | 1443 |
|
---|
[24299] | 1444 | MultiResult rc(S_OK);
|
---|
[24279] | 1445 |
|
---|
[24299] | 1446 | ComObjPtr<Snapshot> pOldFirstSnap = mData->mFirstSnapshot;
|
---|
| 1447 | ComObjPtr<Snapshot> pOldCurrentSnap = mData->mCurrentSnapshot;
|
---|
[24279] | 1448 |
|
---|
[24299] | 1449 | bool fOnline = Global::IsOnline(mSnapshotData.mLastState);
|
---|
[24279] | 1450 |
|
---|
[24299] | 1451 | if (aSuccess)
|
---|
| 1452 | {
|
---|
| 1453 | // new snapshot becomes the current one
|
---|
| 1454 | mData->mCurrentSnapshot = mSnapshotData.mSnapshot;
|
---|
[24279] | 1455 |
|
---|
[24299] | 1456 | /* memorize the first snapshot if necessary */
|
---|
| 1457 | if (!mData->mFirstSnapshot)
|
---|
| 1458 | mData->mFirstSnapshot = mData->mCurrentSnapshot;
|
---|
| 1459 |
|
---|
| 1460 | if (!fOnline)
|
---|
| 1461 | /* the machine was powered off or saved when taking a snapshot, so
|
---|
| 1462 | * reset the mCurrentStateModified flag */
|
---|
| 1463 | mData->mCurrentStateModified = FALSE;
|
---|
| 1464 |
|
---|
| 1465 | rc = saveSettings();
|
---|
[24279] | 1466 | }
|
---|
| 1467 |
|
---|
[24299] | 1468 | if (aSuccess && SUCCEEDED(rc))
|
---|
| 1469 | {
|
---|
| 1470 | /* associate old hard disks with the snapshot and do locking/unlocking*/
|
---|
| 1471 | fixupMedia(true /* aCommit */, fOnline);
|
---|
[24279] | 1472 |
|
---|
[24299] | 1473 | /* inform callbacks */
|
---|
| 1474 | mParent->onSnapshotTaken(mData->mUuid,
|
---|
| 1475 | mSnapshotData.mSnapshot->getId());
|
---|
| 1476 | }
|
---|
| 1477 | else
|
---|
[24279] | 1478 | {
|
---|
[24299] | 1479 | /* delete all differencing hard disks created (this will also attach
|
---|
| 1480 | * their parents back by rolling back mMediaData) */
|
---|
| 1481 | fixupMedia(false /* aCommit */);
|
---|
[24279] | 1482 |
|
---|
[24299] | 1483 | mData->mFirstSnapshot = pOldFirstSnap; // might have been changed above
|
---|
| 1484 | mData->mCurrentSnapshot = pOldCurrentSnap; // might have been changed above
|
---|
[24279] | 1485 |
|
---|
[24299] | 1486 | /* delete the saved state file (it might have been already created) */
|
---|
| 1487 | if (mSnapshotData.mSnapshot->stateFilePath().length())
|
---|
| 1488 | RTFileDelete(mSnapshotData.mSnapshot->stateFilePath().c_str());
|
---|
[24279] | 1489 |
|
---|
[24299] | 1490 | mSnapshotData.mSnapshot->uninit();
|
---|
| 1491 | }
|
---|
[24279] | 1492 |
|
---|
[24299] | 1493 | /* clear out the snapshot data */
|
---|
| 1494 | mSnapshotData.mLastState = MachineState_Null;
|
---|
| 1495 | mSnapshotData.mSnapshot.setNull();
|
---|
| 1496 |
|
---|
[24279] | 1497 | LogFlowThisFuncLeave();
|
---|
[24299] | 1498 | return rc;
|
---|
[24279] | 1499 | }
|
---|
| 1500 |
|
---|
[24299] | 1501 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 1502 | //
|
---|
| 1503 | // RestoreSnapshot methods (SessionMachine and related tasks)
|
---|
| 1504 | //
|
---|
| 1505 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 1506 |
|
---|
[24279] | 1507 | /**
|
---|
[24299] | 1508 | * Implementation for IInternalMachineControl::restoreSnapshot().
|
---|
| 1509 | *
|
---|
| 1510 | * Gets called from Console::RestoreSnapshot(), and that's basically the
|
---|
| 1511 | * only thing Console does. Restoring a snapshot happens entirely on the
|
---|
| 1512 | * server side since the machine cannot be running.
|
---|
| 1513 | *
|
---|
| 1514 | * This creates a new thread that does the work and returns a progress
|
---|
| 1515 | * object to the client which is then returned to the caller of
|
---|
| 1516 | * Console::RestoreSnapshot().
|
---|
| 1517 | *
|
---|
| 1518 | * Actual work then takes place in RestoreSnapshotTask::handler().
|
---|
| 1519 | *
|
---|
| 1520 | * @note Locks this + children objects for writing!
|
---|
| 1521 | *
|
---|
| 1522 | * @param aInitiator in: rhe console on which Console::RestoreSnapshot was called.
|
---|
| 1523 | * @param aSnapshot in: the snapshot to restore.
|
---|
| 1524 | * @param aMachineState in: client-side machine state.
|
---|
| 1525 | * @param aProgress out: progress object to monitor restore thread.
|
---|
| 1526 | * @return
|
---|
[24279] | 1527 | */
|
---|
| 1528 | STDMETHODIMP SessionMachine::RestoreSnapshot(IConsole *aInitiator,
|
---|
| 1529 | ISnapshot *aSnapshot,
|
---|
| 1530 | MachineState_T *aMachineState,
|
---|
| 1531 | IProgress **aProgress)
|
---|
| 1532 | {
|
---|
| 1533 | LogFlowThisFuncEnter();
|
---|
| 1534 |
|
---|
| 1535 | AssertReturn(aInitiator, E_INVALIDARG);
|
---|
| 1536 | AssertReturn(aSnapshot && aMachineState && aProgress, E_POINTER);
|
---|
| 1537 |
|
---|
| 1538 | AutoCaller autoCaller(this);
|
---|
| 1539 | AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
|
---|
| 1540 |
|
---|
[25310] | 1541 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[24279] | 1542 |
|
---|
[24299] | 1543 | // machine must not be running
|
---|
[24279] | 1544 | ComAssertRet(!Global::IsOnlineOrTransient(mData->mMachineState),
|
---|
| 1545 | E_FAIL);
|
---|
| 1546 |
|
---|
| 1547 | ComObjPtr<Snapshot> pSnapshot(static_cast<Snapshot*>(aSnapshot));
|
---|
[25152] | 1548 | ComObjPtr<SnapshotMachine> pSnapMachine = pSnapshot->getSnapshotMachine();
|
---|
[24279] | 1549 |
|
---|
[24299] | 1550 | // create a progress object. The number of operations is:
|
---|
| 1551 | // 1 (preparing) + # of hard disks + 1 (if we need to copy the saved state file) */
|
---|
[24279] | 1552 | LogFlowThisFunc(("Going thru snapshot machine attachments to determine progress setup\n"));
|
---|
| 1553 |
|
---|
| 1554 | ULONG ulOpCount = 1; // one for preparations
|
---|
| 1555 | ULONG ulTotalWeight = 1; // one for preparations
|
---|
[24345] | 1556 | for (MediaData::AttachmentList::iterator it = pSnapMachine->mMediaData->mAttachments.begin();
|
---|
| 1557 | it != pSnapMachine->mMediaData->mAttachments.end();
|
---|
[24279] | 1558 | ++it)
|
---|
| 1559 | {
|
---|
| 1560 | ComObjPtr<MediumAttachment> &pAttach = *it;
|
---|
[25310] | 1561 | AutoReadLock attachLock(pAttach COMMA_LOCKVAL_SRC_POS);
|
---|
[24989] | 1562 | if (pAttach->getType() == DeviceType_HardDisk)
|
---|
[24279] | 1563 | {
|
---|
| 1564 | ++ulOpCount;
|
---|
| 1565 | ++ulTotalWeight; // assume one MB weight for each differencing hard disk to manage
|
---|
[24989] | 1566 | Assert(pAttach->getMedium());
|
---|
| 1567 | LogFlowThisFunc(("op %d: considering hard disk attachment %s\n", ulOpCount, pAttach->getMedium()->getName().c_str()));
|
---|
[24279] | 1568 | }
|
---|
| 1569 | }
|
---|
| 1570 |
|
---|
| 1571 | ULONG ulStateFileSizeMB = 0;
|
---|
| 1572 | if (pSnapshot->stateFilePath().length())
|
---|
| 1573 | {
|
---|
| 1574 | ++ulOpCount; // one for the saved state
|
---|
| 1575 |
|
---|
| 1576 | uint64_t ullSize;
|
---|
| 1577 | int irc = RTFileQuerySize(pSnapshot->stateFilePath().c_str(), &ullSize);
|
---|
| 1578 | if (!RT_SUCCESS(irc))
|
---|
| 1579 | // if we can't access the file here, then we'll be doomed later also, so fail right away
|
---|
| 1580 | setError(E_FAIL, tr("Cannot access state file '%s', runtime error, %Rra"), pSnapshot->stateFilePath().c_str(), irc);
|
---|
| 1581 | if (ullSize == 0) // avoid division by zero
|
---|
| 1582 | ullSize = _1M;
|
---|
| 1583 |
|
---|
| 1584 | ulStateFileSizeMB = (ULONG)(ullSize / _1M);
|
---|
| 1585 | LogFlowThisFunc(("op %d: saved state file '%s' has %RI64 bytes (%d MB)\n",
|
---|
| 1586 | ulOpCount, pSnapshot->stateFilePath().raw(), ullSize, ulStateFileSizeMB));
|
---|
| 1587 |
|
---|
| 1588 | ulTotalWeight += ulStateFileSizeMB;
|
---|
| 1589 | }
|
---|
| 1590 |
|
---|
[24345] | 1591 | ComObjPtr<Progress> pProgress;
|
---|
| 1592 | pProgress.createObject();
|
---|
| 1593 | pProgress->init(mParent, aInitiator,
|
---|
| 1594 | BstrFmt(tr("Restoring snapshot '%s'"), pSnapshot->getName().c_str()),
|
---|
| 1595 | FALSE /* aCancelable */,
|
---|
| 1596 | ulOpCount,
|
---|
| 1597 | ulTotalWeight,
|
---|
| 1598 | Bstr(tr("Restoring machine settings")),
|
---|
| 1599 | 1);
|
---|
[24279] | 1600 |
|
---|
| 1601 | /* create and start the task on a separate thread (note that it will not
|
---|
| 1602 | * start working until we release alock) */
|
---|
[24298] | 1603 | RestoreSnapshotTask *task = new RestoreSnapshotTask(this,
|
---|
[24345] | 1604 | pProgress,
|
---|
[24298] | 1605 | pSnapshot,
|
---|
| 1606 | ulStateFileSizeMB);
|
---|
[24279] | 1607 | int vrc = RTThreadCreate(NULL,
|
---|
| 1608 | taskHandler,
|
---|
| 1609 | (void*)task,
|
---|
| 1610 | 0,
|
---|
| 1611 | RTTHREADTYPE_MAIN_WORKER,
|
---|
| 1612 | 0,
|
---|
| 1613 | "RestoreSnap");
|
---|
| 1614 | if (RT_FAILURE(vrc))
|
---|
| 1615 | {
|
---|
| 1616 | delete task;
|
---|
| 1617 | ComAssertRCRet(vrc, E_FAIL);
|
---|
| 1618 | }
|
---|
| 1619 |
|
---|
| 1620 | /* set the proper machine state (note: after creating a Task instance) */
|
---|
| 1621 | setMachineState(MachineState_RestoringSnapshot);
|
---|
| 1622 |
|
---|
| 1623 | /* return the progress to the caller */
|
---|
[24345] | 1624 | pProgress.queryInterfaceTo(aProgress);
|
---|
[24279] | 1625 |
|
---|
| 1626 | /* return the new state to the caller */
|
---|
| 1627 | *aMachineState = mData->mMachineState;
|
---|
| 1628 |
|
---|
| 1629 | LogFlowThisFuncLeave();
|
---|
| 1630 |
|
---|
| 1631 | return S_OK;
|
---|
| 1632 | }
|
---|
| 1633 |
|
---|
[24299] | 1634 | /**
|
---|
| 1635 | * Worker method for the restore snapshot thread created by SessionMachine::RestoreSnapshot().
|
---|
| 1636 | * This method gets called indirectly through SessionMachine::taskHandler() which then
|
---|
| 1637 | * calls RestoreSnapshotTask::handler().
|
---|
| 1638 | *
|
---|
| 1639 | * The RestoreSnapshotTask contains the progress object returned to the console by
|
---|
| 1640 | * SessionMachine::RestoreSnapshot, through which progress and results are reported.
|
---|
| 1641 | *
|
---|
| 1642 | * @note Locks mParent + this object for writing.
|
---|
| 1643 | *
|
---|
| 1644 | * @param aTask Task data.
|
---|
| 1645 | */
|
---|
| 1646 | void SessionMachine::restoreSnapshotHandler(RestoreSnapshotTask &aTask)
|
---|
[24279] | 1647 | {
|
---|
[24299] | 1648 | LogFlowThisFuncEnter();
|
---|
[24279] | 1649 |
|
---|
[24299] | 1650 | AutoCaller autoCaller(this);
|
---|
[24279] | 1651 |
|
---|
[24299] | 1652 | LogFlowThisFunc(("state=%d\n", autoCaller.state()));
|
---|
| 1653 | if (!autoCaller.isOk())
|
---|
| 1654 | {
|
---|
| 1655 | /* we might have been uninitialized because the session was accidentally
|
---|
| 1656 | * closed by the client, so don't assert */
|
---|
| 1657 | aTask.pProgress->notifyComplete(E_FAIL,
|
---|
| 1658 | COM_IIDOF(IMachine),
|
---|
| 1659 | getComponentName(),
|
---|
| 1660 | tr("The session has been accidentally closed"));
|
---|
[24279] | 1661 |
|
---|
[24299] | 1662 | LogFlowThisFuncLeave();
|
---|
| 1663 | return;
|
---|
| 1664 | }
|
---|
| 1665 |
|
---|
| 1666 | /* saveSettings() needs mParent lock */
|
---|
[25310] | 1667 | AutoWriteLock vboxLock(mParent COMMA_LOCKVAL_SRC_POS);
|
---|
[24299] | 1668 |
|
---|
| 1669 | /* @todo We don't need mParent lock so far so unlock() it. Better is to
|
---|
| 1670 | * provide an AutoWriteLock argument that lets create a non-locking
|
---|
| 1671 | * instance */
|
---|
[25279] | 1672 | vboxLock.release();
|
---|
[24299] | 1673 |
|
---|
[25310] | 1674 | AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
|
---|
[24299] | 1675 |
|
---|
| 1676 | /* discard all current changes to mUserData (name, OSType etc.) (note that
|
---|
| 1677 | * the machine is powered off, so there is no need to inform the direct
|
---|
| 1678 | * session) */
|
---|
| 1679 | if (isModified())
|
---|
| 1680 | rollback(false /* aNotify */);
|
---|
| 1681 |
|
---|
| 1682 | HRESULT rc = S_OK;
|
---|
| 1683 |
|
---|
| 1684 | bool stateRestored = false;
|
---|
| 1685 |
|
---|
| 1686 | try
|
---|
| 1687 | {
|
---|
| 1688 | /* discard the saved state file if the machine was Saved prior to this
|
---|
| 1689 | * operation */
|
---|
| 1690 | if (aTask.machineStateBackup == MachineState_Saved)
|
---|
| 1691 | {
|
---|
| 1692 | Assert(!mSSData->mStateFilePath.isEmpty());
|
---|
| 1693 | RTFileDelete(mSSData->mStateFilePath.c_str());
|
---|
| 1694 | mSSData->mStateFilePath.setNull();
|
---|
| 1695 | aTask.modifyBackedUpState(MachineState_PoweredOff);
|
---|
| 1696 | rc = saveStateSettings(SaveSTS_StateFilePath);
|
---|
[25149] | 1697 | if (FAILED(rc)) throw rc;
|
---|
[24299] | 1698 | }
|
---|
| 1699 |
|
---|
| 1700 | RTTIMESPEC snapshotTimeStamp;
|
---|
| 1701 | RTTimeSpecSetMilli(&snapshotTimeStamp, 0);
|
---|
| 1702 |
|
---|
| 1703 | {
|
---|
[25310] | 1704 | AutoReadLock snapshotLock(aTask.pSnapshot COMMA_LOCKVAL_SRC_POS);
|
---|
[24299] | 1705 |
|
---|
| 1706 | /* remember the timestamp of the snapshot we're restoring from */
|
---|
| 1707 | snapshotTimeStamp = aTask.pSnapshot->getTimeStamp();
|
---|
| 1708 |
|
---|
| 1709 | ComPtr<SnapshotMachine> pSnapshotMachine(aTask.pSnapshot->getSnapshotMachine());
|
---|
| 1710 |
|
---|
| 1711 | /* copy all hardware data from the snapshot */
|
---|
| 1712 | copyFrom(pSnapshotMachine);
|
---|
| 1713 |
|
---|
| 1714 | LogFlowThisFunc(("Restoring hard disks from the snapshot...\n"));
|
---|
| 1715 |
|
---|
| 1716 | /* restore the attachments from the snapshot */
|
---|
| 1717 | mMediaData.backup();
|
---|
| 1718 | mMediaData->mAttachments = pSnapshotMachine->mMediaData->mAttachments;
|
---|
| 1719 |
|
---|
| 1720 | /* leave the locks before the potentially lengthy operation */
|
---|
[25279] | 1721 | snapshotLock.release();
|
---|
[24299] | 1722 | alock.leave();
|
---|
| 1723 |
|
---|
| 1724 | rc = createImplicitDiffs(mUserData->mSnapshotFolderFull,
|
---|
| 1725 | aTask.pProgress,
|
---|
| 1726 | 1,
|
---|
| 1727 | false /* aOnline */);
|
---|
[25149] | 1728 | if (FAILED(rc)) throw rc;
|
---|
[24299] | 1729 |
|
---|
| 1730 | alock.enter();
|
---|
[25279] | 1731 | snapshotLock.acquire();
|
---|
[24299] | 1732 |
|
---|
| 1733 | /* Note: on success, current (old) hard disks will be
|
---|
| 1734 | * deassociated/deleted on #commit() called from #saveSettings() at
|
---|
| 1735 | * the end. On failure, newly created implicit diffs will be
|
---|
| 1736 | * deleted by #rollback() at the end. */
|
---|
| 1737 |
|
---|
| 1738 | /* should not have a saved state file associated at this point */
|
---|
| 1739 | Assert(mSSData->mStateFilePath.isEmpty());
|
---|
| 1740 |
|
---|
| 1741 | if (!aTask.pSnapshot->stateFilePath().isEmpty())
|
---|
| 1742 | {
|
---|
| 1743 | Utf8Str snapStateFilePath = aTask.pSnapshot->stateFilePath();
|
---|
| 1744 |
|
---|
| 1745 | Utf8Str stateFilePath = Utf8StrFmt("%ls%c{%RTuuid}.sav",
|
---|
| 1746 | mUserData->mSnapshotFolderFull.raw(),
|
---|
| 1747 | RTPATH_DELIMITER,
|
---|
| 1748 | mData->mUuid.raw());
|
---|
| 1749 |
|
---|
| 1750 | LogFlowThisFunc(("Copying saved state file from '%s' to '%s'...\n",
|
---|
| 1751 | snapStateFilePath.raw(), stateFilePath.raw()));
|
---|
| 1752 |
|
---|
| 1753 | aTask.pProgress->SetNextOperation(Bstr(tr("Restoring the execution state")),
|
---|
| 1754 | aTask.m_ulStateFileSizeMB); // weight
|
---|
| 1755 |
|
---|
| 1756 | /* leave the lock before the potentially lengthy operation */
|
---|
[25279] | 1757 | snapshotLock.release();
|
---|
[24299] | 1758 | alock.leave();
|
---|
| 1759 |
|
---|
| 1760 | /* copy the state file */
|
---|
| 1761 | int vrc = RTFileCopyEx(snapStateFilePath.c_str(),
|
---|
| 1762 | stateFilePath.c_str(),
|
---|
| 1763 | 0,
|
---|
| 1764 | progressCallback,
|
---|
[24556] | 1765 | static_cast<IProgress*>(aTask.pProgress));
|
---|
[24299] | 1766 |
|
---|
| 1767 | alock.enter();
|
---|
[25279] | 1768 | snapshotLock.acquire();
|
---|
[24299] | 1769 |
|
---|
| 1770 | if (RT_SUCCESS(vrc))
|
---|
| 1771 | mSSData->mStateFilePath = stateFilePath;
|
---|
| 1772 | else
|
---|
| 1773 | throw setError(E_FAIL,
|
---|
| 1774 | tr("Could not copy the state file '%s' to '%s' (%Rrc)"),
|
---|
| 1775 | snapStateFilePath.raw(),
|
---|
| 1776 | stateFilePath.raw(),
|
---|
| 1777 | vrc);
|
---|
| 1778 | }
|
---|
| 1779 |
|
---|
| 1780 | LogFlowThisFunc(("Setting new current snapshot {%RTuuid}\n", aTask.pSnapshot->getId().raw()));
|
---|
| 1781 | /* make the snapshot we restored from the current snapshot */
|
---|
| 1782 | mData->mCurrentSnapshot = aTask.pSnapshot;
|
---|
| 1783 | }
|
---|
| 1784 |
|
---|
| 1785 | /* grab differencing hard disks from the old attachments that will
|
---|
| 1786 | * become unused and need to be auto-deleted */
|
---|
| 1787 |
|
---|
| 1788 | std::list< ComObjPtr<MediumAttachment> > llDiffAttachmentsToDelete;
|
---|
| 1789 |
|
---|
| 1790 | for (MediaData::AttachmentList::const_iterator it = mMediaData.backedUpData()->mAttachments.begin();
|
---|
| 1791 | it != mMediaData.backedUpData()->mAttachments.end();
|
---|
| 1792 | ++it)
|
---|
| 1793 | {
|
---|
| 1794 | ComObjPtr<MediumAttachment> pAttach = *it;
|
---|
[24989] | 1795 | ComObjPtr<Medium> pMedium = pAttach->getMedium();
|
---|
[24299] | 1796 |
|
---|
| 1797 | /* while the hard disk is attached, the number of children or the
|
---|
| 1798 | * parent cannot change, so no lock */
|
---|
| 1799 | if ( !pMedium.isNull()
|
---|
[24989] | 1800 | && pAttach->getType() == DeviceType_HardDisk
|
---|
| 1801 | && !pMedium->getParent().isNull()
|
---|
| 1802 | && pMedium->getChildren().size() == 0
|
---|
[24299] | 1803 | )
|
---|
| 1804 | {
|
---|
[24989] | 1805 | LogFlowThisFunc(("Picked differencing image '%s' for deletion\n", pMedium->getName().raw()));
|
---|
[24299] | 1806 |
|
---|
| 1807 | llDiffAttachmentsToDelete.push_back(pAttach);
|
---|
| 1808 | }
|
---|
| 1809 | }
|
---|
| 1810 |
|
---|
| 1811 | int saveFlags = 0;
|
---|
| 1812 |
|
---|
| 1813 | /* @todo saveSettings() below needs a VirtualBox write lock and we need
|
---|
| 1814 | * to leave this object's lock to do this to follow the {parent-child}
|
---|
| 1815 | * locking rule. This is the last chance to do that while we are still
|
---|
| 1816 | * in a protective state which allows us to temporarily leave the lock*/
|
---|
[25279] | 1817 | alock.release();
|
---|
| 1818 | vboxLock.acquire();
|
---|
| 1819 | alock.acquire();
|
---|
[24299] | 1820 |
|
---|
| 1821 | /* we have already discarded the current state, so set the execution
|
---|
| 1822 | * state accordingly no matter of the discard snapshot result */
|
---|
| 1823 | if (!mSSData->mStateFilePath.isEmpty())
|
---|
| 1824 | setMachineState(MachineState_Saved);
|
---|
| 1825 | else
|
---|
| 1826 | setMachineState(MachineState_PoweredOff);
|
---|
| 1827 |
|
---|
| 1828 | updateMachineStateOnClient();
|
---|
| 1829 | stateRestored = true;
|
---|
| 1830 |
|
---|
| 1831 | /* assign the timestamp from the snapshot */
|
---|
| 1832 | Assert(RTTimeSpecGetMilli (&snapshotTimeStamp) != 0);
|
---|
| 1833 | mData->mLastStateChange = snapshotTimeStamp;
|
---|
| 1834 |
|
---|
| 1835 | // detach the current-state diffs that we detected above and build a list of
|
---|
| 1836 | // images to delete _after_ saveSettings()
|
---|
| 1837 |
|
---|
[25151] | 1838 | MediaList llDiffsToDelete;
|
---|
[24299] | 1839 |
|
---|
| 1840 | for (std::list< ComObjPtr<MediumAttachment> >::iterator it = llDiffAttachmentsToDelete.begin();
|
---|
| 1841 | it != llDiffAttachmentsToDelete.end();
|
---|
| 1842 | ++it)
|
---|
| 1843 | {
|
---|
| 1844 | ComObjPtr<MediumAttachment> pAttach = *it; // guaranteed to have only attachments where medium != NULL
|
---|
[24989] | 1845 | ComObjPtr<Medium> pMedium = pAttach->getMedium();
|
---|
[24299] | 1846 |
|
---|
[25310] | 1847 | AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS);
|
---|
[24299] | 1848 |
|
---|
[24989] | 1849 | LogFlowThisFunc(("Detaching old current state in differencing image '%s'\n", pMedium->getName().raw()));
|
---|
[24299] | 1850 |
|
---|
[24458] | 1851 | // Normally we "detach" the medium by removing the attachment object
|
---|
| 1852 | // from the current machine data; saveSettings() below would then
|
---|
[24447] | 1853 | // compare the current machine data with the one in the backup
|
---|
[24458] | 1854 | // and actually call Medium::detachFrom(). But that works only half
|
---|
| 1855 | // the time in our case so instead we force a detachment here:
|
---|
| 1856 | // remove from machine data
|
---|
[24299] | 1857 | mMediaData->mAttachments.remove(pAttach);
|
---|
[24458] | 1858 | // remove it from the backup or else saveSettings will try to detach
|
---|
| 1859 | // it again and assert
|
---|
| 1860 | mMediaData.backedUpData()->mAttachments.remove(pAttach);
|
---|
| 1861 | // then clean up backrefs
|
---|
| 1862 | pMedium->detachFrom(mData->mUuid);
|
---|
[24299] | 1863 |
|
---|
| 1864 | llDiffsToDelete.push_back(pMedium);
|
---|
| 1865 | }
|
---|
| 1866 |
|
---|
| 1867 | // save all settings, reset the modified flag and commit;
|
---|
| 1868 | rc = saveSettings(SaveS_ResetCurStateModified | saveFlags);
|
---|
[25149] | 1869 | if (FAILED(rc)) throw rc;
|
---|
| 1870 |
|
---|
[24299] | 1871 | // from here on we cannot roll back on failure any more
|
---|
| 1872 |
|
---|
[25151] | 1873 | for (MediaList::iterator it = llDiffsToDelete.begin();
|
---|
[24299] | 1874 | it != llDiffsToDelete.end();
|
---|
| 1875 | ++it)
|
---|
| 1876 | {
|
---|
| 1877 | ComObjPtr<Medium> &pMedium = *it;
|
---|
[24989] | 1878 | LogFlowThisFunc(("Deleting old current state in differencing image '%s'\n", pMedium->getName().raw()));
|
---|
[24299] | 1879 |
|
---|
| 1880 | HRESULT rc2 = pMedium->deleteStorageAndWait();
|
---|
| 1881 | // ignore errors here because we cannot roll back after saveSettings() above
|
---|
| 1882 | if (SUCCEEDED(rc2))
|
---|
| 1883 | pMedium->uninit();
|
---|
| 1884 | }
|
---|
| 1885 | }
|
---|
| 1886 | catch (HRESULT aRC)
|
---|
| 1887 | {
|
---|
| 1888 | rc = aRC;
|
---|
| 1889 | }
|
---|
| 1890 |
|
---|
| 1891 | if (FAILED(rc))
|
---|
| 1892 | {
|
---|
| 1893 | /* preserve existing error info */
|
---|
| 1894 | ErrorInfoKeeper eik;
|
---|
| 1895 |
|
---|
| 1896 | /* undo all changes on failure */
|
---|
| 1897 | rollback(false /* aNotify */);
|
---|
| 1898 |
|
---|
| 1899 | if (!stateRestored)
|
---|
| 1900 | {
|
---|
| 1901 | /* restore the machine state */
|
---|
| 1902 | setMachineState(aTask.machineStateBackup);
|
---|
| 1903 | updateMachineStateOnClient();
|
---|
| 1904 | }
|
---|
| 1905 | }
|
---|
| 1906 |
|
---|
| 1907 | /* set the result (this will try to fetch current error info on failure) */
|
---|
| 1908 | aTask.pProgress->notifyComplete(rc);
|
---|
| 1909 |
|
---|
| 1910 | if (SUCCEEDED(rc))
|
---|
| 1911 | mParent->onSnapshotDeleted(mData->mUuid, Guid());
|
---|
| 1912 |
|
---|
| 1913 | LogFlowThisFunc(("Done restoring snapshot (rc=%08X)\n", rc));
|
---|
| 1914 |
|
---|
| 1915 | LogFlowThisFuncLeave();
|
---|
[24279] | 1916 | }
|
---|
| 1917 |
|
---|
[24299] | 1918 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 1919 | //
|
---|
| 1920 | // DeleteSnapshot methods (SessionMachine and related tasks)
|
---|
| 1921 | //
|
---|
| 1922 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 1923 |
|
---|
[24279] | 1924 | /**
|
---|
[24299] | 1925 | * Implementation for IInternalMachineControl::deleteSnapshot().
|
---|
[24279] | 1926 | *
|
---|
[24299] | 1927 | * Gets called from Console::DeleteSnapshot(), and that's basically the
|
---|
| 1928 | * only thing Console does. Deleting a snapshot happens entirely on the
|
---|
| 1929 | * server side since the machine cannot be running.
|
---|
[24279] | 1930 | *
|
---|
[24299] | 1931 | * This creates a new thread that does the work and returns a progress
|
---|
| 1932 | * object to the client which is then returned to the caller of
|
---|
| 1933 | * Console::DeleteSnapshot().
|
---|
[24279] | 1934 | *
|
---|
[24299] | 1935 | * Actual work then takes place in DeleteSnapshotTask::handler().
|
---|
| 1936 | *
|
---|
| 1937 | * @note Locks mParent + this + children objects for writing!
|
---|
[24279] | 1938 | */
|
---|
[24299] | 1939 | STDMETHODIMP SessionMachine::DeleteSnapshot(IConsole *aInitiator,
|
---|
| 1940 | IN_BSTR aId,
|
---|
| 1941 | MachineState_T *aMachineState,
|
---|
| 1942 | IProgress **aProgress)
|
---|
[24279] | 1943 | {
|
---|
| 1944 | LogFlowThisFuncEnter();
|
---|
| 1945 |
|
---|
[24299] | 1946 | Guid id(aId);
|
---|
| 1947 | AssertReturn(aInitiator && !id.isEmpty(), E_INVALIDARG);
|
---|
| 1948 | AssertReturn(aMachineState && aProgress, E_POINTER);
|
---|
| 1949 |
|
---|
[24279] | 1950 | AutoCaller autoCaller(this);
|
---|
| 1951 | AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
|
---|
| 1952 |
|
---|
[24299] | 1953 | /* saveSettings() needs mParent lock */
|
---|
[25310] | 1954 | AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
|
---|
[24279] | 1955 |
|
---|
[24299] | 1956 | // machine must not be running
|
---|
| 1957 | ComAssertRet(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
|
---|
[24279] | 1958 |
|
---|
[25310] | 1959 | AutoWriteLock treeLock(snapshotsTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
|
---|
[24279] | 1960 |
|
---|
[24345] | 1961 | ComObjPtr<Snapshot> pSnapshot;
|
---|
| 1962 | HRESULT rc = findSnapshot(id, pSnapshot, true /* aSetError */);
|
---|
[25149] | 1963 | if (FAILED(rc)) return rc;
|
---|
[24279] | 1964 |
|
---|
[25310] | 1965 | AutoWriteLock snapshotLock(pSnapshot COMMA_LOCKVAL_SRC_POS);
|
---|
[24279] | 1966 |
|
---|
[24345] | 1967 | size_t childrenCount = pSnapshot->getChildrenCount();
|
---|
[24299] | 1968 | if (childrenCount > 1)
|
---|
| 1969 | return setError(VBOX_E_INVALID_OBJECT_STATE,
|
---|
| 1970 | tr("Snapshot '%s' of the machine '%ls' cannot be deleted. because it has %d child snapshots, which is more than the one snapshot allowed for deletion"),
|
---|
[24345] | 1971 | pSnapshot->getName().c_str(),
|
---|
[24299] | 1972 | mUserData->mName.raw(),
|
---|
| 1973 | childrenCount);
|
---|
| 1974 |
|
---|
| 1975 | /* If the snapshot being discarded is the current one, ensure current
|
---|
| 1976 | * settings are committed and saved.
|
---|
| 1977 | */
|
---|
[24345] | 1978 | if (pSnapshot == mData->mCurrentSnapshot)
|
---|
[24279] | 1979 | {
|
---|
[24299] | 1980 | if (isModified())
|
---|
| 1981 | {
|
---|
| 1982 | rc = saveSettings();
|
---|
[25149] | 1983 | if (FAILED(rc)) return rc;
|
---|
[24299] | 1984 | }
|
---|
| 1985 | }
|
---|
[24279] | 1986 |
|
---|
[25152] | 1987 | ComObjPtr<SnapshotMachine> pSnapMachine = pSnapshot->getSnapshotMachine();
|
---|
[24345] | 1988 |
|
---|
[24299] | 1989 | /* create a progress object. The number of operations is:
|
---|
[24345] | 1990 | * 1 (preparing) + 1 if the snapshot is online + # of normal hard disks
|
---|
[24299] | 1991 | */
|
---|
[24345] | 1992 | LogFlowThisFunc(("Going thru snapshot machine attachments to determine progress setup\n"));
|
---|
[24279] | 1993 |
|
---|
[24345] | 1994 | ULONG ulOpCount = 1; // one for preparations
|
---|
| 1995 | ULONG ulTotalWeight = 1; // one for preparations
|
---|
| 1996 |
|
---|
| 1997 | if (pSnapshot->stateFilePath().length())
|
---|
| 1998 | {
|
---|
| 1999 | ++ulOpCount;
|
---|
| 2000 | ++ulTotalWeight; // assume 1 MB for deleting the state file
|
---|
| 2001 | }
|
---|
| 2002 |
|
---|
| 2003 | // count normal hard disks and add their sizes to the weight
|
---|
| 2004 | for (MediaData::AttachmentList::iterator it = pSnapMachine->mMediaData->mAttachments.begin();
|
---|
| 2005 | it != pSnapMachine->mMediaData->mAttachments.end();
|
---|
| 2006 | ++it)
|
---|
| 2007 | {
|
---|
| 2008 | ComObjPtr<MediumAttachment> &pAttach = *it;
|
---|
[25310] | 2009 | AutoReadLock attachLock(pAttach COMMA_LOCKVAL_SRC_POS);
|
---|
[24989] | 2010 | if (pAttach->getType() == DeviceType_HardDisk)
|
---|
[24345] | 2011 | {
|
---|
[24989] | 2012 | ComObjPtr<Medium> pHD = pAttach->getMedium();
|
---|
| 2013 | Assert(pHD);
|
---|
[25310] | 2014 | AutoReadLock mlock(pHD COMMA_LOCKVAL_SRC_POS);
|
---|
[24989] | 2015 | if (pHD->getType() == MediumType_Normal)
|
---|
[24345] | 2016 | {
|
---|
| 2017 | ++ulOpCount;
|
---|
[24992] | 2018 | ulTotalWeight += (ULONG)(pHD->getSize() / _1M);
|
---|
[24345] | 2019 | }
|
---|
[24989] | 2020 | LogFlowThisFunc(("op %d: considering hard disk attachment %s\n", ulOpCount, pHD->getName().c_str()));
|
---|
[24345] | 2021 | }
|
---|
| 2022 | }
|
---|
| 2023 |
|
---|
| 2024 | ComObjPtr<Progress> pProgress;
|
---|
| 2025 | pProgress.createObject();
|
---|
| 2026 | pProgress->init(mParent, aInitiator,
|
---|
| 2027 | BstrFmt(tr("Deleting snapshot '%s'"), pSnapshot->getName().c_str()),
|
---|
| 2028 | FALSE /* aCancelable */,
|
---|
| 2029 | ulOpCount,
|
---|
| 2030 | ulTotalWeight,
|
---|
| 2031 | Bstr(tr("Setting up")),
|
---|
| 2032 | 1);
|
---|
| 2033 |
|
---|
[24299] | 2034 | /* create and start the task on a separate thread */
|
---|
[24345] | 2035 | DeleteSnapshotTask *task = new DeleteSnapshotTask(this, pProgress, pSnapshot);
|
---|
[24299] | 2036 | int vrc = RTThreadCreate(NULL,
|
---|
| 2037 | taskHandler,
|
---|
| 2038 | (void*)task,
|
---|
| 2039 | 0,
|
---|
| 2040 | RTTHREADTYPE_MAIN_WORKER,
|
---|
| 2041 | 0,
|
---|
| 2042 | "DeleteSnapshot");
|
---|
| 2043 | if (RT_FAILURE(vrc))
|
---|
[24279] | 2044 | {
|
---|
[24299] | 2045 | delete task;
|
---|
| 2046 | return E_FAIL;
|
---|
[24279] | 2047 | }
|
---|
| 2048 |
|
---|
[24299] | 2049 | /* set the proper machine state (note: after creating a Task instance) */
|
---|
| 2050 | setMachineState(MachineState_DeletingSnapshot);
|
---|
[24279] | 2051 |
|
---|
[24299] | 2052 | /* return the progress to the caller */
|
---|
[24345] | 2053 | pProgress.queryInterfaceTo(aProgress);
|
---|
[24279] | 2054 |
|
---|
[24299] | 2055 | /* return the new state to the caller */
|
---|
| 2056 | *aMachineState = mData->mMachineState;
|
---|
[24279] | 2057 |
|
---|
[24299] | 2058 | LogFlowThisFuncLeave();
|
---|
[24279] | 2059 |
|
---|
[24299] | 2060 | return S_OK;
|
---|
[24279] | 2061 | }
|
---|
| 2062 |
|
---|
| 2063 | /**
|
---|
| 2064 | * Helper struct for SessionMachine::deleteSnapshotHandler().
|
---|
| 2065 | */
|
---|
| 2066 | struct MediumDiscardRec
|
---|
| 2067 | {
|
---|
[24298] | 2068 | MediumDiscardRec()
|
---|
| 2069 | : chain(NULL)
|
---|
| 2070 | {}
|
---|
[24279] | 2071 |
|
---|
[24298] | 2072 | MediumDiscardRec(const ComObjPtr<Medium> &aHd,
|
---|
| 2073 | Medium::MergeChain *aChain = NULL)
|
---|
| 2074 | : hd(aHd),
|
---|
| 2075 | chain(aChain)
|
---|
| 2076 | {}
|
---|
[24279] | 2077 |
|
---|
[24298] | 2078 | MediumDiscardRec(const ComObjPtr<Medium> &aHd,
|
---|
| 2079 | Medium::MergeChain *aChain,
|
---|
| 2080 | const ComObjPtr<Medium> &aReplaceHd,
|
---|
| 2081 | const ComObjPtr<MediumAttachment> &aReplaceHda,
|
---|
| 2082 | const Guid &aSnapshotId)
|
---|
| 2083 | : hd(aHd),
|
---|
[24299] | 2084 | chain(aChain),
|
---|
[24298] | 2085 | replaceHd(aReplaceHd),
|
---|
| 2086 | replaceHda(aReplaceHda),
|
---|
| 2087 | snapshotId(aSnapshotId)
|
---|
| 2088 | {}
|
---|
[24279] | 2089 |
|
---|
| 2090 | ComObjPtr<Medium> hd;
|
---|
| 2091 | Medium::MergeChain *chain;
|
---|
| 2092 | /* these are for the replace hard disk case: */
|
---|
| 2093 | ComObjPtr<Medium> replaceHd;
|
---|
| 2094 | ComObjPtr<MediumAttachment> replaceHda;
|
---|
| 2095 | Guid snapshotId;
|
---|
| 2096 | };
|
---|
| 2097 |
|
---|
| 2098 | typedef std::list <MediumDiscardRec> MediumDiscardRecList;
|
---|
| 2099 |
|
---|
| 2100 | /**
|
---|
[24299] | 2101 | * Worker method for the delete snapshot thread created by SessionMachine::DeleteSnapshot().
|
---|
| 2102 | * This method gets called indirectly through SessionMachine::taskHandler() which then
|
---|
| 2103 | * calls DeleteSnapshotTask::handler().
|
---|
[24279] | 2104 | *
|
---|
[24299] | 2105 | * The DeleteSnapshotTask contains the progress object returned to the console by
|
---|
| 2106 | * SessionMachine::DeleteSnapshot, through which progress and results are reported.
|
---|
[24279] | 2107 | *
|
---|
| 2108 | * @note Locks mParent + this + child objects for writing!
|
---|
[24299] | 2109 | *
|
---|
| 2110 | * @param aTask Task data.
|
---|
[24279] | 2111 | */
|
---|
| 2112 | void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask)
|
---|
| 2113 | {
|
---|
| 2114 | LogFlowThisFuncEnter();
|
---|
| 2115 |
|
---|
| 2116 | AutoCaller autoCaller(this);
|
---|
| 2117 |
|
---|
| 2118 | LogFlowThisFunc(("state=%d\n", autoCaller.state()));
|
---|
| 2119 | if (!autoCaller.isOk())
|
---|
| 2120 | {
|
---|
| 2121 | /* we might have been uninitialized because the session was accidentally
|
---|
| 2122 | * closed by the client, so don't assert */
|
---|
[24298] | 2123 | aTask.pProgress->notifyComplete(E_FAIL,
|
---|
| 2124 | COM_IIDOF(IMachine),
|
---|
| 2125 | getComponentName(),
|
---|
| 2126 | tr("The session has been accidentally closed"));
|
---|
[24279] | 2127 | LogFlowThisFuncLeave();
|
---|
| 2128 | return;
|
---|
| 2129 | }
|
---|
| 2130 |
|
---|
| 2131 | /* Locking order: */
|
---|
| 2132 | AutoMultiWriteLock3 alock(this->lockHandle(),
|
---|
| 2133 | this->snapshotsTreeLockHandle(),
|
---|
[25310] | 2134 | aTask.pSnapshot->lockHandle()
|
---|
| 2135 | COMMA_LOCKVAL_SRC_POS);
|
---|
[24279] | 2136 |
|
---|
[25152] | 2137 | ComObjPtr<SnapshotMachine> pSnapMachine = aTask.pSnapshot->getSnapshotMachine();
|
---|
[24279] | 2138 | /* no need to lock the snapshot machine since it is const by definiton */
|
---|
| 2139 |
|
---|
| 2140 | HRESULT rc = S_OK;
|
---|
| 2141 |
|
---|
| 2142 | /* save the snapshot ID (for callbacks) */
|
---|
[24989] | 2143 | Guid snapshotId1 = aTask.pSnapshot->getId();
|
---|
[24279] | 2144 |
|
---|
| 2145 | MediumDiscardRecList toDiscard;
|
---|
| 2146 |
|
---|
| 2147 | bool settingsChanged = false;
|
---|
| 2148 |
|
---|
| 2149 | try
|
---|
| 2150 | {
|
---|
| 2151 | /* first pass: */
|
---|
| 2152 | LogFlowThisFunc(("1: Checking hard disk merge prerequisites...\n"));
|
---|
| 2153 |
|
---|
[24433] | 2154 | // go thru the attachments of the snapshot machine
|
---|
| 2155 | // (the media in here point to the disk states _before_ the snapshot
|
---|
| 2156 | // was taken, i.e. the state we're restoring to; for each such
|
---|
| 2157 | // medium, we will need to merge it with its one and only child (the
|
---|
| 2158 | // diff image holding the changes written after the snapshot was taken)
|
---|
[24345] | 2159 | for (MediaData::AttachmentList::iterator it = pSnapMachine->mMediaData->mAttachments.begin();
|
---|
| 2160 | it != pSnapMachine->mMediaData->mAttachments.end();
|
---|
| 2161 | ++it)
|
---|
[24279] | 2162 | {
|
---|
[24345] | 2163 | ComObjPtr<MediumAttachment> &pAttach = *it;
|
---|
[25310] | 2164 | AutoReadLock attachLock(pAttach COMMA_LOCKVAL_SRC_POS);
|
---|
[24989] | 2165 | if (pAttach->getType() == DeviceType_HardDisk)
|
---|
[24345] | 2166 | {
|
---|
[24989] | 2167 | Assert(pAttach->getMedium());
|
---|
| 2168 | ComObjPtr<Medium> pHD = pAttach->getMedium();
|
---|
[24433] | 2169 | // do not lock, prepareDiscared() has a write lock which will hang otherwise
|
---|
[24279] | 2170 |
|
---|
[24509] | 2171 | #ifdef DEBUG
|
---|
| 2172 | pHD->dumpBackRefs();
|
---|
| 2173 | #endif
|
---|
| 2174 |
|
---|
[24345] | 2175 | Medium::MergeChain *chain = NULL;
|
---|
[24279] | 2176 |
|
---|
[24433] | 2177 | // needs to be discarded (merged with the child if any), check prerequisites
|
---|
[24345] | 2178 | rc = pHD->prepareDiscard(chain);
|
---|
[25149] | 2179 | if (FAILED(rc)) throw rc;
|
---|
[24279] | 2180 |
|
---|
[24433] | 2181 | // for simplicity, we merge pHd onto its child (forward merge), not the
|
---|
| 2182 | // other way round, because that saves us from updating the attachments
|
---|
| 2183 | // for the machine that follows the snapshot (next snapshot or real machine),
|
---|
| 2184 | // unless it's a base image:
|
---|
| 2185 |
|
---|
[24989] | 2186 | if ( pHD->getParent().isNull()
|
---|
[24433] | 2187 | && chain != NULL
|
---|
| 2188 | )
|
---|
[24345] | 2189 | {
|
---|
[24433] | 2190 | // parent is null -> this disk is a base hard disk: we will
|
---|
| 2191 | // then do a backward merge, i.e. merge its only child onto
|
---|
| 2192 | // the base disk; prepareDiscard() does necessary checks.
|
---|
| 2193 | // So here we need then to update the attachment that refers
|
---|
| 2194 | // to the child and have it point to the parent instead
|
---|
[24279] | 2195 |
|
---|
[24345] | 2196 | /* The below assert would be nice but I don't want to move
|
---|
[24433] | 2197 | * Medium::MergeChain to the header just for that
|
---|
| 2198 | * Assert (!chain->isForward()); */
|
---|
[24279] | 2199 |
|
---|
[24433] | 2200 | // prepareDiscard() should have raised an error already
|
---|
| 2201 | // if there was more than one child
|
---|
[24989] | 2202 | Assert(pHD->getChildren().size() == 1);
|
---|
[24279] | 2203 |
|
---|
[24989] | 2204 | ComObjPtr<Medium> pReplaceHD = pHD->getChildren().front();
|
---|
[24279] | 2205 |
|
---|
[24433] | 2206 | const Guid *pReplaceMachineId = pReplaceHD->getFirstMachineBackrefId();
|
---|
[24572] | 2207 | NOREF(pReplaceMachineId);
|
---|
[24433] | 2208 | Assert(pReplaceMachineId);
|
---|
| 2209 | Assert(*pReplaceMachineId == mData->mUuid);
|
---|
[24279] | 2210 |
|
---|
[24345] | 2211 | Guid snapshotId;
|
---|
[24433] | 2212 | const Guid *pSnapshotId = pReplaceHD->getFirstMachineBackrefSnapshotId();
|
---|
[24345] | 2213 | if (pSnapshotId)
|
---|
| 2214 | snapshotId = *pSnapshotId;
|
---|
[24279] | 2215 |
|
---|
[24345] | 2216 | HRESULT rc2 = S_OK;
|
---|
[24279] | 2217 |
|
---|
[25279] | 2218 | attachLock.release();
|
---|
[24433] | 2219 |
|
---|
| 2220 | // First we must detach the child (otherwise mergeTo() called
|
---|
| 2221 | // by discard() will assert because it will be going to delete
|
---|
| 2222 | // the child), so adjust the backreferences:
|
---|
| 2223 | // 1) detach the first child hard disk
|
---|
| 2224 | rc2 = pReplaceHD->detachFrom(mData->mUuid, snapshotId);
|
---|
[24345] | 2225 | AssertComRC(rc2);
|
---|
[24433] | 2226 | // 2) attach to machine and snapshot
|
---|
[24345] | 2227 | rc2 = pHD->attachTo(mData->mUuid, snapshotId);
|
---|
| 2228 | AssertComRC(rc2);
|
---|
[24279] | 2229 |
|
---|
[24345] | 2230 | /* replace the hard disk in the attachment object */
|
---|
| 2231 | if (snapshotId.isEmpty())
|
---|
| 2232 | {
|
---|
| 2233 | /* in current state */
|
---|
[24433] | 2234 | AssertBreak(pAttach = findAttachment(mMediaData->mAttachments, pReplaceHD));
|
---|
[24345] | 2235 | }
|
---|
| 2236 | else
|
---|
| 2237 | {
|
---|
| 2238 | /* in snapshot */
|
---|
| 2239 | ComObjPtr<Snapshot> snapshot;
|
---|
| 2240 | rc2 = findSnapshot(snapshotId, snapshot);
|
---|
| 2241 | AssertComRC(rc2);
|
---|
[24279] | 2242 |
|
---|
[24345] | 2243 | /* don't lock the snapshot; cannot be modified outside */
|
---|
| 2244 | MediaData::AttachmentList &snapAtts = snapshot->getSnapshotMachine()->mMediaData->mAttachments;
|
---|
[24433] | 2245 | AssertBreak(pAttach = findAttachment(snapAtts, pReplaceHD));
|
---|
[24345] | 2246 | }
|
---|
[24279] | 2247 |
|
---|
[25310] | 2248 | AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
|
---|
[24345] | 2249 | pAttach->updateMedium(pHD, false /* aImplicit */);
|
---|
[24279] | 2250 |
|
---|
[24345] | 2251 | toDiscard.push_back(MediumDiscardRec(pHD,
|
---|
| 2252 | chain,
|
---|
[24433] | 2253 | pReplaceHD,
|
---|
[24345] | 2254 | pAttach,
|
---|
| 2255 | snapshotId));
|
---|
| 2256 | continue;
|
---|
[24279] | 2257 | }
|
---|
| 2258 |
|
---|
[24345] | 2259 | toDiscard.push_back(MediumDiscardRec(pHD, chain));
|
---|
[24279] | 2260 | }
|
---|
| 2261 | }
|
---|
| 2262 |
|
---|
| 2263 | /* Now we checked that we can successfully merge all normal hard disks
|
---|
| 2264 | * (unless a runtime error like end-of-disc happens). Prior to
|
---|
| 2265 | * performing the actual merge, we want to discard the snapshot itself
|
---|
| 2266 | * and remove it from the XML file to make sure that a possible merge
|
---|
| 2267 | * ruintime error will not make this snapshot inconsistent because of
|
---|
| 2268 | * the partially merged or corrupted hard disks */
|
---|
| 2269 |
|
---|
| 2270 | /* second pass: */
|
---|
| 2271 | LogFlowThisFunc(("2: Discarding snapshot...\n"));
|
---|
| 2272 |
|
---|
| 2273 | {
|
---|
[25152] | 2274 | ComObjPtr<Snapshot> parentSnapshot = aTask.pSnapshot->getParent();
|
---|
[24298] | 2275 | Utf8Str stateFilePath = aTask.pSnapshot->stateFilePath();
|
---|
[24279] | 2276 |
|
---|
| 2277 | /* Note that discarding the snapshot will deassociate it from the
|
---|
| 2278 | * hard disks which will allow the merge+delete operation for them*/
|
---|
[24298] | 2279 | aTask.pSnapshot->beginDiscard();
|
---|
| 2280 | aTask.pSnapshot->uninit();
|
---|
[24279] | 2281 |
|
---|
| 2282 | rc = saveAllSnapshots();
|
---|
[25149] | 2283 | if (FAILED(rc)) throw rc;
|
---|
[24279] | 2284 |
|
---|
| 2285 | /// @todo (dmik)
|
---|
| 2286 | // if we implement some warning mechanism later, we'll have
|
---|
| 2287 | // to return a warning if the state file path cannot be deleted
|
---|
[24291] | 2288 | if (!stateFilePath.isEmpty())
|
---|
[24279] | 2289 | {
|
---|
[24298] | 2290 | aTask.pProgress->SetNextOperation(Bstr(tr("Discarding the execution state")),
|
---|
| 2291 | 1); // weight
|
---|
[24279] | 2292 |
|
---|
[24291] | 2293 | RTFileDelete(stateFilePath.c_str());
|
---|
[24279] | 2294 | }
|
---|
| 2295 |
|
---|
| 2296 | /// @todo NEWMEDIA to provide a good level of fauilt tolerance, we
|
---|
| 2297 | /// should restore the shapshot in the snapshot tree if
|
---|
| 2298 | /// saveSnapshotSettings fails. Actually, we may call
|
---|
| 2299 | /// #saveSnapshotSettings() with a special flag that will tell it to
|
---|
| 2300 | /// skip the given snapshot as if it would have been discarded and
|
---|
| 2301 | /// only actually discard it if the save operation succeeds.
|
---|
| 2302 | }
|
---|
| 2303 |
|
---|
| 2304 | /* here we come when we've irrevesibly discarded the snapshot which
|
---|
| 2305 | * means that the VM settigns (our relevant changes to mData) need to be
|
---|
| 2306 | * saved too */
|
---|
| 2307 | /// @todo NEWMEDIA maybe save everything in one operation in place of
|
---|
| 2308 | /// saveSnapshotSettings() above
|
---|
| 2309 | settingsChanged = true;
|
---|
| 2310 |
|
---|
| 2311 | /* third pass: */
|
---|
| 2312 | LogFlowThisFunc(("3: Performing actual hard disk merging...\n"));
|
---|
| 2313 |
|
---|
| 2314 | /* leave the locks before the potentially lengthy operation */
|
---|
| 2315 | alock.leave();
|
---|
| 2316 |
|
---|
| 2317 | /// @todo NEWMEDIA turn the following errors into warnings because the
|
---|
| 2318 | /// snapshot itself has been already deleted (and interpret these
|
---|
| 2319 | /// warnings properly on the GUI side)
|
---|
| 2320 |
|
---|
| 2321 | for (MediumDiscardRecList::iterator it = toDiscard.begin();
|
---|
| 2322 | it != toDiscard.end();)
|
---|
| 2323 | {
|
---|
[24345] | 2324 | rc = it->hd->discard(aTask.pProgress,
|
---|
[24992] | 2325 | (ULONG)(it->hd->getSize() / _1M), // weight
|
---|
[24345] | 2326 | it->chain);
|
---|
[25149] | 2327 | if (FAILED(rc)) throw rc;
|
---|
[24279] | 2328 |
|
---|
| 2329 | /* prevent from calling cancelDiscard() */
|
---|
[24345] | 2330 | it = toDiscard.erase(it);
|
---|
[24279] | 2331 | }
|
---|
| 2332 |
|
---|
[24345] | 2333 | LogFlowThisFunc(("Entering locks again...\n"));
|
---|
[24279] | 2334 | alock.enter();
|
---|
[24345] | 2335 | LogFlowThisFunc(("Entered locks OK\n"));
|
---|
[24279] | 2336 | }
|
---|
| 2337 | catch (HRESULT aRC) { rc = aRC; }
|
---|
| 2338 |
|
---|
| 2339 | if (FAILED(rc))
|
---|
| 2340 | {
|
---|
| 2341 | HRESULT rc2 = S_OK;
|
---|
| 2342 |
|
---|
| 2343 | /* un-prepare the remaining hard disks */
|
---|
| 2344 | for (MediumDiscardRecList::const_iterator it = toDiscard.begin();
|
---|
| 2345 | it != toDiscard.end(); ++it)
|
---|
| 2346 | {
|
---|
| 2347 | it->hd->cancelDiscard (it->chain);
|
---|
| 2348 |
|
---|
| 2349 | if (!it->replaceHd.isNull())
|
---|
| 2350 | {
|
---|
| 2351 | /* undo hard disk replacement */
|
---|
| 2352 |
|
---|
[24989] | 2353 | rc2 = it->replaceHd->attachTo(mData->mUuid, it->snapshotId);
|
---|
[24279] | 2354 | AssertComRC(rc2);
|
---|
| 2355 |
|
---|
| 2356 | rc2 = it->hd->detachFrom (mData->mUuid, it->snapshotId);
|
---|
| 2357 | AssertComRC(rc2);
|
---|
| 2358 |
|
---|
[25310] | 2359 | AutoWriteLock attLock(it->replaceHda COMMA_LOCKVAL_SRC_POS);
|
---|
[24279] | 2360 | it->replaceHda->updateMedium(it->replaceHd, false /* aImplicit */);
|
---|
| 2361 | }
|
---|
| 2362 | }
|
---|
| 2363 | }
|
---|
| 2364 |
|
---|
[25279] | 2365 | alock.release();
|
---|
[24354] | 2366 |
|
---|
| 2367 | // whether we were successful or not, we need to set the machine
|
---|
| 2368 | // state and save the machine settings;
|
---|
[24279] | 2369 | {
|
---|
[24354] | 2370 | // preserve existing error info so that the result can
|
---|
| 2371 | // be properly reported to the progress object below
|
---|
[24298] | 2372 | ErrorInfoKeeper eik;
|
---|
[24279] | 2373 |
|
---|
[24354] | 2374 | // restore the machine state that was saved when the
|
---|
| 2375 | // task was started
|
---|
[24298] | 2376 | setMachineState(aTask.machineStateBackup);
|
---|
| 2377 | updateMachineStateOnClient();
|
---|
[24279] | 2378 |
|
---|
[24298] | 2379 | if (settingsChanged)
|
---|
[24354] | 2380 | {
|
---|
| 2381 | // saveSettings needs VirtualBox write lock in addition to our own
|
---|
| 2382 | // (parent -> child locking order!)
|
---|
[25310] | 2383 | AutoWriteLock vboxLock(mParent COMMA_LOCKVAL_SRC_POS);
|
---|
[25279] | 2384 | alock.acquire();
|
---|
[24354] | 2385 |
|
---|
[24298] | 2386 | saveSettings(SaveS_InformCallbacksAnyway);
|
---|
[24354] | 2387 | }
|
---|
[24279] | 2388 | }
|
---|
| 2389 |
|
---|
[24354] | 2390 | // report the result (this will try to fetch current error info on failure)
|
---|
| 2391 | aTask.pProgress->notifyComplete(rc);
|
---|
| 2392 |
|
---|
[24279] | 2393 | if (SUCCEEDED(rc))
|
---|
[24989] | 2394 | mParent->onSnapshotDeleted(mData->mUuid, snapshotId1);
|
---|
[24279] | 2395 |
|
---|
[24298] | 2396 | LogFlowThisFunc(("Done deleting snapshot (rc=%08X)\n", rc));
|
---|
[24279] | 2397 | LogFlowThisFuncLeave();
|
---|
| 2398 | }
|
---|
| 2399 |
|
---|