[14628] | 1 | /* $Id: HardDisk2Impl.cpp 16560 2009-02-06 18:06:04Z vboxsync $ */
|
---|
| 2 |
|
---|
[13580] | 3 | /** @file
|
---|
| 4 | *
|
---|
| 5 | * VirtualBox COM class implementation
|
---|
| 6 | */
|
---|
| 7 |
|
---|
| 8 | /*
|
---|
| 9 | * Copyright (C) 2008 Sun Microsystems, Inc.
|
---|
| 10 | *
|
---|
| 11 | * This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
| 12 | * available from http://www.virtualbox.org. This file is free software;
|
---|
| 13 | * you can redistribute it and/or modify it under the terms of the GNU
|
---|
| 14 | * General Public License (GPL) as published by the Free Software
|
---|
| 15 | * Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
| 16 | * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
| 17 | * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
| 18 | *
|
---|
| 19 | * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
|
---|
| 20 | * Clara, CA 95054 USA or visit http://www.sun.com if you need
|
---|
| 21 | * additional information or have any questions.
|
---|
| 22 | */
|
---|
| 23 |
|
---|
| 24 | #include "HardDisk2Impl.h"
|
---|
| 25 |
|
---|
| 26 | #include "ProgressImpl.h"
|
---|
| 27 | #include "SystemPropertiesImpl.h"
|
---|
| 28 |
|
---|
| 29 | #include "Logging.h"
|
---|
| 30 |
|
---|
| 31 | #include <VBox/com/array.h>
|
---|
| 32 | #include <VBox/com/SupportErrorInfo.h>
|
---|
| 33 |
|
---|
| 34 | #include <VBox/err.h>
|
---|
[16560] | 35 | #include <VBox/settings.h>
|
---|
[13580] | 36 |
|
---|
| 37 | #include <iprt/param.h>
|
---|
| 38 | #include <iprt/path.h>
|
---|
| 39 | #include <iprt/file.h>
|
---|
[14929] | 40 | #include <iprt/tcp.h>
|
---|
[13580] | 41 |
|
---|
| 42 | #include <list>
|
---|
| 43 | #include <memory>
|
---|
| 44 |
|
---|
| 45 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 46 | // Globals
|
---|
| 47 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 48 |
|
---|
| 49 | /**
|
---|
| 50 | * Asynchronous task thread parameter bucket.
|
---|
| 51 | *
|
---|
| 52 | * Note that instances of this class must be created using new() because the
|
---|
| 53 | * task thread function will delete them when the task is complete!
|
---|
| 54 | *
|
---|
| 55 | * @note The constructor of this class adds a caller on the managed HardDisk2
|
---|
| 56 | * object which is automatically released upon destruction.
|
---|
| 57 | */
|
---|
| 58 | struct HardDisk2::Task : public com::SupportErrorInfoBase
|
---|
| 59 | {
|
---|
[15556] | 60 | enum Operation { CreateDynamic, CreateFixed, CreateDiff,
|
---|
| 61 | Merge, Clone, Delete };
|
---|
[13580] | 62 |
|
---|
| 63 | HardDisk2 *that;
|
---|
| 64 | VirtualBoxBaseProto::AutoCaller autoCaller;
|
---|
| 65 |
|
---|
| 66 | ComObjPtr <Progress> progress;
|
---|
| 67 | Operation operation;
|
---|
| 68 |
|
---|
| 69 | /** Where to save the result when executed using #runNow(). */
|
---|
| 70 | HRESULT rc;
|
---|
| 71 |
|
---|
| 72 | Task (HardDisk2 *aThat, Progress *aProgress, Operation aOperation)
|
---|
| 73 | : that (aThat), autoCaller (aThat)
|
---|
| 74 | , progress (aProgress)
|
---|
| 75 | , operation (aOperation)
|
---|
| 76 | , rc (S_OK) {}
|
---|
| 77 |
|
---|
| 78 | ~Task();
|
---|
| 79 |
|
---|
| 80 | void setData (HardDisk2 *aTarget)
|
---|
| 81 | {
|
---|
| 82 | d.target = aTarget;
|
---|
| 83 | HRESULT rc = d.target->addCaller();
|
---|
| 84 | AssertComRC (rc);
|
---|
| 85 | }
|
---|
| 86 |
|
---|
| 87 | void setData (MergeChain *aChain)
|
---|
| 88 | {
|
---|
| 89 | AssertReturnVoid (aChain != NULL);
|
---|
| 90 | d.chain.reset (aChain);
|
---|
| 91 | }
|
---|
| 92 |
|
---|
| 93 | HRESULT startThread();
|
---|
| 94 | HRESULT runNow();
|
---|
| 95 |
|
---|
| 96 | struct Data
|
---|
| 97 | {
|
---|
| 98 | Data() : size (0) {}
|
---|
| 99 |
|
---|
| 100 | /* CreateDynamic, CreateStatic */
|
---|
| 101 |
|
---|
| 102 | uint64_t size;
|
---|
| 103 |
|
---|
| 104 | /* CreateDiff */
|
---|
| 105 |
|
---|
| 106 | ComObjPtr <HardDisk2> target;
|
---|
| 107 |
|
---|
| 108 | /* Merge */
|
---|
| 109 |
|
---|
| 110 | /** Hard disks to merge, in {parent,child} order */
|
---|
| 111 | std::auto_ptr <MergeChain> chain;
|
---|
| 112 | }
|
---|
| 113 | d;
|
---|
| 114 |
|
---|
| 115 | protected:
|
---|
| 116 |
|
---|
| 117 | // SupportErrorInfoBase interface
|
---|
| 118 | const GUID &mainInterfaceID() const { return COM_IIDOF (IHardDisk2); }
|
---|
| 119 | const char *componentName() const { return HardDisk2::ComponentName(); }
|
---|
| 120 | };
|
---|
| 121 |
|
---|
| 122 | HardDisk2::Task::~Task()
|
---|
| 123 | {
|
---|
| 124 | /* remove callers added by setData() */
|
---|
| 125 | if (!d.target.isNull())
|
---|
| 126 | d.target->releaseCaller();
|
---|
| 127 | }
|
---|
| 128 |
|
---|
| 129 | /**
|
---|
| 130 | * Starts a new thread driven by the HardDisk2::taskThread() function and passes
|
---|
| 131 | * this Task instance as an argument.
|
---|
| 132 | *
|
---|
| 133 | * Note that if this method returns success, this Task object becomes an ownee
|
---|
| 134 | * of the started thread and will be automatically deleted when the thread
|
---|
| 135 | * terminates.
|
---|
| 136 | *
|
---|
| 137 | * @note When the task is executed by this method, IProgress::notifyComplete()
|
---|
| 138 | * is automatically called for the progress object associated with this
|
---|
| 139 | * task when the task is finished to signal the operation completion for
|
---|
| 140 | * other threads asynchronously waiting for it.
|
---|
| 141 | */
|
---|
| 142 | HRESULT HardDisk2::Task::startThread()
|
---|
| 143 | {
|
---|
| 144 | int vrc = RTThreadCreate (NULL, HardDisk2::taskThread, this,
|
---|
| 145 | 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
|
---|
| 146 | "HardDisk::Task");
|
---|
| 147 | ComAssertMsgRCRet (vrc,
|
---|
[13837] | 148 | ("Could not create HardDisk::Task thread (%Rrc)\n", vrc), E_FAIL);
|
---|
[13580] | 149 |
|
---|
| 150 | return S_OK;
|
---|
| 151 | }
|
---|
| 152 |
|
---|
| 153 | /**
|
---|
| 154 | * Runs HardDisk2::taskThread() by passing it this Task instance as an argument
|
---|
| 155 | * on the current thread instead of creating a new one.
|
---|
| 156 | *
|
---|
| 157 | * This call implies that it is made on another temporary thread created for
|
---|
| 158 | * some asynchronous task. Avoid calling it from a normal thread since the task
|
---|
| 159 | * operatinos are potentially lengthy and will block the calling thread in this
|
---|
| 160 | * case.
|
---|
| 161 | *
|
---|
| 162 | * Note that this Task object will be deleted by taskThread() when this method
|
---|
| 163 | * returns!
|
---|
| 164 | *
|
---|
| 165 | * @note When the task is executed by this method, IProgress::notifyComplete()
|
---|
| 166 | * is not called for the progress object associated with this task when
|
---|
| 167 | * the task is finished. Instead, the result of the operation is returned
|
---|
| 168 | * by this method directly and it's the caller's responsibility to
|
---|
| 169 | * complete the progress object in this case.
|
---|
| 170 | */
|
---|
| 171 | HRESULT HardDisk2::Task::runNow()
|
---|
| 172 | {
|
---|
| 173 | HardDisk2::taskThread (NIL_RTTHREAD, this);
|
---|
| 174 |
|
---|
| 175 | return rc;
|
---|
| 176 | }
|
---|
| 177 |
|
---|
| 178 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 179 |
|
---|
| 180 | /**
|
---|
| 181 | * Helper class for merge operations.
|
---|
[14143] | 182 | *
|
---|
| 183 | * @note It is assumed that when modifying methods of this class are called,
|
---|
| 184 | * HardDisk2::treeLock() is held in read mode.
|
---|
[13580] | 185 | */
|
---|
| 186 | class HardDisk2::MergeChain : public HardDisk2::List,
|
---|
| 187 | public com::SupportErrorInfoBase
|
---|
| 188 | {
|
---|
| 189 | public:
|
---|
| 190 |
|
---|
| 191 | MergeChain (bool aForward, bool aIgnoreAttachments)
|
---|
| 192 | : mForward (aForward)
|
---|
| 193 | , mIgnoreAttachments (aIgnoreAttachments) {}
|
---|
| 194 |
|
---|
| 195 | ~MergeChain()
|
---|
| 196 | {
|
---|
| 197 | for (iterator it = mChildren.begin(); it != mChildren.end(); ++ it)
|
---|
| 198 | {
|
---|
| 199 | HRESULT rc = (*it)->UnlockWrite (NULL);
|
---|
| 200 | AssertComRC (rc);
|
---|
| 201 |
|
---|
| 202 | (*it)->releaseCaller();
|
---|
| 203 | }
|
---|
| 204 |
|
---|
| 205 | for (iterator it = begin(); it != end(); ++ it)
|
---|
| 206 | {
|
---|
| 207 | AutoWriteLock alock (*it);
|
---|
| 208 | Assert ((*it)->m.state == MediaState_LockedWrite ||
|
---|
| 209 | (*it)->m.state == MediaState_Deleting);
|
---|
| 210 | if ((*it)->m.state == MediaState_LockedWrite)
|
---|
| 211 | (*it)->UnlockWrite (NULL);
|
---|
| 212 | else
|
---|
| 213 | (*it)->m.state = MediaState_Created;
|
---|
| 214 |
|
---|
| 215 | (*it)->releaseCaller();
|
---|
| 216 | }
|
---|
| 217 |
|
---|
| 218 | if (!mParent.isNull())
|
---|
| 219 | mParent->releaseCaller();
|
---|
| 220 | }
|
---|
| 221 |
|
---|
| 222 | HRESULT addSource (HardDisk2 *aHardDisk)
|
---|
| 223 | {
|
---|
| 224 | HRESULT rc = aHardDisk->addCaller();
|
---|
| 225 | CheckComRCReturnRC (rc);
|
---|
| 226 |
|
---|
| 227 | AutoWriteLock alock (aHardDisk);
|
---|
| 228 |
|
---|
| 229 | if (mForward)
|
---|
| 230 | {
|
---|
| 231 | rc = checkChildrenAndAttachmentsAndImmutable (aHardDisk);
|
---|
| 232 | if (FAILED (rc))
|
---|
| 233 | {
|
---|
| 234 | aHardDisk->releaseCaller();
|
---|
| 235 | return rc;
|
---|
| 236 | }
|
---|
| 237 | }
|
---|
| 238 |
|
---|
| 239 | /* go to Deleting */
|
---|
| 240 | switch (aHardDisk->m.state)
|
---|
| 241 | {
|
---|
| 242 | case MediaState_Created:
|
---|
| 243 | aHardDisk->m.state = MediaState_Deleting;
|
---|
| 244 | break;
|
---|
| 245 | default:
|
---|
| 246 | aHardDisk->releaseCaller();
|
---|
| 247 | return aHardDisk->setStateError();
|
---|
| 248 | }
|
---|
| 249 |
|
---|
| 250 | push_front (aHardDisk);
|
---|
| 251 |
|
---|
| 252 | if (mForward)
|
---|
| 253 | {
|
---|
| 254 | /* we will need parent to reparent target */
|
---|
| 255 | if (!aHardDisk->mParent.isNull())
|
---|
| 256 | {
|
---|
| 257 | rc = aHardDisk->mParent->addCaller();
|
---|
| 258 | CheckComRCReturnRC (rc);
|
---|
| 259 |
|
---|
| 260 | mParent = aHardDisk->mParent;
|
---|
| 261 | }
|
---|
| 262 | }
|
---|
| 263 | else
|
---|
| 264 | {
|
---|
| 265 | /* we will need to reparent children */
|
---|
| 266 | for (List::const_iterator it = aHardDisk->children().begin();
|
---|
| 267 | it != aHardDisk->children().end(); ++ it)
|
---|
| 268 | {
|
---|
| 269 | rc = (*it)->addCaller();
|
---|
| 270 | CheckComRCReturnRC (rc);
|
---|
| 271 |
|
---|
| 272 | rc = (*it)->LockWrite (NULL);
|
---|
| 273 | if (FAILED (rc))
|
---|
| 274 | {
|
---|
| 275 | (*it)->releaseCaller();
|
---|
| 276 | return rc;
|
---|
| 277 | }
|
---|
| 278 |
|
---|
| 279 | mChildren.push_back (*it);
|
---|
| 280 | }
|
---|
| 281 | }
|
---|
| 282 |
|
---|
| 283 | return S_OK;
|
---|
| 284 | }
|
---|
| 285 |
|
---|
| 286 | HRESULT addTarget (HardDisk2 *aHardDisk)
|
---|
| 287 | {
|
---|
| 288 | HRESULT rc = aHardDisk->addCaller();
|
---|
| 289 | CheckComRCReturnRC (rc);
|
---|
| 290 |
|
---|
| 291 | AutoWriteLock alock (aHardDisk);
|
---|
| 292 |
|
---|
| 293 | if (!mForward)
|
---|
| 294 | {
|
---|
| 295 | rc = checkChildrenAndImmutable (aHardDisk);
|
---|
| 296 | if (FAILED (rc))
|
---|
| 297 | {
|
---|
| 298 | aHardDisk->releaseCaller();
|
---|
| 299 | return rc;
|
---|
| 300 | }
|
---|
| 301 | }
|
---|
| 302 |
|
---|
| 303 | /* go to LockedWrite */
|
---|
| 304 | rc = aHardDisk->LockWrite (NULL);
|
---|
| 305 | if (FAILED (rc))
|
---|
| 306 | {
|
---|
| 307 | aHardDisk->releaseCaller();
|
---|
| 308 | return rc;
|
---|
| 309 | }
|
---|
| 310 |
|
---|
| 311 | push_front (aHardDisk);
|
---|
| 312 |
|
---|
| 313 | return S_OK;
|
---|
| 314 | }
|
---|
| 315 |
|
---|
| 316 | HRESULT addIntermediate (HardDisk2 *aHardDisk)
|
---|
| 317 | {
|
---|
| 318 | HRESULT rc = aHardDisk->addCaller();
|
---|
| 319 | CheckComRCReturnRC (rc);
|
---|
| 320 |
|
---|
| 321 | AutoWriteLock alock (aHardDisk);
|
---|
| 322 |
|
---|
| 323 | rc = checkChildrenAndAttachments (aHardDisk);
|
---|
| 324 | if (FAILED (rc))
|
---|
| 325 | {
|
---|
| 326 | aHardDisk->releaseCaller();
|
---|
| 327 | return rc;
|
---|
| 328 | }
|
---|
| 329 |
|
---|
| 330 | /* go to Deleting */
|
---|
| 331 | switch (aHardDisk->m.state)
|
---|
| 332 | {
|
---|
| 333 | case MediaState_Created:
|
---|
| 334 | aHardDisk->m.state = MediaState_Deleting;
|
---|
| 335 | break;
|
---|
| 336 | default:
|
---|
| 337 | aHardDisk->releaseCaller();
|
---|
| 338 | return aHardDisk->setStateError();
|
---|
| 339 | }
|
---|
| 340 |
|
---|
| 341 | push_front (aHardDisk);
|
---|
| 342 |
|
---|
| 343 | return S_OK;
|
---|
| 344 | }
|
---|
| 345 |
|
---|
| 346 | bool isForward() const { return mForward; }
|
---|
| 347 | HardDisk2 *parent() const { return mParent; }
|
---|
| 348 | const List &children() const { return mChildren; }
|
---|
| 349 |
|
---|
| 350 | HardDisk2 *source() const
|
---|
| 351 | { AssertReturn (size() > 0, NULL); return mForward ? front() : back(); }
|
---|
| 352 |
|
---|
| 353 | HardDisk2 *target() const
|
---|
| 354 | { AssertReturn (size() > 0, NULL); return mForward ? back() : front(); }
|
---|
| 355 |
|
---|
| 356 | protected:
|
---|
| 357 |
|
---|
| 358 | // SupportErrorInfoBase interface
|
---|
| 359 | const GUID &mainInterfaceID() const { return COM_IIDOF (IHardDisk2); }
|
---|
| 360 | const char *componentName() const { return HardDisk2::ComponentName(); }
|
---|
| 361 |
|
---|
| 362 | private:
|
---|
| 363 |
|
---|
| 364 | HRESULT check (HardDisk2 *aHardDisk, bool aChildren, bool aAttachments,
|
---|
| 365 | bool aImmutable)
|
---|
| 366 | {
|
---|
| 367 | if (aChildren)
|
---|
| 368 | {
|
---|
| 369 | /* not going to multi-merge as it's too expensive */
|
---|
| 370 | if (aHardDisk->children().size() > 1)
|
---|
| 371 | {
|
---|
| 372 | return setError (E_FAIL,
|
---|
| 373 | tr ("Hard disk '%ls' involved in the merge operation "
|
---|
| 374 | "has more than one child hard disk (%d)"),
|
---|
| 375 | aHardDisk->m.locationFull.raw(),
|
---|
| 376 | aHardDisk->children().size());
|
---|
| 377 | }
|
---|
| 378 | }
|
---|
| 379 |
|
---|
| 380 | if (aAttachments && !mIgnoreAttachments)
|
---|
| 381 | {
|
---|
| 382 | if (aHardDisk->m.backRefs.size() != 0)
|
---|
| 383 | return setError (E_FAIL,
|
---|
| 384 | tr ("Hard disk '%ls' is attached to %d virtual machines"),
|
---|
| 385 | aHardDisk->m.locationFull.raw(),
|
---|
| 386 | aHardDisk->m.backRefs.size());
|
---|
| 387 | }
|
---|
| 388 |
|
---|
| 389 | if (aImmutable)
|
---|
| 390 | {
|
---|
| 391 | if (aHardDisk->mm.type == HardDiskType_Immutable)
|
---|
| 392 | return setError (E_FAIL,
|
---|
| 393 | tr ("Hard disk '%ls' is immutable"),
|
---|
| 394 | aHardDisk->m.locationFull.raw());
|
---|
| 395 | }
|
---|
| 396 |
|
---|
| 397 | return S_OK;
|
---|
| 398 | }
|
---|
| 399 |
|
---|
| 400 | HRESULT checkChildren (HardDisk2 *aHardDisk)
|
---|
| 401 | { return check (aHardDisk, true, false, false); }
|
---|
| 402 |
|
---|
| 403 | HRESULT checkChildrenAndImmutable (HardDisk2 *aHardDisk)
|
---|
| 404 | { return check (aHardDisk, true, false, true); }
|
---|
| 405 |
|
---|
| 406 | HRESULT checkChildrenAndAttachments (HardDisk2 *aHardDisk)
|
---|
| 407 | { return check (aHardDisk, true, true, false); }
|
---|
| 408 |
|
---|
| 409 | HRESULT checkChildrenAndAttachmentsAndImmutable (HardDisk2 *aHardDisk)
|
---|
| 410 | { return check (aHardDisk, true, true, true); }
|
---|
| 411 |
|
---|
| 412 | /** true if forward merge, false if backward */
|
---|
| 413 | bool mForward : 1;
|
---|
| 414 | /** true to not perform attachment checks */
|
---|
| 415 | bool mIgnoreAttachments : 1;
|
---|
| 416 |
|
---|
| 417 | /** Parent of the source when forward merge (if any) */
|
---|
| 418 | ComObjPtr <HardDisk2> mParent;
|
---|
| 419 | /** Children of the source when backward merge (if any) */
|
---|
| 420 | List mChildren;
|
---|
| 421 | };
|
---|
| 422 |
|
---|
| 423 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 424 | // HardDisk2 class
|
---|
| 425 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 426 |
|
---|
| 427 | // constructor / destructor
|
---|
| 428 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 429 |
|
---|
| 430 | DEFINE_EMPTY_CTOR_DTOR (HardDisk2)
|
---|
| 431 |
|
---|
| 432 | HRESULT HardDisk2::FinalConstruct()
|
---|
| 433 | {
|
---|
| 434 | /* Initialize the callbacks of the VD error interface */
|
---|
| 435 | mm.vdIfCallsError.cbSize = sizeof (VDINTERFACEERROR);
|
---|
| 436 | mm.vdIfCallsError.enmInterface = VDINTERFACETYPE_ERROR;
|
---|
| 437 | mm.vdIfCallsError.pfnError = vdErrorCall;
|
---|
| 438 |
|
---|
| 439 | /* Initialize the callbacks of the VD progress interface */
|
---|
| 440 | mm.vdIfCallsProgress.cbSize = sizeof (VDINTERFACEPROGRESS);
|
---|
| 441 | mm.vdIfCallsProgress.enmInterface = VDINTERFACETYPE_PROGRESS;
|
---|
| 442 | mm.vdIfCallsProgress.pfnProgress = vdProgressCall;
|
---|
| 443 |
|
---|
[14783] | 444 | /* Initialize the callbacks of the VD config interface */
|
---|
[14929] | 445 | mm.vdIfCallsConfig.cbSize = sizeof (VDINTERFACECONFIG);
|
---|
[14783] | 446 | mm.vdIfCallsConfig.enmInterface = VDINTERFACETYPE_CONFIG;
|
---|
[14929] | 447 | mm.vdIfCallsConfig.pfnAreKeysValid = vdConfigAreKeysValid;
|
---|
| 448 | mm.vdIfCallsConfig.pfnQuerySize = vdConfigQuerySize;
|
---|
| 449 | mm.vdIfCallsConfig.pfnQuery = vdConfigQuery;
|
---|
[14783] | 450 |
|
---|
[14929] | 451 | /* Initialize the callbacks of the VD TCP interface (we always use the host
|
---|
| 452 | * IP stack for now) */
|
---|
| 453 | mm.vdIfCallsTcpNet.cbSize = sizeof (VDINTERFACETCPNET);
|
---|
| 454 | mm.vdIfCallsTcpNet.enmInterface = VDINTERFACETYPE_TCPNET;
|
---|
| 455 | mm.vdIfCallsTcpNet.pfnClientConnect = RTTcpClientConnect;
|
---|
| 456 | mm.vdIfCallsTcpNet.pfnClientClose = RTTcpClientClose;
|
---|
| 457 | mm.vdIfCallsTcpNet.pfnSelectOne = RTTcpSelectOne;
|
---|
| 458 | mm.vdIfCallsTcpNet.pfnRead = RTTcpRead;
|
---|
| 459 | mm.vdIfCallsTcpNet.pfnWrite = RTTcpWrite;
|
---|
| 460 | mm.vdIfCallsTcpNet.pfnFlush = RTTcpFlush;
|
---|
| 461 |
|
---|
[13580] | 462 | /* Initialize the per-disk interface chain */
|
---|
| 463 | int vrc;
|
---|
| 464 | vrc = VDInterfaceAdd (&mm.vdIfError,
|
---|
| 465 | "HardDisk2::vdInterfaceError",
|
---|
| 466 | VDINTERFACETYPE_ERROR,
|
---|
| 467 | &mm.vdIfCallsError, this, &mm.vdDiskIfaces);
|
---|
| 468 | AssertRCReturn (vrc, E_FAIL);
|
---|
[14929] | 469 |
|
---|
[13580] | 470 | vrc = VDInterfaceAdd (&mm.vdIfProgress,
|
---|
| 471 | "HardDisk2::vdInterfaceProgress",
|
---|
| 472 | VDINTERFACETYPE_PROGRESS,
|
---|
| 473 | &mm.vdIfCallsProgress, this, &mm.vdDiskIfaces);
|
---|
| 474 | AssertRCReturn (vrc, E_FAIL);
|
---|
| 475 |
|
---|
[14929] | 476 | vrc = VDInterfaceAdd (&mm.vdIfConfig,
|
---|
| 477 | "HardDisk2::vdInterfaceConfig",
|
---|
| 478 | VDINTERFACETYPE_CONFIG,
|
---|
| 479 | &mm.vdIfCallsConfig, this, &mm.vdDiskIfaces);
|
---|
| 480 | AssertRCReturn (vrc, E_FAIL);
|
---|
| 481 |
|
---|
| 482 | vrc = VDInterfaceAdd (&mm.vdIfTcpNet,
|
---|
| 483 | "HardDisk2::vdInterfaceTcpNet",
|
---|
| 484 | VDINTERFACETYPE_TCPNET,
|
---|
| 485 | &mm.vdIfCallsTcpNet, this, &mm.vdDiskIfaces);
|
---|
| 486 | AssertRCReturn (vrc, E_FAIL);
|
---|
| 487 |
|
---|
[13580] | 488 | return S_OK;
|
---|
| 489 | }
|
---|
| 490 |
|
---|
| 491 | void HardDisk2::FinalRelease()
|
---|
| 492 | {
|
---|
| 493 | uninit();
|
---|
| 494 | }
|
---|
| 495 |
|
---|
| 496 | // public initializer/uninitializer for internal purposes only
|
---|
| 497 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 498 |
|
---|
| 499 | /**
|
---|
| 500 | * Initializes the hard disk object without creating or opening an associated
|
---|
| 501 | * storage unit.
|
---|
| 502 | *
|
---|
[14783] | 503 | * For hard disks that don't have the VD_CAP_CREATE_FIXED or
|
---|
| 504 | * VD_CAP_CREATE_DYNAMIC capability (and therefore cannot be created or deleted
|
---|
| 505 | * with the means of VirtualBox) the associated storage unit is assumed to be
|
---|
| 506 | * ready for use so the state of the hard disk object will be set to Created.
|
---|
| 507 | *
|
---|
[13580] | 508 | * @param aVirtualBox VirtualBox object.
|
---|
| 509 | * @param aLocaiton Storage unit location.
|
---|
| 510 | */
|
---|
[15051] | 511 | HRESULT HardDisk2::init (VirtualBox *aVirtualBox, CBSTR aFormat,
|
---|
| 512 | CBSTR aLocation)
|
---|
[13580] | 513 | {
|
---|
[14783] | 514 | AssertReturn (aVirtualBox != NULL, E_FAIL);
|
---|
| 515 | AssertReturn (aFormat != NULL && *aFormat != '\0', E_FAIL);
|
---|
[13580] | 516 |
|
---|
| 517 | /* Enclose the state transition NotReady->InInit->Ready */
|
---|
| 518 | AutoInitSpan autoInitSpan (this);
|
---|
[14579] | 519 | AssertReturn (autoInitSpan.isOk(), E_FAIL);
|
---|
[13580] | 520 |
|
---|
| 521 | HRESULT rc = S_OK;
|
---|
| 522 |
|
---|
| 523 | /* share VirtualBox weakly (parent remains NULL so far) */
|
---|
| 524 | unconst (mVirtualBox) = aVirtualBox;
|
---|
| 525 |
|
---|
| 526 | /* register with VirtualBox early, since uninit() will
|
---|
| 527 | * unconditionally unregister on failure */
|
---|
| 528 | aVirtualBox->addDependentChild (this);
|
---|
| 529 |
|
---|
| 530 | /* no storage yet */
|
---|
| 531 | m.state = MediaState_NotCreated;
|
---|
| 532 |
|
---|
[14272] | 533 | /* No storage unit is created yet, no need to queryInfo() */
|
---|
| 534 |
|
---|
| 535 | rc = setFormat (aFormat);
|
---|
| 536 | CheckComRCReturnRC (rc);
|
---|
| 537 |
|
---|
[14783] | 538 | if (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File)
|
---|
| 539 | {
|
---|
| 540 | rc = setLocation (aLocation);
|
---|
| 541 | CheckComRCReturnRC (rc);
|
---|
| 542 | }
|
---|
| 543 | else
|
---|
| 544 | {
|
---|
| 545 | rc = setLocation (aLocation);
|
---|
| 546 | CheckComRCReturnRC (rc);
|
---|
[13580] | 547 |
|
---|
[14783] | 548 | /// @todo later we may want to use a pfnComposeLocation backend info
|
---|
| 549 | /// callback to generate a well-formed location value (based on the hard
|
---|
| 550 | /// disk properties we have) rather than allowing each caller to invent
|
---|
| 551 | /// its own (pseudo-)location.
|
---|
| 552 | }
|
---|
| 553 |
|
---|
| 554 | if (!(mm.formatObj->capabilities() &
|
---|
| 555 | (HardDiskFormatCapabilities_CreateFixed |
|
---|
| 556 | HardDiskFormatCapabilities_CreateDynamic)))
|
---|
| 557 | {
|
---|
| 558 | /* storage for hard disks of this format can neither be explicitly
|
---|
| 559 | * created by VirtualBox nor deleted, so we place the hard disk to
|
---|
| 560 | * Created state here and also add it to the registry */
|
---|
| 561 | m.state = MediaState_Created;
|
---|
| 562 | unconst (m.id).create();
|
---|
| 563 | rc = mVirtualBox->registerHardDisk2 (this);
|
---|
| 564 |
|
---|
| 565 | /// @todo later we may want to use a pfnIsConfigSufficient backend info
|
---|
| 566 | /// callback that would tell us when we have enough properties to work
|
---|
| 567 | /// with the hard disk and this information could be used to actually
|
---|
| 568 | /// move such hard disks from NotCreated to Created state. Instead of
|
---|
| 569 | /// pfnIsConfigSufficient we can use HardDiskFormat property
|
---|
| 570 | /// descriptions to see which properties are mandatory
|
---|
| 571 | }
|
---|
| 572 |
|
---|
[13580] | 573 | /* Confirm a successful initialization when it's the case */
|
---|
| 574 | if (SUCCEEDED (rc))
|
---|
| 575 | autoInitSpan.setSucceeded();
|
---|
| 576 |
|
---|
| 577 | return rc;
|
---|
| 578 | }
|
---|
| 579 |
|
---|
| 580 | /**
|
---|
| 581 | * Initializes the hard disk object by opening the storage unit at the specified
|
---|
| 582 | * location.
|
---|
| 583 | *
|
---|
| 584 | * Note that the UUID, format and the parent of this hard disk will be
|
---|
| 585 | * determined when reading the hard disk storage unit. If the detected parent is
|
---|
| 586 | * not known to VirtualBox, then this method will fail.
|
---|
| 587 | *
|
---|
| 588 | * @param aVirtualBox VirtualBox object.
|
---|
| 589 | * @param aLocaiton Storage unit location.
|
---|
| 590 | */
|
---|
[15051] | 591 | HRESULT HardDisk2::init (VirtualBox *aVirtualBox, CBSTR aLocation)
|
---|
[13580] | 592 | {
|
---|
| 593 | AssertReturn (aVirtualBox, E_INVALIDARG);
|
---|
| 594 | AssertReturn (aLocation, E_INVALIDARG);
|
---|
| 595 |
|
---|
| 596 | /* Enclose the state transition NotReady->InInit->Ready */
|
---|
| 597 | AutoInitSpan autoInitSpan (this);
|
---|
[14579] | 598 | AssertReturn (autoInitSpan.isOk(), E_FAIL);
|
---|
[13580] | 599 |
|
---|
| 600 | HRESULT rc = S_OK;
|
---|
| 601 |
|
---|
| 602 | /* share VirtualBox weakly (parent remains NULL so far) */
|
---|
| 603 | unconst (mVirtualBox) = aVirtualBox;
|
---|
| 604 |
|
---|
| 605 | /* register with VirtualBox early, since uninit() will
|
---|
| 606 | * unconditionally unregister on failure */
|
---|
| 607 | aVirtualBox->addDependentChild (this);
|
---|
| 608 |
|
---|
| 609 | /* there must be a storage unit */
|
---|
| 610 | m.state = MediaState_Created;
|
---|
| 611 |
|
---|
| 612 | rc = setLocation (aLocation);
|
---|
| 613 | CheckComRCReturnRC (rc);
|
---|
| 614 |
|
---|
| 615 | /* get all the information about the medium from the storage unit */
|
---|
| 616 | rc = queryInfo();
|
---|
| 617 | if (SUCCEEDED (rc))
|
---|
| 618 | {
|
---|
| 619 | /* if the storage unit is not accessible, it's not acceptable for the
|
---|
| 620 | * newly opened media so convert this into an error */
|
---|
| 621 | if (m.state == MediaState_Inaccessible)
|
---|
| 622 | {
|
---|
[15215] | 623 | Assert (!m.lastAccessError.isEmpty());
|
---|
[13580] | 624 | rc = setError (E_FAIL, Utf8Str (m.lastAccessError));
|
---|
| 625 | }
|
---|
[14272] | 626 |
|
---|
| 627 | /* storage format must be detected by queryInfo() if the medium is
|
---|
| 628 | * accessible */
|
---|
| 629 | AssertReturn (!m.id.isEmpty() && !mm.format.isNull(), E_FAIL);
|
---|
[13580] | 630 | }
|
---|
| 631 |
|
---|
| 632 | /* Confirm a successful initialization when it's the case */
|
---|
| 633 | if (SUCCEEDED (rc))
|
---|
| 634 | autoInitSpan.setSucceeded();
|
---|
| 635 |
|
---|
| 636 | return rc;
|
---|
| 637 | }
|
---|
| 638 |
|
---|
| 639 | /**
|
---|
| 640 | * Initializes the hard disk object by loading its data from the given settings
|
---|
| 641 | * node.
|
---|
| 642 | *
|
---|
| 643 | * @param aVirtualBox VirtualBox object.
|
---|
| 644 | * @param aParent Parent hard disk or NULL for a root hard disk.
|
---|
| 645 | * @param aNode <HardDisk> settings node.
|
---|
[14143] | 646 | *
|
---|
| 647 | * @note Locks VirtualBox lock for writing, treeLock() for writing.
|
---|
[13580] | 648 | */
|
---|
| 649 | HRESULT HardDisk2::init (VirtualBox *aVirtualBox, HardDisk2 *aParent,
|
---|
| 650 | const settings::Key &aNode)
|
---|
| 651 | {
|
---|
| 652 | using namespace settings;
|
---|
| 653 |
|
---|
| 654 | AssertReturn (aVirtualBox, E_INVALIDARG);
|
---|
| 655 |
|
---|
| 656 | /* Enclose the state transition NotReady->InInit->Ready */
|
---|
| 657 | AutoInitSpan autoInitSpan (this);
|
---|
[14579] | 658 | AssertReturn (autoInitSpan.isOk(), E_FAIL);
|
---|
[13580] | 659 |
|
---|
| 660 | HRESULT rc = S_OK;
|
---|
| 661 |
|
---|
| 662 | /* share VirtualBox and parent weakly */
|
---|
| 663 | unconst (mVirtualBox) = aVirtualBox;
|
---|
| 664 |
|
---|
| 665 | /* register with VirtualBox/parent early, since uninit() will
|
---|
| 666 | * unconditionally unregister on failure */
|
---|
[14143] | 667 | if (aParent == NULL)
|
---|
[13580] | 668 | aVirtualBox->addDependentChild (this);
|
---|
[14164] | 669 | else
|
---|
[14143] | 670 | {
|
---|
| 671 | /* we set mParent */
|
---|
| 672 | AutoWriteLock treeLock (this->treeLock());
|
---|
| 673 |
|
---|
| 674 | mParent = aParent;
|
---|
[13580] | 675 | aParent->addDependentChild (this);
|
---|
[14143] | 676 | }
|
---|
[13580] | 677 |
|
---|
| 678 | /* see below why we don't call queryInfo() (and therefore treat the medium
|
---|
| 679 | * as inaccessible for now */
|
---|
| 680 | m.state = MediaState_Inaccessible;
|
---|
[15215] | 681 | m.lastAccessError = tr ("Accessibility check was not yet performed");
|
---|
[13580] | 682 |
|
---|
| 683 | /* required */
|
---|
| 684 | unconst (m.id) = aNode.value <Guid> ("uuid");
|
---|
[14272] | 685 |
|
---|
[13580] | 686 | /* optional */
|
---|
| 687 | {
|
---|
| 688 | settings::Key descNode = aNode.findKey ("Description");
|
---|
| 689 | if (!descNode.isNull())
|
---|
| 690 | m.description = descNode.keyStringValue();
|
---|
| 691 | }
|
---|
| 692 |
|
---|
| 693 | /* required */
|
---|
[14272] | 694 | Bstr format = aNode.stringValue ("format");
|
---|
| 695 | AssertReturn (!format.isNull(), E_FAIL);
|
---|
| 696 | rc = setFormat (format);
|
---|
| 697 | CheckComRCReturnRC (rc);
|
---|
[13580] | 698 |
|
---|
[14783] | 699 | /* properties (after setting the format as it populates the map). Note that
|
---|
| 700 | * if some properties are not supported but preseint in the settings file,
|
---|
| 701 | * they will still be read and accessible (for possible backward
|
---|
| 702 | * compatibility; we can also clean them up from the XML upon next
|
---|
| 703 | * XML format versino change if we wish) */
|
---|
[14698] | 704 | Key::List properties = aNode.keys ("Property");
|
---|
| 705 | for (Key::List::const_iterator it = properties.begin();
|
---|
| 706 | it != properties.end(); ++ it)
|
---|
| 707 | {
|
---|
| 708 | mm.properties [Bstr (it->stringValue ("name"))] =
|
---|
| 709 | Bstr (it->stringValue ("value"));
|
---|
| 710 | }
|
---|
| 711 |
|
---|
[14272] | 712 | /* required */
|
---|
| 713 | Bstr location = aNode.stringValue ("location");
|
---|
| 714 | rc = setLocation (location);
|
---|
| 715 | CheckComRCReturnRC (rc);
|
---|
| 716 |
|
---|
| 717 | /* type is only for base hard disks */
|
---|
[13580] | 718 | if (mParent.isNull())
|
---|
| 719 | {
|
---|
| 720 | const char *type = aNode.stringValue ("type");
|
---|
| 721 | if (strcmp (type, "Normal") == 0)
|
---|
| 722 | mm.type = HardDiskType_Normal;
|
---|
| 723 | else if (strcmp (type, "Immutable") == 0)
|
---|
| 724 | mm.type = HardDiskType_Immutable;
|
---|
| 725 | else if (strcmp (type, "Writethrough") == 0)
|
---|
| 726 | mm.type = HardDiskType_Writethrough;
|
---|
| 727 | else
|
---|
| 728 | AssertFailed();
|
---|
| 729 | }
|
---|
| 730 |
|
---|
[15124] | 731 | LogFlowThisFunc (("m.locationFull='%ls', mm.format=%ls, m.id={%RTuuid}\n",
|
---|
| 732 | m.locationFull.raw(), mm.format.raw(), m.id.raw()));
|
---|
[13580] | 733 |
|
---|
| 734 | /* Don't call queryInfo() for registered media to prevent the calling
|
---|
| 735 | * thread (i.e. the VirtualBox server startup thread) from an unexpected
|
---|
| 736 | * freeze but mark it as initially inaccessible instead. The vital UUID,
|
---|
| 737 | * location and format properties are read from the registry file above; to
|
---|
[14294] | 738 | * get the actual state and the rest of the data, the user will have to call
|
---|
[13580] | 739 | * COMGETTER(State). */
|
---|
| 740 |
|
---|
| 741 | /* load all children */
|
---|
| 742 | Key::List hardDisks = aNode.keys ("HardDisk");
|
---|
| 743 | for (Key::List::const_iterator it = hardDisks.begin();
|
---|
| 744 | it != hardDisks.end(); ++ it)
|
---|
| 745 | {
|
---|
| 746 | ComObjPtr <HardDisk2> hardDisk;
|
---|
| 747 | hardDisk.createObject();
|
---|
| 748 | rc = hardDisk->init (aVirtualBox, this, *it);
|
---|
| 749 | CheckComRCBreakRC (rc);
|
---|
| 750 |
|
---|
| 751 | rc = mVirtualBox->registerHardDisk2 (hardDisk, false /* aSaveRegistry */);
|
---|
| 752 | CheckComRCBreakRC (rc);
|
---|
| 753 | }
|
---|
| 754 |
|
---|
| 755 | /* Confirm a successful initialization when it's the case */
|
---|
| 756 | if (SUCCEEDED (rc))
|
---|
| 757 | autoInitSpan.setSucceeded();
|
---|
| 758 |
|
---|
| 759 | return rc;
|
---|
| 760 | }
|
---|
| 761 |
|
---|
| 762 | /**
|
---|
| 763 | * Uninitializes the instance.
|
---|
| 764 | *
|
---|
| 765 | * Called either from FinalRelease() or by the parent when it gets destroyed.
|
---|
| 766 | *
|
---|
| 767 | * @note All children of this hard disk get uninitialized by calling their
|
---|
| 768 | * uninit() methods.
|
---|
[14143] | 769 | *
|
---|
| 770 | * @note Locks treeLock() for writing, VirtualBox for writing.
|
---|
[13580] | 771 | */
|
---|
| 772 | void HardDisk2::uninit()
|
---|
| 773 | {
|
---|
| 774 | /* Enclose the state transition Ready->InUninit->NotReady */
|
---|
| 775 | AutoUninitSpan autoUninitSpan (this);
|
---|
| 776 | if (autoUninitSpan.uninitDone())
|
---|
| 777 | return;
|
---|
| 778 |
|
---|
[14272] | 779 | if (!mm.formatObj.isNull())
|
---|
| 780 | {
|
---|
| 781 | /* remove the caller reference we added in setFormat() */
|
---|
| 782 | mm.formatObj->releaseCaller();
|
---|
| 783 | mm.formatObj.setNull();
|
---|
| 784 | }
|
---|
| 785 |
|
---|
[13580] | 786 | if (m.state == MediaState_Deleting)
|
---|
| 787 | {
|
---|
| 788 | /* we are being uninitialized after've been deleted by merge.
|
---|
| 789 | * Reparenting has already been done so don't touch it here (we are
|
---|
| 790 | * now orphans and remoeDependentChild() will assert) */
|
---|
[14143] | 791 |
|
---|
| 792 | Assert (mParent.isNull());
|
---|
[13580] | 793 | }
|
---|
| 794 | else
|
---|
| 795 | {
|
---|
[14143] | 796 | /* we uninit children and reset mParent
|
---|
| 797 | * and VirtualBox::removeDependentChild() needs a write lock */
|
---|
| 798 | AutoMultiWriteLock2 alock (mVirtualBox->lockHandle(), this->treeLock());
|
---|
| 799 |
|
---|
[13580] | 800 | uninitDependentChildren();
|
---|
| 801 |
|
---|
[14143] | 802 | if (!mParent.isNull())
|
---|
| 803 | {
|
---|
[13580] | 804 | mParent->removeDependentChild (this);
|
---|
[14143] | 805 | mParent.setNull();
|
---|
| 806 | }
|
---|
[13580] | 807 | else
|
---|
| 808 | mVirtualBox->removeDependentChild (this);
|
---|
| 809 | }
|
---|
| 810 |
|
---|
| 811 | unconst (mVirtualBox).setNull();
|
---|
| 812 | }
|
---|
| 813 |
|
---|
| 814 | // IHardDisk2 properties
|
---|
| 815 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 816 |
|
---|
| 817 | STDMETHODIMP HardDisk2::COMGETTER(Format) (BSTR *aFormat)
|
---|
| 818 | {
|
---|
| 819 | if (aFormat == NULL)
|
---|
| 820 | return E_POINTER;
|
---|
| 821 |
|
---|
| 822 | AutoCaller autoCaller (this);
|
---|
| 823 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 824 |
|
---|
| 825 | /* no need to lock, mm.format is const */
|
---|
| 826 | mm.format.cloneTo (aFormat);
|
---|
| 827 |
|
---|
| 828 | return S_OK;
|
---|
| 829 | }
|
---|
| 830 |
|
---|
| 831 | STDMETHODIMP HardDisk2::COMGETTER(Type) (HardDiskType_T *aType)
|
---|
| 832 | {
|
---|
| 833 | if (aType == NULL)
|
---|
| 834 | return E_POINTER;
|
---|
| 835 |
|
---|
| 836 | AutoCaller autoCaller (this);
|
---|
| 837 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 838 |
|
---|
| 839 | AutoReadLock alock (this);
|
---|
| 840 |
|
---|
| 841 | *aType = mm.type;
|
---|
| 842 |
|
---|
| 843 | return S_OK;
|
---|
| 844 | }
|
---|
| 845 |
|
---|
| 846 | STDMETHODIMP HardDisk2::COMSETTER(Type) (HardDiskType_T aType)
|
---|
| 847 | {
|
---|
| 848 | AutoCaller autoCaller (this);
|
---|
| 849 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 850 |
|
---|
| 851 | /* VirtualBox::saveSettings() needs a write lock */
|
---|
| 852 | AutoMultiWriteLock2 alock (mVirtualBox, this);
|
---|
| 853 |
|
---|
| 854 | switch (m.state)
|
---|
| 855 | {
|
---|
| 856 | case MediaState_Created:
|
---|
| 857 | case MediaState_Inaccessible:
|
---|
| 858 | break;
|
---|
| 859 | default:
|
---|
| 860 | return setStateError();
|
---|
| 861 | }
|
---|
| 862 |
|
---|
| 863 | if (mm.type == aType)
|
---|
| 864 | {
|
---|
| 865 | /* Nothing to do */
|
---|
| 866 | return S_OK;
|
---|
| 867 | }
|
---|
| 868 |
|
---|
[14143] | 869 | /* we access mParent & children() */
|
---|
| 870 | AutoReadLock treeLock (this->treeLock());
|
---|
| 871 |
|
---|
[13580] | 872 | /* cannot change the type of a differencing hard disk */
|
---|
| 873 | if (!mParent.isNull())
|
---|
| 874 | return setError (E_FAIL,
|
---|
| 875 | tr ("Hard disk '%ls' is a differencing hard disk"),
|
---|
| 876 | m.locationFull.raw());
|
---|
| 877 |
|
---|
| 878 | /* cannot change the type of a hard disk being in use */
|
---|
| 879 | if (m.backRefs.size() != 0)
|
---|
| 880 | return setError (E_FAIL,
|
---|
| 881 | tr ("Hard disk '%ls' is attached to %d virtual machines"),
|
---|
| 882 | m.locationFull.raw(), m.backRefs.size());
|
---|
| 883 |
|
---|
| 884 | switch (aType)
|
---|
| 885 | {
|
---|
| 886 | case HardDiskType_Normal:
|
---|
| 887 | case HardDiskType_Immutable:
|
---|
| 888 | {
|
---|
| 889 | /* normal can be easily converted to imutable and vice versa even
|
---|
| 890 | * if they have children as long as they are not attached to any
|
---|
| 891 | * machine themselves */
|
---|
| 892 | break;
|
---|
| 893 | }
|
---|
| 894 | case HardDiskType_Writethrough:
|
---|
| 895 | {
|
---|
| 896 | /* cannot change to writethrough if there are children */
|
---|
| 897 | if (children().size() != 0)
|
---|
| 898 | return setError (E_FAIL,
|
---|
| 899 | tr ("Hard disk '%ls' has %d child hard disks"),
|
---|
| 900 | children().size());
|
---|
| 901 | break;
|
---|
| 902 | }
|
---|
| 903 | default:
|
---|
| 904 | AssertFailedReturn (E_FAIL);
|
---|
| 905 | }
|
---|
| 906 |
|
---|
| 907 | mm.type = aType;
|
---|
| 908 |
|
---|
| 909 | HRESULT rc = mVirtualBox->saveSettings();
|
---|
| 910 |
|
---|
| 911 | return rc;
|
---|
| 912 | }
|
---|
| 913 |
|
---|
| 914 | STDMETHODIMP HardDisk2::COMGETTER(Parent) (IHardDisk2 **aParent)
|
---|
| 915 | {
|
---|
| 916 | if (aParent == NULL)
|
---|
| 917 | return E_POINTER;
|
---|
| 918 |
|
---|
| 919 | AutoCaller autoCaller (this);
|
---|
| 920 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 921 |
|
---|
[14143] | 922 | /* we access mParent */
|
---|
| 923 | AutoReadLock treeLock (this->treeLock());
|
---|
[13580] | 924 |
|
---|
| 925 | mParent.queryInterfaceTo (aParent);
|
---|
| 926 |
|
---|
| 927 | return S_OK;
|
---|
| 928 | }
|
---|
| 929 |
|
---|
| 930 | STDMETHODIMP HardDisk2::COMGETTER(Children) (ComSafeArrayOut (IHardDisk2 *, aChildren))
|
---|
| 931 | {
|
---|
| 932 | if (ComSafeArrayOutIsNull (aChildren))
|
---|
| 933 | return E_POINTER;
|
---|
| 934 |
|
---|
| 935 | AutoCaller autoCaller (this);
|
---|
| 936 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 937 |
|
---|
[14143] | 938 | /* we access children */
|
---|
| 939 | AutoReadLock treeLock (this->treeLock());
|
---|
[13580] | 940 |
|
---|
| 941 | SafeIfaceArray <IHardDisk2> children (this->children());
|
---|
| 942 | children.detachTo (ComSafeArrayOutArg (aChildren));
|
---|
| 943 |
|
---|
| 944 | return S_OK;
|
---|
| 945 | }
|
---|
| 946 |
|
---|
| 947 | STDMETHODIMP HardDisk2::COMGETTER(Root) (IHardDisk2 **aRoot)
|
---|
| 948 | {
|
---|
| 949 | if (aRoot == NULL)
|
---|
| 950 | return E_POINTER;
|
---|
| 951 |
|
---|
| 952 | /* root() will do callers/locking */
|
---|
| 953 |
|
---|
| 954 | root().queryInterfaceTo (aRoot);
|
---|
| 955 |
|
---|
| 956 | return S_OK;
|
---|
| 957 | }
|
---|
| 958 |
|
---|
| 959 | STDMETHODIMP HardDisk2::COMGETTER(ReadOnly) (BOOL *aReadOnly)
|
---|
| 960 | {
|
---|
| 961 | if (aReadOnly == NULL)
|
---|
| 962 | return E_POINTER;
|
---|
| 963 |
|
---|
| 964 | AutoCaller autoCaller (this);
|
---|
| 965 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 966 |
|
---|
| 967 | /* isRadOnly() will do locking */
|
---|
| 968 |
|
---|
| 969 | *aReadOnly = isReadOnly();
|
---|
| 970 |
|
---|
| 971 | return S_OK;
|
---|
| 972 | }
|
---|
| 973 |
|
---|
| 974 | STDMETHODIMP HardDisk2::COMGETTER(LogicalSize) (ULONG64 *aLogicalSize)
|
---|
| 975 | {
|
---|
| 976 | if (aLogicalSize == NULL)
|
---|
| 977 | return E_POINTER;
|
---|
| 978 |
|
---|
| 979 | {
|
---|
| 980 | AutoCaller autoCaller (this);
|
---|
| 981 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 982 |
|
---|
| 983 | AutoReadLock alock (this);
|
---|
| 984 |
|
---|
[14143] | 985 | /* we access mParent */
|
---|
| 986 | AutoReadLock treeLock (this->treeLock());
|
---|
| 987 |
|
---|
[13580] | 988 | if (mParent.isNull())
|
---|
| 989 | {
|
---|
| 990 | *aLogicalSize = mm.logicalSize;
|
---|
| 991 |
|
---|
| 992 | return S_OK;
|
---|
| 993 | }
|
---|
| 994 | }
|
---|
| 995 |
|
---|
| 996 | /* We assume that some backend may decide to return a meaningless value in
|
---|
| 997 | * response to VDGetSize() for differencing hard disks and therefore
|
---|
| 998 | * always ask the base hard disk ourselves. */
|
---|
| 999 |
|
---|
| 1000 | /* root() will do callers/locking */
|
---|
| 1001 |
|
---|
| 1002 | return root()->COMGETTER (LogicalSize) (aLogicalSize);
|
---|
| 1003 | }
|
---|
| 1004 |
|
---|
| 1005 | // IHardDisk2 methods
|
---|
| 1006 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 1007 |
|
---|
[15051] | 1008 | STDMETHODIMP HardDisk2::GetProperty (IN_BSTR aName, BSTR *aValue)
|
---|
[14573] | 1009 | {
|
---|
[14596] | 1010 | CheckComArgStrNotEmptyOrNull (aName);
|
---|
| 1011 | CheckComArgOutPointerValid (aValue);
|
---|
[14573] | 1012 |
|
---|
[14596] | 1013 | AutoCaller autoCaller (this);
|
---|
| 1014 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 1015 |
|
---|
| 1016 | AutoReadLock alock (this);
|
---|
| 1017 |
|
---|
| 1018 | Data::PropertyMap::const_iterator it = mm.properties.find (Bstr (aName));
|
---|
| 1019 | if (it == mm.properties.end())
|
---|
| 1020 | return setError (VBOX_E_OBJECT_NOT_FOUND,
|
---|
| 1021 | tr ("Property '%ls' does not exist"), aName);
|
---|
| 1022 |
|
---|
| 1023 | it->second.cloneTo (aValue);
|
---|
| 1024 |
|
---|
| 1025 | return S_OK;
|
---|
[14573] | 1026 | }
|
---|
| 1027 |
|
---|
[15051] | 1028 | STDMETHODIMP HardDisk2::SetProperty (IN_BSTR aName, IN_BSTR aValue)
|
---|
[14573] | 1029 | {
|
---|
[14596] | 1030 | CheckComArgStrNotEmptyOrNull (aName);
|
---|
[14573] | 1031 |
|
---|
[15044] | 1032 | AutoCaller autoCaller (this);
|
---|
| 1033 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 1034 |
|
---|
[14698] | 1035 | /* VirtualBox::saveSettings() needs a write lock */
|
---|
| 1036 | AutoMultiWriteLock2 alock (mVirtualBox, this);
|
---|
[14596] | 1037 |
|
---|
[14698] | 1038 | switch (m.state)
|
---|
| 1039 | {
|
---|
| 1040 | case MediaState_Created:
|
---|
| 1041 | case MediaState_Inaccessible:
|
---|
| 1042 | break;
|
---|
| 1043 | default:
|
---|
| 1044 | return setStateError();
|
---|
| 1045 | }
|
---|
| 1046 |
|
---|
[14596] | 1047 | Data::PropertyMap::iterator it = mm.properties.find (Bstr (aName));
|
---|
| 1048 | if (it == mm.properties.end())
|
---|
| 1049 | return setError (VBOX_E_OBJECT_NOT_FOUND,
|
---|
| 1050 | tr ("Property '%ls' does not exist"), aName);
|
---|
| 1051 |
|
---|
| 1052 | it->second = aValue;
|
---|
| 1053 |
|
---|
[14698] | 1054 | HRESULT rc = mVirtualBox->saveSettings();
|
---|
| 1055 |
|
---|
| 1056 | return rc;
|
---|
[14573] | 1057 | }
|
---|
| 1058 |
|
---|
[15051] | 1059 | STDMETHODIMP HardDisk2::GetProperties (IN_BSTR aNames,
|
---|
[14573] | 1060 | ComSafeArrayOut (BSTR, aReturnNames),
|
---|
| 1061 | ComSafeArrayOut (BSTR, aReturnValues))
|
---|
| 1062 | {
|
---|
[14596] | 1063 | CheckComArgOutSafeArrayPointerValid (aReturnNames);
|
---|
| 1064 | CheckComArgOutSafeArrayPointerValid (aReturnValues);
|
---|
[14573] | 1065 |
|
---|
[14596] | 1066 | AutoCaller autoCaller (this);
|
---|
| 1067 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 1068 |
|
---|
| 1069 | AutoReadLock alock (this);
|
---|
| 1070 |
|
---|
[14698] | 1071 | /// @todo make use of aNames according to the documentation
|
---|
| 1072 | NOREF (aNames);
|
---|
| 1073 |
|
---|
[14596] | 1074 | com::SafeArray <BSTR> names (mm.properties.size());
|
---|
| 1075 | com::SafeArray <BSTR> values (mm.properties.size());
|
---|
| 1076 | size_t i = 0;
|
---|
| 1077 |
|
---|
| 1078 | for (Data::PropertyMap::const_iterator it = mm.properties.begin();
|
---|
| 1079 | it != mm.properties.end(); ++ it)
|
---|
| 1080 | {
|
---|
| 1081 | it->first.cloneTo (&names [i]);
|
---|
| 1082 | it->second.cloneTo (&values [i]);
|
---|
| 1083 | ++ i;
|
---|
| 1084 | }
|
---|
| 1085 |
|
---|
| 1086 | names.detachTo (ComSafeArrayOutArg (aReturnNames));
|
---|
| 1087 | values.detachTo (ComSafeArrayOutArg (aReturnValues));
|
---|
| 1088 |
|
---|
| 1089 | return S_OK;
|
---|
[14573] | 1090 | }
|
---|
| 1091 |
|
---|
[15051] | 1092 | STDMETHODIMP HardDisk2::SetProperties (ComSafeArrayIn (IN_BSTR, aNames),
|
---|
| 1093 | ComSafeArrayIn (IN_BSTR, aValues))
|
---|
[15044] | 1094 | {
|
---|
| 1095 | CheckComArgSafeArrayNotNull (aNames);
|
---|
| 1096 | CheckComArgSafeArrayNotNull (aValues);
|
---|
| 1097 |
|
---|
| 1098 | AutoCaller autoCaller (this);
|
---|
| 1099 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 1100 |
|
---|
| 1101 | /* VirtualBox::saveSettings() needs a write lock */
|
---|
| 1102 | AutoMultiWriteLock2 alock (mVirtualBox, this);
|
---|
| 1103 |
|
---|
[15051] | 1104 | com::SafeArray <IN_BSTR> names (ComSafeArrayInArg (aNames));
|
---|
| 1105 | com::SafeArray <IN_BSTR> values (ComSafeArrayInArg (aValues));
|
---|
[15044] | 1106 |
|
---|
| 1107 | /* first pass: validate names */
|
---|
| 1108 | for (size_t i = 0; i < names.size(); ++ i)
|
---|
| 1109 | {
|
---|
| 1110 | if (mm.properties.find (Bstr (names [i])) == mm.properties.end())
|
---|
| 1111 | return setError (VBOX_E_OBJECT_NOT_FOUND,
|
---|
| 1112 | tr ("Property '%ls' does not exist"), names [i]);
|
---|
| 1113 | }
|
---|
| 1114 |
|
---|
| 1115 | /* second pass: assign */
|
---|
| 1116 | for (size_t i = 0; i < names.size(); ++ i)
|
---|
| 1117 | {
|
---|
| 1118 | Data::PropertyMap::iterator it = mm.properties.find (Bstr (names [i]));
|
---|
| 1119 | AssertReturn (it != mm.properties.end(), E_FAIL);
|
---|
| 1120 |
|
---|
| 1121 | it->second = values [i];
|
---|
| 1122 | }
|
---|
| 1123 |
|
---|
| 1124 | HRESULT rc = mVirtualBox->saveSettings();
|
---|
| 1125 |
|
---|
| 1126 | return rc;
|
---|
| 1127 | }
|
---|
| 1128 |
|
---|
[13580] | 1129 | STDMETHODIMP HardDisk2::CreateDynamicStorage (ULONG64 aLogicalSize,
|
---|
| 1130 | IProgress **aProgress)
|
---|
| 1131 | {
|
---|
[15556] | 1132 | CheckComArgOutPointerValid (aProgress);
|
---|
[13580] | 1133 |
|
---|
| 1134 | AutoCaller autoCaller (this);
|
---|
| 1135 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 1136 |
|
---|
| 1137 | AutoWriteLock alock (this);
|
---|
| 1138 |
|
---|
[14783] | 1139 | if (!(mm.formatObj->capabilities() &
|
---|
| 1140 | HardDiskFormatCapabilities_CreateDynamic))
|
---|
| 1141 | return setError (VBOX_E_NOT_SUPPORTED,
|
---|
| 1142 | tr ("Hard disk format '%ls' does not support dynamic storage "
|
---|
| 1143 | "creation"), mm.format.raw());
|
---|
| 1144 |
|
---|
[13580] | 1145 | switch (m.state)
|
---|
| 1146 | {
|
---|
| 1147 | case MediaState_NotCreated:
|
---|
| 1148 | break;
|
---|
| 1149 | default:
|
---|
| 1150 | return setStateError();
|
---|
| 1151 | }
|
---|
| 1152 |
|
---|
| 1153 | ComObjPtr <Progress> progress;
|
---|
| 1154 | progress.createObject();
|
---|
| 1155 | HRESULT rc = progress->init (mVirtualBox, static_cast <IHardDisk2 *> (this),
|
---|
| 1156 | BstrFmt (tr ("Creating dynamic hard disk storage unit '%ls'"),
|
---|
[16118] | 1157 | m.locationFull.raw()),
|
---|
[13580] | 1158 | FALSE /* aCancelable */);
|
---|
| 1159 | CheckComRCReturnRC (rc);
|
---|
| 1160 |
|
---|
| 1161 | /* setup task object and thread to carry out the operation
|
---|
| 1162 | * asynchronously */
|
---|
| 1163 |
|
---|
| 1164 | std::auto_ptr <Task> task (new Task (this, progress, Task::CreateDynamic));
|
---|
| 1165 | AssertComRCReturnRC (task->autoCaller.rc());
|
---|
| 1166 |
|
---|
| 1167 | task->d.size = aLogicalSize;
|
---|
| 1168 |
|
---|
| 1169 | rc = task->startThread();
|
---|
| 1170 | CheckComRCReturnRC (rc);
|
---|
| 1171 |
|
---|
| 1172 | /* go to Creating state on success */
|
---|
| 1173 | m.state = MediaState_Creating;
|
---|
| 1174 |
|
---|
| 1175 | /* task is now owned by taskThread() so release it */
|
---|
| 1176 | task.release();
|
---|
| 1177 |
|
---|
| 1178 | /* return progress to the caller */
|
---|
| 1179 | progress.queryInterfaceTo (aProgress);
|
---|
| 1180 |
|
---|
| 1181 | return S_OK;
|
---|
| 1182 | }
|
---|
| 1183 |
|
---|
| 1184 | STDMETHODIMP HardDisk2::CreateFixedStorage (ULONG64 aLogicalSize,
|
---|
| 1185 | IProgress **aProgress)
|
---|
| 1186 | {
|
---|
[15556] | 1187 | CheckComArgOutPointerValid (aProgress);
|
---|
[13580] | 1188 |
|
---|
| 1189 | AutoCaller autoCaller (this);
|
---|
| 1190 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 1191 |
|
---|
| 1192 | AutoWriteLock alock (this);
|
---|
| 1193 |
|
---|
[14783] | 1194 | if (!(mm.formatObj->capabilities() &
|
---|
| 1195 | HardDiskFormatCapabilities_CreateFixed))
|
---|
| 1196 | return setError (VBOX_E_NOT_SUPPORTED,
|
---|
| 1197 | tr ("Hard disk format '%ls' does not support fixed storage "
|
---|
| 1198 | "creation"), mm.format.raw());
|
---|
| 1199 |
|
---|
[13580] | 1200 | switch (m.state)
|
---|
| 1201 | {
|
---|
| 1202 | case MediaState_NotCreated:
|
---|
| 1203 | break;
|
---|
| 1204 | default:
|
---|
| 1205 | return setStateError();
|
---|
| 1206 | }
|
---|
| 1207 |
|
---|
| 1208 | ComObjPtr <Progress> progress;
|
---|
| 1209 | progress.createObject();
|
---|
| 1210 | HRESULT rc = progress->init (mVirtualBox, static_cast <IHardDisk2 *> (this),
|
---|
| 1211 | BstrFmt (tr ("Creating fixed hard disk storage unit '%ls'"),
|
---|
[16118] | 1212 | m.locationFull.raw()),
|
---|
[13580] | 1213 | FALSE /* aCancelable */);
|
---|
| 1214 | CheckComRCReturnRC (rc);
|
---|
| 1215 |
|
---|
| 1216 | /* setup task object and thread to carry out the operation
|
---|
| 1217 | * asynchronously */
|
---|
| 1218 |
|
---|
| 1219 | std::auto_ptr <Task> task (new Task (this, progress, Task::CreateFixed));
|
---|
| 1220 | AssertComRCReturnRC (task->autoCaller.rc());
|
---|
| 1221 |
|
---|
| 1222 | task->d.size = aLogicalSize;
|
---|
| 1223 |
|
---|
| 1224 | rc = task->startThread();
|
---|
| 1225 | CheckComRCReturnRC (rc);
|
---|
| 1226 |
|
---|
| 1227 | /* go to Creating state on success */
|
---|
| 1228 | m.state = MediaState_Creating;
|
---|
| 1229 |
|
---|
| 1230 | /* task is now owned by taskThread() so release it */
|
---|
| 1231 | task.release();
|
---|
| 1232 |
|
---|
| 1233 | /* return progress to the caller */
|
---|
| 1234 | progress.queryInterfaceTo (aProgress);
|
---|
| 1235 |
|
---|
| 1236 | return S_OK;
|
---|
| 1237 | }
|
---|
| 1238 |
|
---|
| 1239 | STDMETHODIMP HardDisk2::DeleteStorage (IProgress **aProgress)
|
---|
| 1240 | {
|
---|
[15556] | 1241 | CheckComArgOutPointerValid (aProgress);
|
---|
[13580] | 1242 |
|
---|
[15556] | 1243 | AutoCaller autoCaller (this);
|
---|
| 1244 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 1245 |
|
---|
[13580] | 1246 | ComObjPtr <Progress> progress;
|
---|
| 1247 |
|
---|
| 1248 | HRESULT rc = deleteStorageNoWait (progress);
|
---|
| 1249 | if (SUCCEEDED (rc))
|
---|
| 1250 | {
|
---|
| 1251 | /* return progress to the caller */
|
---|
| 1252 | progress.queryInterfaceTo (aProgress);
|
---|
| 1253 | }
|
---|
| 1254 |
|
---|
| 1255 | return rc;
|
---|
| 1256 | }
|
---|
| 1257 |
|
---|
| 1258 | STDMETHODIMP HardDisk2::CreateDiffStorage (IHardDisk2 *aTarget, IProgress **aProgress)
|
---|
| 1259 | {
|
---|
[15556] | 1260 | CheckComArgNotNull (aTarget);
|
---|
| 1261 | CheckComArgOutPointerValid (aProgress);
|
---|
[13580] | 1262 |
|
---|
| 1263 | AutoCaller autoCaller (this);
|
---|
| 1264 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 1265 |
|
---|
| 1266 | ComObjPtr <HardDisk2> diff;
|
---|
| 1267 | HRESULT rc = mVirtualBox->cast (aTarget, diff);
|
---|
| 1268 | CheckComRCReturnRC (rc);
|
---|
| 1269 |
|
---|
| 1270 | AutoWriteLock alock (this);
|
---|
| 1271 |
|
---|
| 1272 | if (mm.type == HardDiskType_Writethrough)
|
---|
[16118] | 1273 | return setError (E_FAIL,
|
---|
| 1274 | tr ("Hard disk '%ls' is Writethrough"),
|
---|
| 1275 | m.locationFull.raw());
|
---|
[13580] | 1276 |
|
---|
| 1277 | /* We want to be locked for reading as long as our diff child is being
|
---|
| 1278 | * created */
|
---|
| 1279 | rc = LockRead (NULL);
|
---|
| 1280 | CheckComRCReturnRC (rc);
|
---|
| 1281 |
|
---|
| 1282 | ComObjPtr <Progress> progress;
|
---|
| 1283 |
|
---|
| 1284 | rc = createDiffStorageNoWait (diff, progress);
|
---|
| 1285 | if (FAILED (rc))
|
---|
| 1286 | {
|
---|
| 1287 | HRESULT rc2 = UnlockRead (NULL);
|
---|
| 1288 | AssertComRC (rc2);
|
---|
| 1289 | /* Note: on success, taskThread() will unlock this */
|
---|
| 1290 | }
|
---|
| 1291 | else
|
---|
| 1292 | {
|
---|
| 1293 | /* return progress to the caller */
|
---|
| 1294 | progress.queryInterfaceTo (aProgress);
|
---|
| 1295 | }
|
---|
| 1296 |
|
---|
| 1297 | return rc;
|
---|
| 1298 | }
|
---|
| 1299 |
|
---|
[15051] | 1300 | STDMETHODIMP HardDisk2::MergeTo (IN_GUID aTargetId, IProgress **aProgress)
|
---|
[13580] | 1301 | {
|
---|
| 1302 | AutoCaller autoCaller (this);
|
---|
| 1303 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 1304 |
|
---|
[14715] | 1305 | ReturnComNotImplemented();
|
---|
[13580] | 1306 | }
|
---|
| 1307 |
|
---|
| 1308 | STDMETHODIMP HardDisk2::CloneTo (IHardDisk2 *aTarget, IProgress **aProgress)
|
---|
| 1309 | {
|
---|
[15556] | 1310 | CheckComArgNotNull (aTarget);
|
---|
| 1311 | CheckComArgOutPointerValid (aProgress);
|
---|
| 1312 |
|
---|
[13580] | 1313 | AutoCaller autoCaller (this);
|
---|
| 1314 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 1315 |
|
---|
[15556] | 1316 | ComObjPtr <HardDisk2> target;
|
---|
| 1317 | HRESULT rc = mVirtualBox->cast (aTarget, target);
|
---|
| 1318 | CheckComRCReturnRC (rc);
|
---|
| 1319 |
|
---|
| 1320 | AutoMultiWriteLock2 alock (this, target);
|
---|
| 1321 |
|
---|
| 1322 | /* We want to be locked for reading as long as the clone hard disk is being
|
---|
| 1323 | * created*/
|
---|
| 1324 | rc = LockRead (NULL);
|
---|
| 1325 | CheckComRCReturnRC (rc);
|
---|
| 1326 |
|
---|
| 1327 | ComObjPtr <Progress> progress;
|
---|
| 1328 |
|
---|
| 1329 | try
|
---|
| 1330 | {
|
---|
| 1331 | if (target->m.state != MediaState_NotCreated)
|
---|
| 1332 | throw target->setStateError();
|
---|
| 1333 |
|
---|
| 1334 | progress.createObject();
|
---|
| 1335 | rc = progress->init (mVirtualBox, static_cast <IHardDisk2 *> (this),
|
---|
[16118] | 1336 | BstrFmt (tr ("Creating clone hard disk '%ls'"),
|
---|
| 1337 | target->m.locationFull.raw()),
|
---|
[15556] | 1338 | FALSE /* aCancelable */);
|
---|
| 1339 | CheckComRCThrowRC (rc);
|
---|
| 1340 |
|
---|
| 1341 | /* setup task object and thread to carry out the operation
|
---|
| 1342 | * asynchronously */
|
---|
| 1343 |
|
---|
| 1344 | std::auto_ptr <Task> task (new Task (this, progress, Task::Clone));
|
---|
| 1345 | AssertComRCThrowRC (task->autoCaller.rc());
|
---|
| 1346 |
|
---|
| 1347 | task->setData (target);
|
---|
| 1348 |
|
---|
| 1349 | rc = task->startThread();
|
---|
| 1350 | CheckComRCThrowRC (rc);
|
---|
| 1351 |
|
---|
| 1352 | /* go to Creating state before leaving the lock */
|
---|
| 1353 | target->m.state = MediaState_Creating;
|
---|
| 1354 |
|
---|
| 1355 | /* task is now owned (or already deleted) by taskThread() so release it */
|
---|
| 1356 | task.release();
|
---|
| 1357 | }
|
---|
| 1358 | catch (HRESULT aRC)
|
---|
| 1359 | {
|
---|
| 1360 | rc = aRC;
|
---|
| 1361 | }
|
---|
| 1362 |
|
---|
| 1363 | if (FAILED (rc))
|
---|
| 1364 | {
|
---|
| 1365 | HRESULT rc2 = UnlockRead (NULL);
|
---|
| 1366 | AssertComRC (rc2);
|
---|
| 1367 | /* Note: on success, taskThread() will unlock this */
|
---|
| 1368 | }
|
---|
| 1369 | else
|
---|
| 1370 | {
|
---|
| 1371 | /* return progress to the caller */
|
---|
| 1372 | progress.queryInterfaceTo (aProgress);
|
---|
| 1373 | }
|
---|
| 1374 |
|
---|
| 1375 | return rc;
|
---|
[13580] | 1376 | }
|
---|
| 1377 |
|
---|
| 1378 | STDMETHODIMP HardDisk2::FlattenTo (IHardDisk2 *aTarget, IProgress **aProgress)
|
---|
| 1379 | {
|
---|
| 1380 | AutoCaller autoCaller (this);
|
---|
| 1381 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 1382 |
|
---|
[14715] | 1383 | ReturnComNotImplemented();
|
---|
[13580] | 1384 | }
|
---|
| 1385 |
|
---|
[15486] | 1386 | STDMETHODIMP HardDisk2::Compact (IProgress **aProgress)
|
---|
| 1387 | {
|
---|
| 1388 | AutoCaller autoCaller (this);
|
---|
| 1389 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 1390 |
|
---|
| 1391 | ReturnComNotImplemented();
|
---|
| 1392 | }
|
---|
| 1393 |
|
---|
[13580] | 1394 | // public methods for internal purposes only
|
---|
| 1395 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 1396 |
|
---|
| 1397 | /**
|
---|
| 1398 | * Checks if the given change of \a aOldPath to \a aNewPath affects the location
|
---|
| 1399 | * of this hard disk or any its child and updates the paths if necessary to
|
---|
| 1400 | * reflect the new location.
|
---|
| 1401 | *
|
---|
| 1402 | * @param aOldPath Old path (full).
|
---|
| 1403 | * @param aNewPath New path (full).
|
---|
| 1404 | *
|
---|
[14143] | 1405 | * @note Locks treeLock() for reading, this object and all children for writing.
|
---|
[13580] | 1406 | */
|
---|
| 1407 | void HardDisk2::updatePaths (const char *aOldPath, const char *aNewPath)
|
---|
| 1408 | {
|
---|
| 1409 | AssertReturnVoid (aOldPath);
|
---|
| 1410 | AssertReturnVoid (aNewPath);
|
---|
| 1411 |
|
---|
| 1412 | AutoCaller autoCaller (this);
|
---|
| 1413 | AssertComRCReturnVoid (autoCaller.rc());
|
---|
| 1414 |
|
---|
| 1415 | AutoWriteLock alock (this);
|
---|
| 1416 |
|
---|
[14143] | 1417 | /* we access children() */
|
---|
| 1418 | AutoReadLock treeLock (this->treeLock());
|
---|
| 1419 |
|
---|
[13580] | 1420 | updatePath (aOldPath, aNewPath);
|
---|
| 1421 |
|
---|
| 1422 | /* update paths of all children */
|
---|
| 1423 | for (List::const_iterator it = children().begin();
|
---|
| 1424 | it != children().end();
|
---|
| 1425 | ++ it)
|
---|
| 1426 | {
|
---|
| 1427 | (*it)->updatePaths (aOldPath, aNewPath);
|
---|
| 1428 | }
|
---|
| 1429 | }
|
---|
| 1430 |
|
---|
| 1431 | /**
|
---|
| 1432 | * Returns the base hard disk of the hard disk chain this hard disk is part of.
|
---|
| 1433 | *
|
---|
| 1434 | * The root hard disk is found by walking up the parent-child relationship axis.
|
---|
| 1435 | * If the hard disk doesn't have a parent (i.e. it's a base hard disk), it
|
---|
| 1436 | * returns itself in response to this method.
|
---|
| 1437 | *
|
---|
| 1438 | * @param aLevel Where to store the number of ancestors of this hard disk
|
---|
| 1439 | * (zero for the root), may be @c NULL.
|
---|
| 1440 | *
|
---|
[14143] | 1441 | * @note Locks treeLock() for reading.
|
---|
[13580] | 1442 | */
|
---|
| 1443 | ComObjPtr <HardDisk2> HardDisk2::root (uint32_t *aLevel /*= NULL*/)
|
---|
| 1444 | {
|
---|
| 1445 | ComObjPtr <HardDisk2> root;
|
---|
| 1446 | uint32_t level;
|
---|
| 1447 |
|
---|
[14143] | 1448 | AutoCaller autoCaller (this);
|
---|
| 1449 | AssertReturn (autoCaller.isOk(), root);
|
---|
[13580] | 1450 |
|
---|
[14143] | 1451 | /* we access mParent */
|
---|
| 1452 | AutoReadLock treeLock (this->treeLock());
|
---|
[13580] | 1453 |
|
---|
[14143] | 1454 | root = this;
|
---|
| 1455 | level = 0;
|
---|
[13580] | 1456 |
|
---|
[14143] | 1457 | if (!mParent.isNull())
|
---|
| 1458 | {
|
---|
| 1459 | for (;;)
|
---|
| 1460 | {
|
---|
| 1461 | AutoCaller rootCaller (root);
|
---|
| 1462 | AssertReturn (rootCaller.isOk(), root);
|
---|
[13580] | 1463 |
|
---|
| 1464 | if (root->mParent.isNull())
|
---|
| 1465 | break;
|
---|
| 1466 |
|
---|
| 1467 | root = root->mParent;
|
---|
| 1468 | ++ level;
|
---|
| 1469 | }
|
---|
| 1470 | }
|
---|
| 1471 |
|
---|
| 1472 | if (aLevel != NULL)
|
---|
| 1473 | *aLevel = level;
|
---|
| 1474 |
|
---|
| 1475 | return root;
|
---|
| 1476 | }
|
---|
| 1477 |
|
---|
| 1478 | /**
|
---|
| 1479 | * Returns @c true if this hard disk cannot be modified because it has
|
---|
| 1480 | * dependants (children) or is part of the snapshot. Related to the hard disk
|
---|
| 1481 | * type and posterity, not to the current media state.
|
---|
| 1482 | *
|
---|
[14143] | 1483 | * @note Locks this object and treeLock() for reading.
|
---|
[13580] | 1484 | */
|
---|
| 1485 | bool HardDisk2::isReadOnly()
|
---|
| 1486 | {
|
---|
| 1487 | AutoCaller autoCaller (this);
|
---|
| 1488 | AssertComRCReturn (autoCaller.rc(), false);
|
---|
| 1489 |
|
---|
| 1490 | AutoReadLock alock (this);
|
---|
| 1491 |
|
---|
[14143] | 1492 | /* we access children */
|
---|
| 1493 | AutoReadLock treeLock (this->treeLock());
|
---|
| 1494 |
|
---|
[13580] | 1495 | switch (mm.type)
|
---|
| 1496 | {
|
---|
| 1497 | case HardDiskType_Normal:
|
---|
| 1498 | {
|
---|
| 1499 | if (children().size() != 0)
|
---|
| 1500 | return true;
|
---|
| 1501 |
|
---|
| 1502 | for (BackRefList::const_iterator it = m.backRefs.begin();
|
---|
| 1503 | it != m.backRefs.end(); ++ it)
|
---|
| 1504 | if (it->snapshotIds.size() != 0)
|
---|
| 1505 | return true;
|
---|
| 1506 |
|
---|
| 1507 | return false;
|
---|
| 1508 | }
|
---|
| 1509 | case HardDiskType_Immutable:
|
---|
| 1510 | {
|
---|
| 1511 | return true;
|
---|
| 1512 | }
|
---|
| 1513 | case HardDiskType_Writethrough:
|
---|
| 1514 | {
|
---|
| 1515 | return false;
|
---|
| 1516 | }
|
---|
| 1517 | default:
|
---|
| 1518 | break;
|
---|
| 1519 | }
|
---|
| 1520 |
|
---|
| 1521 | AssertFailedReturn (false);
|
---|
| 1522 | }
|
---|
| 1523 |
|
---|
| 1524 | /**
|
---|
| 1525 | * Saves hard disk data by appending a new <HardDisk> child node to the given
|
---|
| 1526 | * parent node which can be either <HardDisks> or <HardDisk>.
|
---|
| 1527 | *
|
---|
| 1528 | * @param aaParentNode Parent <HardDisks> or <HardDisk> node.
|
---|
| 1529 | *
|
---|
[14143] | 1530 | * @note Locks this object, treeLock() and children for reading.
|
---|
[13580] | 1531 | */
|
---|
| 1532 | HRESULT HardDisk2::saveSettings (settings::Key &aParentNode)
|
---|
| 1533 | {
|
---|
| 1534 | using namespace settings;
|
---|
| 1535 |
|
---|
| 1536 | AssertReturn (!aParentNode.isNull(), E_FAIL);
|
---|
| 1537 |
|
---|
| 1538 | AutoCaller autoCaller (this);
|
---|
| 1539 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 1540 |
|
---|
| 1541 | AutoReadLock alock (this);
|
---|
| 1542 |
|
---|
[14143] | 1543 | /* we access mParent */
|
---|
| 1544 | AutoReadLock treeLock (this->treeLock());
|
---|
| 1545 |
|
---|
[13580] | 1546 | Key diskNode = aParentNode.appendKey ("HardDisk");
|
---|
| 1547 | /* required */
|
---|
| 1548 | diskNode.setValue <Guid> ("uuid", m.id);
|
---|
| 1549 | /* required (note: the original locaiton, not full) */
|
---|
| 1550 | diskNode.setValue <Bstr> ("location", m.location);
|
---|
| 1551 | /* required */
|
---|
| 1552 | diskNode.setValue <Bstr> ("format", mm.format);
|
---|
| 1553 | /* optional */
|
---|
| 1554 | if (!m.description.isNull())
|
---|
| 1555 | {
|
---|
| 1556 | Key descNode = diskNode.createKey ("Description");
|
---|
| 1557 | descNode.setKeyValue <Bstr> (m.description);
|
---|
| 1558 | }
|
---|
| 1559 |
|
---|
[14698] | 1560 | /* optional properties */
|
---|
| 1561 | for (Data::PropertyMap::const_iterator it = mm.properties.begin();
|
---|
| 1562 | it != mm.properties.end(); ++ it)
|
---|
| 1563 | {
|
---|
| 1564 | /* only save properties that have non-default values */
|
---|
| 1565 | if (!it->second.isNull())
|
---|
| 1566 | {
|
---|
| 1567 | Key propNode = diskNode.appendKey ("Property");
|
---|
| 1568 | propNode.setValue <Bstr> ("name", it->first);
|
---|
| 1569 | propNode.setValue <Bstr> ("value", it->second);
|
---|
| 1570 | }
|
---|
| 1571 | }
|
---|
| 1572 |
|
---|
[13580] | 1573 | /* only for base hard disks */
|
---|
| 1574 | if (mParent.isNull())
|
---|
| 1575 | {
|
---|
| 1576 | const char *type =
|
---|
| 1577 | mm.type == HardDiskType_Normal ? "Normal" :
|
---|
| 1578 | mm.type == HardDiskType_Immutable ? "Immutable" :
|
---|
| 1579 | mm.type == HardDiskType_Writethrough ? "Writethrough" : NULL;
|
---|
| 1580 | Assert (type != NULL);
|
---|
| 1581 | diskNode.setStringValue ("type", type);
|
---|
| 1582 | }
|
---|
| 1583 |
|
---|
| 1584 | /* save all children */
|
---|
| 1585 | for (List::const_iterator it = children().begin();
|
---|
| 1586 | it != children().end();
|
---|
| 1587 | ++ it)
|
---|
| 1588 | {
|
---|
| 1589 | HRESULT rc = (*it)->saveSettings (diskNode);
|
---|
| 1590 | AssertComRCReturnRC (rc);
|
---|
| 1591 | }
|
---|
| 1592 |
|
---|
| 1593 | return S_OK;
|
---|
| 1594 | }
|
---|
| 1595 |
|
---|
| 1596 | /**
|
---|
| 1597 | * Compares the location of this hard disk to the given location.
|
---|
| 1598 | *
|
---|
| 1599 | * The comparison takes the location details into account. For example, if the
|
---|
| 1600 | * location is a file in the host's filesystem, a case insensitive comparison
|
---|
| 1601 | * will be performed for case insensitive filesystems.
|
---|
| 1602 | *
|
---|
| 1603 | * @param aLocation Location to compare to (as is).
|
---|
| 1604 | * @param aResult Where to store the result of comparison: 0 if locations
|
---|
| 1605 | * are equal, 1 if this object's location is greater than
|
---|
| 1606 | * the specified location, and -1 otherwise.
|
---|
| 1607 | */
|
---|
| 1608 | HRESULT HardDisk2::compareLocationTo (const char *aLocation, int &aResult)
|
---|
| 1609 | {
|
---|
| 1610 | AutoCaller autoCaller (this);
|
---|
| 1611 | AssertComRCReturnRC (autoCaller.rc());
|
---|
| 1612 |
|
---|
| 1613 | AutoReadLock alock (this);
|
---|
| 1614 |
|
---|
| 1615 | Utf8Str locationFull (m.locationFull);
|
---|
| 1616 |
|
---|
| 1617 | /// @todo NEWMEDIA delegate the comparison to the backend?
|
---|
| 1618 |
|
---|
[14272] | 1619 | if (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File)
|
---|
[13580] | 1620 | {
|
---|
| 1621 | Utf8Str location (aLocation);
|
---|
| 1622 |
|
---|
| 1623 | /* For locations represented by files, append the default path if
|
---|
| 1624 | * only the name is given, and then get the full path. */
|
---|
| 1625 | if (!RTPathHavePath (aLocation))
|
---|
| 1626 | {
|
---|
| 1627 | AutoReadLock propsLock (mVirtualBox->systemProperties());
|
---|
| 1628 | location = Utf8StrFmt ("%ls%c%s",
|
---|
| 1629 | mVirtualBox->systemProperties()->defaultHardDiskFolder().raw(),
|
---|
| 1630 | RTPATH_DELIMITER, aLocation);
|
---|
| 1631 | }
|
---|
| 1632 |
|
---|
| 1633 | int vrc = mVirtualBox->calculateFullPath (location, location);
|
---|
[13835] | 1634 | if (RT_FAILURE (vrc))
|
---|
[13580] | 1635 | return setError (E_FAIL,
|
---|
[13837] | 1636 | tr ("Invalid hard disk storage file location '%s' (%Rrc)"),
|
---|
[13580] | 1637 | location.raw(), vrc);
|
---|
| 1638 |
|
---|
| 1639 | aResult = RTPathCompare (locationFull, location);
|
---|
| 1640 | }
|
---|
| 1641 | else
|
---|
| 1642 | aResult = locationFull.compare (aLocation);
|
---|
| 1643 |
|
---|
| 1644 | return S_OK;
|
---|
| 1645 | }
|
---|
| 1646 |
|
---|
| 1647 | /**
|
---|
| 1648 | * Returns a short version of the location attribute.
|
---|
| 1649 | *
|
---|
| 1650 | * Reimplements MediumBase::name() to specially treat non-FS-path locations.
|
---|
| 1651 | *
|
---|
| 1652 | * @note Must be called from under this object's read or write lock.
|
---|
| 1653 | */
|
---|
| 1654 | Utf8Str HardDisk2::name()
|
---|
| 1655 | {
|
---|
| 1656 | /// @todo NEWMEDIA treat non-FS-paths specially! (may require to requiest
|
---|
| 1657 | /// this information from the VD backend)
|
---|
| 1658 |
|
---|
| 1659 | Utf8Str location (m.locationFull);
|
---|
| 1660 |
|
---|
| 1661 | Utf8Str name = RTPathFilename (location);
|
---|
| 1662 | return name;
|
---|
| 1663 | }
|
---|
| 1664 |
|
---|
| 1665 | /**
|
---|
| 1666 | * Checks that this hard disk may be discarded and performs necessary state
|
---|
| 1667 | * changes.
|
---|
| 1668 | *
|
---|
| 1669 | * This method is to be called prior to calling the #discrad() to perform
|
---|
| 1670 | * necessary consistency checks and place involved hard disks to appropriate
|
---|
| 1671 | * states. If #discard() is not called or fails, the state modifications
|
---|
| 1672 | * performed by this method must be undone by #cancelDiscard().
|
---|
| 1673 | *
|
---|
| 1674 | * See #discard() for more info about discarding hard disks.
|
---|
| 1675 | *
|
---|
| 1676 | * @param aChain Where to store the created merge chain (may return NULL
|
---|
| 1677 | * if no real merge is necessary).
|
---|
| 1678 | *
|
---|
[14143] | 1679 | * @note Locks treeLock() for reading. Locks this object, aTarget and all
|
---|
[13580] | 1680 | * intermediate hard disks for writing.
|
---|
| 1681 | */
|
---|
| 1682 | HRESULT HardDisk2::prepareDiscard (MergeChain * &aChain)
|
---|
| 1683 | {
|
---|
| 1684 | AutoCaller autoCaller (this);
|
---|
| 1685 | AssertComRCReturnRC (autoCaller.rc());
|
---|
| 1686 |
|
---|
| 1687 | aChain = NULL;
|
---|
| 1688 |
|
---|
| 1689 | AutoWriteLock alock (this);
|
---|
| 1690 |
|
---|
[14143] | 1691 | /* we access mParent & children() */
|
---|
| 1692 | AutoReadLock treeLock (this->treeLock());
|
---|
| 1693 |
|
---|
[13580] | 1694 | AssertReturn (mm.type == HardDiskType_Normal, E_FAIL);
|
---|
| 1695 |
|
---|
| 1696 | if (children().size() == 0)
|
---|
| 1697 | {
|
---|
| 1698 | /* special treatment of the last hard disk in the chain: */
|
---|
| 1699 |
|
---|
| 1700 | if (mParent.isNull())
|
---|
| 1701 | {
|
---|
| 1702 | /* lock only, to prevent any usage; discard() will unlock */
|
---|
| 1703 | return LockWrite (NULL);
|
---|
| 1704 | }
|
---|
| 1705 |
|
---|
| 1706 | /* the differencing hard disk w/o children will be deleted, protect it
|
---|
| 1707 | * from attaching to other VMs (this is why Deleting) */
|
---|
| 1708 |
|
---|
| 1709 | switch (m.state)
|
---|
| 1710 | {
|
---|
| 1711 | case MediaState_Created:
|
---|
| 1712 | m.state = MediaState_Deleting;
|
---|
| 1713 | break;
|
---|
| 1714 | default:
|
---|
| 1715 | return setStateError();
|
---|
| 1716 | }
|
---|
| 1717 |
|
---|
| 1718 | /* aChain is intentionally NULL here */
|
---|
| 1719 |
|
---|
| 1720 | return S_OK;
|
---|
| 1721 | }
|
---|
| 1722 |
|
---|
| 1723 | /* not going multi-merge as it's too expensive */
|
---|
| 1724 | if (children().size() > 1)
|
---|
| 1725 | return setError (E_FAIL,
|
---|
| 1726 | tr ("Hard disk '%ls' has more than one child hard disk (%d)"),
|
---|
| 1727 | m.locationFull.raw(), children().size());
|
---|
| 1728 |
|
---|
| 1729 | /* this is a read-only hard disk with children; it must be associated with
|
---|
| 1730 | * exactly one snapshot (when the snapshot is being taken, none of the
|
---|
| 1731 | * current VM's hard disks may be attached to other VMs). Note that by the
|
---|
| 1732 | * time when discard() is called, there must be no any attachments at all
|
---|
| 1733 | * (the code calling prepareDiscard() should detach). */
|
---|
| 1734 | AssertReturn (m.backRefs.size() == 1 &&
|
---|
| 1735 | !m.backRefs.front().inCurState &&
|
---|
| 1736 | m.backRefs.front().snapshotIds.size() == 1, E_FAIL);
|
---|
| 1737 |
|
---|
| 1738 | ComObjPtr <HardDisk2> child = children().front();
|
---|
| 1739 |
|
---|
| 1740 | /* we keep this locked, so lock the affected child to make sure the lock
|
---|
| 1741 | * order is correct when calling prepareMergeTo() */
|
---|
| 1742 | AutoWriteLock childLock (child);
|
---|
| 1743 |
|
---|
| 1744 | /* delegate the rest to the profi */
|
---|
| 1745 | if (mParent.isNull())
|
---|
| 1746 | {
|
---|
| 1747 | /* base hard disk, backward merge */
|
---|
| 1748 |
|
---|
| 1749 | Assert (child->m.backRefs.size() == 1);
|
---|
| 1750 | if (child->m.backRefs.front().machineId != m.backRefs.front().machineId)
|
---|
| 1751 | {
|
---|
| 1752 | /* backward merge is too tricky, we'll just detach on discard, so
|
---|
| 1753 | * lock only, to prevent any usage; discard() will only unlock
|
---|
| 1754 | * (since we return NULL in aChain) */
|
---|
| 1755 | return LockWrite (NULL);
|
---|
| 1756 | }
|
---|
| 1757 |
|
---|
| 1758 | return child->prepareMergeTo (this, aChain,
|
---|
| 1759 | true /* aIgnoreAttachments */);
|
---|
| 1760 | }
|
---|
| 1761 | else
|
---|
| 1762 | {
|
---|
| 1763 | /* forward merge */
|
---|
| 1764 | return prepareMergeTo (child, aChain,
|
---|
| 1765 | true /* aIgnoreAttachments */);
|
---|
| 1766 | }
|
---|
| 1767 | }
|
---|
| 1768 |
|
---|
| 1769 | /**
|
---|
| 1770 | * Discards this hard disk.
|
---|
| 1771 | *
|
---|
| 1772 | * Discarding the hard disk is merging its contents to its differencing child
|
---|
| 1773 | * hard disk (forward merge) or contents of its child hard disk to itself
|
---|
| 1774 | * (backward merge) if this hard disk is a base hard disk. If this hard disk is
|
---|
| 1775 | * a differencing hard disk w/o children, then it will be simply deleted.
|
---|
| 1776 | * Calling this method on a base hard disk w/o children will do nothing and
|
---|
| 1777 | * silently succeed. If this hard disk has more than one child, the method will
|
---|
| 1778 | * currently return an error (since merging in this case would be too expensive
|
---|
| 1779 | * and result in data duplication).
|
---|
| 1780 | *
|
---|
| 1781 | * When the backward merge takes place (i.e. this hard disk is a target) then,
|
---|
| 1782 | * on success, this hard disk will automatically replace the differencing child
|
---|
| 1783 | * hard disk used as a source (which will then be deleted) in the attachment
|
---|
| 1784 | * this child hard disk is associated with. This will happen only if both hard
|
---|
| 1785 | * disks belong to the same machine because otherwise such a replace would be
|
---|
| 1786 | * too tricky and could be not expected by the other machine. Same relates to a
|
---|
| 1787 | * case when the child hard disk is not associated with any machine at all. When
|
---|
| 1788 | * the backward merge is not applied, the method behaves as if the base hard
|
---|
| 1789 | * disk were not attached at all -- i.e. simply detaches it from the machine but
|
---|
| 1790 | * leaves the hard disk chain intact.
|
---|
| 1791 | *
|
---|
| 1792 | * This method is basically a wrapper around #mergeTo() that selects the correct
|
---|
| 1793 | * merge direction and performs additional actions as described above and.
|
---|
| 1794 | *
|
---|
| 1795 | * Note that this method will not return until the merge operation is complete
|
---|
| 1796 | * (which may be quite time consuming depending on the size of the merged hard
|
---|
| 1797 | * disks).
|
---|
| 1798 | *
|
---|
| 1799 | * Note that #prepareDiscard() must be called before calling this method. If
|
---|
| 1800 | * this method returns a failure, the caller must call #cancelDiscard(). On
|
---|
| 1801 | * success, #cancelDiscard() must not be called (this method will perform all
|
---|
| 1802 | * necessary steps such as resetting states of all involved hard disks and
|
---|
| 1803 | * deleting @a aChain).
|
---|
| 1804 | *
|
---|
| 1805 | * @param aChain Merge chain created by #prepareDiscard() (may be NULL if
|
---|
| 1806 | * no real merge takes place).
|
---|
| 1807 | *
|
---|
[14143] | 1808 | * @note Locks the hard disks from the chain for writing. Locks the machine
|
---|
| 1809 | * object when the backward merge takes place. Locks treeLock() lock for
|
---|
| 1810 | * reading or writing.
|
---|
[13580] | 1811 | */
|
---|
| 1812 | HRESULT HardDisk2::discard (ComObjPtr <Progress> &aProgress, MergeChain *aChain)
|
---|
| 1813 | {
|
---|
| 1814 | AssertReturn (!aProgress.isNull(), E_FAIL);
|
---|
| 1815 |
|
---|
| 1816 | ComObjPtr <HardDisk2> hdFrom;
|
---|
| 1817 |
|
---|
| 1818 | HRESULT rc = S_OK;
|
---|
| 1819 |
|
---|
| 1820 | {
|
---|
| 1821 | AutoCaller autoCaller (this);
|
---|
| 1822 | AssertComRCReturnRC (autoCaller.rc());
|
---|
| 1823 |
|
---|
| 1824 | aProgress->advanceOperation (BstrFmt (
|
---|
| 1825 | tr ("Discarding hard disk '%s'"), name().raw()));
|
---|
| 1826 |
|
---|
| 1827 | if (aChain == NULL)
|
---|
| 1828 | {
|
---|
| 1829 | AutoWriteLock alock (this);
|
---|
| 1830 |
|
---|
[14143] | 1831 | /* we access mParent & children() */
|
---|
| 1832 | AutoReadLock treeLock (this->treeLock());
|
---|
| 1833 |
|
---|
[13580] | 1834 | Assert (children().size() == 0);
|
---|
| 1835 |
|
---|
| 1836 | /* special treatment of the last hard disk in the chain: */
|
---|
| 1837 |
|
---|
| 1838 | if (mParent.isNull())
|
---|
| 1839 | {
|
---|
| 1840 | rc = UnlockWrite (NULL);
|
---|
| 1841 | AssertComRC (rc);
|
---|
| 1842 | return rc;
|
---|
| 1843 | }
|
---|
| 1844 |
|
---|
| 1845 | /* delete the differencing hard disk w/o children */
|
---|
| 1846 |
|
---|
| 1847 | Assert (m.state == MediaState_Deleting);
|
---|
| 1848 |
|
---|
| 1849 | /* go back to Created since deleteStorage() expects this state */
|
---|
| 1850 | m.state = MediaState_Created;
|
---|
| 1851 |
|
---|
| 1852 | hdFrom = this;
|
---|
| 1853 |
|
---|
| 1854 | rc = deleteStorageAndWait (&aProgress);
|
---|
| 1855 | }
|
---|
| 1856 | else
|
---|
| 1857 | {
|
---|
| 1858 | hdFrom = aChain->source();
|
---|
| 1859 |
|
---|
| 1860 | rc = hdFrom->mergeToAndWait (aChain, &aProgress);
|
---|
| 1861 | }
|
---|
| 1862 | }
|
---|
| 1863 |
|
---|
| 1864 | if (SUCCEEDED (rc))
|
---|
| 1865 | {
|
---|
| 1866 | /* mergeToAndWait() cannot uninitialize the initiator because of
|
---|
| 1867 | * possible AutoCallers on the current thread, deleteStorageAndWait()
|
---|
| 1868 | * doesn't do it either; do it ourselves */
|
---|
| 1869 | hdFrom->uninit();
|
---|
| 1870 | }
|
---|
| 1871 |
|
---|
| 1872 | return rc;
|
---|
| 1873 | }
|
---|
| 1874 |
|
---|
| 1875 | /**
|
---|
| 1876 | * Undoes what #prepareDiscard() did. Must be called if #discard() is not called
|
---|
| 1877 | * or fails. Frees memory occupied by @a aChain.
|
---|
| 1878 | *
|
---|
| 1879 | * @param aChain Merge chain created by #prepareDiscard() (may be NULL if
|
---|
| 1880 | * no real merge takes place).
|
---|
| 1881 | *
|
---|
[14143] | 1882 | * @note Locks the hard disks from the chain for writing. Locks treeLock() for
|
---|
| 1883 | * reading.
|
---|
[13580] | 1884 | */
|
---|
| 1885 | void HardDisk2::cancelDiscard (MergeChain *aChain)
|
---|
| 1886 | {
|
---|
| 1887 | AutoCaller autoCaller (this);
|
---|
| 1888 | AssertComRCReturnVoid (autoCaller.rc());
|
---|
| 1889 |
|
---|
| 1890 | if (aChain == NULL)
|
---|
| 1891 | {
|
---|
| 1892 | AutoWriteLock alock (this);
|
---|
| 1893 |
|
---|
[14143] | 1894 | /* we access mParent & children() */
|
---|
| 1895 | AutoReadLock treeLock (this->treeLock());
|
---|
| 1896 |
|
---|
[13580] | 1897 | Assert (children().size() == 0);
|
---|
| 1898 |
|
---|
| 1899 | /* special treatment of the last hard disk in the chain: */
|
---|
| 1900 |
|
---|
| 1901 | if (mParent.isNull())
|
---|
| 1902 | {
|
---|
| 1903 | HRESULT rc = UnlockWrite (NULL);
|
---|
| 1904 | AssertComRC (rc);
|
---|
| 1905 | return;
|
---|
| 1906 | }
|
---|
| 1907 |
|
---|
| 1908 | /* the differencing hard disk w/o children will be deleted, protect it
|
---|
| 1909 | * from attaching to other VMs (this is why Deleting) */
|
---|
| 1910 |
|
---|
| 1911 | Assert (m.state == MediaState_Deleting);
|
---|
| 1912 | m.state = MediaState_Created;
|
---|
| 1913 |
|
---|
| 1914 | return;
|
---|
| 1915 | }
|
---|
| 1916 |
|
---|
| 1917 | /* delegate the rest to the profi */
|
---|
| 1918 | cancelMergeTo (aChain);
|
---|
| 1919 | }
|
---|
| 1920 |
|
---|
[14225] | 1921 | /**
|
---|
| 1922 | * Returns a preferred format for differencing hard disks.
|
---|
| 1923 | */
|
---|
| 1924 | Bstr HardDisk2::preferredDiffFormat()
|
---|
| 1925 | {
|
---|
| 1926 | Bstr format;
|
---|
| 1927 |
|
---|
| 1928 | AutoCaller autoCaller (this);
|
---|
| 1929 | AssertComRCReturn (autoCaller.rc(), format);
|
---|
| 1930 |
|
---|
| 1931 | /* mm.format is const, no need to lock */
|
---|
| 1932 | format = mm.format;
|
---|
| 1933 |
|
---|
| 1934 | /* check that our own format supports diffs */
|
---|
[14272] | 1935 | if (!(mm.formatObj->capabilities() & HardDiskFormatCapabilities_Differencing))
|
---|
| 1936 | {
|
---|
| 1937 | /* use the default format if not */
|
---|
| 1938 | AutoReadLock propsLock (mVirtualBox->systemProperties());
|
---|
[14225] | 1939 | format = mVirtualBox->systemProperties()->defaultHardDiskFormat();
|
---|
[14272] | 1940 | }
|
---|
[14225] | 1941 |
|
---|
| 1942 | return format;
|
---|
| 1943 | }
|
---|
| 1944 |
|
---|
[13580] | 1945 | // protected methods
|
---|
| 1946 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 1947 |
|
---|
| 1948 | /**
|
---|
| 1949 | * Deletes the hard disk storage unit.
|
---|
| 1950 | *
|
---|
| 1951 | * If @a aProgress is not NULL but the object it points to is @c null then a new
|
---|
| 1952 | * progress object will be created and assigned to @a *aProgress on success,
|
---|
| 1953 | * otherwise the existing progress object is used. If Progress is NULL, then no
|
---|
| 1954 | * progress object is created/used at all.
|
---|
| 1955 | *
|
---|
| 1956 | * When @a aWait is @c false, this method will create a thread to perform the
|
---|
| 1957 | * delete operation asynchronously and will return immediately. Otherwise, it
|
---|
| 1958 | * will perform the operation on the calling thread and will not return to the
|
---|
| 1959 | * caller until the operation is completed. Note that @a aProgress cannot be
|
---|
| 1960 | * NULL when @a aWait is @c false (this method will assert in this case).
|
---|
| 1961 | *
|
---|
| 1962 | * @param aProgress Where to find/store a Progress object to track operation
|
---|
| 1963 | * completion.
|
---|
| 1964 | * @param aWait @c true if this method should block instead of creating
|
---|
| 1965 | * an asynchronous thread.
|
---|
| 1966 | *
|
---|
[14143] | 1967 | * @note Locks mVirtualBox and this object for writing. Locks treeLock() for
|
---|
| 1968 | * writing.
|
---|
[13580] | 1969 | */
|
---|
| 1970 | HRESULT HardDisk2::deleteStorage (ComObjPtr <Progress> *aProgress, bool aWait)
|
---|
| 1971 | {
|
---|
| 1972 | AssertReturn (aProgress != NULL || aWait == true, E_FAIL);
|
---|
| 1973 |
|
---|
| 1974 | /* unregisterWithVirtualBox() needs a write lock. We want to unregister
|
---|
| 1975 | * ourselves atomically after detecting that deletion is possible to make
|
---|
| 1976 | * sure that we don't do that after another thread has done
|
---|
| 1977 | * VirtualBox::findHardDisk2() but before it starts using us (provided that
|
---|
[14143] | 1978 | * it holds a mVirtualBox lock too of course). */
|
---|
[13580] | 1979 |
|
---|
[14143] | 1980 | AutoWriteLock vboxLock (mVirtualBox);
|
---|
[13580] | 1981 |
|
---|
[14143] | 1982 | AutoWriteLock alock (this);
|
---|
| 1983 |
|
---|
[14783] | 1984 | if (!(mm.formatObj->capabilities() &
|
---|
| 1985 | (HardDiskFormatCapabilities_CreateDynamic |
|
---|
| 1986 | HardDiskFormatCapabilities_CreateFixed)))
|
---|
| 1987 | return setError (VBOX_E_NOT_SUPPORTED,
|
---|
[14931] | 1988 | tr ("Hard disk format '%ls' does not support storage deletion"),
|
---|
[14783] | 1989 | mm.format.raw());
|
---|
| 1990 |
|
---|
[15215] | 1991 | /* Note that we are fine with Inaccessible state too: a) for symmetry with
|
---|
| 1992 | * create calls and b) because it doesn't really harm to try, if it is
|
---|
| 1993 | * really inaccessibke, the delete operation will fail anyway. Accepting
|
---|
| 1994 | * Inaccessible state is especially important because all registered hard
|
---|
| 1995 | * disks are initially Inaccessible upon VBoxSVC startup until
|
---|
| 1996 | * COMGETTER(State) is called. */
|
---|
| 1997 |
|
---|
[13580] | 1998 | switch (m.state)
|
---|
| 1999 | {
|
---|
| 2000 | case MediaState_Created:
|
---|
[15215] | 2001 | case MediaState_Inaccessible:
|
---|
[13580] | 2002 | break;
|
---|
| 2003 | default:
|
---|
| 2004 | return setStateError();
|
---|
| 2005 | }
|
---|
| 2006 |
|
---|
| 2007 | if (m.backRefs.size() != 0)
|
---|
[14947] | 2008 | return setError (VBOX_E_OBJECT_IN_USE,
|
---|
[13580] | 2009 | tr ("Hard disk '%ls' is attached to %d virtual machines"),
|
---|
| 2010 | m.locationFull.raw(), m.backRefs.size());
|
---|
| 2011 |
|
---|
| 2012 | HRESULT rc = canClose();
|
---|
| 2013 | CheckComRCReturnRC (rc);
|
---|
| 2014 |
|
---|
[14143] | 2015 | /* go to Deleting state before leaving the lock */
|
---|
| 2016 | m.state = MediaState_Deleting;
|
---|
| 2017 |
|
---|
| 2018 | /* we need to leave this object's write lock now because of
|
---|
| 2019 | * unregisterWithVirtualBox() that locks treeLock() for writing */
|
---|
| 2020 | alock.leave();
|
---|
| 2021 |
|
---|
[13580] | 2022 | /* try to remove from the list of known hard disks before performing actual
|
---|
| 2023 | * deletion (we favor the consistency of the media registry in the first
|
---|
| 2024 | * place which would have been broken if unregisterWithVirtualBox() failed
|
---|
| 2025 | * after we successfully deleted the storage) */
|
---|
| 2026 |
|
---|
| 2027 | rc = unregisterWithVirtualBox();
|
---|
[14143] | 2028 |
|
---|
| 2029 | alock.enter();
|
---|
| 2030 |
|
---|
| 2031 | /* restore the state because we may fail below; we will set it later again*/
|
---|
| 2032 | m.state = MediaState_Created;
|
---|
| 2033 |
|
---|
[13580] | 2034 | CheckComRCReturnRC (rc);
|
---|
| 2035 |
|
---|
| 2036 | ComObjPtr <Progress> progress;
|
---|
| 2037 |
|
---|
| 2038 | if (aProgress != NULL)
|
---|
| 2039 | {
|
---|
| 2040 | /* use the existing progress object... */
|
---|
| 2041 | progress = *aProgress;
|
---|
| 2042 |
|
---|
| 2043 | /* ...but create a new one if it is null */
|
---|
| 2044 | if (progress.isNull())
|
---|
| 2045 | {
|
---|
| 2046 | progress.createObject();
|
---|
| 2047 | rc = progress->init (mVirtualBox, static_cast <IHardDisk2 *> (this),
|
---|
| 2048 | BstrFmt (tr ("Deleting hard disk storage unit '%ls'"),
|
---|
[16118] | 2049 | m.locationFull.raw()),
|
---|
[13580] | 2050 | FALSE /* aCancelable */);
|
---|
| 2051 | CheckComRCReturnRC (rc);
|
---|
| 2052 | }
|
---|
| 2053 | }
|
---|
| 2054 |
|
---|
| 2055 | std::auto_ptr <Task> task (new Task (this, progress, Task::Delete));
|
---|
| 2056 | AssertComRCReturnRC (task->autoCaller.rc());
|
---|
| 2057 |
|
---|
| 2058 | if (aWait)
|
---|
| 2059 | {
|
---|
| 2060 | /* go to Deleting state before starting the task */
|
---|
| 2061 | m.state = MediaState_Deleting;
|
---|
| 2062 |
|
---|
| 2063 | rc = task->runNow();
|
---|
| 2064 | }
|
---|
| 2065 | else
|
---|
| 2066 | {
|
---|
| 2067 | rc = task->startThread();
|
---|
| 2068 | CheckComRCReturnRC (rc);
|
---|
| 2069 |
|
---|
| 2070 | /* go to Deleting state before leaving the lock */
|
---|
| 2071 | m.state = MediaState_Deleting;
|
---|
| 2072 | }
|
---|
| 2073 |
|
---|
| 2074 | /* task is now owned (or already deleted) by taskThread() so release it */
|
---|
| 2075 | task.release();
|
---|
| 2076 |
|
---|
| 2077 | if (aProgress != NULL)
|
---|
| 2078 | {
|
---|
| 2079 | /* return progress to the caller */
|
---|
| 2080 | *aProgress = progress;
|
---|
| 2081 | }
|
---|
| 2082 |
|
---|
| 2083 | return rc;
|
---|
| 2084 | }
|
---|
| 2085 |
|
---|
| 2086 | /**
|
---|
| 2087 | * Creates a new differencing storage unit using the given target hard disk's
|
---|
| 2088 | * format and the location. Note that @c aTarget must be NotCreated.
|
---|
| 2089 | *
|
---|
| 2090 | * As opposed to the CreateDiffStorage() method, this method doesn't try to lock
|
---|
| 2091 | * this hard disk for reading assuming that the caller has already done so. This
|
---|
[14700] | 2092 | * is used when taking an online snaopshot (where all original hard disks are
|
---|
[13580] | 2093 | * locked for writing and must remain such). Note however that if @a aWait is
|
---|
| 2094 | * @c false and this method returns a success then the thread started by
|
---|
[14700] | 2095 | * this method will unlock the hard disk (unless it is in
|
---|
[13580] | 2096 | * MediaState_LockedWrite state) so make sure the hard disk is either in
|
---|
| 2097 | * MediaState_LockedWrite or call #LockRead() before calling this method! If @a
|
---|
| 2098 | * aWait is @c true then this method neither locks nor unlocks the hard disk, so
|
---|
| 2099 | * make sure you do it yourself as needed.
|
---|
| 2100 | *
|
---|
| 2101 | * If @a aProgress is not NULL but the object it points to is @c null then a new
|
---|
| 2102 | * progress object will be created and assigned to @a *aProgress on success,
|
---|
[14700] | 2103 | * otherwise the existing progress object is used. If @a aProgress is NULL, then no
|
---|
[13580] | 2104 | * progress object is created/used at all.
|
---|
| 2105 | *
|
---|
| 2106 | * When @a aWait is @c false, this method will create a thread to perform the
|
---|
| 2107 | * create operation asynchronously and will return immediately. Otherwise, it
|
---|
| 2108 | * will perform the operation on the calling thread and will not return to the
|
---|
| 2109 | * caller until the operation is completed. Note that @a aProgress cannot be
|
---|
| 2110 | * NULL when @a aWait is @c false (this method will assert in this case).
|
---|
| 2111 | *
|
---|
| 2112 | * @param aTarget Target hard disk.
|
---|
| 2113 | * @param aProgress Where to find/store a Progress object to track operation
|
---|
| 2114 | * completion.
|
---|
| 2115 | * @param aWait @c true if this method should block instead of creating
|
---|
| 2116 | * an asynchronous thread.
|
---|
| 2117 | *
|
---|
[14700] | 2118 | * @note Locks this object and @a aTarget for writing.
|
---|
[13580] | 2119 | */
|
---|
| 2120 | HRESULT HardDisk2::createDiffStorage (ComObjPtr <HardDisk2> &aTarget,
|
---|
| 2121 | ComObjPtr <Progress> *aProgress,
|
---|
| 2122 | bool aWait)
|
---|
| 2123 | {
|
---|
| 2124 | AssertReturn (!aTarget.isNull(), E_FAIL);
|
---|
| 2125 | AssertReturn (aProgress != NULL || aWait == true, E_FAIL);
|
---|
| 2126 |
|
---|
| 2127 | AutoCaller autoCaller (this);
|
---|
| 2128 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 2129 |
|
---|
| 2130 | AutoCaller targetCaller (aTarget);
|
---|
| 2131 | CheckComRCReturnRC (targetCaller.rc());
|
---|
| 2132 |
|
---|
| 2133 | AutoMultiWriteLock2 alock (this, aTarget);
|
---|
| 2134 |
|
---|
| 2135 | AssertReturn (mm.type != HardDiskType_Writethrough, E_FAIL);
|
---|
| 2136 |
|
---|
| 2137 | /* Note: MediaState_LockedWrite is ok when taking an online snapshot */
|
---|
| 2138 | AssertReturn (m.state == MediaState_LockedRead ||
|
---|
| 2139 | m.state == MediaState_LockedWrite, E_FAIL);
|
---|
| 2140 |
|
---|
| 2141 | if (aTarget->m.state != MediaState_NotCreated)
|
---|
| 2142 | return aTarget->setStateError();
|
---|
| 2143 |
|
---|
| 2144 | HRESULT rc = S_OK;
|
---|
| 2145 |
|
---|
| 2146 | /* check that the hard disk is not attached to any VM in the current state*/
|
---|
| 2147 | for (BackRefList::const_iterator it = m.backRefs.begin();
|
---|
| 2148 | it != m.backRefs.end(); ++ it)
|
---|
| 2149 | {
|
---|
| 2150 | if (it->inCurState)
|
---|
| 2151 | {
|
---|
| 2152 | /* Note: when a VM snapshot is being taken, all normal hard disks
|
---|
| 2153 | * attached to the VM in the current state will be, as an exception,
|
---|
| 2154 | * also associated with the snapshot which is about to create (see
|
---|
| 2155 | * SnapshotMachine::init()) before deassociating them from the
|
---|
| 2156 | * current state (which takes place only on success in
|
---|
| 2157 | * Machine::fixupHardDisks2()), so that the size of snapshotIds
|
---|
| 2158 | * will be 1 in this case. The given condition is used to filter out
|
---|
| 2159 | * this legal situatinon and do not report an error. */
|
---|
| 2160 |
|
---|
| 2161 | if (it->snapshotIds.size() == 0)
|
---|
| 2162 | {
|
---|
[14842] | 2163 | return setError (VBOX_E_INVALID_OBJECT_STATE,
|
---|
[13580] | 2164 | tr ("Hard disk '%ls' is attached to a virtual machine "
|
---|
[13842] | 2165 | "with UUID {%RTuuid}. No differencing hard disks "
|
---|
[13580] | 2166 | "based on it may be created until it is detached"),
|
---|
[16118] | 2167 | m.locationFull.raw(), it->machineId.raw());
|
---|
[13580] | 2168 | }
|
---|
| 2169 |
|
---|
| 2170 | Assert (it->snapshotIds.size() == 1);
|
---|
| 2171 | }
|
---|
| 2172 | }
|
---|
| 2173 |
|
---|
| 2174 | ComObjPtr <Progress> progress;
|
---|
| 2175 |
|
---|
| 2176 | if (aProgress != NULL)
|
---|
| 2177 | {
|
---|
| 2178 | /* use the existing progress object... */
|
---|
| 2179 | progress = *aProgress;
|
---|
| 2180 |
|
---|
| 2181 | /* ...but create a new one if it is null */
|
---|
| 2182 | if (progress.isNull())
|
---|
| 2183 | {
|
---|
| 2184 | progress.createObject();
|
---|
| 2185 | rc = progress->init (mVirtualBox, static_cast <IHardDisk2 *> (this),
|
---|
| 2186 | BstrFmt (tr ("Creating differencing hard disk storage unit '%ls'"),
|
---|
[16118] | 2187 | aTarget->m.locationFull.raw()),
|
---|
[13580] | 2188 | FALSE /* aCancelable */);
|
---|
| 2189 | CheckComRCReturnRC (rc);
|
---|
| 2190 | }
|
---|
| 2191 | }
|
---|
| 2192 |
|
---|
| 2193 | /* setup task object and thread to carry out the operation
|
---|
| 2194 | * asynchronously */
|
---|
| 2195 |
|
---|
| 2196 | std::auto_ptr <Task> task (new Task (this, progress, Task::CreateDiff));
|
---|
| 2197 | AssertComRCReturnRC (task->autoCaller.rc());
|
---|
| 2198 |
|
---|
| 2199 | task->setData (aTarget);
|
---|
| 2200 |
|
---|
| 2201 | /* register a task (it will deregister itself when done) */
|
---|
| 2202 | ++ mm.numCreateDiffTasks;
|
---|
| 2203 | Assert (mm.numCreateDiffTasks != 0); /* overflow? */
|
---|
| 2204 |
|
---|
| 2205 | if (aWait)
|
---|
| 2206 | {
|
---|
| 2207 | /* go to Creating state before starting the task */
|
---|
| 2208 | aTarget->m.state = MediaState_Creating;
|
---|
| 2209 |
|
---|
| 2210 | rc = task->runNow();
|
---|
| 2211 | }
|
---|
| 2212 | else
|
---|
| 2213 | {
|
---|
| 2214 | rc = task->startThread();
|
---|
| 2215 | CheckComRCReturnRC (rc);
|
---|
| 2216 |
|
---|
| 2217 | /* go to Creating state before leaving the lock */
|
---|
| 2218 | aTarget->m.state = MediaState_Creating;
|
---|
| 2219 | }
|
---|
| 2220 |
|
---|
| 2221 | /* task is now owned (or already deleted) by taskThread() so release it */
|
---|
| 2222 | task.release();
|
---|
| 2223 |
|
---|
| 2224 | if (aProgress != NULL)
|
---|
| 2225 | {
|
---|
| 2226 | /* return progress to the caller */
|
---|
| 2227 | *aProgress = progress;
|
---|
| 2228 | }
|
---|
| 2229 |
|
---|
| 2230 | return rc;
|
---|
| 2231 | }
|
---|
| 2232 |
|
---|
| 2233 | /**
|
---|
| 2234 | * Prepares this (source) hard disk, target hard disk and all intermediate hard
|
---|
| 2235 | * disks for the merge operation.
|
---|
| 2236 | *
|
---|
| 2237 | * This method is to be called prior to calling the #mergeTo() to perform
|
---|
| 2238 | * necessary consistency checks and place involved hard disks to appropriate
|
---|
| 2239 | * states. If #mergeTo() is not called or fails, the state modifications
|
---|
| 2240 | * performed by this method must be undone by #cancelMergeTo().
|
---|
| 2241 | *
|
---|
| 2242 | * Note that when @a aIgnoreAttachments is @c true then it's the caller's
|
---|
| 2243 | * responsibility to detach the source and all intermediate hard disks before
|
---|
| 2244 | * calling #mergeTo() (which will fail otherwise).
|
---|
| 2245 | *
|
---|
| 2246 | * See #mergeTo() for more information about merging.
|
---|
| 2247 | *
|
---|
| 2248 | * @param aTarget Target hard disk.
|
---|
| 2249 | * @param aChain Where to store the created merge chain.
|
---|
| 2250 | * @param aIgnoreAttachments Don't check if the source or any intermediate
|
---|
| 2251 | * hard disk is attached to any VM.
|
---|
| 2252 | *
|
---|
[14143] | 2253 | * @note Locks treeLock() for reading. Locks this object, aTarget and all
|
---|
[13580] | 2254 | * intermediate hard disks for writing.
|
---|
| 2255 | */
|
---|
| 2256 | HRESULT HardDisk2::prepareMergeTo (HardDisk2 *aTarget,
|
---|
| 2257 | MergeChain * &aChain,
|
---|
| 2258 | bool aIgnoreAttachments /*= false*/)
|
---|
| 2259 | {
|
---|
| 2260 | AssertReturn (aTarget != NULL, E_FAIL);
|
---|
| 2261 |
|
---|
| 2262 | AutoCaller autoCaller (this);
|
---|
| 2263 | AssertComRCReturnRC (autoCaller.rc());
|
---|
| 2264 |
|
---|
[14143] | 2265 | AutoCaller targetCaller (aTarget);
|
---|
[13580] | 2266 | AssertComRCReturnRC (targetCaller.rc());
|
---|
| 2267 |
|
---|
| 2268 | aChain = NULL;
|
---|
| 2269 |
|
---|
[14143] | 2270 | /* we walk the tree */
|
---|
[13580] | 2271 | AutoReadLock treeLock (this->treeLock());
|
---|
| 2272 |
|
---|
| 2273 | HRESULT rc = S_OK;
|
---|
| 2274 |
|
---|
| 2275 | /* detect the merge direction */
|
---|
| 2276 | bool forward;
|
---|
| 2277 | {
|
---|
| 2278 | HardDisk2 *parent = mParent;
|
---|
| 2279 | while (parent != NULL && parent != aTarget)
|
---|
| 2280 | parent = parent->mParent;
|
---|
| 2281 | if (parent == aTarget)
|
---|
| 2282 | forward = false;
|
---|
| 2283 | else
|
---|
| 2284 | {
|
---|
| 2285 | parent = aTarget->mParent;
|
---|
| 2286 | while (parent != NULL && parent != this)
|
---|
| 2287 | parent = parent->mParent;
|
---|
| 2288 | if (parent == this)
|
---|
| 2289 | forward = true;
|
---|
| 2290 | else
|
---|
| 2291 | {
|
---|
| 2292 | Bstr tgtLoc;
|
---|
[14143] | 2293 | {
|
---|
| 2294 | AutoReadLock alock (this);
|
---|
| 2295 | tgtLoc = aTarget->locationFull();
|
---|
| 2296 | }
|
---|
[13580] | 2297 |
|
---|
| 2298 | AutoReadLock alock (this);
|
---|
| 2299 | return setError (E_FAIL,
|
---|
| 2300 | tr ("Hard disks '%ls' and '%ls' are unrelated"),
|
---|
| 2301 | m.locationFull.raw(), tgtLoc.raw());
|
---|
| 2302 | }
|
---|
| 2303 | }
|
---|
| 2304 | }
|
---|
| 2305 |
|
---|
| 2306 | /* build the chain (will do necessary checks and state changes) */
|
---|
| 2307 | std::auto_ptr <MergeChain> chain (new MergeChain (forward,
|
---|
| 2308 | aIgnoreAttachments));
|
---|
| 2309 | {
|
---|
| 2310 | HardDisk2 *last = forward ? aTarget : this;
|
---|
| 2311 | HardDisk2 *first = forward ? this : aTarget;
|
---|
| 2312 |
|
---|
| 2313 | for (;;)
|
---|
| 2314 | {
|
---|
| 2315 | if (last == aTarget)
|
---|
| 2316 | rc = chain->addTarget (last);
|
---|
| 2317 | else if (last == this)
|
---|
| 2318 | rc = chain->addSource (last);
|
---|
| 2319 | else
|
---|
| 2320 | rc = chain->addIntermediate (last);
|
---|
| 2321 | CheckComRCReturnRC (rc);
|
---|
| 2322 |
|
---|
| 2323 | if (last == first)
|
---|
| 2324 | break;
|
---|
| 2325 |
|
---|
| 2326 | last = last->mParent;
|
---|
| 2327 | }
|
---|
| 2328 | }
|
---|
| 2329 |
|
---|
| 2330 | aChain = chain.release();
|
---|
| 2331 |
|
---|
| 2332 | return S_OK;
|
---|
| 2333 | }
|
---|
| 2334 |
|
---|
| 2335 | /**
|
---|
| 2336 | * Merges this hard disk to the specified hard disk which must be either its
|
---|
| 2337 | * direct ancestor or descendant.
|
---|
| 2338 | *
|
---|
| 2339 | * Given this hard disk is SOURCE and the specified hard disk is TARGET, we will
|
---|
| 2340 | * get two varians of the merge operation:
|
---|
| 2341 | *
|
---|
| 2342 | * forward merge
|
---|
| 2343 | * ------------------------->
|
---|
| 2344 | * [Extra] <- SOURCE <- Intermediate <- TARGET
|
---|
| 2345 | * Any Del Del LockWr
|
---|
| 2346 | *
|
---|
| 2347 | *
|
---|
| 2348 | * backward merge
|
---|
| 2349 | * <-------------------------
|
---|
| 2350 | * TARGET <- Intermediate <- SOURCE <- [Extra]
|
---|
| 2351 | * LockWr Del Del LockWr
|
---|
| 2352 | *
|
---|
| 2353 | * Each scheme shows the involved hard disks on the hard disk chain where
|
---|
| 2354 | * SOURCE and TARGET belong. Under each hard disk there is a state value which
|
---|
| 2355 | * the hard disk must have at a time of the mergeTo() call.
|
---|
| 2356 | *
|
---|
| 2357 | * The hard disks in the square braces may be absent (e.g. when the forward
|
---|
| 2358 | * operation takes place and SOURCE is the base hard disk, or when the backward
|
---|
| 2359 | * merge operation takes place and TARGET is the last child in the chain) but if
|
---|
| 2360 | * they present they are involved too as shown.
|
---|
| 2361 | *
|
---|
| 2362 | * Nor the source hard disk neither intermediate hard disks may be attached to
|
---|
| 2363 | * any VM directly or in the snapshot, otherwise this method will assert.
|
---|
| 2364 | *
|
---|
| 2365 | * The #prepareMergeTo() method must be called prior to this method to place all
|
---|
| 2366 | * involved to necessary states and perform other consistency checks.
|
---|
| 2367 | *
|
---|
| 2368 | * If @a aWait is @c true then this method will perform the operation on the
|
---|
| 2369 | * calling thread and will not return to the caller until the operation is
|
---|
| 2370 | * completed. When this method succeeds, all intermediate hard disk objects in
|
---|
| 2371 | * the chain will be uninitialized, the state of the target hard disk (and all
|
---|
| 2372 | * involved extra hard disks) will be restored and @a aChain will be deleted.
|
---|
| 2373 | * Note that this (source) hard disk is not uninitialized because of possible
|
---|
| 2374 | * AutoCaller instances held by the caller of this method on the current thread.
|
---|
| 2375 | * It's therefore the responsibility of the caller to call HardDisk2::uninit()
|
---|
| 2376 | * after releasing all callers in this case!
|
---|
| 2377 | *
|
---|
| 2378 | * If @a aWait is @c false then this method will crea,te a thread to perform the
|
---|
| 2379 | * create operation asynchronously and will return immediately. If the operation
|
---|
| 2380 | * succeeds, the thread will uninitialize the source hard disk object and all
|
---|
| 2381 | * intermediate hard disk objects in the chain, reset the state of the target
|
---|
| 2382 | * hard disk (and all involved extra hard disks) and delete @a aChain. If the
|
---|
| 2383 | * operation fails, the thread will only reset the states of all involved hard
|
---|
| 2384 | * disks and delete @a aChain.
|
---|
| 2385 | *
|
---|
| 2386 | * When this method fails (regardless of the @a aWait mode), it is a caller's
|
---|
| 2387 | * responsiblity to undo state changes and delete @a aChain using
|
---|
| 2388 | * #cancelMergeTo().
|
---|
| 2389 | *
|
---|
| 2390 | * If @a aProgress is not NULL but the object it points to is @c null then a new
|
---|
| 2391 | * progress object will be created and assigned to @a *aProgress on success,
|
---|
| 2392 | * otherwise the existing progress object is used. If Progress is NULL, then no
|
---|
| 2393 | * progress object is created/used at all. Note that @a aProgress cannot be
|
---|
| 2394 | * NULL when @a aWait is @c false (this method will assert in this case).
|
---|
| 2395 | *
|
---|
| 2396 | * @param aChain Merge chain created by #prepareMergeTo().
|
---|
| 2397 | * @param aProgress Where to find/store a Progress object to track operation
|
---|
| 2398 | * completion.
|
---|
| 2399 | * @param aWait @c true if this method should block instead of creating
|
---|
| 2400 | * an asynchronous thread.
|
---|
| 2401 | *
|
---|
| 2402 | * @note Locks the branch lock for writing. Locks the hard disks from the chain
|
---|
| 2403 | * for writing.
|
---|
| 2404 | */
|
---|
| 2405 | HRESULT HardDisk2::mergeTo (MergeChain *aChain,
|
---|
| 2406 | ComObjPtr <Progress> *aProgress,
|
---|
| 2407 | bool aWait)
|
---|
| 2408 | {
|
---|
| 2409 | AssertReturn (aChain != NULL, E_FAIL);
|
---|
| 2410 | AssertReturn (aProgress != NULL || aWait == true, E_FAIL);
|
---|
| 2411 |
|
---|
| 2412 | AutoCaller autoCaller (this);
|
---|
| 2413 | CheckComRCReturnRC (autoCaller.rc());
|
---|
| 2414 |
|
---|
| 2415 | HRESULT rc = S_OK;
|
---|
| 2416 |
|
---|
| 2417 | ComObjPtr <Progress> progress;
|
---|
| 2418 |
|
---|
| 2419 | if (aProgress != NULL)
|
---|
| 2420 | {
|
---|
| 2421 | /* use the existing progress object... */
|
---|
| 2422 | progress = *aProgress;
|
---|
| 2423 |
|
---|
| 2424 | /* ...but create a new one if it is null */
|
---|
| 2425 | if (progress.isNull())
|
---|
| 2426 | {
|
---|
| 2427 | AutoReadLock alock (this);
|
---|
| 2428 |
|
---|
| 2429 | progress.createObject();
|
---|
| 2430 | rc = progress->init (mVirtualBox, static_cast <IHardDisk2 *> (this),
|
---|
[16118] | 2431 | BstrFmt (tr ("Merging hard disk '%s' to '%s'"),
|
---|
[13580] | 2432 | name().raw(), aChain->target()->name().raw()),
|
---|
| 2433 | FALSE /* aCancelable */);
|
---|
| 2434 | CheckComRCReturnRC (rc);
|
---|
| 2435 | }
|
---|
| 2436 | }
|
---|
| 2437 |
|
---|
| 2438 | /* setup task object and thread to carry out the operation
|
---|
| 2439 | * asynchronously */
|
---|
| 2440 |
|
---|
| 2441 | std::auto_ptr <Task> task (new Task (this, progress, Task::Merge));
|
---|
| 2442 | AssertComRCReturnRC (task->autoCaller.rc());
|
---|
| 2443 |
|
---|
| 2444 | task->setData (aChain);
|
---|
| 2445 |
|
---|
| 2446 | /* Note: task owns aChain (will delete it when not needed) in all cases
|
---|
| 2447 | * except when @a aWait is @c true and runNow() fails -- in this case
|
---|
| 2448 | * aChain will be left away because cancelMergeTo() will be applied by the
|
---|
| 2449 | * caller on it as it is required in the documentation above */
|
---|
| 2450 |
|
---|
| 2451 | if (aWait)
|
---|
| 2452 | {
|
---|
| 2453 | rc = task->runNow();
|
---|
| 2454 | }
|
---|
| 2455 | else
|
---|
| 2456 | {
|
---|
| 2457 | rc = task->startThread();
|
---|
| 2458 | CheckComRCReturnRC (rc);
|
---|
| 2459 | }
|
---|
| 2460 |
|
---|
| 2461 | /* task is now owned (or already deleted) by taskThread() so release it */
|
---|
| 2462 | task.release();
|
---|
| 2463 |
|
---|
| 2464 | if (aProgress != NULL)
|
---|
| 2465 | {
|
---|
| 2466 | /* return progress to the caller */
|
---|
| 2467 | *aProgress = progress;
|
---|
| 2468 | }
|
---|
| 2469 |
|
---|
| 2470 | return rc;
|
---|
| 2471 | }
|
---|
| 2472 |
|
---|
| 2473 | /**
|
---|
| 2474 | * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not called
|
---|
| 2475 | * or fails. Frees memory occupied by @a aChain.
|
---|
| 2476 | *
|
---|
| 2477 | * @param aChain Merge chain created by #prepareMergeTo().
|
---|
| 2478 | *
|
---|
| 2479 | * @note Locks the hard disks from the chain for writing.
|
---|
| 2480 | */
|
---|
| 2481 | void HardDisk2::cancelMergeTo (MergeChain *aChain)
|
---|
| 2482 | {
|
---|
| 2483 | AutoCaller autoCaller (this);
|
---|
| 2484 | AssertComRCReturnVoid (autoCaller.rc());
|
---|
| 2485 |
|
---|
| 2486 | AssertReturnVoid (aChain != NULL);
|
---|
| 2487 |
|
---|
| 2488 | /* the destructor will do the thing */
|
---|
| 2489 | delete aChain;
|
---|
| 2490 | }
|
---|
| 2491 |
|
---|
| 2492 | // private methods
|
---|
| 2493 | ////////////////////////////////////////////////////////////////////////////////
|
---|
| 2494 |
|
---|
| 2495 | /**
|
---|
| 2496 | * Sets the value of m.location and calculates the value of m.locationFull.
|
---|
| 2497 | *
|
---|
| 2498 | * Reimplements MediumBase::setLocation() to specially treat non-FS-path
|
---|
| 2499 | * locations and to prepend the default hard disk folder if the given location
|
---|
| 2500 | * string does not contain any path information at all.
|
---|
| 2501 | *
|
---|
| 2502 | * Also, if the specified location is a file path that ends with '/' then the
|
---|
| 2503 | * file name part will be generated by this method automatically in the format
|
---|
| 2504 | * '{<uuid>}.<ext>' where <uuid> is a fresh UUID that this method will generate
|
---|
| 2505 | * and assign to this medium, and <ext> is the default extension for this
|
---|
| 2506 | * medium's storage format. Note that this procedure requires the media state to
|
---|
| 2507 | * be NotCreated and will return a faiulre otherwise.
|
---|
| 2508 | *
|
---|
| 2509 | * @param aLocation Location of the storage unit. If the locaiton is a FS-path,
|
---|
| 2510 | * then it can be relative to the VirtualBox home directory.
|
---|
| 2511 | *
|
---|
| 2512 | * @note Must be called from under this object's write lock.
|
---|
| 2513 | */
|
---|
[15051] | 2514 | HRESULT HardDisk2::setLocation (CBSTR aLocation)
|
---|
[13580] | 2515 | {
|
---|
[14783] | 2516 | /// @todo so far, we assert but later it makes sense to support null
|
---|
| 2517 | /// locations for hard disks that are not yet created fail to create a
|
---|
| 2518 | /// storage unit instead
|
---|
| 2519 | CheckComArgStrNotEmptyOrNull (aLocation);
|
---|
[13580] | 2520 |
|
---|
[14272] | 2521 | AutoCaller autoCaller (this);
|
---|
| 2522 | AssertComRCReturnRC (autoCaller.rc());
|
---|
[13580] | 2523 |
|
---|
[14272] | 2524 | /* formatObj may be null only when initializing from an existing path and
|
---|
| 2525 | * no format is known yet */
|
---|
| 2526 | AssertReturn ((!mm.format.isNull() && !mm.formatObj.isNull()) ||
|
---|
| 2527 | (autoCaller.state() == InInit &&
|
---|
| 2528 | m.state != MediaState_NotCreated && m.id.isEmpty() &&
|
---|
| 2529 | mm.format.isNull() && mm.formatObj.isNull()),
|
---|
| 2530 | E_FAIL);
|
---|
[13580] | 2531 |
|
---|
[14931] | 2532 | /* are we dealing with a new hard disk constructed using the existing
|
---|
| 2533 | * location? */
|
---|
| 2534 | bool isImport = mm.format.isNull();
|
---|
[13580] | 2535 |
|
---|
[14931] | 2536 | if (isImport ||
|
---|
[14272] | 2537 | (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File))
|
---|
[13580] | 2538 | {
|
---|
[14272] | 2539 | Guid id;
|
---|
[13580] | 2540 |
|
---|
[14272] | 2541 | Utf8Str location (aLocation);
|
---|
| 2542 |
|
---|
[13580] | 2543 | if (m.state == MediaState_NotCreated)
|
---|
| 2544 | {
|
---|
[14272] | 2545 | /* must be a file (formatObj must be already known) */
|
---|
| 2546 | Assert (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File);
|
---|
[13580] | 2547 |
|
---|
[14272] | 2548 | if (RTPathFilename (location) == NULL)
|
---|
| 2549 | {
|
---|
| 2550 | /* no file name is given (either an empty string or ends with a
|
---|
| 2551 | * slash), generate a new UUID + file name if the state allows
|
---|
| 2552 | * this */
|
---|
| 2553 |
|
---|
| 2554 | ComAssertMsgRet (!mm.formatObj->fileExtensions().empty(),
|
---|
| 2555 | ("Must be at least one extension if it is "
|
---|
| 2556 | "HardDiskFormatCapabilities_File\n"),
|
---|
| 2557 | E_FAIL);
|
---|
| 2558 |
|
---|
| 2559 | Bstr ext = mm.formatObj->fileExtensions().front();
|
---|
| 2560 | ComAssertMsgRet (!ext.isEmpty(),
|
---|
| 2561 | ("Default extension must not be empty\n"),
|
---|
| 2562 | E_FAIL);
|
---|
| 2563 |
|
---|
| 2564 | id.create();
|
---|
| 2565 |
|
---|
| 2566 | location = Utf8StrFmt ("%s{%RTuuid}.%ls",
|
---|
| 2567 | location.raw(), id.raw(), ext.raw());
|
---|
| 2568 | }
|
---|
[13580] | 2569 | }
|
---|
[14272] | 2570 |
|
---|
| 2571 | /* append the default folder if no path is given */
|
---|
| 2572 | if (!RTPathHavePath (location))
|
---|
[13580] | 2573 | {
|
---|
[14272] | 2574 | AutoReadLock propsLock (mVirtualBox->systemProperties());
|
---|
| 2575 | location = Utf8StrFmt ("%ls%c%s",
|
---|
| 2576 | mVirtualBox->systemProperties()->defaultHardDiskFolder().raw(),
|
---|
| 2577 | RTPATH_DELIMITER,
|
---|
[13580] | 2578 | location.raw());
|
---|
| 2579 | }
|
---|
[14272] | 2580 |
|
---|
| 2581 | /* get the full file name */
|
---|
| 2582 | Utf8Str locationFull;
|
---|
| 2583 | int vrc = mVirtualBox->calculateFullPath (location, locationFull);
|
---|
| 2584 | if (RT_FAILURE (vrc))
|
---|
[14866] | 2585 | return setError (VBOX_E_FILE_ERROR,
|
---|
[14272] | 2586 | tr ("Invalid hard disk storage file location '%s' (%Rrc)"),
|
---|
| 2587 | location.raw(), vrc);
|
---|
| 2588 |
|
---|
[14931] | 2589 | /* detect the backend from the storage unit if importing */
|
---|
| 2590 | if (isImport)
|
---|
[14272] | 2591 | {
|
---|
| 2592 | char *backendName = NULL;
|
---|
| 2593 |
|
---|
| 2594 | /* is it a file? */
|
---|
| 2595 | {
|
---|
| 2596 | RTFILE file;
|
---|
| 2597 | vrc = RTFileOpen (&file, locationFull, RTFILE_O_READ);
|
---|
| 2598 | if (RT_SUCCESS (vrc))
|
---|
| 2599 | RTFileClose (file);
|
---|
| 2600 | }
|
---|
| 2601 | if (RT_SUCCESS (vrc))
|
---|
| 2602 | {
|
---|
| 2603 | vrc = VDGetFormat (locationFull, &backendName);
|
---|
| 2604 | }
|
---|
| 2605 | else if (vrc != VERR_FILE_NOT_FOUND && vrc != VERR_PATH_NOT_FOUND)
|
---|
| 2606 | {
|
---|
| 2607 | /* assume it's not a file, restore the original location */
|
---|
| 2608 | location = locationFull = aLocation;
|
---|
| 2609 | vrc = VDGetFormat (locationFull, &backendName);
|
---|
| 2610 | }
|
---|
| 2611 |
|
---|
| 2612 | if (RT_FAILURE (vrc))
|
---|
[14866] | 2613 | return setError (VBOX_E_IPRT_ERROR,
|
---|
[14272] | 2614 | tr ("Could not get the storage format of the hard disk "
|
---|
| 2615 | "'%s' (%Rrc)"), locationFull.raw(), vrc);
|
---|
| 2616 |
|
---|
| 2617 | ComAssertRet (backendName != NULL && *backendName != '\0', E_FAIL);
|
---|
| 2618 |
|
---|
| 2619 | HRESULT rc = setFormat (Bstr (backendName));
|
---|
| 2620 | RTStrFree (backendName);
|
---|
| 2621 |
|
---|
| 2622 | /* setFormat() must not fail since we've just used the backend so
|
---|
| 2623 | * the format object must be there */
|
---|
| 2624 | AssertComRCReturnRC (rc);
|
---|
| 2625 | }
|
---|
| 2626 |
|
---|
| 2627 | /* is it still a file? */
|
---|
| 2628 | if (mm.formatObj->capabilities() & HardDiskFormatCapabilities_File)
|
---|
| 2629 | {
|
---|
| 2630 | m.location = location;
|
---|
| 2631 | m.locationFull = locationFull;
|
---|
| 2632 |
|
---|
[14931] | 2633 | if (m.state == MediaState_NotCreated)
|
---|
| 2634 | {
|
---|
| 2635 | /* assign a new UUID (this UUID will be used when calling
|
---|
| 2636 | * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
|
---|
| 2637 | * also do that if we didn't generate it to make sure it is
|
---|
| 2638 | * either generated by us or reset to null */
|
---|
[14272] | 2639 | unconst (m.id) = id;
|
---|
[14931] | 2640 | }
|
---|
[14272] | 2641 | }
|
---|
| 2642 | else
|
---|
| 2643 | {
|
---|
| 2644 | m.location = locationFull;
|
---|
| 2645 | m.locationFull = locationFull;
|
---|
| 2646 | }
|
---|
[13580] | 2647 | }
|
---|
[14272] | 2648 | else
|
---|
| 2649 | {
|
---|
| 2650 | m.location = aLocation;
|
---|
| 2651 | m.locationFull = aLocation;
|
---|
| 2652 | }
|
---|
[13580] | 2653 |
|
---|
[14272] | 2654 | return S_OK;
|
---|
| 2655 | }
|
---|
| 2656 |
|
---|
| 2657 | /**
|
---|
| 2658 | * Checks that the format ID is valid and sets it on success.
|
---|
| 2659 | *
|
---|
| 2660 | * Note that this method will caller-reference the format object on success!
|
---|
| 2661 | * This reference must be released somewhere to let the HardDiskFormat object be
|
---|
| 2662 | * uninitialized.
|
---|
| 2663 | *
|
---|
| 2664 | * @note Must be called from under this object's write lock.
|
---|
| 2665 | */
|
---|
[15051] | 2666 | HRESULT HardDisk2::setFormat (CBSTR aFormat)
|
---|
[14272] | 2667 | {
|
---|
| 2668 | /* get the format object first */
|
---|
[13580] | 2669 | {
|
---|
| 2670 | AutoReadLock propsLock (mVirtualBox->systemProperties());
|
---|
| 2671 |
|
---|
[14272] | 2672 | unconst (mm.formatObj)
|
---|
| 2673 | = mVirtualBox->systemProperties()->hardDiskFormat (aFormat);
|
---|
| 2674 | if (mm.formatObj.isNull())
|
---|
[14866] | 2675 | return setError (E_INVALIDARG,
|
---|
[14272] | 2676 | tr ("Invalid hard disk storage format '%ls'"), aFormat);
|
---|
[13580] | 2677 |
|
---|
[14272] | 2678 | /* reference the format permanently to prevent its unexpected
|
---|
| 2679 | * uninitialization */
|
---|
| 2680 | HRESULT rc = mm.formatObj->addCaller();
|
---|
| 2681 | AssertComRCReturnRC (rc);
|
---|
[14596] | 2682 |
|
---|
| 2683 | /* get properties (preinsert them as keys in the map). Note that the
|
---|
| 2684 | * map doesn't grow over the object life time since the set of
|
---|
| 2685 | * properties is meant to be constant. */
|
---|
| 2686 |
|
---|
[14698] | 2687 | Assert (mm.properties.empty());
|
---|
[14596] | 2688 |
|
---|
| 2689 | for (HardDiskFormat::PropertyList::const_iterator it =
|
---|
| 2690 | mm.formatObj->properties().begin();
|
---|
| 2691 | it != mm.formatObj->properties().end();
|
---|
| 2692 | ++ it)
|
---|
| 2693 | {
|
---|
| 2694 | mm.properties.insert (std::make_pair (it->name, Bstr::Null));
|
---|
| 2695 | }
|
---|
[14272] | 2696 | }
|
---|
[13580] | 2697 |
|
---|
[14272] | 2698 | unconst (mm.format) = aFormat;
|
---|
[13580] | 2699 |
|
---|
| 2700 | return S_OK;
|
---|
| 2701 | }
|
---|
[14272] | 2702 |
|
---|
[13580] | 2703 | /**
|
---|
| 2704 | * Queries information from the image file.
|
---|
| 2705 | *
|
---|
| 2706 | * As a result of this call, the accessibility state and data members such as
|
---|
| 2707 | * size and description will be updated with the current information.
|
---|
| 2708 | *
|
---|
| 2709 | * Reimplements MediumBase::queryInfo() to query hard disk information using the
|
---|
| 2710 | * VD backend interface.
|
---|
| 2711 | *
|
---|
| 2712 | * @note This method may block during a system I/O call that checks storage
|
---|
| 2713 | * accessibility.
|
---|
| 2714 | *
|
---|
[14143] | 2715 | * @note Locks treeLock() for reading and writing (for new diff media checked
|
---|
| 2716 | * for the first time). Locks mParent for reading. Locks this object for
|
---|
| 2717 | * writing.
|
---|
[13580] | 2718 | */
|
---|
| 2719 | HRESULT HardDisk2::queryInfo()
|
---|
| 2720 | {
|
---|
| 2721 | AutoWriteLock alock (this);
|
---|
| 2722 |
|
---|
| 2723 | AssertReturn (m.state == MediaState_Created ||
|
---|
| 2724 | m.state == MediaState_Inaccessible ||
|
---|
| 2725 | m.state == MediaState_LockedRead ||
|
---|
| 2726 | m.state == MediaState_LockedWrite,
|
---|
| 2727 | E_FAIL);
|
---|
| 2728 |
|
---|
| 2729 | HRESULT rc = S_OK;
|
---|
| 2730 |
|
---|
| 2731 | int vrc = VINF_SUCCESS;
|
---|
| 2732 |
|
---|
| 2733 | /* check if a blocking queryInfo() call is in progress on some other thread,
|
---|
| 2734 | * and wait for it to finish if so instead of querying data ourselves */
|
---|
| 2735 | if (m.queryInfoSem != NIL_RTSEMEVENTMULTI)
|
---|
| 2736 | {
|
---|
| 2737 | Assert (m.state == MediaState_LockedRead);
|
---|
| 2738 |
|
---|
| 2739 | ++ m.queryInfoCallers;
|
---|
| 2740 | alock.leave();
|
---|
| 2741 |
|
---|
| 2742 | vrc = RTSemEventMultiWait (m.queryInfoSem, RT_INDEFINITE_WAIT);
|
---|
| 2743 |
|
---|
| 2744 | alock.enter();
|
---|
| 2745 | -- m.queryInfoCallers;
|
---|
| 2746 |
|
---|
| 2747 | if (m.queryInfoCallers == 0)
|
---|
| 2748 | {
|
---|
| 2749 | /* last waiting caller deletes the semaphore */
|
---|
| 2750 | RTSemEventMultiDestroy (m.queryInfoSem);
|
---|
| 2751 | m.queryInfoSem = NIL_RTSEMEVENTMULTI;
|
---|
| 2752 | }
|
---|
| 2753 |
|
---|
| 2754 | AssertRC (vrc);
|
---|
| 2755 |
|
---|
| 2756 | return S_OK;
|
---|
| 2757 | }
|
---|
| 2758 |
|
---|
| 2759 | /* lazily create a semaphore for possible callers */
|
---|
| 2760 | vrc = RTSemEventMultiCreate (&m.queryInfoSem);
|
---|
| 2761 | ComAssertRCRet (vrc, E_FAIL);
|
---|
| 2762 |
|
---|
| 2763 | bool tempStateSet = false;
|
---|
| 2764 | if (m.state != MediaState_LockedRead &&
|
---|
| 2765 | m.state != MediaState_LockedWrite)
|
---|
| 2766 | {
|
---|
| 2767 | /* Cause other methods to prevent any modifications before leaving the
|
---|
| 2768 | * lock. Note that clients will never see this temporary state change
|
---|
| 2769 | * since any COMGETTER(State) is (or will be) blocked until we finish
|
---|
| 2770 | * and restore the actual state. */
|
---|
| 2771 | m.state = MediaState_LockedRead;
|
---|
| 2772 | tempStateSet = true;
|
---|
| 2773 | }
|
---|
| 2774 |
|
---|
| 2775 | /* leave the lock before a blocking operation */
|
---|
| 2776 | alock.leave();
|
---|
| 2777 |
|
---|
| 2778 | bool success = false;
|
---|
| 2779 | Utf8Str lastAccessError;
|
---|
| 2780 |
|
---|
| 2781 | try
|
---|
| 2782 | {
|
---|
| 2783 | Utf8Str location (m.locationFull);
|
---|
| 2784 |
|
---|
[14931] | 2785 | /* are we dealing with a new hard disk constructed using the existing
|
---|
| 2786 | * location? */
|
---|
| 2787 | bool isImport = m.id.isEmpty();
|
---|
[13580] | 2788 |
|
---|
| 2789 | PVBOXHDD hdd;
|
---|
| 2790 | vrc = VDCreate (mm.vdDiskIfaces, &hdd);
|
---|
| 2791 | ComAssertRCThrow (vrc, E_FAIL);
|
---|
| 2792 |
|
---|
| 2793 | try
|
---|
| 2794 | {
|
---|
[14272] | 2795 | unsigned flags = VD_OPEN_FLAGS_INFO;
|
---|
| 2796 |
|
---|
| 2797 | /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
|
---|
| 2798 | * hard disks because that would prevent necessary modifications
|
---|
| 2799 | * when opening hard disks of some third-party formats for the first
|
---|
| 2800 | * time in VirtualBox (such as VMDK for which VDOpen() needs to
|
---|
| 2801 | * generate an UUID if it is missing) */
|
---|
[14931] | 2802 | if (!isImport)
|
---|
[14272] | 2803 | flags |= VD_OPEN_FLAGS_READONLY;
|
---|
| 2804 |
|
---|
[14929] | 2805 | vrc = VDOpen (hdd, Utf8Str (mm.format), location, flags,
|
---|
| 2806 | mm.vdDiskIfaces);
|
---|
[13580] | 2807 | if (RT_FAILURE (vrc))
|
---|
| 2808 | {
|
---|
| 2809 | lastAccessError = Utf8StrFmt (
|
---|
| 2810 | tr ("Could not open the hard disk '%ls'%s"),
|
---|
| 2811 | m.locationFull.raw(), vdError (vrc).raw());
|
---|
| 2812 | throw S_OK;
|
---|
| 2813 | }
|
---|
| 2814 |
|
---|
[14931] | 2815 | if (mm.formatObj->capabilities() & HardDiskFormatCapabilities_Uuid)
|
---|
| 2816 | {
|
---|
| 2817 | /* check the UUID */
|
---|
| 2818 | RTUUID uuid;
|
---|
| 2819 | vrc = VDGetUuid (hdd, 0, &uuid);
|
---|
| 2820 | ComAssertRCThrow (vrc, E_FAIL);
|
---|
[13580] | 2821 |
|
---|
[14931] | 2822 | if (isImport)
|
---|
| 2823 | {
|
---|
| 2824 | unconst (m.id) = uuid;
|
---|
| 2825 | }
|
---|
| 2826 | else
|
---|
| 2827 | {
|
---|
| 2828 | Assert (!m.id.isEmpty());
|
---|
| 2829 |
|
---|
| 2830 | if (m.id != uuid)
|
---|
| 2831 | {
|
---|
| 2832 | lastAccessError = Utf8StrFmt (
|
---|
| 2833 | tr ("UUID {%RTuuid} of the hard disk '%ls' does "
|
---|
| 2834 | "not match the value {%RTuuid} stored in the "
|
---|
| 2835 | "media registry ('%ls')"),
|
---|
| 2836 | &uuid, m.locationFull.raw(), m.id.raw(),
|
---|
| 2837 | mVirtualBox->settingsFileName().raw());
|
---|
| 2838 | throw S_OK;
|
---|
| 2839 | }
|
---|
| 2840 | }
|
---|
[13580] | 2841 | }
|
---|
| 2842 | else
|
---|
| 2843 | {
|
---|
[14931] | 2844 | /* the backend does not support storing UUIDs within the
|
---|
| 2845 | * underlying storage so use what we store in XML */
|
---|
| 2846 |
|
---|
| 2847 | /* generate an UUID for an imported UUID-less hard disk */
|
---|
| 2848 | if (isImport)
|
---|
| 2849 | unconst (m.id).create();
|
---|
[13580] | 2850 | }
|
---|
| 2851 |
|
---|
| 2852 | /* check the type */
|
---|
| 2853 | VDIMAGETYPE type;
|
---|
| 2854 | vrc = VDGetImageType (hdd, 0, &type);
|
---|
| 2855 | ComAssertRCThrow (vrc, E_FAIL);
|
---|
| 2856 |
|
---|
| 2857 | if (type == VD_IMAGE_TYPE_DIFF)
|
---|
| 2858 | {
|
---|
[14931] | 2859 | RTUUID parentId;
|
---|
| 2860 | vrc = VDGetParentUuid (hdd, 0, &parentId);
|
---|
[13580] | 2861 | ComAssertRCThrow (vrc, E_FAIL);
|
---|
| 2862 |
|
---|
[14931] | 2863 | if (isImport)
|
---|
[13580] | 2864 | {
|
---|
| 2865 | /* the parent must be known to us. Note that we freely
|
---|
| 2866 | * call locking methods of mVirtualBox and parent from the
|
---|
| 2867 | * write lock (breaking the {parent,child} lock order)
|
---|
| 2868 | * because there may be no concurrent access to the just
|
---|
| 2869 | * opened hard disk on ther threads yet (and init() will
|
---|
| 2870 | * fail if this method reporst MediaState_Inaccessible) */
|
---|
| 2871 |
|
---|
[14931] | 2872 | Guid id = parentId;
|
---|
[13580] | 2873 | ComObjPtr <HardDisk2> parent;
|
---|
| 2874 | rc = mVirtualBox->findHardDisk2 (&id, NULL,
|
---|
| 2875 | false /* aSetError */,
|
---|
| 2876 | &parent);
|
---|
| 2877 | if (FAILED (rc))
|
---|
| 2878 | {
|
---|
| 2879 | lastAccessError = Utf8StrFmt (
|
---|
[13842] | 2880 | tr ("Parent hard disk with UUID {%RTuuid} of the "
|
---|
[13580] | 2881 | "hard disk '%ls' is not found in the media "
|
---|
| 2882 | "registry ('%ls')"),
|
---|
[14931] | 2883 | &parentId, m.locationFull.raw(),
|
---|
[13580] | 2884 | mVirtualBox->settingsFileName().raw());
|
---|
| 2885 | throw S_OK;
|
---|
| 2886 | }
|
---|
| 2887 |
|
---|
[14143] | 2888 | /* deassociate from VirtualBox, associate with parent */
|
---|
| 2889 |
|
---|
| 2890 | mVirtualBox->removeDependentChild (this);
|
---|
| 2891 |
|
---|
| 2892 | /* we set mParent & children() */
|
---|
| 2893 | AutoWriteLock treeLock (this->treeLock());
|
---|
| 2894 |
|
---|
[13580] | 2895 | Assert (mParent.isNull());
|
---|
| 2896 | mParent = parent;
|
---|
| 2897 | mParent->addDependentChild (this);
|
---|
| 2898 | }
|
---|
| 2899 | else
|
---|
| 2900 | {
|
---|
[14143] | 2901 | /* we access mParent */
|
---|
| 2902 | AutoReadLock treeLock (this->treeLock());
|
---|
| 2903 |
|
---|
[13580] | 2904 | /* check that parent UUIDs match. Note that there's no need
|
---|
| 2905 | * for the parent's AutoCaller (our lifetime is bound to
|
---|
| 2906 | * it) */
|
---|
| 2907 |
|
---|
| 2908 | if (mParent.isNull())
|
---|
| 2909 | {
|
---|
| 2910 | lastAccessError = Utf8StrFmt (
|
---|
| 2911 | tr ("Hard disk '%ls' is differencing but it is not "
|
---|
| 2912 | "associated with any parent hard disk in the "
|
---|
| 2913 | "media registry ('%ls')"),
|
---|
| 2914 | m.locationFull.raw(),
|
---|
| 2915 | mVirtualBox->settingsFileName().raw());
|
---|
| 2916 | throw S_OK;
|
---|
| 2917 | }
|
---|
| 2918 |
|
---|
| 2919 | AutoReadLock parentLock (mParent);
|
---|
| 2920 | if (mParent->state() != MediaState_Inaccessible &&
|
---|
[14931] | 2921 | mParent->id() != parentId)
|
---|
[13580] | 2922 | {
|
---|
| 2923 | lastAccessError = Utf8StrFmt (
|
---|
[13842] | 2924 | tr ("Parent UUID {%RTuuid} of the hard disk '%ls' "
|
---|
| 2925 | "does not match UUID {%RTuuid} of its parent "
|
---|
[13580] | 2926 | "hard disk stored in the media registry ('%ls')"),
|
---|
[14931] | 2927 | &parentId, m.locationFull.raw(),
|
---|
[13580] | 2928 | mParent->id().raw(),
|
---|
| 2929 | mVirtualBox->settingsFileName().raw());
|
---|
| 2930 | throw S_OK;
|
---|
| 2931 | }
|
---|
| 2932 |
|
---|
| 2933 | /// @todo NEWMEDIA what to do if the parent is not
|
---|
| 2934 | /// accessible while the diff is? Probably, nothing. The
|
---|
| 2935 | /// real code will detect the mismatch anyway.
|
---|
| 2936 | }
|
---|
| 2937 | }
|
---|
| 2938 |
|
---|
| 2939 | m.size = VDGetFileSize (hdd, 0);
|
---|
| 2940 | mm.logicalSize = VDGetSize (hdd, 0) / _1M;
|
---|
| 2941 |
|
---|
| 2942 | success = true;
|
---|
| 2943 | }
|
---|
| 2944 | catch (HRESULT aRC)
|
---|
| 2945 | {
|
---|
| 2946 | rc = aRC;
|
---|
| 2947 | }
|
---|
| 2948 |
|
---|
| 2949 | VDDestroy (hdd);
|
---|
| 2950 |
|
---|
| 2951 | }
|
---|
| 2952 | catch (HRESULT aRC)
|
---|
| 2953 | {
|
---|
| 2954 | rc = aRC;
|
---|
| 2955 | }
|
---|
| 2956 |
|
---|
| 2957 | alock.enter();
|
---|
| 2958 |
|
---|
[15215] | 2959 | if (success)
|
---|
| 2960 | m.lastAccessError.setNull();
|
---|
| 2961 | else
|
---|
| 2962 | {
|
---|
| 2963 | m.lastAccessError = lastAccessError;
|
---|
| 2964 | LogWarningFunc (("'%ls' is not accessible (error='%ls', "
|
---|
| 2965 | "rc=%Rhrc, vrc=%Rrc)\n",
|
---|
| 2966 | m.locationFull.raw(), m.lastAccessError.raw(),
|
---|
| 2967 | rc, vrc));
|
---|
| 2968 | }
|
---|
| 2969 |
|
---|
[13580] | 2970 | /* inform other callers if there are any */
|
---|
| 2971 | if (m.queryInfoCallers > 0)
|
---|
| 2972 | {
|
---|
| 2973 | RTSemEventMultiSignal (m.queryInfoSem);
|
---|
| 2974 | }
|
---|
| 2975 | else
|
---|
| 2976 | {
|
---|
| 2977 | /* delete the semaphore ourselves */
|
---|
| 2978 | RTSemEventMultiDestroy (m.queryInfoSem);
|
---|
| 2979 | m.queryInfoSem = NIL_RTSEMEVENTMULTI;
|
---|
| 2980 | }
|
---|
| 2981 |
|
---|
[15215] | 2982 | if (tempStateSet)
|
---|
[13580] | 2983 | {
|
---|
[15215] | 2984 | /* Set the proper state according to the result of the check */
|
---|
| 2985 | if (success)
|
---|
[13580] | 2986 | m.state = MediaState_Created;
|
---|
[15215] | 2987 | else
|
---|
| 2988 | m.state = MediaState_Inaccessible;
|
---|
[13580] | 2989 | }
|
---|
| 2990 | else
|
---|
| 2991 | {
|
---|
[15215] | 2992 | /* we're locked, use a special field to store the result */
|
---|
| 2993 | m.accessibleInLock = success;
|
---|
[13580] | 2994 | }
|
---|
| 2995 |
|
---|
| 2996 | return rc;
|
---|
| 2997 | }
|
---|
| 2998 |
|
---|
| 2999 | /**
|
---|
| 3000 | * @note Called from this object's AutoMayUninitSpan and from under mVirtualBox
|
---|
| 3001 | * write lock.
|
---|
[14143] | 3002 | *
|
---|
| 3003 | * @note Locks treeLock() for reading.
|
---|
[13580] | 3004 | */
|
---|
| 3005 | HRESULT HardDisk2::canClose()
|
---|
| 3006 | {
|
---|
[14143] | 3007 | /* we access children */
|
---|
| 3008 | AutoReadLock treeLock (this->treeLock());
|
---|
| 3009 |
|
---|
[13580] | 3010 | if (children().size() != 0)
|
---|
| 3011 | return setError (E_FAIL,
|
---|
| 3012 | tr ("Hard disk '%ls' has %d child hard disks"),
|
---|
| 3013 | children().size());
|
---|
| 3014 |
|
---|
| 3015 | return S_OK;
|
---|
| 3016 | }
|
---|
| 3017 |
|
---|
| 3018 | /**
|
---|
| 3019 | * @note Called from within this object's AutoWriteLock.
|
---|
| 3020 | */
|
---|
| 3021 | HRESULT HardDisk2::canAttach (const Guid &aMachineId,
|
---|
| 3022 | const Guid &aSnapshotId)
|
---|
| 3023 | {
|
---|
| 3024 | if (mm.numCreateDiffTasks > 0)
|
---|
| 3025 | return setError (E_FAIL,
|
---|
| 3026 | tr ("One or more differencing child hard disks are "
|
---|
| 3027 | "being created for the hard disk '%ls' (%u)"),
|
---|
| 3028 | m.locationFull.raw(), mm.numCreateDiffTasks);
|
---|
| 3029 |
|
---|
| 3030 | return S_OK;
|
---|
| 3031 | }
|
---|
| 3032 |
|
---|
| 3033 | /**
|
---|
| 3034 | * @note Called from within this object's AutoMayUninitSpan (or AutoCaller) and
|
---|
| 3035 | * from under mVirtualBox write lock.
|
---|
| 3036 | *
|
---|
[14143] | 3037 | * @note Locks treeLock() for writing.
|
---|
[13580] | 3038 | */
|
---|
| 3039 | HRESULT HardDisk2::unregisterWithVirtualBox()
|
---|
| 3040 | {
|
---|
| 3041 | /* Note that we need to de-associate ourselves from the parent to let
|
---|
| 3042 | * unregisterHardDisk2() properly save the registry */
|
---|
| 3043 |
|
---|
[14143] | 3044 | /* we modify mParent and access children */
|
---|
| 3045 | AutoWriteLock treeLock (this->treeLock());
|
---|
| 3046 |
|
---|
[13580] | 3047 | const ComObjPtr <HardDisk2, ComWeakRef> parent = mParent;
|
---|
| 3048 |
|
---|
| 3049 | AssertReturn (children().size() == 0, E_FAIL);
|
---|
| 3050 |
|
---|
| 3051 | if (!mParent.isNull())
|
---|
| 3052 | {
|
---|
| 3053 | /* deassociate from the parent, associate with VirtualBox */
|
---|
| 3054 | mVirtualBox->addDependentChild (this);
|
---|
| 3055 | mParent->removeDependentChild (this);
|
---|
| 3056 | mParent.setNull();
|
---|
| 3057 | }
|
---|
| 3058 |
|
---|
| 3059 | HRESULT rc = mVirtualBox->unregisterHardDisk2 (this);
|
---|
| 3060 |
|
---|
| 3061 | if (FAILED (rc))
|
---|
| 3062 | {
|
---|
| 3063 | if (!parent.isNull())
|
---|
| 3064 | {
|
---|
| 3065 | /* re-associate with the parent as we are still relatives in the
|
---|
| 3066 | * registry */
|
---|
| 3067 | mParent = parent;
|
---|
| 3068 | mParent->addDependentChild (this);
|
---|
| 3069 | mVirtualBox->removeDependentChild (this);
|
---|
| 3070 | }
|
---|
| 3071 | }
|
---|
| 3072 |
|
---|
| 3073 | return rc;
|
---|
| 3074 | }
|
---|
| 3075 |
|
---|
| 3076 | /**
|
---|
| 3077 | * Returns the last error message collected by the vdErrorCall callback and
|
---|
| 3078 | * resets it.
|
---|
| 3079 | *
|
---|
| 3080 | * The error message is returned prepended with a dot and a space, like this:
|
---|
| 3081 | * <code>
|
---|
[13837] | 3082 | * ". <error_text> (%Rrc)"
|
---|
[13580] | 3083 | * </code>
|
---|
[13837] | 3084 | * to make it easily appendable to a more general error message. The @c %Rrc
|
---|
[13580] | 3085 | * format string is given @a aVRC as an argument.
|
---|
| 3086 | *
|
---|
| 3087 | * If there is no last error message collected by vdErrorCall or if it is a
|
---|
| 3088 | * null or empty string, then this function returns the following text:
|
---|
| 3089 | * <code>
|
---|
[13837] | 3090 | * " (%Rrc)"
|
---|
[13580] | 3091 | * </code>
|
---|
| 3092 | *
|
---|
| 3093 | * @note Doesn't do any object locking; it is assumed that the caller makes sure
|
---|
| 3094 | * the callback isn't called by more than one thread at a time.
|
---|
| 3095 | *
|
---|
| 3096 | * @param aVRC VBox error code to use when no error message is provided.
|
---|
| 3097 | */
|
---|
| 3098 | Utf8Str HardDisk2::vdError (int aVRC)
|
---|
| 3099 | {
|
---|
| 3100 | Utf8Str error;
|
---|
| 3101 |
|
---|
| 3102 | if (mm.vdError.isEmpty())
|
---|
[13837] | 3103 | error = Utf8StrFmt (" (%Rrc)", aVRC);
|
---|
[13580] | 3104 | else
|
---|
[14929] | 3105 | error = Utf8StrFmt (".\n%s", mm.vdError.raw());
|
---|
[13580] | 3106 |
|
---|
| 3107 | mm.vdError.setNull();
|
---|
| 3108 |
|
---|
| 3109 | return error;
|
---|
| 3110 | }
|
---|
| 3111 |
|
---|
| 3112 | /**
|
---|
| 3113 | * Error message callback.
|
---|
| 3114 | *
|
---|
| 3115 | * Puts the reported error message to the mm.vdError field.
|
---|
| 3116 | *
|
---|
| 3117 | * @note Doesn't do any object locking; it is assumed that the caller makes sure
|
---|
| 3118 | * the callback isn't called by more than one thread at a time.
|
---|
| 3119 | *
|
---|
| 3120 | * @param pvUser The opaque data passed on container creation.
|
---|
| 3121 | * @param rc The VBox error code.
|
---|
| 3122 | * @param RT_SRC_POS_DECL Use RT_SRC_POS.
|
---|
| 3123 | * @param pszFormat Error message format string.
|
---|
| 3124 | * @param va Error message arguments.
|
---|
| 3125 | */
|
---|
| 3126 | /*static*/
|
---|
| 3127 | DECLCALLBACK(void) HardDisk2::vdErrorCall (void *pvUser, int rc, RT_SRC_POS_DECL,
|
---|
| 3128 | const char *pszFormat, va_list va)
|
---|
| 3129 | {
|
---|
| 3130 | HardDisk2 *that = static_cast <HardDisk2 *> (pvUser);
|
---|
| 3131 | AssertReturnVoid (that != NULL);
|
---|
| 3132 |
|
---|
[14929] | 3133 | if (that->mm.vdError.isEmpty())
|
---|
| 3134 | that->mm.vdError =
|
---|
| 3135 | Utf8StrFmt ("%s (%Rrc)", Utf8StrFmtVA (pszFormat, va).raw(), rc);
|
---|
| 3136 | else
|
---|
| 3137 | that->mm.vdError =
|
---|
| 3138 | Utf8StrFmt ("%s.\n%s (%Rrc)", that->mm.vdError.raw(),
|
---|
| 3139 | Utf8StrFmtVA (pszFormat, va).raw(), rc);
|
---|
[13580] | 3140 | }
|
---|
| 3141 |
|
---|
| 3142 | /**
|
---|
| 3143 | * PFNVMPROGRESS callback handler for Task operations.
|
---|
| 3144 | *
|
---|
| 3145 | * @param uPercent Completetion precentage (0-100).
|
---|
| 3146 | * @param pvUser Pointer to the Progress instance.
|
---|
| 3147 | */
|
---|
| 3148 | /*static*/
|
---|
| 3149 | DECLCALLBACK(int) HardDisk2::vdProgressCall (PVM /* pVM */, unsigned uPercent,
|
---|
| 3150 | void *pvUser)
|
---|
| 3151 | {
|
---|
| 3152 | HardDisk2 *that = static_cast <HardDisk2 *> (pvUser);
|
---|
| 3153 | AssertReturn (that != NULL, VERR_GENERAL_FAILURE);
|
---|
| 3154 |
|
---|
| 3155 | if (that->mm.vdProgress != NULL)
|
---|
| 3156 | {
|
---|
| 3157 | /* update the progress object, capping it at 99% as the final percent
|
---|
| 3158 | * is used for additional operations like setting the UUIDs and similar. */
|
---|
| 3159 | that->mm.vdProgress->notifyProgress (RT_MIN (uPercent, 99));
|
---|
| 3160 | }
|
---|
| 3161 |
|
---|
| 3162 | return VINF_SUCCESS;
|
---|
| 3163 | }
|
---|
| 3164 |
|
---|
[14929] | 3165 | /* static */
|
---|
| 3166 | DECLCALLBACK(bool) HardDisk2::vdConfigAreKeysValid (void *pvUser,
|
---|
| 3167 | const char *pszzValid)
|
---|
| 3168 | {
|
---|
| 3169 | HardDisk2 *that = static_cast <HardDisk2 *> (pvUser);
|
---|
[15044] | 3170 | AssertReturn (that != NULL, false);
|
---|
[14929] | 3171 |
|
---|
| 3172 | /* we always return true since the only keys we have are those found in
|
---|
| 3173 | * VDBACKENDINFO */
|
---|
| 3174 | return true;
|
---|
| 3175 | }
|
---|
| 3176 |
|
---|
| 3177 | /* static */
|
---|
| 3178 | DECLCALLBACK(int) HardDisk2::vdConfigQuerySize (void *pvUser, const char *pszName,
|
---|
| 3179 | size_t *pcbValue)
|
---|
| 3180 | {
|
---|
| 3181 | AssertReturn (VALID_PTR (pcbValue), VERR_INVALID_POINTER);
|
---|
| 3182 |
|
---|
| 3183 | HardDisk2 *that = static_cast <HardDisk2 *> (pvUser);
|
---|
| 3184 | AssertReturn (that != NULL, VERR_GENERAL_FAILURE);
|
---|
| 3185 |
|
---|
| 3186 | Data::PropertyMap::const_iterator it =
|
---|
| 3187 | that->mm.properties.find (Bstr (pszName));
|
---|
| 3188 | if (it == that->mm.properties.end())
|
---|
| 3189 | return VERR_CFGM_VALUE_NOT_FOUND;
|
---|
| 3190 |
|
---|
| 3191 | /* we interpret null values as "no value" in HardDisk2 */
|
---|
| 3192 | if (it->second.isNull())
|
---|
| 3193 | return VERR_CFGM_VALUE_NOT_FOUND;
|
---|
| 3194 |
|
---|
| 3195 | *pcbValue = it->second.length() + 1 /* include terminator */;
|
---|
| 3196 |
|
---|
| 3197 | return VINF_SUCCESS;
|
---|
| 3198 | }
|
---|
| 3199 |
|
---|
| 3200 | /* static */
|
---|
| 3201 | DECLCALLBACK(int) HardDisk2::vdConfigQuery (void *pvUser, const char *pszName,
|
---|
| 3202 | char *pszValue, size_t cchValue)
|
---|
| 3203 | {
|
---|
| 3204 | AssertReturn (VALID_PTR (pszValue), VERR_INVALID_POINTER);
|
---|
| 3205 |
|
---|
| 3206 | HardDisk2 *that = static_cast <HardDisk2 *> (pvUser);
|
---|
| 3207 | AssertReturn (that != NULL, VERR_GENERAL_FAILURE);
|
---|
| 3208 |
|
---|
| 3209 | Data::PropertyMap::const_iterator it =
|
---|
| 3210 | that->mm.properties.find (Bstr (pszName));
|
---|
| 3211 | if (it == that->mm.properties.end())
|
---|
| 3212 | return VERR_CFGM_VALUE_NOT_FOUND;
|
---|
| 3213 |
|
---|
| 3214 | Utf8Str value = it->second;
|
---|
| 3215 | if (value.length() >= cchValue)
|
---|
| 3216 | return VERR_CFGM_NOT_ENOUGH_SPACE;
|
---|
| 3217 |
|
---|
| 3218 | /* we interpret null values as "no value" in HardDisk2 */
|
---|
| 3219 | if (it->second.isNull())
|
---|
| 3220 | return VERR_CFGM_VALUE_NOT_FOUND;
|
---|
| 3221 |
|
---|
[15109] | 3222 | memcpy (pszValue, value, value.length() + 1);
|
---|
[14929] | 3223 |
|
---|
| 3224 | return VINF_SUCCESS;
|
---|
| 3225 | }
|
---|
| 3226 |
|
---|
[13580] | 3227 | /**
|
---|
| 3228 | * Thread function for time-consuming tasks.
|
---|
| 3229 | *
|
---|
| 3230 | * The Task structure passed to @a pvUser must be allocated using new and will
|
---|
| 3231 | * be freed by this method before it returns.
|
---|
| 3232 | *
|
---|
| 3233 | * @param pvUser Pointer to the Task instance.
|
---|
| 3234 | */
|
---|
| 3235 | /* static */
|
---|
| 3236 | DECLCALLBACK(int) HardDisk2::taskThread (RTTHREAD thread, void *pvUser)
|
---|
| 3237 | {
|
---|
| 3238 | std::auto_ptr <Task> task (static_cast <Task *> (pvUser));
|
---|
| 3239 | AssertReturn (task.get(), VERR_GENERAL_FAILURE);
|
---|
| 3240 |
|
---|
| 3241 | bool isAsync = thread != NIL_RTTHREAD;
|
---|
| 3242 |
|
---|
| 3243 | HardDisk2 *that = task->that;
|
---|
| 3244 |
|
---|
| 3245 | /// @todo ugly hack, fix ComAssert... later
|
---|
| 3246 | #define setError that->setError
|
---|
| 3247 |
|
---|
| 3248 | /* Note: no need in AutoCaller because Task does that */
|
---|
| 3249 |
|
---|
| 3250 | LogFlowFuncEnter();
|
---|
| 3251 | LogFlowFunc (("{%p}: operation=%d\n", that, task->operation));
|
---|
| 3252 |
|
---|
| 3253 | HRESULT rc = S_OK;
|
---|
| 3254 |
|
---|
| 3255 | switch (task->operation)
|
---|
| 3256 | {
|
---|
| 3257 | ////////////////////////////////////////////////////////////////////////
|
---|
| 3258 |
|
---|
| 3259 | case Task::CreateDynamic:
|
---|
| 3260 | case Task::CreateFixed:
|
---|
| 3261 | {
|
---|
| 3262 | /* The lock is also used as a signal from the task initiator (which
|
---|
| 3263 | * releases it only after RTThreadCreate()) that we can start the job */
|
---|
| 3264 | AutoWriteLock thatLock (that);
|
---|
| 3265 |
|
---|
| 3266 | /* these parameters we need after creation */
|
---|
[14123] | 3267 | uint64_t size = 0, logicalSize = 0;
|
---|
[13580] | 3268 |
|
---|
[15556] | 3269 | /* The object may request a specific UUID (through a special form of
|
---|
| 3270 | * the setLocation() argumet). Otherwise we have to generate it */
|
---|
[14931] | 3271 | Guid id = that->m.id;
|
---|
[15556] | 3272 | bool generateUuid = id.isEmpty();
|
---|
| 3273 | if (generateUuid)
|
---|
| 3274 | {
|
---|
| 3275 | id.create();
|
---|
| 3276 | /* VirtualBox::registerHardDisk2() will need UUID */
|
---|
| 3277 | unconst (that->m.id) = id;
|
---|
| 3278 | }
|
---|
[14931] | 3279 |
|
---|
[13580] | 3280 | try
|
---|
| 3281 | {
|
---|
| 3282 | PVBOXHDD hdd;
|
---|
| 3283 | int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
|
---|
| 3284 | ComAssertRCThrow (vrc, E_FAIL);
|
---|
| 3285 |
|
---|
| 3286 | Utf8Str format (that->mm.format);
|
---|
| 3287 | Utf8Str location (that->m.locationFull);
|
---|
[15044] | 3288 | uint64_t capabilities = that->mm.formatObj->capabilities();
|
---|
[13580] | 3289 |
|
---|
| 3290 | /* unlock before the potentially lengthy operation */
|
---|
| 3291 | Assert (that->m.state == MediaState_Creating);
|
---|
| 3292 | thatLock.leave();
|
---|
| 3293 |
|
---|
| 3294 | try
|
---|
| 3295 | {
|
---|
| 3296 | /* ensure the directory exists */
|
---|
| 3297 | rc = VirtualBox::ensureFilePathExists (location);
|
---|
| 3298 | CheckComRCThrowRC (rc);
|
---|
| 3299 |
|
---|
| 3300 | PDMMEDIAGEOMETRY geo = { 0 }; /* auto-detect */
|
---|
| 3301 |
|
---|
| 3302 | /* needed for vdProgressCallback */
|
---|
| 3303 | that->mm.vdProgress = task->progress;
|
---|
| 3304 |
|
---|
| 3305 | vrc = VDCreateBase (hdd, format, location,
|
---|
| 3306 | task->operation == Task::CreateDynamic ?
|
---|
| 3307 | VD_IMAGE_TYPE_NORMAL :
|
---|
| 3308 | VD_IMAGE_TYPE_FIXED,
|
---|
| 3309 | task->d.size * _1M,
|
---|
| 3310 | VD_IMAGE_FLAGS_NONE,
|
---|
[15556] | 3311 | NULL, &geo, &geo, id.raw(),
|
---|
[13580] | 3312 | VD_OPEN_FLAGS_NORMAL,
|
---|
| 3313 | NULL, that->mm.vdDiskIfaces);
|
---|
| 3314 |
|
---|
| 3315 | if (RT_FAILURE (vrc))
|
---|
| 3316 | {
|
---|
| 3317 | throw setError (E_FAIL,
|
---|
| 3318 | tr ("Could not create the hard disk storage "
|
---|
| 3319 | "unit '%s'%s"),
|
---|
| 3320 | location.raw(), that->vdError (vrc).raw());
|
---|
| 3321 | }
|
---|
| 3322 |
|
---|
| 3323 | size = VDGetFileSize (hdd, 0);
|
---|
| 3324 | logicalSize = VDGetSize (hdd, 0) / _1M;
|
---|
| 3325 | }
|
---|
| 3326 | catch (HRESULT aRC) { rc = aRC; }
|
---|
| 3327 |
|
---|
| 3328 | VDDestroy (hdd);
|
---|
| 3329 | }
|
---|
| 3330 | catch (HRESULT aRC) { rc = aRC; }
|
---|
| 3331 |
|
---|
| 3332 | if (SUCCEEDED (rc))
|
---|
| 3333 | {
|
---|
| 3334 | /* register with mVirtualBox as the last step and move to
|
---|
| 3335 | * Created state only on success (leaving an orphan file is
|
---|
| 3336 | * better than breaking media registry consistency) */
|
---|
| 3337 | rc = that->mVirtualBox->registerHardDisk2 (that);
|
---|
| 3338 | }
|
---|
| 3339 |
|
---|
[15556] | 3340 | thatLock.maybeEnter();
|
---|
| 3341 |
|
---|
| 3342 | if (SUCCEEDED (rc))
|
---|
[13580] | 3343 | {
|
---|
[15556] | 3344 | that->m.state = MediaState_Created;
|
---|
[13580] | 3345 |
|
---|
[15556] | 3346 | that->m.size = size;
|
---|
| 3347 | that->mm.logicalSize = logicalSize;
|
---|
| 3348 | }
|
---|
| 3349 | else
|
---|
| 3350 | {
|
---|
[13580] | 3351 | /* back to NotCreated on failiure */
|
---|
| 3352 | that->m.state = MediaState_NotCreated;
|
---|
[15556] | 3353 |
|
---|
| 3354 | /* reset UUID to prevent it from being reused next time */
|
---|
| 3355 | if (generateUuid)
|
---|
| 3356 | unconst (that->m.id).clear();
|
---|
[13580] | 3357 | }
|
---|
| 3358 |
|
---|
| 3359 | break;
|
---|
| 3360 | }
|
---|
| 3361 |
|
---|
| 3362 | ////////////////////////////////////////////////////////////////////////
|
---|
| 3363 |
|
---|
| 3364 | case Task::CreateDiff:
|
---|
| 3365 | {
|
---|
| 3366 | ComObjPtr <HardDisk2> &target = task->d.target;
|
---|
| 3367 |
|
---|
| 3368 | /* Lock both in {parent,child} order. The lock is also used as a
|
---|
| 3369 | * signal from the task initiator (which releases it only after
|
---|
| 3370 | * RTThreadCreate()) that we can start the job*/
|
---|
| 3371 | AutoMultiWriteLock2 thatLock (that, target);
|
---|
| 3372 |
|
---|
[14123] | 3373 | uint64_t size = 0, logicalSize = 0;
|
---|
[13580] | 3374 |
|
---|
[15556] | 3375 | /* The object may request a specific UUID (through a special form of
|
---|
[15591] | 3376 | * the setLocation() argument). Otherwise we have to generate it */
|
---|
[15556] | 3377 | Guid targetId = target->m.id;
|
---|
| 3378 | bool generateUuid = targetId.isEmpty();
|
---|
| 3379 | if (generateUuid)
|
---|
| 3380 | {
|
---|
| 3381 | targetId.create();
|
---|
| 3382 | /* VirtualBox::registerHardDisk2() will need UUID */
|
---|
| 3383 | unconst (target->m.id) = targetId;
|
---|
| 3384 | }
|
---|
| 3385 |
|
---|
[13580] | 3386 | try
|
---|
| 3387 | {
|
---|
| 3388 | PVBOXHDD hdd;
|
---|
| 3389 | int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
|
---|
| 3390 | ComAssertRCThrow (vrc, E_FAIL);
|
---|
| 3391 |
|
---|
[15596] | 3392 | Guid id = that->m.id;
|
---|
[13580] | 3393 | Utf8Str format (that->mm.format);
|
---|
| 3394 | Utf8Str location (that->m.locationFull);
|
---|
| 3395 |
|
---|
| 3396 | Utf8Str targetFormat (target->mm.format);
|
---|
| 3397 | Utf8Str targetLocation (target->m.locationFull);
|
---|
| 3398 |
|
---|
| 3399 | Assert (target->m.state == MediaState_Creating);
|
---|
| 3400 |
|
---|
| 3401 | /* Note: MediaState_LockedWrite is ok when taking an online
|
---|
| 3402 | * snapshot */
|
---|
| 3403 | Assert (that->m.state == MediaState_LockedRead ||
|
---|
| 3404 | that->m.state == MediaState_LockedWrite);
|
---|
| 3405 |
|
---|
| 3406 | /* unlock before the potentially lengthy operation */
|
---|
| 3407 | thatLock.leave();
|
---|
| 3408 |
|
---|
| 3409 | try
|
---|
| 3410 | {
|
---|
| 3411 | vrc = VDOpen (hdd, format, location,
|
---|
| 3412 | VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
|
---|
[15568] | 3413 | that->mm.vdDiskIfaces);
|
---|
[13580] | 3414 | if (RT_FAILURE (vrc))
|
---|
| 3415 | {
|
---|
| 3416 | throw setError (E_FAIL,
|
---|
| 3417 | tr ("Could not open the hard disk storage "
|
---|
| 3418 | "unit '%s'%s"),
|
---|
| 3419 | location.raw(), that->vdError (vrc).raw());
|
---|
| 3420 | }
|
---|
| 3421 |
|
---|
| 3422 | /* ensure the target directory exists */
|
---|
| 3423 | rc = VirtualBox::ensureFilePathExists (targetLocation);
|
---|
| 3424 | CheckComRCThrowRC (rc);
|
---|
| 3425 |
|
---|
| 3426 | /* needed for vdProgressCallback */
|
---|
| 3427 | that->mm.vdProgress = task->progress;
|
---|
| 3428 |
|
---|
| 3429 | vrc = VDCreateDiff (hdd, targetFormat, targetLocation,
|
---|
| 3430 | VD_IMAGE_FLAGS_NONE,
|
---|
| 3431 | NULL, targetId.raw(),
|
---|
[15596] | 3432 | id.raw(),
|
---|
[13580] | 3433 | VD_OPEN_FLAGS_NORMAL,
|
---|
[15568] | 3434 | target->mm.vdDiskIfaces,
|
---|
| 3435 | that->mm.vdDiskIfaces);
|
---|
[13580] | 3436 |
|
---|
| 3437 | that->mm.vdProgress = NULL;
|
---|
| 3438 |
|
---|
| 3439 | if (RT_FAILURE (vrc))
|
---|
| 3440 | {
|
---|
| 3441 | throw setError (E_FAIL,
|
---|
| 3442 | tr ("Could not create the differencing hard disk "
|
---|
| 3443 | "storage unit '%s'%s"),
|
---|
| 3444 | targetLocation.raw(), that->vdError (vrc).raw());
|
---|
| 3445 | }
|
---|
| 3446 |
|
---|
| 3447 | size = VDGetFileSize (hdd, 0);
|
---|
| 3448 | logicalSize = VDGetSize (hdd, 0) / _1M;
|
---|
| 3449 | }
|
---|
| 3450 | catch (HRESULT aRC) { rc = aRC; }
|
---|
| 3451 |
|
---|
| 3452 | VDDestroy (hdd);
|
---|
| 3453 | }
|
---|
| 3454 | catch (HRESULT aRC) { rc = aRC; }
|
---|
| 3455 |
|
---|
| 3456 | if (SUCCEEDED (rc))
|
---|
| 3457 | {
|
---|
[14143] | 3458 | /* we set mParent & children() (note that thatLock is released
|
---|
| 3459 | * here), but lock VirtualBox first to follow the rule */
|
---|
| 3460 | AutoMultiWriteLock2 alock (that->mVirtualBox->lockHandle(),
|
---|
| 3461 | that->treeLock());
|
---|
[13580] | 3462 |
|
---|
| 3463 | Assert (target->mParent.isNull());
|
---|
| 3464 |
|
---|
| 3465 | /* associate the child with the parent and deassociate from
|
---|
| 3466 | * VirtualBox */
|
---|
| 3467 | target->mParent = that;
|
---|
| 3468 | that->addDependentChild (target);
|
---|
| 3469 | target->mVirtualBox->removeDependentChild (target);
|
---|
| 3470 |
|
---|
| 3471 | /* register with mVirtualBox as the last step and move to
|
---|
| 3472 | * Created state only on success (leaving an orphan file is
|
---|
| 3473 | * better than breaking media registry consistency) */
|
---|
| 3474 | rc = that->mVirtualBox->registerHardDisk2 (target);
|
---|
| 3475 |
|
---|
[14143] | 3476 | if (FAILED (rc))
|
---|
[13580] | 3477 | {
|
---|
| 3478 | /* break the parent association on failure to register */
|
---|
| 3479 | target->mVirtualBox->addDependentChild (target);
|
---|
| 3480 | that->removeDependentChild (target);
|
---|
| 3481 | target->mParent.setNull();
|
---|
| 3482 | }
|
---|
| 3483 | }
|
---|
| 3484 |
|
---|
[14143] | 3485 | thatLock.maybeEnter();
|
---|
| 3486 |
|
---|
| 3487 | if (SUCCEEDED (rc))
|
---|
[13580] | 3488 | {
|
---|
[14143] | 3489 | target->m.state = MediaState_Created;
|
---|
[13580] | 3490 |
|
---|
[14143] | 3491 | target->m.size = size;
|
---|
| 3492 | target->mm.logicalSize = logicalSize;
|
---|
| 3493 | }
|
---|
| 3494 | else
|
---|
| 3495 | {
|
---|
[13580] | 3496 | /* back to NotCreated on failiure */
|
---|
| 3497 | target->m.state = MediaState_NotCreated;
|
---|
[15556] | 3498 |
|
---|
| 3499 | /* reset UUID to prevent it from being reused next time */
|
---|
| 3500 | if (generateUuid)
|
---|
| 3501 | unconst (target->m.id).clear();
|
---|
[13580] | 3502 | }
|
---|
| 3503 |
|
---|
| 3504 | if (isAsync)
|
---|
| 3505 | {
|
---|
| 3506 | /* unlock ourselves when done (unless in MediaState_LockedWrite
|
---|
| 3507 | * state because of taking the online snapshot*/
|
---|
| 3508 | if (that->m.state != MediaState_LockedWrite)
|
---|
| 3509 | {
|
---|
| 3510 | HRESULT rc2 = that->UnlockRead (NULL);
|
---|
| 3511 | AssertComRC (rc2);
|
---|
| 3512 | }
|
---|
| 3513 | }
|
---|
| 3514 |
|
---|
| 3515 | /* deregister the task registered in createDiffStorage() */
|
---|
| 3516 | Assert (that->mm.numCreateDiffTasks != 0);
|
---|
| 3517 | -- that->mm.numCreateDiffTasks;
|
---|
| 3518 |
|
---|
| 3519 | /* Note that in sync mode, it's the caller's responsibility to
|
---|
| 3520 | * unlock the hard disk */
|
---|
| 3521 |
|
---|
| 3522 | break;
|
---|
| 3523 | }
|
---|
| 3524 |
|
---|
| 3525 | ////////////////////////////////////////////////////////////////////////
|
---|
| 3526 |
|
---|
| 3527 | case Task::Merge:
|
---|
| 3528 | {
|
---|
| 3529 | /* The lock is also used as a signal from the task initiator (which
|
---|
| 3530 | * releases it only after RTThreadCreate()) that we can start the
|
---|
| 3531 | * job. We don't actually need the lock for anything else since the
|
---|
| 3532 | * object is protected by MediaState_Deleting and we don't modify
|
---|
| 3533 | * its sensitive fields below */
|
---|
| 3534 | {
|
---|
| 3535 | AutoWriteLock thatLock (that);
|
---|
| 3536 | }
|
---|
| 3537 |
|
---|
| 3538 | MergeChain *chain = task->d.chain.get();
|
---|
| 3539 |
|
---|
[16118] | 3540 | #if 0
|
---|
[13580] | 3541 | LogFlow (("*** MERGE forward = %RTbool\n", chain->isForward()));
|
---|
| 3542 | #endif
|
---|
| 3543 |
|
---|
| 3544 | try
|
---|
| 3545 | {
|
---|
| 3546 | PVBOXHDD hdd;
|
---|
| 3547 | int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
|
---|
| 3548 | ComAssertRCThrow (vrc, E_FAIL);
|
---|
| 3549 |
|
---|
| 3550 | try
|
---|
| 3551 | {
|
---|
| 3552 | /* open all hard disks in the chain (they are in the
|
---|
| 3553 | * {parent,child} order in there. Note that we don't lock
|
---|
| 3554 | * objects in this chain since they must be in states
|
---|
| 3555 | * (Deleting and LockedWrite) that prevent from chaning
|
---|
| 3556 | * their format and location fields from outside. */
|
---|
| 3557 |
|
---|
| 3558 | for (MergeChain::const_iterator it = chain->begin();
|
---|
| 3559 | it != chain->end(); ++ it)
|
---|
| 3560 | {
|
---|
| 3561 | /* complex sanity (sane complexity) */
|
---|
| 3562 | Assert ((chain->isForward() &&
|
---|
[14143] | 3563 | ((*it != chain->back() &&
|
---|
| 3564 | (*it)->m.state == MediaState_Deleting) ||
|
---|
| 3565 | (*it == chain->back() &&
|
---|
| 3566 | (*it)->m.state == MediaState_LockedWrite))) ||
|
---|
[13580] | 3567 | (!chain->isForward() &&
|
---|
[14143] | 3568 | ((*it != chain->front() &&
|
---|
| 3569 | (*it)->m.state == MediaState_Deleting) ||
|
---|
| 3570 | (*it == chain->front() &&
|
---|
| 3571 | (*it)->m.state == MediaState_LockedWrite))));
|
---|
[13580] | 3572 |
|
---|
| 3573 | Assert (*it == chain->target() ||
|
---|
| 3574 | (*it)->m.backRefs.size() == 0);
|
---|
| 3575 |
|
---|
| 3576 | /* open the first image with VDOPEN_FLAGS_INFO because
|
---|
| 3577 | * it's not necessarily the base one */
|
---|
| 3578 | vrc = VDOpen (hdd, Utf8Str ((*it)->mm.format),
|
---|
| 3579 | Utf8Str ((*it)->m.locationFull),
|
---|
| 3580 | it == chain->begin() ?
|
---|
| 3581 | VD_OPEN_FLAGS_INFO : 0,
|
---|
[15568] | 3582 | (*it)->mm.vdDiskIfaces);
|
---|
[13580] | 3583 | if (RT_FAILURE (vrc))
|
---|
| 3584 | throw vrc;
|
---|
[16118] | 3585 | #if 0
|
---|
[13580] | 3586 | LogFlow (("*** MERGE disk = %ls\n",
|
---|
| 3587 | (*it)->m.locationFull.raw()));
|
---|
| 3588 | #endif
|
---|
| 3589 | }
|
---|
| 3590 |
|
---|
| 3591 | /* needed for vdProgressCallback */
|
---|
| 3592 | that->mm.vdProgress = task->progress;
|
---|
| 3593 |
|
---|
| 3594 | unsigned start = chain->isForward() ?
|
---|
| 3595 | 0 : chain->size() - 1;
|
---|
| 3596 | unsigned end = chain->isForward() ?
|
---|
| 3597 | chain->size() - 1 : 0;
|
---|
[16118] | 3598 | #if 0
|
---|
[13580] | 3599 | LogFlow (("*** MERGE from %d to %d\n", start, end));
|
---|
| 3600 | #endif
|
---|
| 3601 | vrc = VDMerge (hdd, start, end, that->mm.vdDiskIfaces);
|
---|
| 3602 |
|
---|
| 3603 | that->mm.vdProgress = NULL;
|
---|
| 3604 |
|
---|
| 3605 | if (RT_FAILURE (vrc))
|
---|
| 3606 | throw vrc;
|
---|
| 3607 |
|
---|
| 3608 | /* update parent UUIDs */
|
---|
| 3609 | /// @todo VDMerge should be taught to do so, including the
|
---|
| 3610 | /// multiple children case
|
---|
| 3611 | if (chain->isForward())
|
---|
| 3612 | {
|
---|
| 3613 | /* target's UUID needs to be updated (note that target
|
---|
| 3614 | * is the only image in the container on success) */
|
---|
| 3615 | vrc = VDSetParentUuid (hdd, 0, chain->parent()->m.id);
|
---|
| 3616 | if (RT_FAILURE (vrc))
|
---|
| 3617 | throw vrc;
|
---|
| 3618 | }
|
---|
| 3619 | else
|
---|
| 3620 | {
|
---|
| 3621 | /* we need to update UUIDs of all source's children
|
---|
| 3622 | * which cannot be part of the container at once so
|
---|
| 3623 | * add each one in there individually */
|
---|
| 3624 | if (chain->children().size() > 0)
|
---|
| 3625 | {
|
---|
| 3626 | for (List::const_iterator it = chain->children().begin();
|
---|
| 3627 | it != chain->children().end(); ++ it)
|
---|
| 3628 | {
|
---|
| 3629 | /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
|
---|
| 3630 | vrc = VDOpen (hdd, Utf8Str ((*it)->mm.format),
|
---|
| 3631 | Utf8Str ((*it)->m.locationFull),
|
---|
[15568] | 3632 | VD_OPEN_FLAGS_INFO,
|
---|
| 3633 | (*it)->mm.vdDiskIfaces);
|
---|
[13580] | 3634 | if (RT_FAILURE (vrc))
|
---|
| 3635 | throw vrc;
|
---|
| 3636 |
|
---|
| 3637 | vrc = VDSetParentUuid (hdd, 1,
|
---|
| 3638 | chain->target()->m.id);
|
---|
| 3639 | if (RT_FAILURE (vrc))
|
---|
| 3640 | throw vrc;
|
---|
| 3641 |
|
---|
| 3642 | vrc = VDClose (hdd, false /* fDelete */);
|
---|
| 3643 | if (RT_FAILURE (vrc))
|
---|
| 3644 | throw vrc;
|
---|
| 3645 | }
|
---|
| 3646 | }
|
---|
| 3647 | }
|
---|
| 3648 | }
|
---|
| 3649 | catch (HRESULT aRC) { rc = aRC; }
|
---|
| 3650 | catch (int aVRC)
|
---|
| 3651 | {
|
---|
| 3652 | throw setError (E_FAIL,
|
---|
| 3653 | tr ("Could not merge the hard disk '%ls' to '%ls'%s"),
|
---|
| 3654 | chain->source()->m.locationFull.raw(),
|
---|
| 3655 | chain->target()->m.locationFull.raw(),
|
---|
| 3656 | that->vdError (aVRC).raw());
|
---|
| 3657 | }
|
---|
| 3658 |
|
---|
| 3659 | VDDestroy (hdd);
|
---|
| 3660 | }
|
---|
| 3661 | catch (HRESULT aRC) { rc = aRC; }
|
---|
| 3662 |
|
---|
| 3663 | HRESULT rc2;
|
---|
| 3664 |
|
---|
| 3665 | bool saveSettingsFailed = false;
|
---|
| 3666 |
|
---|
| 3667 | if (SUCCEEDED (rc))
|
---|
| 3668 | {
|
---|
| 3669 | /* all hard disks but the target were successfully deleted by
|
---|
| 3670 | * VDMerge; reparent the last one and uninitialize deleted */
|
---|
| 3671 |
|
---|
[14143] | 3672 | /* we set mParent & children() (note that thatLock is released
|
---|
| 3673 | * here), but lock VirtualBox first to follow the rule */
|
---|
| 3674 | AutoMultiWriteLock2 alock (that->mVirtualBox->lockHandle(),
|
---|
| 3675 | that->treeLock());
|
---|
[13580] | 3676 |
|
---|
| 3677 | HardDisk2 *source = chain->source();
|
---|
| 3678 | HardDisk2 *target = chain->target();
|
---|
| 3679 |
|
---|
| 3680 | if (chain->isForward())
|
---|
| 3681 | {
|
---|
| 3682 | /* first, unregister the target since it may become a base
|
---|
| 3683 | * hard disk which needs re-registration */
|
---|
| 3684 | rc2 = target->mVirtualBox->
|
---|
| 3685 | unregisterHardDisk2 (target, false /* aSaveSettings */);
|
---|
| 3686 | AssertComRC (rc2);
|
---|
| 3687 |
|
---|
| 3688 | /* then, reparent it and disconnect the deleted branch at
|
---|
| 3689 | * both ends (chain->parent() is source's parent) */
|
---|
| 3690 | target->mParent->removeDependentChild (target);
|
---|
| 3691 | target->mParent = chain->parent();
|
---|
| 3692 | if (!target->mParent.isNull())
|
---|
| 3693 | {
|
---|
| 3694 | target->mParent->addDependentChild (target);
|
---|
| 3695 | target->mParent->removeDependentChild (source);
|
---|
| 3696 | source->mParent.setNull();
|
---|
| 3697 | }
|
---|
| 3698 | else
|
---|
| 3699 | {
|
---|
| 3700 | target->mVirtualBox->addDependentChild (target);
|
---|
| 3701 | target->mVirtualBox->removeDependentChild (source);
|
---|
| 3702 | }
|
---|
| 3703 |
|
---|
| 3704 | /* then, register again */
|
---|
| 3705 | rc2 = target->mVirtualBox->
|
---|
| 3706 | registerHardDisk2 (target, false /* aSaveSettings */);
|
---|
| 3707 | AssertComRC (rc2);
|
---|
| 3708 | }
|
---|
| 3709 | else
|
---|
| 3710 | {
|
---|
| 3711 | Assert (target->children().size() == 1);
|
---|
| 3712 | HardDisk2 *targetChild = target->children().front();
|
---|
| 3713 |
|
---|
| 3714 | /* disconnect the deleted branch at the elder end */
|
---|
| 3715 | target->removeDependentChild (targetChild);
|
---|
| 3716 | targetChild->mParent.setNull();
|
---|
| 3717 |
|
---|
| 3718 | const List &children = chain->children();
|
---|
| 3719 |
|
---|
| 3720 | /* reparent source's chidren and disconnect the deleted
|
---|
| 3721 | * branch at the younger end m*/
|
---|
| 3722 | if (children.size() > 0)
|
---|
| 3723 | {
|
---|
| 3724 | /* obey {parent,child} lock order */
|
---|
| 3725 | AutoWriteLock sourceLock (source);
|
---|
| 3726 |
|
---|
| 3727 | for (List::const_iterator it = children.begin();
|
---|
| 3728 | it != children.end(); ++ it)
|
---|
| 3729 | {
|
---|
| 3730 | AutoWriteLock childLock (*it);
|
---|
| 3731 |
|
---|
| 3732 | (*it)->mParent = target;
|
---|
| 3733 | (*it)->mParent->addDependentChild (*it);
|
---|
| 3734 | source->removeDependentChild (*it);
|
---|
| 3735 | }
|
---|
| 3736 | }
|
---|
| 3737 | }
|
---|
| 3738 |
|
---|
| 3739 | /* try to save the hard disk registry */
|
---|
| 3740 | rc = that->mVirtualBox->saveSettings();
|
---|
| 3741 |
|
---|
| 3742 | if (SUCCEEDED (rc))
|
---|
| 3743 | {
|
---|
| 3744 | /* unregister and uninitialize all hard disks in the chain
|
---|
| 3745 | * but the target */
|
---|
| 3746 |
|
---|
| 3747 | for (MergeChain::iterator it = chain->begin();
|
---|
| 3748 | it != chain->end();)
|
---|
| 3749 | {
|
---|
| 3750 | if (*it == chain->target())
|
---|
| 3751 | {
|
---|
| 3752 | ++ it;
|
---|
| 3753 | continue;
|
---|
| 3754 | }
|
---|
| 3755 |
|
---|
| 3756 | rc2 = (*it)->mVirtualBox->
|
---|
| 3757 | unregisterHardDisk2 (*it, false /* aSaveSettings */);
|
---|
| 3758 | AssertComRC (rc2);
|
---|
| 3759 |
|
---|
| 3760 | /* now, uninitialize the deleted hard disk (note that
|
---|
| 3761 | * due to the Deleting state, uninit() will not touch
|
---|
| 3762 | * the parent-child relationship so we need to
|
---|
| 3763 | * uninitialize each disk individually) */
|
---|
| 3764 |
|
---|
| 3765 | /* note that the operation initiator hard disk (which is
|
---|
| 3766 | * normally also the source hard disk) is a special case
|
---|
| 3767 | * -- there is one more caller added by Task to it which
|
---|
| 3768 | * we must release. Also, if we are in sync mode, the
|
---|
| 3769 | * caller may still hold an AutoCaller instance for it
|
---|
| 3770 | * and therefore we cannot uninit() it (it's therefore
|
---|
| 3771 | * the caller's responsibility) */
|
---|
| 3772 | if (*it == that)
|
---|
| 3773 | task->autoCaller.release();
|
---|
| 3774 |
|
---|
| 3775 | /* release the caller added by MergeChain before
|
---|
| 3776 | * uninit() */
|
---|
| 3777 | (*it)->releaseCaller();
|
---|
| 3778 |
|
---|
| 3779 | if (isAsync || *it != that)
|
---|
| 3780 | (*it)->uninit();
|
---|
| 3781 |
|
---|
| 3782 | /* delete (to prevent uninitialization in MergeChain
|
---|
| 3783 | * dtor) and advance to the next item */
|
---|
| 3784 | it = chain->erase (it);
|
---|
| 3785 | }
|
---|
| 3786 |
|
---|
| 3787 | /* Note that states of all other hard disks (target, parent,
|
---|
| 3788 | * children) will be restored by the MergeChain dtor */
|
---|
| 3789 | }
|
---|
| 3790 | else
|
---|
| 3791 | {
|
---|
| 3792 | /* too bad if we fail, but we'll need to rollback everything
|
---|
[14294] | 3793 | * we did above to at least keep the HD tree in sync with
|
---|
| 3794 | * the current registry on disk */
|
---|
[13580] | 3795 |
|
---|
| 3796 | saveSettingsFailed = true;
|
---|
| 3797 |
|
---|
| 3798 | /// @todo NEWMEDIA implement a proper undo
|
---|
| 3799 |
|
---|
| 3800 | AssertFailed();
|
---|
| 3801 | }
|
---|
| 3802 | }
|
---|
| 3803 |
|
---|
| 3804 | if (FAILED (rc))
|
---|
| 3805 | {
|
---|
| 3806 | /* Here we come if either VDMerge() failed (in which case we
|
---|
| 3807 | * assume that it tried to do everything to make a further
|
---|
| 3808 | * retry possible -- e.g. not deleted intermediate hard disks
|
---|
| 3809 | * and so on) or VirtualBox::saveSettings() failed (where we
|
---|
| 3810 | * should have the original tree but with intermediate storage
|
---|
| 3811 | * units deleted by VDMerge()). We have to only restore states
|
---|
[14294] | 3812 | * (through the MergeChain dtor) unless we are run synchronously
|
---|
[13580] | 3813 | * in which case it's the responsibility of the caller as stated
|
---|
| 3814 | * in the mergeTo() docs. The latter also implies that we
|
---|
| 3815 | * don't own the merge chain, so release it in this case. */
|
---|
| 3816 |
|
---|
| 3817 | if (!isAsync)
|
---|
| 3818 | task->d.chain.release();
|
---|
| 3819 |
|
---|
| 3820 | NOREF (saveSettingsFailed);
|
---|
| 3821 | }
|
---|
| 3822 |
|
---|
| 3823 | break;
|
---|
| 3824 | }
|
---|
| 3825 |
|
---|
| 3826 | ////////////////////////////////////////////////////////////////////////
|
---|
| 3827 |
|
---|
[15556] | 3828 | case Task::Clone:
|
---|
| 3829 | {
|
---|
| 3830 | ComObjPtr <HardDisk2> &target = task->d.target;
|
---|
| 3831 |
|
---|
| 3832 | /* Lock both in {parent,child} order. The lock is also used as a
|
---|
| 3833 | * signal from the task initiator (which releases it only after
|
---|
| 3834 | * RTThreadCreate()) that we can start the job*/
|
---|
| 3835 | AutoMultiWriteLock2 thatLock (that, target);
|
---|
| 3836 |
|
---|
| 3837 | uint64_t size = 0, logicalSize = 0;
|
---|
| 3838 |
|
---|
| 3839 | /* The object may request a specific UUID (through a special form of
|
---|
| 3840 | * the setLocation() argumet). Otherwise we have to generate it */
|
---|
| 3841 | Guid targetId = target->m.id;
|
---|
| 3842 | bool generateUuid = targetId.isEmpty();
|
---|
| 3843 | if (generateUuid)
|
---|
| 3844 | {
|
---|
| 3845 | targetId.create();
|
---|
| 3846 | /* VirtualBox::registerHardDisk2() will need UUID */
|
---|
| 3847 | unconst (target->m.id) = targetId;
|
---|
| 3848 | }
|
---|
| 3849 |
|
---|
| 3850 | try
|
---|
| 3851 | {
|
---|
| 3852 | PVBOXHDD hdd;
|
---|
| 3853 | int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
|
---|
| 3854 | ComAssertRCThrow (vrc, E_FAIL);
|
---|
| 3855 |
|
---|
| 3856 | Utf8Str format (that->mm.format);
|
---|
| 3857 | Utf8Str location (that->m.locationFull);
|
---|
| 3858 |
|
---|
| 3859 | Utf8Str targetFormat (target->mm.format);
|
---|
| 3860 | Utf8Str targetLocation (target->m.locationFull);
|
---|
| 3861 |
|
---|
| 3862 | Assert (target->m.state == MediaState_Creating);
|
---|
| 3863 |
|
---|
| 3864 | Assert (that->m.state == MediaState_LockedRead);
|
---|
| 3865 |
|
---|
| 3866 | /* unlock before the potentially lengthy operation */
|
---|
| 3867 | thatLock.leave();
|
---|
| 3868 |
|
---|
| 3869 | try
|
---|
| 3870 | {
|
---|
| 3871 | vrc = VDOpen (hdd, format, location,
|
---|
| 3872 | VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
|
---|
[15568] | 3873 | that->mm.vdDiskIfaces);
|
---|
[15556] | 3874 | if (RT_FAILURE (vrc))
|
---|
| 3875 | {
|
---|
| 3876 | throw setError (E_FAIL,
|
---|
| 3877 | tr ("Could not open the hard disk storage "
|
---|
| 3878 | "unit '%s'%s"),
|
---|
| 3879 | location.raw(), that->vdError (vrc).raw());
|
---|
| 3880 | }
|
---|
| 3881 |
|
---|
| 3882 | /* ensure the target directory exists */
|
---|
| 3883 | rc = VirtualBox::ensureFilePathExists (targetLocation);
|
---|
| 3884 | CheckComRCThrowRC (rc);
|
---|
| 3885 |
|
---|
| 3886 | /* needed for vdProgressCallback */
|
---|
| 3887 | that->mm.vdProgress = task->progress;
|
---|
| 3888 |
|
---|
| 3889 | PVBOXHDD targetHdd;
|
---|
| 3890 | int vrc = VDCreate (that->mm.vdDiskIfaces, &targetHdd);
|
---|
| 3891 | ComAssertRCThrow (vrc, E_FAIL);
|
---|
| 3892 |
|
---|
| 3893 | vrc = VDCopy (hdd, 0, targetHdd, targetFormat,
|
---|
| 3894 | targetLocation, false, 0, targetId.raw(),
|
---|
[15568] | 3895 | NULL, target->mm.vdDiskIfaces,
|
---|
| 3896 | that->mm.vdDiskIfaces);
|
---|
[15556] | 3897 |
|
---|
| 3898 | that->mm.vdProgress = NULL;
|
---|
| 3899 |
|
---|
| 3900 | if (RT_FAILURE (vrc))
|
---|
| 3901 | {
|
---|
| 3902 | VDDestroy (targetHdd);
|
---|
| 3903 |
|
---|
| 3904 | throw setError (E_FAIL,
|
---|
| 3905 | tr ("Could not create the clone hard disk "
|
---|
| 3906 | "'%s'%s"),
|
---|
| 3907 | targetLocation.raw(), that->vdError (vrc).raw());
|
---|
| 3908 | }
|
---|
| 3909 |
|
---|
| 3910 | size = VDGetFileSize (hdd, 0);
|
---|
| 3911 | logicalSize = VDGetSize (hdd, 0) / _1M;
|
---|
| 3912 |
|
---|
| 3913 | VDDestroy (targetHdd);
|
---|
| 3914 | }
|
---|
| 3915 | catch (HRESULT aRC) { rc = aRC; }
|
---|
| 3916 |
|
---|
| 3917 | VDDestroy (hdd);
|
---|
| 3918 | }
|
---|
| 3919 | catch (HRESULT aRC) { rc = aRC; }
|
---|
| 3920 |
|
---|
| 3921 | if (SUCCEEDED (rc))
|
---|
| 3922 | {
|
---|
| 3923 | /* we set mParent & children() (note that thatLock is released
|
---|
| 3924 | * here), but lock VirtualBox first to follow the rule */
|
---|
| 3925 | AutoMultiWriteLock2 alock (that->mVirtualBox->lockHandle(),
|
---|
| 3926 | that->treeLock());
|
---|
| 3927 |
|
---|
| 3928 | Assert (target->mParent.isNull());
|
---|
| 3929 |
|
---|
| 3930 | if (!that->mParent.isNull())
|
---|
| 3931 | {
|
---|
| 3932 | /* associate the clone with the original's parent and
|
---|
| 3933 | * deassociate from VirtualBox */
|
---|
| 3934 | target->mParent = that->mParent;
|
---|
| 3935 | that->mParent->addDependentChild (target);
|
---|
| 3936 | target->mVirtualBox->removeDependentChild (target);
|
---|
| 3937 |
|
---|
| 3938 | /* register with mVirtualBox as the last step and move to
|
---|
| 3939 | * Created state only on success (leaving an orphan file is
|
---|
| 3940 | * better than breaking media registry consistency) */
|
---|
| 3941 | rc = that->mVirtualBox->registerHardDisk2 (target);
|
---|
| 3942 |
|
---|
| 3943 | if (FAILED (rc))
|
---|
| 3944 | {
|
---|
| 3945 | /* break the parent association on failure to register */
|
---|
| 3946 | target->mVirtualBox->addDependentChild (target);
|
---|
| 3947 | that->mParent->removeDependentChild (target);
|
---|
| 3948 | target->mParent.setNull();
|
---|
| 3949 | }
|
---|
| 3950 | }
|
---|
| 3951 | else
|
---|
| 3952 | {
|
---|
| 3953 | /* just register */
|
---|
| 3954 | rc = that->mVirtualBox->registerHardDisk2 (target);
|
---|
| 3955 | }
|
---|
| 3956 | }
|
---|
| 3957 |
|
---|
| 3958 | thatLock.maybeEnter();
|
---|
| 3959 |
|
---|
| 3960 | if (SUCCEEDED (rc))
|
---|
| 3961 | {
|
---|
| 3962 | target->m.state = MediaState_Created;
|
---|
| 3963 |
|
---|
| 3964 | target->m.size = size;
|
---|
| 3965 | target->mm.logicalSize = logicalSize;
|
---|
| 3966 | }
|
---|
| 3967 | else
|
---|
| 3968 | {
|
---|
| 3969 | /* back to NotCreated on failiure */
|
---|
| 3970 | target->m.state = MediaState_NotCreated;
|
---|
| 3971 |
|
---|
| 3972 | /* reset UUID to prevent it from being reused next time */
|
---|
| 3973 | if (generateUuid)
|
---|
| 3974 | unconst (target->m.id).clear();
|
---|
| 3975 | }
|
---|
| 3976 |
|
---|
| 3977 | if (isAsync)
|
---|
| 3978 | {
|
---|
| 3979 | /* unlock ourselves when done (unless in MediaState_LockedWrite
|
---|
| 3980 | * state because of taking the online snapshot*/
|
---|
| 3981 | if (that->m.state != MediaState_LockedWrite)
|
---|
| 3982 | {
|
---|
| 3983 | HRESULT rc2 = that->UnlockRead (NULL);
|
---|
| 3984 | AssertComRC (rc2);
|
---|
| 3985 | }
|
---|
| 3986 | }
|
---|
| 3987 |
|
---|
| 3988 | /* Note that in sync mode, it's the caller's responsibility to
|
---|
| 3989 | * unlock the hard disk */
|
---|
| 3990 |
|
---|
| 3991 | break;
|
---|
| 3992 | }
|
---|
| 3993 |
|
---|
| 3994 | ////////////////////////////////////////////////////////////////////////
|
---|
| 3995 |
|
---|
[13580] | 3996 | case Task::Delete:
|
---|
| 3997 | {
|
---|
| 3998 | /* The lock is also used as a signal from the task initiator (which
|
---|
| 3999 | * releases it only after RTThreadCreate()) that we can start the job */
|
---|
| 4000 | AutoWriteLock thatLock (that);
|
---|
| 4001 |
|
---|
| 4002 | try
|
---|
| 4003 | {
|
---|
| 4004 | PVBOXHDD hdd;
|
---|
| 4005 | int vrc = VDCreate (that->mm.vdDiskIfaces, &hdd);
|
---|
| 4006 | ComAssertRCThrow (vrc, E_FAIL);
|
---|
| 4007 |
|
---|
| 4008 | Utf8Str format (that->mm.format);
|
---|
| 4009 | Utf8Str location (that->m.locationFull);
|
---|
| 4010 |
|
---|
| 4011 | /* unlock before the potentially lengthy operation */
|
---|
| 4012 | Assert (that->m.state == MediaState_Deleting);
|
---|
| 4013 | thatLock.leave();
|
---|
| 4014 |
|
---|
| 4015 | try
|
---|
| 4016 | {
|
---|
| 4017 | vrc = VDOpen (hdd, format, location,
|
---|
| 4018 | VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
|
---|
[15568] | 4019 | that->mm.vdDiskIfaces);
|
---|
[13580] | 4020 | if (RT_SUCCESS (vrc))
|
---|
| 4021 | vrc = VDClose (hdd, true /* fDelete */);
|
---|
| 4022 |
|
---|
| 4023 | if (RT_FAILURE (vrc))
|
---|
| 4024 | {
|
---|
| 4025 | throw setError (E_FAIL,
|
---|
| 4026 | tr ("Could not delete the hard disk storage "
|
---|
| 4027 | "unit '%s'%s"),
|
---|
| 4028 | location.raw(), that->vdError (vrc).raw());
|
---|
| 4029 | }
|
---|
| 4030 |
|
---|
| 4031 | }
|
---|
| 4032 | catch (HRESULT aRC) { rc = aRC; }
|
---|
| 4033 |
|
---|
| 4034 | VDDestroy (hdd);
|
---|
| 4035 | }
|
---|
| 4036 | catch (HRESULT aRC) { rc = aRC; }
|
---|
| 4037 |
|
---|
| 4038 | thatLock.maybeEnter();
|
---|
| 4039 |
|
---|
| 4040 | /* go to the NotCreated state even on failure since the storage
|
---|
| 4041 | * may have been already partially deleted and cannot be used any
|
---|
| 4042 | * more. One will be able to manually re-open the storage if really
|
---|
| 4043 | * needed to re-register it. */
|
---|
| 4044 | that->m.state = MediaState_NotCreated;
|
---|
| 4045 |
|
---|
[14931] | 4046 | /* Reset UUID to prevent Create* from reusing it again */
|
---|
| 4047 | unconst (that->m.id).clear();
|
---|
| 4048 |
|
---|
[13580] | 4049 | break;
|
---|
| 4050 | }
|
---|
| 4051 |
|
---|
| 4052 | default:
|
---|
| 4053 | AssertFailedReturn (VERR_GENERAL_FAILURE);
|
---|
| 4054 | }
|
---|
| 4055 |
|
---|
| 4056 | /* complete the progress if run asynchronously */
|
---|
| 4057 | if (isAsync)
|
---|
| 4058 | {
|
---|
| 4059 | if (!task->progress.isNull())
|
---|
| 4060 | task->progress->notifyComplete (rc);
|
---|
| 4061 | }
|
---|
| 4062 | else
|
---|
| 4063 | {
|
---|
| 4064 | task->rc = rc;
|
---|
| 4065 | }
|
---|
| 4066 |
|
---|
| 4067 | LogFlowFunc (("rc=%Rhrc\n", rc));
|
---|
| 4068 | LogFlowFuncLeave();
|
---|
| 4069 |
|
---|
| 4070 | return VINF_SUCCESS;
|
---|
| 4071 |
|
---|
| 4072 | /// @todo ugly hack, fix ComAssert... later
|
---|
| 4073 | #undef setError
|
---|
| 4074 | }
|
---|
[14772] | 4075 | /* vi: set tabstop=4 shiftwidth=4 expandtab: */
|
---|