~siretart/ubuntu/raring/virtualbox-ose/bug.1101867

« back to all changes in this revision

Viewing changes to src/VBox/Main/src-server/MediumImpl.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Felix Geyer
  • Date: 2011-01-30 23:27:25 UTC
  • mfrom: (0.3.12 upstream)
  • Revision ID: james.westby@ubuntu.com-20110130232725-2ouajjd2ggdet0zd
Tags: 4.0.2-dfsg-1ubuntu1
* Merge from Debian unstable, remaining changes:
  - Add Apport hook.
    - debian/virtualbox-ose.files/source_virtualbox-ose.py
    - debian/virtualbox-ose.install
  - Drop *-source packages.
* Drop ubuntu-01-fix-build-gcc45.patch, fixed upstream.
* Drop ubuntu-02-as-needed.patch, added to the Debian package.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: MediumImpl.cpp 35553 2011-01-14 00:02:56Z vboxsync $ */
 
2
/** @file
 
3
 * VirtualBox COM class implementation
 
4
 */
 
5
 
 
6
/*
 
7
 * Copyright (C) 2008-2010 Oracle Corporation
 
8
 *
 
9
 * This file is part of VirtualBox Open Source Edition (OSE), as
 
10
 * available from http://www.virtualbox.org. This file is free software;
 
11
 * you can redistribute it and/or modify it under the terms of the GNU
 
12
 * General Public License (GPL) as published by the Free Software
 
13
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 
14
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 
15
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 
16
 */
 
17
 
 
18
#include "MediumImpl.h"
 
19
#include "ProgressImpl.h"
 
20
#include "SystemPropertiesImpl.h"
 
21
#include "VirtualBoxImpl.h"
 
22
 
 
23
#include "AutoCaller.h"
 
24
#include "Logging.h"
 
25
 
 
26
#include <VBox/com/array.h>
 
27
#include "VBox/com/MultiResult.h"
 
28
#include "VBox/com/ErrorInfo.h"
 
29
 
 
30
#include <VBox/err.h>
 
31
#include <VBox/settings.h>
 
32
 
 
33
#include <iprt/param.h>
 
34
#include <iprt/path.h>
 
35
#include <iprt/file.h>
 
36
#include <iprt/tcp.h>
 
37
#include <iprt/cpp/utils.h>
 
38
 
 
39
#include <VBox/vd.h>
 
40
 
 
41
#include <algorithm>
 
42
 
 
43
////////////////////////////////////////////////////////////////////////////////
 
44
//
 
45
// Medium data definition
 
46
//
 
47
////////////////////////////////////////////////////////////////////////////////
 
48
 
 
49
/** Describes how a machine refers to this medium. */
 
50
struct BackRef
 
51
{
 
52
    /** Equality predicate for stdc++. */
 
53
    struct EqualsTo : public std::unary_function <BackRef, bool>
 
54
    {
 
55
        explicit EqualsTo(const Guid &aMachineId) : machineId(aMachineId) {}
 
56
 
 
57
        bool operator()(const argument_type &aThat) const
 
58
        {
 
59
            return aThat.machineId == machineId;
 
60
        }
 
61
 
 
62
        const Guid machineId;
 
63
    };
 
64
 
 
65
    BackRef(const Guid &aMachineId,
 
66
            const Guid &aSnapshotId = Guid::Empty)
 
67
        : machineId(aMachineId),
 
68
          fInCurState(aSnapshotId.isEmpty())
 
69
    {
 
70
        if (!aSnapshotId.isEmpty())
 
71
            llSnapshotIds.push_back(aSnapshotId);
 
72
    }
 
73
 
 
74
    Guid machineId;
 
75
    bool fInCurState : 1;
 
76
    GuidList llSnapshotIds;
 
77
};
 
78
 
 
79
typedef std::list<BackRef> BackRefList;
 
80
 
 
81
struct Medium::Data
 
82
{
 
83
    Data()
 
84
        : pVirtualBox(NULL),
 
85
          state(MediumState_NotCreated),
 
86
          variant(MediumVariant_Standard),
 
87
          size(0),
 
88
          readers(0),
 
89
          preLockState(MediumState_NotCreated),
 
90
          queryInfoSem(NIL_RTSEMEVENTMULTI),
 
91
          queryInfoRunning(false),
 
92
          type(MediumType_Normal),
 
93
          devType(DeviceType_HardDisk),
 
94
          logicalSize(0),
 
95
          hddOpenMode(OpenReadWrite),
 
96
          autoReset(false),
 
97
          hostDrive(false),
 
98
          implicit(false),
 
99
          numCreateDiffTasks(0),
 
100
          vdDiskIfaces(NULL),
 
101
          vdImageIfaces(NULL)
 
102
    { }
 
103
 
 
104
    /** weak VirtualBox parent */
 
105
    VirtualBox * const pVirtualBox;
 
106
 
 
107
    // pParent and llChildren are protected by VirtualBox::getMediaTreeLockHandle()
 
108
    ComObjPtr<Medium> pParent;
 
109
    MediaList llChildren;           // to add a child, just call push_back; to remove a child, call child->deparent() which does a lookup
 
110
 
 
111
    GuidList llRegistryIDs;         // media registries in which this medium is listed
 
112
 
 
113
    const Guid id;
 
114
    Utf8Str strDescription;
 
115
    MediumState_T state;
 
116
    MediumVariant_T variant;
 
117
    Utf8Str strLocationFull;
 
118
    uint64_t size;
 
119
    Utf8Str strLastAccessError;
 
120
 
 
121
    BackRefList backRefs;
 
122
 
 
123
    size_t readers;
 
124
    MediumState_T preLockState;
 
125
 
 
126
    RTSEMEVENTMULTI queryInfoSem;
 
127
    bool queryInfoRunning : 1;
 
128
 
 
129
    const Utf8Str strFormat;
 
130
    ComObjPtr<MediumFormat> formatObj;
 
131
 
 
132
    MediumType_T type;
 
133
    DeviceType_T devType;
 
134
    uint64_t logicalSize;
 
135
 
 
136
    HDDOpenMode hddOpenMode;
 
137
 
 
138
    bool autoReset : 1;
 
139
 
 
140
    const Guid uuidImage;
 
141
    const Guid uuidParentImage;
 
142
 
 
143
    bool hostDrive : 1;
 
144
 
 
145
    settings::StringsMap mapProperties;
 
146
 
 
147
    bool implicit : 1;
 
148
 
 
149
    uint32_t numCreateDiffTasks;
 
150
 
 
151
    Utf8Str vdError;        /*< Error remembered by the VD error callback. */
 
152
 
 
153
    VDINTERFACE vdIfError;
 
154
    VDINTERFACEERROR vdIfCallsError;
 
155
 
 
156
    VDINTERFACE vdIfConfig;
 
157
    VDINTERFACECONFIG vdIfCallsConfig;
 
158
 
 
159
    VDINTERFACE vdIfTcpNet;
 
160
    VDINTERFACETCPNET vdIfCallsTcpNet;
 
161
 
 
162
    PVDINTERFACE vdDiskIfaces;
 
163
    PVDINTERFACE vdImageIfaces;
 
164
};
 
165
 
 
166
typedef struct VDSOCKETINT
 
167
{
 
168
    /** Socket handle. */
 
169
    RTSOCKET hSocket;
 
170
} VDSOCKETINT, *PVDSOCKETINT;
 
171
 
 
172
////////////////////////////////////////////////////////////////////////////////
 
173
//
 
174
// Globals
 
175
//
 
176
////////////////////////////////////////////////////////////////////////////////
 
177
 
 
178
/**
 
179
 * Medium::Task class for asynchronous operations.
 
180
 *
 
181
 * @note Instances of this class must be created using new() because the
 
182
 *       task thread function will delete them when the task is complete.
 
183
 *
 
184
 * @note The constructor of this class adds a caller on the managed Medium
 
185
 *       object which is automatically released upon destruction.
 
186
 */
 
187
class Medium::Task
 
188
{
 
189
public:
 
190
    Task(Medium *aMedium, Progress *aProgress)
 
191
        : mVDOperationIfaces(NULL),
 
192
          m_pllRegistriesThatNeedSaving(NULL),
 
193
          mMedium(aMedium),
 
194
          mMediumCaller(aMedium),
 
195
          mThread(NIL_RTTHREAD),
 
196
          mProgress(aProgress),
 
197
          mVirtualBoxCaller(NULL)
 
198
    {
 
199
        AssertReturnVoidStmt(aMedium, mRC = E_FAIL);
 
200
        mRC = mMediumCaller.rc();
 
201
        if (FAILED(mRC))
 
202
            return;
 
203
 
 
204
        /* Get strong VirtualBox reference, see below. */
 
205
        VirtualBox *pVirtualBox = aMedium->m->pVirtualBox;
 
206
        mVirtualBox = pVirtualBox;
 
207
        mVirtualBoxCaller.attach(pVirtualBox);
 
208
        mRC = mVirtualBoxCaller.rc();
 
209
        if (FAILED(mRC))
 
210
            return;
 
211
 
 
212
        /* Set up a per-operation progress interface, can be used freely (for
 
213
         * binary operations you can use it either on the source or target). */
 
214
        mVDIfCallsProgress.cbSize = sizeof(VDINTERFACEPROGRESS);
 
215
        mVDIfCallsProgress.enmInterface = VDINTERFACETYPE_PROGRESS;
 
216
        mVDIfCallsProgress.pfnProgress = vdProgressCall;
 
217
        int vrc = VDInterfaceAdd(&mVDIfProgress,
 
218
                                "Medium::Task::vdInterfaceProgress",
 
219
                                VDINTERFACETYPE_PROGRESS,
 
220
                                &mVDIfCallsProgress,
 
221
                                mProgress,
 
222
                                &mVDOperationIfaces);
 
223
        AssertRC(vrc);
 
224
        if (RT_FAILURE(vrc))
 
225
            mRC = E_FAIL;
 
226
    }
 
227
 
 
228
    // Make all destructors virtual. Just in case.
 
229
    virtual ~Task()
 
230
    {}
 
231
 
 
232
    HRESULT rc() const { return mRC; }
 
233
    bool isOk() const { return SUCCEEDED(rc()); }
 
234
 
 
235
    static int fntMediumTask(RTTHREAD aThread, void *pvUser);
 
236
 
 
237
    bool isAsync() { return mThread != NIL_RTTHREAD; }
 
238
 
 
239
    PVDINTERFACE mVDOperationIfaces;
 
240
 
 
241
    // Whether the caller needs to call VirtualBox::saveSettings() after
 
242
    // the task function returns. Only used in synchronous (wait) mode;
 
243
    // otherwise the task will save the settings itself.
 
244
    GuidList *m_pllRegistriesThatNeedSaving;
 
245
 
 
246
    const ComObjPtr<Medium> mMedium;
 
247
    AutoCaller mMediumCaller;
 
248
 
 
249
    friend HRESULT Medium::runNow(Medium::Task*, GuidList *);
 
250
 
 
251
protected:
 
252
    HRESULT mRC;
 
253
    RTTHREAD mThread;
 
254
 
 
255
private:
 
256
    virtual HRESULT handler() = 0;
 
257
 
 
258
    const ComObjPtr<Progress> mProgress;
 
259
 
 
260
    static DECLCALLBACK(int) vdProgressCall(void *pvUser, unsigned uPercent);
 
261
 
 
262
    VDINTERFACE mVDIfProgress;
 
263
    VDINTERFACEPROGRESS mVDIfCallsProgress;
 
264
 
 
265
    /* Must have a strong VirtualBox reference during a task otherwise the
 
266
     * reference count might drop to 0 while a task is still running. This
 
267
     * would result in weird behavior, including deadlocks due to uninit and
 
268
     * locking order issues. The deadlock often is not detectable because the
 
269
     * uninit uses event semaphores which sabotages deadlock detection. */
 
270
    ComObjPtr<VirtualBox> mVirtualBox;
 
271
    AutoCaller mVirtualBoxCaller;
 
272
};
 
273
 
 
274
class Medium::CreateBaseTask : public Medium::Task
 
275
{
 
276
public:
 
277
    CreateBaseTask(Medium *aMedium,
 
278
                   Progress *aProgress,
 
279
                   uint64_t aSize,
 
280
                   MediumVariant_T aVariant)
 
281
        : Medium::Task(aMedium, aProgress),
 
282
          mSize(aSize),
 
283
          mVariant(aVariant)
 
284
    {}
 
285
 
 
286
    uint64_t mSize;
 
287
    MediumVariant_T mVariant;
 
288
 
 
289
private:
 
290
    virtual HRESULT handler();
 
291
};
 
292
 
 
293
class Medium::CreateDiffTask : public Medium::Task
 
294
{
 
295
public:
 
296
    CreateDiffTask(Medium *aMedium,
 
297
                   Progress *aProgress,
 
298
                   Medium *aTarget,
 
299
                   MediumVariant_T aVariant,
 
300
                   MediumLockList *aMediumLockList,
 
301
                   bool fKeepMediumLockList = false)
 
302
        : Medium::Task(aMedium, aProgress),
 
303
          mpMediumLockList(aMediumLockList),
 
304
          mTarget(aTarget),
 
305
          mVariant(aVariant),
 
306
          mTargetCaller(aTarget),
 
307
          mfKeepMediumLockList(fKeepMediumLockList)
 
308
    {
 
309
        AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
 
310
        mRC = mTargetCaller.rc();
 
311
        if (FAILED(mRC))
 
312
            return;
 
313
    }
 
314
 
 
315
    ~CreateDiffTask()
 
316
    {
 
317
        if (!mfKeepMediumLockList && mpMediumLockList)
 
318
            delete mpMediumLockList;
 
319
    }
 
320
 
 
321
    MediumLockList *mpMediumLockList;
 
322
 
 
323
    const ComObjPtr<Medium> mTarget;
 
324
    MediumVariant_T mVariant;
 
325
 
 
326
private:
 
327
    virtual HRESULT handler();
 
328
 
 
329
    AutoCaller mTargetCaller;
 
330
    bool mfKeepMediumLockList;
 
331
};
 
332
 
 
333
class Medium::CloneTask : public Medium::Task
 
334
{
 
335
public:
 
336
    CloneTask(Medium *aMedium,
 
337
              Progress *aProgress,
 
338
              Medium *aTarget,
 
339
              MediumVariant_T aVariant,
 
340
              Medium *aParent,
 
341
              MediumLockList *aSourceMediumLockList,
 
342
              MediumLockList *aTargetMediumLockList,
 
343
              bool fKeepSourceMediumLockList = false,
 
344
              bool fKeepTargetMediumLockList = false)
 
345
        : Medium::Task(aMedium, aProgress),
 
346
          mTarget(aTarget),
 
347
          mParent(aParent),
 
348
          mpSourceMediumLockList(aSourceMediumLockList),
 
349
          mpTargetMediumLockList(aTargetMediumLockList),
 
350
          mVariant(aVariant),
 
351
          mTargetCaller(aTarget),
 
352
          mParentCaller(aParent),
 
353
          mfKeepSourceMediumLockList(fKeepSourceMediumLockList),
 
354
          mfKeepTargetMediumLockList(fKeepTargetMediumLockList)
 
355
    {
 
356
        AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
 
357
        mRC = mTargetCaller.rc();
 
358
        if (FAILED(mRC))
 
359
            return;
 
360
        /* aParent may be NULL */
 
361
        mRC = mParentCaller.rc();
 
362
        if (FAILED(mRC))
 
363
            return;
 
364
        AssertReturnVoidStmt(aSourceMediumLockList != NULL, mRC = E_FAIL);
 
365
        AssertReturnVoidStmt(aTargetMediumLockList != NULL, mRC = E_FAIL);
 
366
    }
 
367
 
 
368
    ~CloneTask()
 
369
    {
 
370
        if (!mfKeepSourceMediumLockList && mpSourceMediumLockList)
 
371
            delete mpSourceMediumLockList;
 
372
        if (!mfKeepTargetMediumLockList && mpTargetMediumLockList)
 
373
            delete mpTargetMediumLockList;
 
374
    }
 
375
 
 
376
    const ComObjPtr<Medium> mTarget;
 
377
    const ComObjPtr<Medium> mParent;
 
378
    MediumLockList *mpSourceMediumLockList;
 
379
    MediumLockList *mpTargetMediumLockList;
 
380
    MediumVariant_T mVariant;
 
381
 
 
382
private:
 
383
    virtual HRESULT handler();
 
384
 
 
385
    AutoCaller mTargetCaller;
 
386
    AutoCaller mParentCaller;
 
387
    bool mfKeepSourceMediumLockList;
 
388
    bool mfKeepTargetMediumLockList;
 
389
};
 
390
 
 
391
class Medium::CompactTask : public Medium::Task
 
392
{
 
393
public:
 
394
    CompactTask(Medium *aMedium,
 
395
                Progress *aProgress,
 
396
                MediumLockList *aMediumLockList,
 
397
                bool fKeepMediumLockList = false)
 
398
        : Medium::Task(aMedium, aProgress),
 
399
          mpMediumLockList(aMediumLockList),
 
400
          mfKeepMediumLockList(fKeepMediumLockList)
 
401
    {
 
402
        AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
 
403
    }
 
404
 
 
405
    ~CompactTask()
 
406
    {
 
407
        if (!mfKeepMediumLockList && mpMediumLockList)
 
408
            delete mpMediumLockList;
 
409
    }
 
410
 
 
411
    MediumLockList *mpMediumLockList;
 
412
 
 
413
private:
 
414
    virtual HRESULT handler();
 
415
 
 
416
    bool mfKeepMediumLockList;
 
417
};
 
418
 
 
419
class Medium::ResizeTask : public Medium::Task
 
420
{
 
421
public:
 
422
    ResizeTask(Medium *aMedium,
 
423
               uint64_t aSize,
 
424
               Progress *aProgress,
 
425
               MediumLockList *aMediumLockList,
 
426
               bool fKeepMediumLockList = false)
 
427
        : Medium::Task(aMedium, aProgress),
 
428
          mSize(aSize),
 
429
          mpMediumLockList(aMediumLockList),
 
430
          mfKeepMediumLockList(fKeepMediumLockList)
 
431
    {
 
432
        AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
 
433
    }
 
434
 
 
435
    ~ResizeTask()
 
436
    {
 
437
        if (!mfKeepMediumLockList && mpMediumLockList)
 
438
            delete mpMediumLockList;
 
439
    }
 
440
 
 
441
    uint64_t        mSize;
 
442
    MediumLockList *mpMediumLockList;
 
443
 
 
444
private:
 
445
    virtual HRESULT handler();
 
446
 
 
447
    bool mfKeepMediumLockList;
 
448
};
 
449
 
 
450
class Medium::ResetTask : public Medium::Task
 
451
{
 
452
public:
 
453
    ResetTask(Medium *aMedium,
 
454
              Progress *aProgress,
 
455
              MediumLockList *aMediumLockList,
 
456
              bool fKeepMediumLockList = false)
 
457
        : Medium::Task(aMedium, aProgress),
 
458
          mpMediumLockList(aMediumLockList),
 
459
          mfKeepMediumLockList(fKeepMediumLockList)
 
460
    {}
 
461
 
 
462
    ~ResetTask()
 
463
    {
 
464
        if (!mfKeepMediumLockList && mpMediumLockList)
 
465
            delete mpMediumLockList;
 
466
    }
 
467
 
 
468
    MediumLockList *mpMediumLockList;
 
469
 
 
470
private:
 
471
    virtual HRESULT handler();
 
472
 
 
473
    bool mfKeepMediumLockList;
 
474
};
 
475
 
 
476
class Medium::DeleteTask : public Medium::Task
 
477
{
 
478
public:
 
479
    DeleteTask(Medium *aMedium,
 
480
               Progress *aProgress,
 
481
               MediumLockList *aMediumLockList,
 
482
               bool fKeepMediumLockList = false)
 
483
        : Medium::Task(aMedium, aProgress),
 
484
          mpMediumLockList(aMediumLockList),
 
485
          mfKeepMediumLockList(fKeepMediumLockList)
 
486
    {}
 
487
 
 
488
    ~DeleteTask()
 
489
    {
 
490
        if (!mfKeepMediumLockList && mpMediumLockList)
 
491
            delete mpMediumLockList;
 
492
    }
 
493
 
 
494
    MediumLockList *mpMediumLockList;
 
495
 
 
496
private:
 
497
    virtual HRESULT handler();
 
498
 
 
499
    bool mfKeepMediumLockList;
 
500
};
 
501
 
 
502
class Medium::MergeTask : public Medium::Task
 
503
{
 
504
public:
 
505
    MergeTask(Medium *aMedium,
 
506
              Medium *aTarget,
 
507
              bool fMergeForward,
 
508
              Medium *aParentForTarget,
 
509
              const MediaList &aChildrenToReparent,
 
510
              Progress *aProgress,
 
511
              MediumLockList *aMediumLockList,
 
512
              bool fKeepMediumLockList = false)
 
513
        : Medium::Task(aMedium, aProgress),
 
514
          mTarget(aTarget),
 
515
          mfMergeForward(fMergeForward),
 
516
          mParentForTarget(aParentForTarget),
 
517
          mChildrenToReparent(aChildrenToReparent),
 
518
          mpMediumLockList(aMediumLockList),
 
519
          mTargetCaller(aTarget),
 
520
          mParentForTargetCaller(aParentForTarget),
 
521
          mfChildrenCaller(false),
 
522
          mfKeepMediumLockList(fKeepMediumLockList)
 
523
    {
 
524
        AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
 
525
        for (MediaList::const_iterator it = mChildrenToReparent.begin();
 
526
             it != mChildrenToReparent.end();
 
527
             ++it)
 
528
        {
 
529
            HRESULT rc2 = (*it)->addCaller();
 
530
            if (FAILED(rc2))
 
531
            {
 
532
                mRC = E_FAIL;
 
533
                for (MediaList::const_iterator it2 = mChildrenToReparent.begin();
 
534
                     it2 != it;
 
535
                     --it2)
 
536
                {
 
537
                    (*it2)->releaseCaller();
 
538
                }
 
539
                return;
 
540
            }
 
541
        }
 
542
        mfChildrenCaller = true;
 
543
    }
 
544
 
 
545
    ~MergeTask()
 
546
    {
 
547
        if (!mfKeepMediumLockList && mpMediumLockList)
 
548
            delete mpMediumLockList;
 
549
        if (mfChildrenCaller)
 
550
        {
 
551
            for (MediaList::const_iterator it = mChildrenToReparent.begin();
 
552
                 it != mChildrenToReparent.end();
 
553
                 ++it)
 
554
            {
 
555
                (*it)->releaseCaller();
 
556
            }
 
557
        }
 
558
    }
 
559
 
 
560
    const ComObjPtr<Medium> mTarget;
 
561
    bool mfMergeForward;
 
562
    /* When mChildrenToReparent is empty then mParentForTarget is non-null.
 
563
     * In other words: they are used in different cases. */
 
564
    const ComObjPtr<Medium> mParentForTarget;
 
565
    MediaList mChildrenToReparent;
 
566
    MediumLockList *mpMediumLockList;
 
567
 
 
568
private:
 
569
    virtual HRESULT handler();
 
570
 
 
571
    AutoCaller mTargetCaller;
 
572
    AutoCaller mParentForTargetCaller;
 
573
    bool mfChildrenCaller;
 
574
    bool mfKeepMediumLockList;
 
575
};
 
576
 
 
577
class Medium::ExportTask : public Medium::Task
 
578
{
 
579
public:
 
580
    ExportTask(Medium *aMedium,
 
581
               Progress *aProgress,
 
582
               const char *aFilename,
 
583
               MediumFormat *aFormat,
 
584
               MediumVariant_T aVariant,
 
585
               void *aVDImageIOCallbacks,
 
586
               void *aVDImageIOUser,
 
587
               MediumLockList *aSourceMediumLockList,
 
588
               bool fKeepSourceMediumLockList = false)
 
589
        : Medium::Task(aMedium, aProgress),
 
590
          mpSourceMediumLockList(aSourceMediumLockList),
 
591
          mFilename(aFilename),
 
592
          mFormat(aFormat),
 
593
          mVariant(aVariant),
 
594
          mfKeepSourceMediumLockList(fKeepSourceMediumLockList)
 
595
    {
 
596
        AssertReturnVoidStmt(aSourceMediumLockList != NULL, mRC = E_FAIL);
 
597
 
 
598
        mVDImageIfaces = aMedium->m->vdImageIfaces;
 
599
        if (aVDImageIOCallbacks)
 
600
        {
 
601
            int vrc = VDInterfaceAdd(&mVDInterfaceIO, "Medium::vdInterfaceIO",
 
602
                                     VDINTERFACETYPE_IO, aVDImageIOCallbacks,
 
603
                                     aVDImageIOUser, &mVDImageIfaces);
 
604
            AssertRCReturnVoidStmt(vrc, mRC = E_FAIL);
 
605
        }
 
606
    }
 
607
 
 
608
    ~ExportTask()
 
609
    {
 
610
        if (!mfKeepSourceMediumLockList && mpSourceMediumLockList)
 
611
            delete mpSourceMediumLockList;
 
612
    }
 
613
 
 
614
    MediumLockList *mpSourceMediumLockList;
 
615
    Utf8Str mFilename;
 
616
    ComObjPtr<MediumFormat> mFormat;
 
617
    MediumVariant_T mVariant;
 
618
    PVDINTERFACE mVDImageIfaces;
 
619
 
 
620
private:
 
621
    virtual HRESULT handler();
 
622
 
 
623
    bool mfKeepSourceMediumLockList;
 
624
    VDINTERFACE mVDInterfaceIO;
 
625
};
 
626
 
 
627
class Medium::ImportTask : public Medium::Task
 
628
{
 
629
public:
 
630
    ImportTask(Medium *aMedium,
 
631
               Progress *aProgress,
 
632
               const char *aFilename,
 
633
               MediumFormat *aFormat,
 
634
               MediumVariant_T aVariant,
 
635
               void *aVDImageIOCallbacks,
 
636
               void *aVDImageIOUser,
 
637
               Medium *aParent,
 
638
               MediumLockList *aTargetMediumLockList,
 
639
               bool fKeepTargetMediumLockList = false)
 
640
        : Medium::Task(aMedium, aProgress),
 
641
          mFilename(aFilename),
 
642
          mFormat(aFormat),
 
643
          mVariant(aVariant),
 
644
          mParent(aParent),
 
645
          mpTargetMediumLockList(aTargetMediumLockList),
 
646
          mParentCaller(aParent),
 
647
          mfKeepTargetMediumLockList(fKeepTargetMediumLockList)
 
648
    {
 
649
        AssertReturnVoidStmt(aTargetMediumLockList != NULL, mRC = E_FAIL);
 
650
        /* aParent may be NULL */
 
651
        mRC = mParentCaller.rc();
 
652
        if (FAILED(mRC))
 
653
            return;
 
654
 
 
655
        mVDImageIfaces = aMedium->m->vdImageIfaces;
 
656
        if (aVDImageIOCallbacks)
 
657
        {
 
658
            int vrc = VDInterfaceAdd(&mVDInterfaceIO, "Medium::vdInterfaceIO",
 
659
                                     VDINTERFACETYPE_IO, aVDImageIOCallbacks,
 
660
                                     aVDImageIOUser, &mVDImageIfaces);
 
661
            AssertRCReturnVoidStmt(vrc, mRC = E_FAIL);
 
662
        }
 
663
    }
 
664
 
 
665
    ~ImportTask()
 
666
    {
 
667
        if (!mfKeepTargetMediumLockList && mpTargetMediumLockList)
 
668
            delete mpTargetMediumLockList;
 
669
    }
 
670
 
 
671
    Utf8Str mFilename;
 
672
    ComObjPtr<MediumFormat> mFormat;
 
673
    MediumVariant_T mVariant;
 
674
    const ComObjPtr<Medium> mParent;
 
675
    MediumLockList *mpTargetMediumLockList;
 
676
    PVDINTERFACE mVDImageIfaces;
 
677
 
 
678
private:
 
679
    virtual HRESULT handler();
 
680
 
 
681
    AutoCaller mParentCaller;
 
682
    bool mfKeepTargetMediumLockList;
 
683
    VDINTERFACE mVDInterfaceIO;
 
684
};
 
685
 
 
686
/**
 
687
 * Thread function for time-consuming medium tasks.
 
688
 *
 
689
 * @param pvUser    Pointer to the Medium::Task instance.
 
690
 */
 
691
/* static */
 
692
DECLCALLBACK(int) Medium::Task::fntMediumTask(RTTHREAD aThread, void *pvUser)
 
693
{
 
694
    LogFlowFuncEnter();
 
695
    AssertReturn(pvUser, (int)E_INVALIDARG);
 
696
    Medium::Task *pTask = static_cast<Medium::Task *>(pvUser);
 
697
 
 
698
    pTask->mThread = aThread;
 
699
 
 
700
    HRESULT rc = pTask->handler();
 
701
 
 
702
    /* complete the progress if run asynchronously */
 
703
    if (pTask->isAsync())
 
704
    {
 
705
        if (!pTask->mProgress.isNull())
 
706
            pTask->mProgress->notifyComplete(rc);
 
707
    }
 
708
 
 
709
    /* pTask is no longer needed, delete it. */
 
710
    delete pTask;
 
711
 
 
712
    LogFlowFunc(("rc=%Rhrc\n", rc));
 
713
    LogFlowFuncLeave();
 
714
 
 
715
    return (int)rc;
 
716
}
 
717
 
 
718
/**
 
719
 * PFNVDPROGRESS callback handler for Task operations.
 
720
 *
 
721
 * @param pvUser      Pointer to the Progress instance.
 
722
 * @param uPercent    Completion percentage (0-100).
 
723
 */
 
724
/*static*/
 
725
DECLCALLBACK(int) Medium::Task::vdProgressCall(void *pvUser, unsigned uPercent)
 
726
{
 
727
    Progress *that = static_cast<Progress *>(pvUser);
 
728
 
 
729
    if (that != NULL)
 
730
    {
 
731
        /* update the progress object, capping it at 99% as the final percent
 
732
         * is used for additional operations like setting the UUIDs and similar. */
 
733
        HRESULT rc = that->SetCurrentOperationProgress(uPercent * 99 / 100);
 
734
        if (FAILED(rc))
 
735
        {
 
736
            if (rc == E_FAIL)
 
737
                return VERR_CANCELLED;
 
738
            else
 
739
                return VERR_INVALID_STATE;
 
740
        }
 
741
    }
 
742
 
 
743
    return VINF_SUCCESS;
 
744
}
 
745
 
 
746
/**
 
747
 * Implementation code for the "create base" task.
 
748
 */
 
749
HRESULT Medium::CreateBaseTask::handler()
 
750
{
 
751
    return mMedium->taskCreateBaseHandler(*this);
 
752
}
 
753
 
 
754
/**
 
755
 * Implementation code for the "create diff" task.
 
756
 */
 
757
HRESULT Medium::CreateDiffTask::handler()
 
758
{
 
759
    return mMedium->taskCreateDiffHandler(*this);
 
760
}
 
761
 
 
762
/**
 
763
 * Implementation code for the "clone" task.
 
764
 */
 
765
HRESULT Medium::CloneTask::handler()
 
766
{
 
767
    return mMedium->taskCloneHandler(*this);
 
768
}
 
769
 
 
770
/**
 
771
 * Implementation code for the "compact" task.
 
772
 */
 
773
HRESULT Medium::CompactTask::handler()
 
774
{
 
775
    return mMedium->taskCompactHandler(*this);
 
776
}
 
777
 
 
778
/**
 
779
 * Implementation code for the "resize" task.
 
780
 */
 
781
HRESULT Medium::ResizeTask::handler()
 
782
{
 
783
    return mMedium->taskResizeHandler(*this);
 
784
}
 
785
 
 
786
 
 
787
/**
 
788
 * Implementation code for the "reset" task.
 
789
 */
 
790
HRESULT Medium::ResetTask::handler()
 
791
{
 
792
    return mMedium->taskResetHandler(*this);
 
793
}
 
794
 
 
795
/**
 
796
 * Implementation code for the "delete" task.
 
797
 */
 
798
HRESULT Medium::DeleteTask::handler()
 
799
{
 
800
    return mMedium->taskDeleteHandler(*this);
 
801
}
 
802
 
 
803
/**
 
804
 * Implementation code for the "merge" task.
 
805
 */
 
806
HRESULT Medium::MergeTask::handler()
 
807
{
 
808
    return mMedium->taskMergeHandler(*this);
 
809
}
 
810
 
 
811
/**
 
812
 * Implementation code for the "export" task.
 
813
 */
 
814
HRESULT Medium::ExportTask::handler()
 
815
{
 
816
    return mMedium->taskExportHandler(*this);
 
817
}
 
818
 
 
819
/**
 
820
 * Implementation code for the "import" task.
 
821
 */
 
822
HRESULT Medium::ImportTask::handler()
 
823
{
 
824
    return mMedium->taskImportHandler(*this);
 
825
}
 
826
 
 
827
////////////////////////////////////////////////////////////////////////////////
 
828
//
 
829
// Medium constructor / destructor
 
830
//
 
831
////////////////////////////////////////////////////////////////////////////////
 
832
 
 
833
DEFINE_EMPTY_CTOR_DTOR(Medium)
 
834
 
 
835
HRESULT Medium::FinalConstruct()
 
836
{
 
837
    m = new Data;
 
838
 
 
839
    /* Initialize the callbacks of the VD error interface */
 
840
    m->vdIfCallsError.cbSize = sizeof(VDINTERFACEERROR);
 
841
    m->vdIfCallsError.enmInterface = VDINTERFACETYPE_ERROR;
 
842
    m->vdIfCallsError.pfnError = vdErrorCall;
 
843
    m->vdIfCallsError.pfnMessage = NULL;
 
844
 
 
845
    /* Initialize the callbacks of the VD config interface */
 
846
    m->vdIfCallsConfig.cbSize = sizeof(VDINTERFACECONFIG);
 
847
    m->vdIfCallsConfig.enmInterface = VDINTERFACETYPE_CONFIG;
 
848
    m->vdIfCallsConfig.pfnAreKeysValid = vdConfigAreKeysValid;
 
849
    m->vdIfCallsConfig.pfnQuerySize = vdConfigQuerySize;
 
850
    m->vdIfCallsConfig.pfnQuery = vdConfigQuery;
 
851
 
 
852
    /* Initialize the callbacks of the VD TCP interface (we always use the host
 
853
     * IP stack for now) */
 
854
    m->vdIfCallsTcpNet.cbSize = sizeof(VDINTERFACETCPNET);
 
855
    m->vdIfCallsTcpNet.enmInterface = VDINTERFACETYPE_TCPNET;
 
856
    m->vdIfCallsTcpNet.pfnSocketCreate = vdTcpSocketCreate;
 
857
    m->vdIfCallsTcpNet.pfnSocketDestroy = vdTcpSocketDestroy;
 
858
    m->vdIfCallsTcpNet.pfnClientConnect = vdTcpClientConnect;
 
859
    m->vdIfCallsTcpNet.pfnClientClose = vdTcpClientClose;
 
860
    m->vdIfCallsTcpNet.pfnIsClientConnected = vdTcpIsClientConnected;
 
861
    m->vdIfCallsTcpNet.pfnSelectOne = vdTcpSelectOne;
 
862
    m->vdIfCallsTcpNet.pfnRead = vdTcpRead;
 
863
    m->vdIfCallsTcpNet.pfnWrite = vdTcpWrite;
 
864
    m->vdIfCallsTcpNet.pfnSgWrite = vdTcpSgWrite;
 
865
    m->vdIfCallsTcpNet.pfnFlush = vdTcpFlush;
 
866
    m->vdIfCallsTcpNet.pfnSetSendCoalescing = vdTcpSetSendCoalescing;
 
867
    m->vdIfCallsTcpNet.pfnGetLocalAddress = vdTcpGetLocalAddress;
 
868
    m->vdIfCallsTcpNet.pfnGetPeerAddress = vdTcpGetPeerAddress;
 
869
    m->vdIfCallsTcpNet.pfnSelectOneEx = NULL;
 
870
    m->vdIfCallsTcpNet.pfnPoke = NULL;
 
871
 
 
872
    /* Initialize the per-disk interface chain (could be done more globally,
 
873
     * but it's not wasting much time or space so it's not worth it). */
 
874
    int vrc;
 
875
    vrc = VDInterfaceAdd(&m->vdIfError,
 
876
                         "Medium::vdInterfaceError",
 
877
                         VDINTERFACETYPE_ERROR,
 
878
                         &m->vdIfCallsError, this, &m->vdDiskIfaces);
 
879
    AssertRCReturn(vrc, E_FAIL);
 
880
 
 
881
    /* Initialize the per-image interface chain */
 
882
    vrc = VDInterfaceAdd(&m->vdIfConfig,
 
883
                         "Medium::vdInterfaceConfig",
 
884
                         VDINTERFACETYPE_CONFIG,
 
885
                         &m->vdIfCallsConfig, this, &m->vdImageIfaces);
 
886
    AssertRCReturn(vrc, E_FAIL);
 
887
 
 
888
    vrc = VDInterfaceAdd(&m->vdIfTcpNet,
 
889
                         "Medium::vdInterfaceTcpNet",
 
890
                         VDINTERFACETYPE_TCPNET,
 
891
                         &m->vdIfCallsTcpNet, this, &m->vdImageIfaces);
 
892
    AssertRCReturn(vrc, E_FAIL);
 
893
 
 
894
    vrc = RTSemEventMultiCreate(&m->queryInfoSem);
 
895
    AssertRCReturn(vrc, E_FAIL);
 
896
    vrc = RTSemEventMultiSignal(m->queryInfoSem);
 
897
    AssertRCReturn(vrc, E_FAIL);
 
898
 
 
899
    return S_OK;
 
900
}
 
901
 
 
902
void Medium::FinalRelease()
 
903
{
 
904
    uninit();
 
905
 
 
906
    delete m;
 
907
}
 
908
 
 
909
/**
 
910
 * Initializes an empty hard disk object without creating or opening an associated
 
911
 * storage unit.
 
912
 *
 
913
 * This gets called by VirtualBox::CreateHardDisk() in which case uuidMachineRegistry
 
914
 * is empty since starting with VirtualBox 4.0, we no longer add opened media to a
 
915
 * registry automatically (this is deferred until the medium is attached to a machine).
 
916
 *
 
917
 * This also gets called when VirtualBox creates diff images; in this case uuidMachineRegistry
 
918
 * is set to the registry of the parent image to make sure they all end up in the same
 
919
 * file.
 
920
 *
 
921
 * For hard disks that don't have the VD_CAP_CREATE_FIXED or
 
922
 * VD_CAP_CREATE_DYNAMIC capability (and therefore cannot be created or deleted
 
923
 * with the means of VirtualBox) the associated storage unit is assumed to be
 
924
 * ready for use so the state of the hard disk object will be set to Created.
 
925
 *
 
926
 * @param aVirtualBox   VirtualBox object.
 
927
 * @param aFormat
 
928
 * @param aLocation     Storage unit location.
 
929
 * @param uuidMachineRegistry The registry to which this medium should be added (global registry UUI or medium UUID or empty if none).
 
930
 * @param pllRegistriesThatNeedSaving Optional list to receive the UUIDs of the media registries that need saving.
 
931
 */
 
932
HRESULT Medium::init(VirtualBox *aVirtualBox,
 
933
                     const Utf8Str &aFormat,
 
934
                     const Utf8Str &aLocation,
 
935
                     const Guid &uuidMachineRegistry,
 
936
                     GuidList *pllRegistriesThatNeedSaving)
 
937
{
 
938
    AssertReturn(aVirtualBox != NULL, E_FAIL);
 
939
    AssertReturn(!aFormat.isEmpty(), E_FAIL);
 
940
 
 
941
    /* Enclose the state transition NotReady->InInit->Ready */
 
942
    AutoInitSpan autoInitSpan(this);
 
943
    AssertReturn(autoInitSpan.isOk(), E_FAIL);
 
944
 
 
945
    HRESULT rc = S_OK;
 
946
 
 
947
    unconst(m->pVirtualBox) = aVirtualBox;
 
948
 
 
949
    if (!uuidMachineRegistry.isEmpty())
 
950
        m->llRegistryIDs.push_back(uuidMachineRegistry);
 
951
 
 
952
    /* no storage yet */
 
953
    m->state = MediumState_NotCreated;
 
954
 
 
955
    /* cannot be a host drive */
 
956
    m->hostDrive = false;
 
957
 
 
958
    /* No storage unit is created yet, no need to queryInfo() */
 
959
 
 
960
    rc = setFormat(aFormat);
 
961
    if (FAILED(rc)) return rc;
 
962
 
 
963
    rc = setLocation(aLocation);
 
964
    if (FAILED(rc)) return rc;
 
965
 
 
966
    if (!(m->formatObj->getCapabilities() & (   MediumFormatCapabilities_CreateFixed
 
967
                                              | MediumFormatCapabilities_CreateDynamic))
 
968
       )
 
969
    {
 
970
        /* Storage for hard disks of this format can neither be explicitly
 
971
         * created by VirtualBox nor deleted, so we place the hard disk to
 
972
         * Inaccessible state here and also add it to the registry. The
 
973
         * state means that one has to use RefreshState() to update the
 
974
         * medium format specific fields. */
 
975
        m->state = MediumState_Inaccessible;
 
976
        // create new UUID
 
977
        unconst(m->id).create();
 
978
 
 
979
        AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
980
        rc = m->pVirtualBox->registerHardDisk(this, pllRegistriesThatNeedSaving);
 
981
    }
 
982
 
 
983
    /* Confirm a successful initialization when it's the case */
 
984
    if (SUCCEEDED(rc))
 
985
        autoInitSpan.setSucceeded();
 
986
 
 
987
    return rc;
 
988
}
 
989
 
 
990
/**
 
991
 * Initializes the medium object by opening the storage unit at the specified
 
992
 * location. The enOpenMode parameter defines whether the medium will be opened
 
993
 * read/write or read-only.
 
994
 *
 
995
 * This gets called by VirtualBox::OpenMedium() and also by
 
996
 * Machine::AttachDevice() and createImplicitDiffs() when new diff
 
997
 * images are created.
 
998
 *
 
999
 * There is no registry for this case since starting with VirtualBox 4.0, we
 
1000
 * no longer add opened media to a registry automatically (this is deferred
 
1001
 * until the medium is attached to a machine).
 
1002
 *
 
1003
 * For hard disks, the UUID, format and the parent of this medium will be
 
1004
 * determined when reading the medium storage unit. For DVD and floppy images,
 
1005
 * which have no UUIDs in their storage units, new UUIDs are created.
 
1006
 * If the detected or set parent is not known to VirtualBox, then this method
 
1007
 * will fail.
 
1008
 *
 
1009
 * @param aVirtualBox   VirtualBox object.
 
1010
 * @param aLocation     Storage unit location.
 
1011
 * @param enOpenMode    Whether to open the medium read/write or read-only.
 
1012
 * @param aDeviceType   Device type of medium.
 
1013
 */
 
1014
HRESULT Medium::init(VirtualBox *aVirtualBox,
 
1015
                     const Utf8Str &aLocation,
 
1016
                     HDDOpenMode enOpenMode,
 
1017
                     DeviceType_T aDeviceType)
 
1018
{
 
1019
    AssertReturn(aVirtualBox, E_INVALIDARG);
 
1020
    AssertReturn(!aLocation.isEmpty(), E_INVALIDARG);
 
1021
 
 
1022
    /* Enclose the state transition NotReady->InInit->Ready */
 
1023
    AutoInitSpan autoInitSpan(this);
 
1024
    AssertReturn(autoInitSpan.isOk(), E_FAIL);
 
1025
 
 
1026
    HRESULT rc = S_OK;
 
1027
 
 
1028
    unconst(m->pVirtualBox) = aVirtualBox;
 
1029
 
 
1030
    /* there must be a storage unit */
 
1031
    m->state = MediumState_Created;
 
1032
 
 
1033
    /* remember device type for correct unregistering later */
 
1034
    m->devType = aDeviceType;
 
1035
 
 
1036
    /* cannot be a host drive */
 
1037
    m->hostDrive = false;
 
1038
 
 
1039
    /* remember the open mode (defaults to ReadWrite) */
 
1040
    m->hddOpenMode = enOpenMode;
 
1041
 
 
1042
    if (aDeviceType == DeviceType_DVD)
 
1043
        m->type = MediumType_Readonly;
 
1044
    else if (aDeviceType == DeviceType_Floppy)
 
1045
        m->type = MediumType_Writethrough;
 
1046
 
 
1047
    rc = setLocation(aLocation);
 
1048
    if (FAILED(rc)) return rc;
 
1049
 
 
1050
    /* get all the information about the medium from the storage unit */
 
1051
    rc = queryInfo(false /* fSetImageId */, false /* fSetParentId */);
 
1052
 
 
1053
    if (SUCCEEDED(rc))
 
1054
    {
 
1055
        /* if the storage unit is not accessible, it's not acceptable for the
 
1056
         * newly opened media so convert this into an error */
 
1057
        if (m->state == MediumState_Inaccessible)
 
1058
        {
 
1059
            Assert(!m->strLastAccessError.isEmpty());
 
1060
            rc = setError(E_FAIL, "%s", m->strLastAccessError.c_str());
 
1061
        }
 
1062
        else
 
1063
        {
 
1064
            AssertReturn(!m->id.isEmpty(), E_FAIL);
 
1065
 
 
1066
            /* storage format must be detected by queryInfo() if the medium is accessible */
 
1067
            AssertReturn(!m->strFormat.isEmpty(), E_FAIL);
 
1068
        }
 
1069
    }
 
1070
 
 
1071
    /* Confirm a successful initialization when it's the case */
 
1072
    if (SUCCEEDED(rc))
 
1073
        autoInitSpan.setSucceeded();
 
1074
 
 
1075
    return rc;
 
1076
}
 
1077
 
 
1078
/**
 
1079
 * Initializes the medium object by loading its data from the given settings
 
1080
 * node. In this mode, the medium will always be opened read/write.
 
1081
 *
 
1082
 * In this case, since we're loading from a registry, uuidMachineRegistry is
 
1083
 * always set: it's either the global registry UUID or a machine UUID when
 
1084
 * loading from a per-machine registry.
 
1085
 *
 
1086
 * @param aVirtualBox   VirtualBox object.
 
1087
 * @param aParent       Parent medium disk or NULL for a root (base) medium.
 
1088
 * @param aDeviceType   Device type of the medium.
 
1089
 * @param uuidMachineRegistry The registry to which this medium should be added (global registry UUI or medium UUID).
 
1090
 * @param aNode         Configuration settings.
 
1091
 * @param strMachineFolder The machine folder with which to resolve relative paths; if empty, then we use the VirtualBox home directory
 
1092
 *
 
1093
 * @note Locks the medium tree for writing.
 
1094
 */
 
1095
HRESULT Medium::init(VirtualBox *aVirtualBox,
 
1096
                     Medium *aParent,
 
1097
                     DeviceType_T aDeviceType,
 
1098
                     const Guid &uuidMachineRegistry,
 
1099
                     const settings::Medium &data,
 
1100
                     const Utf8Str &strMachineFolder)
 
1101
{
 
1102
    using namespace settings;
 
1103
 
 
1104
    AssertReturn(aVirtualBox, E_INVALIDARG);
 
1105
 
 
1106
    /* Enclose the state transition NotReady->InInit->Ready */
 
1107
    AutoInitSpan autoInitSpan(this);
 
1108
    AssertReturn(autoInitSpan.isOk(), E_FAIL);
 
1109
 
 
1110
    HRESULT rc = S_OK;
 
1111
 
 
1112
    unconst(m->pVirtualBox) = aVirtualBox;
 
1113
 
 
1114
    if (!uuidMachineRegistry.isEmpty())
 
1115
        m->llRegistryIDs.push_back(uuidMachineRegistry);
 
1116
 
 
1117
    /* register with VirtualBox/parent early, since uninit() will
 
1118
     * unconditionally unregister on failure */
 
1119
    if (aParent)
 
1120
    {
 
1121
        // differencing medium: add to parent
 
1122
        AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
1123
        m->pParent = aParent;
 
1124
        aParent->m->llChildren.push_back(this);
 
1125
    }
 
1126
 
 
1127
    /* see below why we don't call queryInfo() (and therefore treat the medium
 
1128
     * as inaccessible for now */
 
1129
    m->state = MediumState_Inaccessible;
 
1130
    m->strLastAccessError = tr("Accessibility check was not yet performed");
 
1131
 
 
1132
    /* required */
 
1133
    unconst(m->id) = data.uuid;
 
1134
 
 
1135
    /* assume not a host drive */
 
1136
    m->hostDrive = false;
 
1137
 
 
1138
    /* optional */
 
1139
    m->strDescription = data.strDescription;
 
1140
 
 
1141
    /* required */
 
1142
    if (aDeviceType == DeviceType_HardDisk)
 
1143
    {
 
1144
        AssertReturn(!data.strFormat.isEmpty(), E_FAIL);
 
1145
        rc = setFormat(data.strFormat);
 
1146
        if (FAILED(rc)) return rc;
 
1147
    }
 
1148
    else
 
1149
    {
 
1150
        /// @todo handle host drive settings here as well?
 
1151
        if (!data.strFormat.isEmpty())
 
1152
            rc = setFormat(data.strFormat);
 
1153
        else
 
1154
            rc = setFormat("RAW");
 
1155
        if (FAILED(rc)) return rc;
 
1156
    }
 
1157
 
 
1158
    /* optional, only for diffs, default is false; we can only auto-reset
 
1159
     * diff media so they must have a parent */
 
1160
    if (aParent != NULL)
 
1161
        m->autoReset = data.fAutoReset;
 
1162
    else
 
1163
        m->autoReset = false;
 
1164
 
 
1165
    /* properties (after setting the format as it populates the map). Note that
 
1166
     * if some properties are not supported but present in the settings file,
 
1167
     * they will still be read and accessible (for possible backward
 
1168
     * compatibility; we can also clean them up from the XML upon next
 
1169
     * XML format version change if we wish) */
 
1170
    for (settings::StringsMap::const_iterator it = data.properties.begin();
 
1171
         it != data.properties.end();
 
1172
         ++it)
 
1173
    {
 
1174
        const Utf8Str &name = it->first;
 
1175
        const Utf8Str &value = it->second;
 
1176
        m->mapProperties[name] = value;
 
1177
    }
 
1178
 
 
1179
    // compose full path of the medium, if it's not fully qualified...
 
1180
    // slightly convoluted logic here. If the caller has given us a
 
1181
    // machine folder, then a relative path will be relative to that:
 
1182
    Utf8Str strFull;
 
1183
    if (    !strMachineFolder.isEmpty()
 
1184
         && !RTPathStartsWithRoot(data.strLocation.c_str())
 
1185
       )
 
1186
    {
 
1187
        strFull = strMachineFolder;
 
1188
        strFull += RTPATH_SLASH;
 
1189
        strFull += data.strLocation;
 
1190
    }
 
1191
    else
 
1192
    {
 
1193
        // Otherwise use the old VirtualBox "make absolute path" logic:
 
1194
        rc = m->pVirtualBox->calculateFullPath(data.strLocation, strFull);
 
1195
        if (FAILED(rc)) return rc;
 
1196
    }
 
1197
 
 
1198
    rc = setLocation(strFull);
 
1199
    if (FAILED(rc)) return rc;
 
1200
 
 
1201
    if (aDeviceType == DeviceType_HardDisk)
 
1202
    {
 
1203
        /* type is only for base hard disks */
 
1204
        if (m->pParent.isNull())
 
1205
            m->type = data.hdType;
 
1206
    }
 
1207
    else if (aDeviceType == DeviceType_DVD)
 
1208
        m->type = MediumType_Readonly;
 
1209
    else
 
1210
        m->type = MediumType_Writethrough;
 
1211
 
 
1212
    /* remember device type for correct unregistering later */
 
1213
    m->devType = aDeviceType;
 
1214
 
 
1215
    LogFlowThisFunc(("m->strLocationFull='%s', m->strFormat=%s, m->id={%RTuuid}\n",
 
1216
                     m->strLocationFull.c_str(), m->strFormat.c_str(), m->id.raw()));
 
1217
 
 
1218
    /* Don't call queryInfo() for registered media to prevent the calling
 
1219
     * thread (i.e. the VirtualBox server startup thread) from an unexpected
 
1220
     * freeze but mark it as initially inaccessible instead. The vital UUID,
 
1221
     * location and format properties are read from the registry file above; to
 
1222
     * get the actual state and the rest of the data, the user will have to call
 
1223
     * COMGETTER(State). */
 
1224
 
 
1225
    AutoWriteLock treeLock(aVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
1226
 
 
1227
    /* load all children */
 
1228
    for (settings::MediaList::const_iterator it = data.llChildren.begin();
 
1229
         it != data.llChildren.end();
 
1230
         ++it)
 
1231
    {
 
1232
        const settings::Medium &med = *it;
 
1233
 
 
1234
        ComObjPtr<Medium> pHD;
 
1235
        pHD.createObject();
 
1236
        rc = pHD->init(aVirtualBox,
 
1237
                       this,            // parent
 
1238
                       aDeviceType,
 
1239
                       uuidMachineRegistry,
 
1240
                       med,               // child data
 
1241
                       strMachineFolder);
 
1242
        if (FAILED(rc)) break;
 
1243
 
 
1244
        rc = m->pVirtualBox->registerHardDisk(pHD, NULL /* pllRegistriesThatNeedSaving */ );
 
1245
        if (FAILED(rc)) break;
 
1246
    }
 
1247
 
 
1248
    /* Confirm a successful initialization when it's the case */
 
1249
    if (SUCCEEDED(rc))
 
1250
        autoInitSpan.setSucceeded();
 
1251
 
 
1252
    return rc;
 
1253
}
 
1254
 
 
1255
/**
 
1256
 * Initializes the medium object by providing the host drive information.
 
1257
 * Not used for anything but the host floppy/host DVD case.
 
1258
 *
 
1259
 * There is no registry for this case.
 
1260
 *
 
1261
 * @param aVirtualBox   VirtualBox object.
 
1262
 * @param aDeviceType   Device type of the medium.
 
1263
 * @param aLocation     Location of the host drive.
 
1264
 * @param aDescription  Comment for this host drive.
 
1265
 *
 
1266
 * @note Locks VirtualBox lock for writing.
 
1267
 */
 
1268
HRESULT Medium::init(VirtualBox *aVirtualBox,
 
1269
                     DeviceType_T aDeviceType,
 
1270
                     const Utf8Str &aLocation,
 
1271
                     const Utf8Str &aDescription /* = Utf8Str::Empty */)
 
1272
{
 
1273
    ComAssertRet(aDeviceType == DeviceType_DVD || aDeviceType == DeviceType_Floppy, E_INVALIDARG);
 
1274
    ComAssertRet(!aLocation.isEmpty(), E_INVALIDARG);
 
1275
 
 
1276
    /* Enclose the state transition NotReady->InInit->Ready */
 
1277
    AutoInitSpan autoInitSpan(this);
 
1278
    AssertReturn(autoInitSpan.isOk(), E_FAIL);
 
1279
 
 
1280
    unconst(m->pVirtualBox) = aVirtualBox;
 
1281
 
 
1282
    /* fake up a UUID which is unique, but also reproducible */
 
1283
    RTUUID uuid;
 
1284
    RTUuidClear(&uuid);
 
1285
    if (aDeviceType == DeviceType_DVD)
 
1286
        memcpy(&uuid.au8[0], "DVD", 3);
 
1287
    else
 
1288
        memcpy(&uuid.au8[0], "FD", 2);
 
1289
    /* use device name, adjusted to the end of uuid, shortened if necessary */
 
1290
    size_t lenLocation = aLocation.length();
 
1291
    if (lenLocation > 12)
 
1292
        memcpy(&uuid.au8[4], aLocation.c_str() + (lenLocation - 12), 12);
 
1293
    else
 
1294
        memcpy(&uuid.au8[4 + 12 - lenLocation], aLocation.c_str(), lenLocation);
 
1295
    unconst(m->id) = uuid;
 
1296
 
 
1297
    if (aDeviceType == DeviceType_DVD)
 
1298
        m->type = MediumType_Readonly;
 
1299
    else
 
1300
        m->type = MediumType_Writethrough;
 
1301
    m->devType = aDeviceType;
 
1302
    m->state = MediumState_Created;
 
1303
    m->hostDrive = true;
 
1304
    HRESULT rc = setFormat("RAW");
 
1305
    if (FAILED(rc)) return rc;
 
1306
    rc = setLocation(aLocation);
 
1307
    if (FAILED(rc)) return rc;
 
1308
    m->strDescription = aDescription;
 
1309
 
 
1310
/// @todo generate uuid (similarly to host network interface uuid) from location and device type
 
1311
 
 
1312
    autoInitSpan.setSucceeded();
 
1313
    return S_OK;
 
1314
}
 
1315
 
 
1316
/**
 
1317
 * Uninitializes the instance.
 
1318
 *
 
1319
 * Called either from FinalRelease() or by the parent when it gets destroyed.
 
1320
 *
 
1321
 * @note All children of this medium get uninitialized by calling their
 
1322
 *       uninit() methods.
 
1323
 *
 
1324
 * @note Caller must hold the tree lock of the medium tree this medium is on.
 
1325
 */
 
1326
void Medium::uninit()
 
1327
{
 
1328
    /* Enclose the state transition Ready->InUninit->NotReady */
 
1329
    AutoUninitSpan autoUninitSpan(this);
 
1330
    if (autoUninitSpan.uninitDone())
 
1331
        return;
 
1332
 
 
1333
    if (!m->formatObj.isNull())
 
1334
    {
 
1335
        /* remove the caller reference we added in setFormat() */
 
1336
        m->formatObj->releaseCaller();
 
1337
        m->formatObj.setNull();
 
1338
    }
 
1339
 
 
1340
    if (m->state == MediumState_Deleting)
 
1341
    {
 
1342
        /* This medium has been already deleted (directly or as part of a
 
1343
         * merge).  Reparenting has already been done. */
 
1344
        Assert(m->pParent.isNull());
 
1345
    }
 
1346
    else
 
1347
    {
 
1348
        MediaList::iterator it;
 
1349
        for (it = m->llChildren.begin();
 
1350
            it != m->llChildren.end();
 
1351
            ++it)
 
1352
        {
 
1353
            Medium *pChild = *it;
 
1354
            pChild->m->pParent.setNull();
 
1355
            pChild->uninit();
 
1356
        }
 
1357
        m->llChildren.clear();          // this unsets all the ComPtrs and probably calls delete
 
1358
 
 
1359
        if (m->pParent)
 
1360
        {
 
1361
            // this is a differencing disk: then remove it from the parent's children list
 
1362
            deparent();
 
1363
        }
 
1364
    }
 
1365
 
 
1366
    RTSemEventMultiSignal(m->queryInfoSem);
 
1367
    RTSemEventMultiDestroy(m->queryInfoSem);
 
1368
    m->queryInfoSem = NIL_RTSEMEVENTMULTI;
 
1369
 
 
1370
    unconst(m->pVirtualBox) = NULL;
 
1371
}
 
1372
 
 
1373
/**
 
1374
 * Internal helper that removes "this" from the list of children of its
 
1375
 * parent. Used in uninit() and other places when reparenting is necessary.
 
1376
 *
 
1377
 * The caller must hold the medium tree lock!
 
1378
 */
 
1379
void Medium::deparent()
 
1380
{
 
1381
    MediaList &llParent = m->pParent->m->llChildren;
 
1382
    for (MediaList::iterator it = llParent.begin();
 
1383
         it != llParent.end();
 
1384
         ++it)
 
1385
    {
 
1386
        Medium *pParentsChild = *it;
 
1387
        if (this == pParentsChild)
 
1388
        {
 
1389
            llParent.erase(it);
 
1390
            break;
 
1391
        }
 
1392
    }
 
1393
    m->pParent.setNull();
 
1394
}
 
1395
 
 
1396
/**
 
1397
 * Internal helper that removes "this" from the list of children of its
 
1398
 * parent. Used in uninit() and other places when reparenting is necessary.
 
1399
 *
 
1400
 * The caller must hold the medium tree lock!
 
1401
 */
 
1402
void Medium::setParent(const ComObjPtr<Medium> &pParent)
 
1403
{
 
1404
    m->pParent = pParent;
 
1405
    if (pParent)
 
1406
        pParent->m->llChildren.push_back(this);
 
1407
}
 
1408
 
 
1409
 
 
1410
////////////////////////////////////////////////////////////////////////////////
 
1411
//
 
1412
// IMedium public methods
 
1413
//
 
1414
////////////////////////////////////////////////////////////////////////////////
 
1415
 
 
1416
STDMETHODIMP Medium::COMGETTER(Id)(BSTR *aId)
 
1417
{
 
1418
    CheckComArgOutPointerValid(aId);
 
1419
 
 
1420
    AutoCaller autoCaller(this);
 
1421
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1422
 
 
1423
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1424
 
 
1425
    m->id.toUtf16().cloneTo(aId);
 
1426
 
 
1427
    return S_OK;
 
1428
}
 
1429
 
 
1430
STDMETHODIMP Medium::COMGETTER(Description)(BSTR *aDescription)
 
1431
{
 
1432
    CheckComArgOutPointerValid(aDescription);
 
1433
 
 
1434
    AutoCaller autoCaller(this);
 
1435
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1436
 
 
1437
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1438
 
 
1439
    m->strDescription.cloneTo(aDescription);
 
1440
 
 
1441
    return S_OK;
 
1442
}
 
1443
 
 
1444
STDMETHODIMP Medium::COMSETTER(Description)(IN_BSTR aDescription)
 
1445
{
 
1446
    AutoCaller autoCaller(this);
 
1447
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1448
 
 
1449
//     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1450
 
 
1451
    /// @todo update m->description and save the global registry (and local
 
1452
    /// registries of portable VMs referring to this medium), this will also
 
1453
    /// require to add the mRegistered flag to data
 
1454
 
 
1455
    NOREF(aDescription);
 
1456
 
 
1457
    ReturnComNotImplemented();
 
1458
}
 
1459
 
 
1460
STDMETHODIMP Medium::COMGETTER(State)(MediumState_T *aState)
 
1461
{
 
1462
    CheckComArgOutPointerValid(aState);
 
1463
 
 
1464
    AutoCaller autoCaller(this);
 
1465
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1466
 
 
1467
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1468
    *aState = m->state;
 
1469
 
 
1470
    return S_OK;
 
1471
}
 
1472
 
 
1473
STDMETHODIMP Medium::COMGETTER(Variant)(ULONG *aVariant)
 
1474
{
 
1475
    CheckComArgOutPointerValid(aVariant);
 
1476
 
 
1477
    AutoCaller autoCaller(this);
 
1478
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1479
 
 
1480
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1481
    *aVariant = m->variant;
 
1482
 
 
1483
    return S_OK;
 
1484
}
 
1485
 
 
1486
 
 
1487
STDMETHODIMP Medium::COMGETTER(Location)(BSTR *aLocation)
 
1488
{
 
1489
    CheckComArgOutPointerValid(aLocation);
 
1490
 
 
1491
    AutoCaller autoCaller(this);
 
1492
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1493
 
 
1494
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1495
 
 
1496
    m->strLocationFull.cloneTo(aLocation);
 
1497
 
 
1498
    return S_OK;
 
1499
}
 
1500
 
 
1501
STDMETHODIMP Medium::COMSETTER(Location)(IN_BSTR aLocation)
 
1502
{
 
1503
    CheckComArgStrNotEmptyOrNull(aLocation);
 
1504
 
 
1505
    AutoCaller autoCaller(this);
 
1506
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1507
 
 
1508
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1509
 
 
1510
    /// @todo NEWMEDIA for file names, add the default extension if no extension
 
1511
    /// is present (using the information from the VD backend which also implies
 
1512
    /// that one more parameter should be passed to setLocation() requesting
 
1513
    /// that functionality since it is only allowed when called from this method
 
1514
 
 
1515
    /// @todo NEWMEDIA rename the file and set m->location on success, then save
 
1516
    /// the global registry (and local registries of portable VMs referring to
 
1517
    /// this medium), this will also require to add the mRegistered flag to data
 
1518
 
 
1519
    ReturnComNotImplemented();
 
1520
}
 
1521
 
 
1522
STDMETHODIMP Medium::COMGETTER(Name)(BSTR *aName)
 
1523
{
 
1524
    CheckComArgOutPointerValid(aName);
 
1525
 
 
1526
    AutoCaller autoCaller(this);
 
1527
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1528
 
 
1529
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1530
 
 
1531
    getName().cloneTo(aName);
 
1532
 
 
1533
    return S_OK;
 
1534
}
 
1535
 
 
1536
STDMETHODIMP Medium::COMGETTER(DeviceType)(DeviceType_T *aDeviceType)
 
1537
{
 
1538
    CheckComArgOutPointerValid(aDeviceType);
 
1539
 
 
1540
    AutoCaller autoCaller(this);
 
1541
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1542
 
 
1543
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1544
 
 
1545
    *aDeviceType = m->devType;
 
1546
 
 
1547
    return S_OK;
 
1548
}
 
1549
 
 
1550
STDMETHODIMP Medium::COMGETTER(HostDrive)(BOOL *aHostDrive)
 
1551
{
 
1552
    CheckComArgOutPointerValid(aHostDrive);
 
1553
 
 
1554
    AutoCaller autoCaller(this);
 
1555
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1556
 
 
1557
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1558
 
 
1559
    *aHostDrive = m->hostDrive;
 
1560
 
 
1561
    return S_OK;
 
1562
}
 
1563
 
 
1564
STDMETHODIMP Medium::COMGETTER(Size)(LONG64 *aSize)
 
1565
{
 
1566
    CheckComArgOutPointerValid(aSize);
 
1567
 
 
1568
    AutoCaller autoCaller(this);
 
1569
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1570
 
 
1571
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1572
 
 
1573
    *aSize = m->size;
 
1574
 
 
1575
    return S_OK;
 
1576
}
 
1577
 
 
1578
STDMETHODIMP Medium::COMGETTER(Format)(BSTR *aFormat)
 
1579
{
 
1580
    CheckComArgOutPointerValid(aFormat);
 
1581
 
 
1582
    AutoCaller autoCaller(this);
 
1583
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1584
 
 
1585
    /* no need to lock, m->strFormat is const */
 
1586
    m->strFormat.cloneTo(aFormat);
 
1587
 
 
1588
    return S_OK;
 
1589
}
 
1590
 
 
1591
STDMETHODIMP Medium::COMGETTER(MediumFormat)(IMediumFormat **aMediumFormat)
 
1592
{
 
1593
    CheckComArgOutPointerValid(aMediumFormat);
 
1594
 
 
1595
    AutoCaller autoCaller(this);
 
1596
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1597
 
 
1598
    /* no need to lock, m->formatObj is const */
 
1599
    m->formatObj.queryInterfaceTo(aMediumFormat);
 
1600
 
 
1601
    return S_OK;
 
1602
}
 
1603
 
 
1604
STDMETHODIMP Medium::COMGETTER(Type)(MediumType_T *aType)
 
1605
{
 
1606
    CheckComArgOutPointerValid(aType);
 
1607
 
 
1608
    AutoCaller autoCaller(this);
 
1609
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1610
 
 
1611
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1612
 
 
1613
    *aType = m->type;
 
1614
 
 
1615
    return S_OK;
 
1616
}
 
1617
 
 
1618
STDMETHODIMP Medium::COMSETTER(Type)(MediumType_T aType)
 
1619
{
 
1620
    AutoCaller autoCaller(this);
 
1621
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1622
 
 
1623
    // we access mParent and members
 
1624
    AutoMultiWriteLock2 mlock(&m->pVirtualBox->getMediaTreeLockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);
 
1625
 
 
1626
    switch (m->state)
 
1627
    {
 
1628
        case MediumState_Created:
 
1629
        case MediumState_Inaccessible:
 
1630
            break;
 
1631
        default:
 
1632
            return setStateError();
 
1633
    }
 
1634
 
 
1635
    if (m->type == aType)
 
1636
    {
 
1637
        /* Nothing to do */
 
1638
        return S_OK;
 
1639
    }
 
1640
 
 
1641
    DeviceType_T devType = getDeviceType();
 
1642
    // DVD media can only be readonly.
 
1643
    if (devType == DeviceType_DVD && aType != MediumType_Readonly)
 
1644
        return setError(VBOX_E_INVALID_OBJECT_STATE,
 
1645
                        tr("Cannot change the type of DVD medium '%s'"),
 
1646
                        m->strLocationFull.c_str());
 
1647
    // Floppy media can only be writethrough or readonly.
 
1648
    if (   devType == DeviceType_Floppy
 
1649
        && aType != MediumType_Writethrough
 
1650
        && aType != MediumType_Readonly)
 
1651
        return setError(VBOX_E_INVALID_OBJECT_STATE,
 
1652
                        tr("Cannot change the type of floppy medium '%s'"),
 
1653
                        m->strLocationFull.c_str());
 
1654
 
 
1655
    /* cannot change the type of a differencing medium */
 
1656
    if (m->pParent)
 
1657
        return setError(VBOX_E_INVALID_OBJECT_STATE,
 
1658
                        tr("Cannot change the type of medium '%s' because it is a differencing medium"),
 
1659
                        m->strLocationFull.c_str());
 
1660
 
 
1661
    /* Cannot change the type of a medium being in use by more than one VM.
 
1662
     * If the change is to Immutable or MultiAttach then it must not be
 
1663
     * directly attached to any VM, otherwise the assumptions about indirect
 
1664
     * attachment elsewhere are violated and the VM becomes inaccessible.
 
1665
     * Attaching an immutable medium triggers the diff creation, and this is
 
1666
     * vital for the correct operation. */
 
1667
    if (   m->backRefs.size() > 1
 
1668
        || (   (   aType == MediumType_Immutable
 
1669
                || aType == MediumType_MultiAttach)
 
1670
            && m->backRefs.size() > 0))
 
1671
        return setError(VBOX_E_INVALID_OBJECT_STATE,
 
1672
                        tr("Cannot change the type of medium '%s' because it is attached to %d virtual machines"),
 
1673
                        m->strLocationFull.c_str(), m->backRefs.size());
 
1674
 
 
1675
    switch (aType)
 
1676
    {
 
1677
        case MediumType_Normal:
 
1678
        case MediumType_Immutable:
 
1679
        case MediumType_MultiAttach:
 
1680
        {
 
1681
            /* normal can be easily converted to immutable and vice versa even
 
1682
             * if they have children as long as they are not attached to any
 
1683
             * machine themselves */
 
1684
            break;
 
1685
        }
 
1686
        case MediumType_Writethrough:
 
1687
        case MediumType_Shareable:
 
1688
        case MediumType_Readonly:
 
1689
        {
 
1690
            /* cannot change to writethrough, shareable or readonly
 
1691
             * if there are children */
 
1692
            if (getChildren().size() != 0)
 
1693
                return setError(VBOX_E_OBJECT_IN_USE,
 
1694
                                tr("Cannot change type for medium '%s' since it has %d child media"),
 
1695
                                m->strLocationFull.c_str(), getChildren().size());
 
1696
            if (aType == MediumType_Shareable)
 
1697
            {
 
1698
                if (m->state == MediumState_Inaccessible)
 
1699
                {
 
1700
                    HRESULT rc = queryInfo(false /* fSetImageId */, false /* fSetParentId */);
 
1701
                    if (FAILED(rc))
 
1702
                        return setError(rc,
 
1703
                                        tr("Cannot change type for medium '%s' to 'Shareable' because the medium is inaccessible"),
 
1704
                                        m->strLocationFull.c_str());
 
1705
                }
 
1706
 
 
1707
                MediumVariant_T variant = getVariant();
 
1708
                if (!(variant & MediumVariant_Fixed))
 
1709
                    return setError(VBOX_E_INVALID_OBJECT_STATE,
 
1710
                                    tr("Cannot change type for medium '%s' to 'Shareable' since it is a dynamic medium storage unit"),
 
1711
                                    m->strLocationFull.c_str());
 
1712
            }
 
1713
            else if (aType == MediumType_Readonly && devType == DeviceType_HardDisk)
 
1714
            {
 
1715
                // Readonly hard disks are not allowed, this medium type is reserved for
 
1716
                // DVDs and floppy images at the moment. Later we might allow readonly hard
 
1717
                // disks, but that's extremely unusual and many guest OSes will have trouble.
 
1718
                return setError(VBOX_E_INVALID_OBJECT_STATE,
 
1719
                                tr("Cannot change type for medium '%s' to 'Readonly' since it is a hard disk"),
 
1720
                                m->strLocationFull.c_str());
 
1721
            }
 
1722
            break;
 
1723
        }
 
1724
        default:
 
1725
            AssertFailedReturn(E_FAIL);
 
1726
    }
 
1727
 
 
1728
    if (aType == MediumType_MultiAttach)
 
1729
    {
 
1730
        // This type is new with VirtualBox 4.0 and therefore requires settings
 
1731
        // version 1.11 in the settings backend. Unfortunately it is not enough to do
 
1732
        // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
 
1733
        // two reasons: The medium type is a property of the media registry tree, which
 
1734
        // can reside in the global config file (for pre-4.0 media); we would therefore
 
1735
        // possibly need to bump the global config version. We don't want to do that though
 
1736
        // because that might make downgrading to pre-4.0 impossible.
 
1737
        // As a result, we can only use these two new types if the medium is NOT in the
 
1738
        // global registry:
 
1739
        const Guid &uuidGlobalRegistry = m->pVirtualBox->getGlobalRegistryId();
 
1740
        if (isInRegistry(uuidGlobalRegistry))
 
1741
            return setError(VBOX_E_INVALID_OBJECT_STATE,
 
1742
                            tr("Cannot change type for medium '%s': the media type 'MultiAttach' can only be used "
 
1743
                               "on media registered with a machine that was created with VirtualBox 4.0 or later"),
 
1744
                            m->strLocationFull.c_str());
 
1745
    }
 
1746
 
 
1747
    m->type = aType;
 
1748
 
 
1749
    // save the settings
 
1750
    GuidList llRegistriesThatNeedSaving;
 
1751
    addToRegistryIDList(llRegistriesThatNeedSaving);
 
1752
    mlock.release();
 
1753
    HRESULT rc = m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
 
1754
 
 
1755
    return rc;
 
1756
}
 
1757
 
 
1758
STDMETHODIMP Medium::COMGETTER(Parent)(IMedium **aParent)
 
1759
{
 
1760
    CheckComArgOutPointerValid(aParent);
 
1761
 
 
1762
    AutoCaller autoCaller(this);
 
1763
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1764
 
 
1765
    /* we access mParent */
 
1766
    AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
1767
 
 
1768
    m->pParent.queryInterfaceTo(aParent);
 
1769
 
 
1770
    return S_OK;
 
1771
}
 
1772
 
 
1773
STDMETHODIMP Medium::COMGETTER(Children)(ComSafeArrayOut(IMedium *, aChildren))
 
1774
{
 
1775
    CheckComArgOutSafeArrayPointerValid(aChildren);
 
1776
 
 
1777
    AutoCaller autoCaller(this);
 
1778
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1779
 
 
1780
    /* we access children */
 
1781
    AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
1782
 
 
1783
    SafeIfaceArray<IMedium> children(this->getChildren());
 
1784
    children.detachTo(ComSafeArrayOutArg(aChildren));
 
1785
 
 
1786
    return S_OK;
 
1787
}
 
1788
 
 
1789
STDMETHODIMP Medium::COMGETTER(Base)(IMedium **aBase)
 
1790
{
 
1791
    CheckComArgOutPointerValid(aBase);
 
1792
 
 
1793
    /* base() will do callers/locking */
 
1794
 
 
1795
    getBase().queryInterfaceTo(aBase);
 
1796
 
 
1797
    return S_OK;
 
1798
}
 
1799
 
 
1800
STDMETHODIMP Medium::COMGETTER(ReadOnly)(BOOL *aReadOnly)
 
1801
{
 
1802
    CheckComArgOutPointerValid(aReadOnly);
 
1803
 
 
1804
    AutoCaller autoCaller(this);
 
1805
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1806
 
 
1807
    /* isRadOnly() will do locking */
 
1808
 
 
1809
    *aReadOnly = isReadOnly();
 
1810
 
 
1811
    return S_OK;
 
1812
}
 
1813
 
 
1814
STDMETHODIMP Medium::COMGETTER(LogicalSize)(LONG64 *aLogicalSize)
 
1815
{
 
1816
    CheckComArgOutPointerValid(aLogicalSize);
 
1817
 
 
1818
    {
 
1819
        AutoCaller autoCaller(this);
 
1820
        if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1821
 
 
1822
        /* we access mParent */
 
1823
        AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
1824
 
 
1825
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1826
 
 
1827
        if (m->pParent.isNull())
 
1828
        {
 
1829
            *aLogicalSize = m->logicalSize;
 
1830
 
 
1831
            return S_OK;
 
1832
        }
 
1833
    }
 
1834
 
 
1835
    /* We assume that some backend may decide to return a meaningless value in
 
1836
     * response to VDGetSize() for differencing media and therefore always
 
1837
     * ask the base medium ourselves. */
 
1838
 
 
1839
    /* base() will do callers/locking */
 
1840
 
 
1841
    return getBase()->COMGETTER(LogicalSize)(aLogicalSize);
 
1842
}
 
1843
 
 
1844
STDMETHODIMP Medium::COMGETTER(AutoReset)(BOOL *aAutoReset)
 
1845
{
 
1846
    CheckComArgOutPointerValid(aAutoReset);
 
1847
 
 
1848
    AutoCaller autoCaller(this);
 
1849
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1850
 
 
1851
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1852
 
 
1853
    if (m->pParent.isNull())
 
1854
        *aAutoReset = FALSE;
 
1855
    else
 
1856
        *aAutoReset = m->autoReset;
 
1857
 
 
1858
    return S_OK;
 
1859
}
 
1860
 
 
1861
STDMETHODIMP Medium::COMSETTER(AutoReset)(BOOL aAutoReset)
 
1862
{
 
1863
    AutoCaller autoCaller(this);
 
1864
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1865
 
 
1866
    AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
 
1867
 
 
1868
    if (m->pParent.isNull())
 
1869
        return setError(VBOX_E_NOT_SUPPORTED,
 
1870
                        tr("Medium '%s' is not differencing"),
 
1871
                        m->strLocationFull.c_str());
 
1872
 
 
1873
    HRESULT rc = S_OK;
 
1874
 
 
1875
    if (m->autoReset != !!aAutoReset)
 
1876
    {
 
1877
        m->autoReset = !!aAutoReset;
 
1878
 
 
1879
        // save the settings
 
1880
        GuidList llRegistriesThatNeedSaving;
 
1881
        addToRegistryIDList(llRegistriesThatNeedSaving);
 
1882
        mlock.release();
 
1883
        rc = m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
 
1884
    }
 
1885
 
 
1886
    return rc;
 
1887
}
 
1888
 
 
1889
STDMETHODIMP Medium::COMGETTER(LastAccessError)(BSTR *aLastAccessError)
 
1890
{
 
1891
    CheckComArgOutPointerValid(aLastAccessError);
 
1892
 
 
1893
    AutoCaller autoCaller(this);
 
1894
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1895
 
 
1896
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1897
 
 
1898
    m->strLastAccessError.cloneTo(aLastAccessError);
 
1899
 
 
1900
    return S_OK;
 
1901
}
 
1902
 
 
1903
STDMETHODIMP Medium::COMGETTER(MachineIds)(ComSafeArrayOut(BSTR,aMachineIds))
 
1904
{
 
1905
    CheckComArgOutSafeArrayPointerValid(aMachineIds);
 
1906
 
 
1907
    AutoCaller autoCaller(this);
 
1908
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1909
 
 
1910
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1911
 
 
1912
    com::SafeArray<BSTR> machineIds;
 
1913
 
 
1914
    if (m->backRefs.size() != 0)
 
1915
    {
 
1916
        machineIds.reset(m->backRefs.size());
 
1917
 
 
1918
        size_t i = 0;
 
1919
        for (BackRefList::const_iterator it = m->backRefs.begin();
 
1920
             it != m->backRefs.end(); ++it, ++i)
 
1921
        {
 
1922
             it->machineId.toUtf16().detachTo(&machineIds[i]);
 
1923
        }
 
1924
    }
 
1925
 
 
1926
    machineIds.detachTo(ComSafeArrayOutArg(aMachineIds));
 
1927
 
 
1928
    return S_OK;
 
1929
}
 
1930
 
 
1931
STDMETHODIMP Medium::SetIDs(BOOL aSetImageId,
 
1932
                            IN_BSTR aImageId,
 
1933
                            BOOL aSetParentId,
 
1934
                            IN_BSTR aParentId)
 
1935
{
 
1936
    AutoCaller autoCaller(this);
 
1937
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1938
 
 
1939
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1940
 
 
1941
    switch (m->state)
 
1942
    {
 
1943
        case MediumState_Created:
 
1944
            break;
 
1945
        default:
 
1946
            return setStateError();
 
1947
    }
 
1948
 
 
1949
    Guid imageId, parentId;
 
1950
    if (aSetImageId)
 
1951
    {
 
1952
        imageId = Guid(aImageId);
 
1953
        if (imageId.isEmpty())
 
1954
            return setError(E_INVALIDARG, tr("Argument %s is empty"), "aImageId");
 
1955
    }
 
1956
    if (aSetParentId)
 
1957
        parentId = Guid(aParentId);
 
1958
 
 
1959
    unconst(m->uuidImage) = imageId;
 
1960
    unconst(m->uuidParentImage) = parentId;
 
1961
 
 
1962
    HRESULT rc = queryInfo(!!aSetImageId /* fSetImageId */,
 
1963
                           !!aSetParentId /* fSetParentId */);
 
1964
 
 
1965
    return rc;
 
1966
}
 
1967
 
 
1968
STDMETHODIMP Medium::RefreshState(MediumState_T *aState)
 
1969
{
 
1970
    CheckComArgOutPointerValid(aState);
 
1971
 
 
1972
    AutoCaller autoCaller(this);
 
1973
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
1974
 
 
1975
    /* queryInfo() locks this for writing. */
 
1976
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
1977
 
 
1978
    HRESULT rc = S_OK;
 
1979
 
 
1980
    switch (m->state)
 
1981
    {
 
1982
        case MediumState_Created:
 
1983
        case MediumState_Inaccessible:
 
1984
        case MediumState_LockedRead:
 
1985
        {
 
1986
            rc = queryInfo(false /* fSetImageId */, false /* fSetParentId */);
 
1987
            break;
 
1988
        }
 
1989
        default:
 
1990
            break;
 
1991
    }
 
1992
 
 
1993
    *aState = m->state;
 
1994
 
 
1995
    return rc;
 
1996
}
 
1997
 
 
1998
STDMETHODIMP Medium::GetSnapshotIds(IN_BSTR aMachineId,
 
1999
                                    ComSafeArrayOut(BSTR, aSnapshotIds))
 
2000
{
 
2001
    CheckComArgExpr(aMachineId, Guid(aMachineId).isEmpty() == false);
 
2002
    CheckComArgOutSafeArrayPointerValid(aSnapshotIds);
 
2003
 
 
2004
    AutoCaller autoCaller(this);
 
2005
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2006
 
 
2007
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2008
 
 
2009
    com::SafeArray<BSTR> snapshotIds;
 
2010
 
 
2011
    Guid id(aMachineId);
 
2012
    for (BackRefList::const_iterator it = m->backRefs.begin();
 
2013
         it != m->backRefs.end(); ++it)
 
2014
    {
 
2015
        if (it->machineId == id)
 
2016
        {
 
2017
            size_t size = it->llSnapshotIds.size();
 
2018
 
 
2019
            /* if the medium is attached to the machine in the current state, we
 
2020
             * return its ID as the first element of the array */
 
2021
            if (it->fInCurState)
 
2022
                ++size;
 
2023
 
 
2024
            if (size > 0)
 
2025
            {
 
2026
                snapshotIds.reset(size);
 
2027
 
 
2028
                size_t j = 0;
 
2029
                if (it->fInCurState)
 
2030
                    it->machineId.toUtf16().detachTo(&snapshotIds[j++]);
 
2031
 
 
2032
                for (GuidList::const_iterator jt = it->llSnapshotIds.begin();
 
2033
                     jt != it->llSnapshotIds.end();
 
2034
                     ++jt, ++j)
 
2035
                {
 
2036
                     (*jt).toUtf16().detachTo(&snapshotIds[j]);
 
2037
                }
 
2038
            }
 
2039
 
 
2040
            break;
 
2041
        }
 
2042
    }
 
2043
 
 
2044
    snapshotIds.detachTo(ComSafeArrayOutArg(aSnapshotIds));
 
2045
 
 
2046
    return S_OK;
 
2047
}
 
2048
 
 
2049
/**
 
2050
 * @note @a aState may be NULL if the state value is not needed (only for
 
2051
 *       in-process calls).
 
2052
 */
 
2053
STDMETHODIMP Medium::LockRead(MediumState_T *aState)
 
2054
{
 
2055
    AutoCaller autoCaller(this);
 
2056
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2057
 
 
2058
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2059
 
 
2060
    /* Wait for a concurrently running queryInfo() to complete */
 
2061
    while (m->queryInfoRunning)
 
2062
    {
 
2063
        alock.leave();
 
2064
        RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
 
2065
        alock.enter();
 
2066
    }
 
2067
 
 
2068
    /* return the current state before */
 
2069
    if (aState)
 
2070
        *aState = m->state;
 
2071
 
 
2072
    HRESULT rc = S_OK;
 
2073
 
 
2074
    switch (m->state)
 
2075
    {
 
2076
        case MediumState_Created:
 
2077
        case MediumState_Inaccessible:
 
2078
        case MediumState_LockedRead:
 
2079
        {
 
2080
            ++m->readers;
 
2081
 
 
2082
            ComAssertMsgBreak(m->readers != 0, ("Counter overflow"), rc = E_FAIL);
 
2083
 
 
2084
            /* Remember pre-lock state */
 
2085
            if (m->state != MediumState_LockedRead)
 
2086
                m->preLockState = m->state;
 
2087
 
 
2088
            LogFlowThisFunc(("Okay - prev state=%d readers=%d\n", m->state, m->readers));
 
2089
            m->state = MediumState_LockedRead;
 
2090
 
 
2091
            break;
 
2092
        }
 
2093
        default:
 
2094
        {
 
2095
            LogFlowThisFunc(("Failing - state=%d\n", m->state));
 
2096
            rc = setStateError();
 
2097
            break;
 
2098
        }
 
2099
    }
 
2100
 
 
2101
    return rc;
 
2102
}
 
2103
 
 
2104
/**
 
2105
 * @note @a aState may be NULL if the state value is not needed (only for
 
2106
 *       in-process calls).
 
2107
 */
 
2108
STDMETHODIMP Medium::UnlockRead(MediumState_T *aState)
 
2109
{
 
2110
    AutoCaller autoCaller(this);
 
2111
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2112
 
 
2113
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2114
 
 
2115
    HRESULT rc = S_OK;
 
2116
 
 
2117
    switch (m->state)
 
2118
    {
 
2119
        case MediumState_LockedRead:
 
2120
        {
 
2121
            Assert(m->readers != 0);
 
2122
            --m->readers;
 
2123
 
 
2124
            /* Reset the state after the last reader */
 
2125
            if (m->readers == 0)
 
2126
            {
 
2127
                m->state = m->preLockState;
 
2128
                /* There are cases where we inject the deleting state into
 
2129
                 * a medium locked for reading. Make sure #unmarkForDeletion()
 
2130
                 * gets the right state afterwards. */
 
2131
                if (m->preLockState == MediumState_Deleting)
 
2132
                    m->preLockState = MediumState_Created;
 
2133
            }
 
2134
 
 
2135
            LogFlowThisFunc(("new state=%d\n", m->state));
 
2136
            break;
 
2137
        }
 
2138
        default:
 
2139
        {
 
2140
            LogFlowThisFunc(("Failing - state=%d\n", m->state));
 
2141
            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
 
2142
                          tr("Medium '%s' is not locked for reading"),
 
2143
                          m->strLocationFull.c_str());
 
2144
            break;
 
2145
        }
 
2146
    }
 
2147
 
 
2148
    /* return the current state after */
 
2149
    if (aState)
 
2150
        *aState = m->state;
 
2151
 
 
2152
    return rc;
 
2153
}
 
2154
 
 
2155
/**
 
2156
 * @note @a aState may be NULL if the state value is not needed (only for
 
2157
 *       in-process calls).
 
2158
 */
 
2159
STDMETHODIMP Medium::LockWrite(MediumState_T *aState)
 
2160
{
 
2161
    AutoCaller autoCaller(this);
 
2162
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2163
 
 
2164
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2165
 
 
2166
    /* Wait for a concurrently running queryInfo() to complete */
 
2167
    while (m->queryInfoRunning)
 
2168
    {
 
2169
        alock.leave();
 
2170
        RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
 
2171
        alock.enter();
 
2172
    }
 
2173
 
 
2174
    /* return the current state before */
 
2175
    if (aState)
 
2176
        *aState = m->state;
 
2177
 
 
2178
    HRESULT rc = S_OK;
 
2179
 
 
2180
    switch (m->state)
 
2181
    {
 
2182
        case MediumState_Created:
 
2183
        case MediumState_Inaccessible:
 
2184
        {
 
2185
            m->preLockState = m->state;
 
2186
 
 
2187
            LogFlowThisFunc(("Okay - prev state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
 
2188
            m->state = MediumState_LockedWrite;
 
2189
            break;
 
2190
        }
 
2191
        default:
 
2192
        {
 
2193
            LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
 
2194
            rc = setStateError();
 
2195
            break;
 
2196
        }
 
2197
    }
 
2198
 
 
2199
    return rc;
 
2200
}
 
2201
 
 
2202
/**
 
2203
 * @note @a aState may be NULL if the state value is not needed (only for
 
2204
 *       in-process calls).
 
2205
 */
 
2206
STDMETHODIMP Medium::UnlockWrite(MediumState_T *aState)
 
2207
{
 
2208
    AutoCaller autoCaller(this);
 
2209
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2210
 
 
2211
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2212
 
 
2213
    HRESULT rc = S_OK;
 
2214
 
 
2215
    switch (m->state)
 
2216
    {
 
2217
        case MediumState_LockedWrite:
 
2218
        {
 
2219
            m->state = m->preLockState;
 
2220
            /* There are cases where we inject the deleting state into
 
2221
             * a medium locked for writing. Make sure #unmarkForDeletion()
 
2222
             * gets the right state afterwards. */
 
2223
            if (m->preLockState == MediumState_Deleting)
 
2224
                m->preLockState = MediumState_Created;
 
2225
            LogFlowThisFunc(("new state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
 
2226
            break;
 
2227
        }
 
2228
        default:
 
2229
        {
 
2230
            LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, getLocationFull().c_str()));
 
2231
            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
 
2232
                          tr("Medium '%s' is not locked for writing"),
 
2233
                          m->strLocationFull.c_str());
 
2234
            break;
 
2235
        }
 
2236
    }
 
2237
 
 
2238
    /* return the current state after */
 
2239
    if (aState)
 
2240
        *aState = m->state;
 
2241
 
 
2242
    return rc;
 
2243
}
 
2244
 
 
2245
STDMETHODIMP Medium::Close()
 
2246
{
 
2247
    AutoCaller autoCaller(this);
 
2248
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2249
 
 
2250
    // make a copy of VirtualBox pointer which gets nulled by uninit()
 
2251
    ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
 
2252
 
 
2253
    GuidList llRegistriesThatNeedSaving;
 
2254
    HRESULT rc = close(&llRegistriesThatNeedSaving, autoCaller);
 
2255
 
 
2256
    pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
 
2257
 
 
2258
    return rc;
 
2259
}
 
2260
 
 
2261
STDMETHODIMP Medium::GetProperty(IN_BSTR aName, BSTR *aValue)
 
2262
{
 
2263
    CheckComArgStrNotEmptyOrNull(aName);
 
2264
    CheckComArgOutPointerValid(aValue);
 
2265
 
 
2266
    AutoCaller autoCaller(this);
 
2267
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2268
 
 
2269
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2270
 
 
2271
    settings::StringsMap::const_iterator it = m->mapProperties.find(Utf8Str(aName));
 
2272
    if (it == m->mapProperties.end())
 
2273
        return setError(VBOX_E_OBJECT_NOT_FOUND,
 
2274
                        tr("Property '%ls' does not exist"), aName);
 
2275
 
 
2276
    it->second.cloneTo(aValue);
 
2277
 
 
2278
    return S_OK;
 
2279
}
 
2280
 
 
2281
STDMETHODIMP Medium::SetProperty(IN_BSTR aName, IN_BSTR aValue)
 
2282
{
 
2283
    CheckComArgStrNotEmptyOrNull(aName);
 
2284
 
 
2285
    AutoCaller autoCaller(this);
 
2286
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2287
 
 
2288
    AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
 
2289
 
 
2290
    switch (m->state)
 
2291
    {
 
2292
        case MediumState_Created:
 
2293
        case MediumState_Inaccessible:
 
2294
            break;
 
2295
        default:
 
2296
            return setStateError();
 
2297
    }
 
2298
 
 
2299
    settings::StringsMap::iterator it = m->mapProperties.find(Utf8Str(aName));
 
2300
    if (it == m->mapProperties.end())
 
2301
        return setError(VBOX_E_OBJECT_NOT_FOUND,
 
2302
                        tr("Property '%ls' does not exist"),
 
2303
                        aName);
 
2304
 
 
2305
    it->second = aValue;
 
2306
 
 
2307
    // save the settings
 
2308
    GuidList llRegistriesThatNeedSaving;
 
2309
    addToRegistryIDList(llRegistriesThatNeedSaving);
 
2310
    mlock.release();
 
2311
    HRESULT rc = m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
 
2312
 
 
2313
    return rc;
 
2314
}
 
2315
 
 
2316
STDMETHODIMP Medium::GetProperties(IN_BSTR aNames,
 
2317
                                   ComSafeArrayOut(BSTR, aReturnNames),
 
2318
                                   ComSafeArrayOut(BSTR, aReturnValues))
 
2319
{
 
2320
    CheckComArgOutSafeArrayPointerValid(aReturnNames);
 
2321
    CheckComArgOutSafeArrayPointerValid(aReturnValues);
 
2322
 
 
2323
    AutoCaller autoCaller(this);
 
2324
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2325
 
 
2326
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2327
 
 
2328
    /// @todo make use of aNames according to the documentation
 
2329
    NOREF(aNames);
 
2330
 
 
2331
    com::SafeArray<BSTR> names(m->mapProperties.size());
 
2332
    com::SafeArray<BSTR> values(m->mapProperties.size());
 
2333
    size_t i = 0;
 
2334
 
 
2335
    for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
 
2336
         it != m->mapProperties.end();
 
2337
         ++it)
 
2338
    {
 
2339
        it->first.cloneTo(&names[i]);
 
2340
        it->second.cloneTo(&values[i]);
 
2341
        ++i;
 
2342
    }
 
2343
 
 
2344
    names.detachTo(ComSafeArrayOutArg(aReturnNames));
 
2345
    values.detachTo(ComSafeArrayOutArg(aReturnValues));
 
2346
 
 
2347
    return S_OK;
 
2348
}
 
2349
 
 
2350
STDMETHODIMP Medium::SetProperties(ComSafeArrayIn(IN_BSTR, aNames),
 
2351
                                   ComSafeArrayIn(IN_BSTR, aValues))
 
2352
{
 
2353
    CheckComArgSafeArrayNotNull(aNames);
 
2354
    CheckComArgSafeArrayNotNull(aValues);
 
2355
 
 
2356
    AutoCaller autoCaller(this);
 
2357
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2358
 
 
2359
    AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
 
2360
 
 
2361
    com::SafeArray<IN_BSTR> names(ComSafeArrayInArg(aNames));
 
2362
    com::SafeArray<IN_BSTR> values(ComSafeArrayInArg(aValues));
 
2363
 
 
2364
    /* first pass: validate names */
 
2365
    for (size_t i = 0;
 
2366
         i < names.size();
 
2367
         ++i)
 
2368
    {
 
2369
        if (m->mapProperties.find(Utf8Str(names[i])) == m->mapProperties.end())
 
2370
            return setError(VBOX_E_OBJECT_NOT_FOUND,
 
2371
                            tr("Property '%ls' does not exist"), names[i]);
 
2372
    }
 
2373
 
 
2374
    /* second pass: assign */
 
2375
    for (size_t i = 0;
 
2376
         i < names.size();
 
2377
         ++i)
 
2378
    {
 
2379
        settings::StringsMap::iterator it = m->mapProperties.find(Utf8Str(names[i]));
 
2380
        AssertReturn(it != m->mapProperties.end(), E_FAIL);
 
2381
 
 
2382
        it->second = Utf8Str(values[i]);
 
2383
    }
 
2384
 
 
2385
    // save the settings
 
2386
    GuidList llRegistriesThatNeedSaving;
 
2387
    addToRegistryIDList(llRegistriesThatNeedSaving);
 
2388
    mlock.release();
 
2389
    HRESULT rc = m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
 
2390
 
 
2391
    return rc;
 
2392
}
 
2393
 
 
2394
STDMETHODIMP Medium::CreateBaseStorage(LONG64 aLogicalSize,
 
2395
                                       ULONG aVariant,
 
2396
                                       IProgress **aProgress)
 
2397
{
 
2398
    CheckComArgOutPointerValid(aProgress);
 
2399
    if (aLogicalSize < 0)
 
2400
        return setError(E_INVALIDARG, tr("The medium size argument (%lld) is negative"), aLogicalSize);
 
2401
 
 
2402
    AutoCaller autoCaller(this);
 
2403
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2404
 
 
2405
    HRESULT rc = S_OK;
 
2406
    ComObjPtr <Progress> pProgress;
 
2407
    Medium::Task *pTask = NULL;
 
2408
 
 
2409
    try
 
2410
    {
 
2411
        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2412
 
 
2413
        aVariant = (MediumVariant_T)((unsigned)aVariant & (unsigned)~MediumVariant_Diff);
 
2414
        if (    !(aVariant & MediumVariant_Fixed)
 
2415
            &&  !(m->formatObj->getCapabilities() & MediumFormatCapabilities_CreateDynamic))
 
2416
            throw setError(VBOX_E_NOT_SUPPORTED,
 
2417
                           tr("Medium format '%s' does not support dynamic storage creation"),
 
2418
                           m->strFormat.c_str());
 
2419
        if (    (aVariant & MediumVariant_Fixed)
 
2420
            &&  !(m->formatObj->getCapabilities() & MediumFormatCapabilities_CreateDynamic))
 
2421
            throw setError(VBOX_E_NOT_SUPPORTED,
 
2422
                           tr("Medium format '%s' does not support fixed storage creation"),
 
2423
                           m->strFormat.c_str());
 
2424
 
 
2425
        if (m->state != MediumState_NotCreated)
 
2426
            throw setStateError();
 
2427
 
 
2428
        pProgress.createObject();
 
2429
        rc = pProgress->init(m->pVirtualBox,
 
2430
                             static_cast<IMedium*>(this),
 
2431
                             (aVariant & MediumVariant_Fixed)
 
2432
                               ? BstrFmt(tr("Creating fixed medium storage unit '%s'"), m->strLocationFull.c_str()).raw()
 
2433
                               : BstrFmt(tr("Creating dynamic medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
 
2434
                             TRUE /* aCancelable */);
 
2435
        if (FAILED(rc))
 
2436
            throw rc;
 
2437
 
 
2438
        /* setup task object to carry out the operation asynchronously */
 
2439
        pTask = new Medium::CreateBaseTask(this, pProgress, aLogicalSize,
 
2440
                                           (MediumVariant_T)aVariant);
 
2441
        rc = pTask->rc();
 
2442
        AssertComRC(rc);
 
2443
        if (FAILED(rc))
 
2444
            throw rc;
 
2445
 
 
2446
        m->state = MediumState_Creating;
 
2447
    }
 
2448
    catch (HRESULT aRC) { rc = aRC; }
 
2449
 
 
2450
    if (SUCCEEDED(rc))
 
2451
    {
 
2452
        rc = startThread(pTask);
 
2453
 
 
2454
        if (SUCCEEDED(rc))
 
2455
            pProgress.queryInterfaceTo(aProgress);
 
2456
    }
 
2457
    else if (pTask != NULL)
 
2458
        delete pTask;
 
2459
 
 
2460
    return rc;
 
2461
}
 
2462
 
 
2463
STDMETHODIMP Medium::DeleteStorage(IProgress **aProgress)
 
2464
{
 
2465
    CheckComArgOutPointerValid(aProgress);
 
2466
 
 
2467
    AutoCaller autoCaller(this);
 
2468
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2469
 
 
2470
    ComObjPtr<Progress> pProgress;
 
2471
 
 
2472
    GuidList llRegistriesThatNeedSaving;
 
2473
    HRESULT rc = deleteStorage(&pProgress,
 
2474
                               false /* aWait */,
 
2475
                               &llRegistriesThatNeedSaving);
 
2476
    m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
 
2477
 
 
2478
    if (SUCCEEDED(rc))
 
2479
        pProgress.queryInterfaceTo(aProgress);
 
2480
 
 
2481
    return rc;
 
2482
}
 
2483
 
 
2484
STDMETHODIMP Medium::CreateDiffStorage(IMedium *aTarget,
 
2485
                                       ULONG aVariant,
 
2486
                                       IProgress **aProgress)
 
2487
{
 
2488
    CheckComArgNotNull(aTarget);
 
2489
    CheckComArgOutPointerValid(aProgress);
 
2490
 
 
2491
    AutoCaller autoCaller(this);
 
2492
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2493
 
 
2494
    ComObjPtr<Medium> diff = static_cast<Medium*>(aTarget);
 
2495
 
 
2496
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2497
 
 
2498
    if (m->type == MediumType_Writethrough)
 
2499
        return setError(VBOX_E_INVALID_OBJECT_STATE,
 
2500
                        tr("Medium type of '%s' is Writethrough"),
 
2501
                        m->strLocationFull.c_str());
 
2502
    else if (m->type == MediumType_Shareable)
 
2503
        return setError(VBOX_E_INVALID_OBJECT_STATE,
 
2504
                        tr("Medium type of '%s' is Shareable"),
 
2505
                        m->strLocationFull.c_str());
 
2506
    else if (m->type == MediumType_Readonly)
 
2507
        return setError(VBOX_E_INVALID_OBJECT_STATE,
 
2508
                        tr("Medium type of '%s' is Readonly"),
 
2509
                        m->strLocationFull.c_str());
 
2510
 
 
2511
    /* Apply the normal locking logic to the entire chain. */
 
2512
    MediumLockList *pMediumLockList(new MediumLockList());
 
2513
    HRESULT rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
 
2514
                                            true /* fMediumLockWrite */,
 
2515
                                            this,
 
2516
                                            *pMediumLockList);
 
2517
    if (FAILED(rc))
 
2518
    {
 
2519
        delete pMediumLockList;
 
2520
        return rc;
 
2521
    }
 
2522
 
 
2523
    ComObjPtr <Progress> pProgress;
 
2524
 
 
2525
    rc = createDiffStorage(diff, (MediumVariant_T)aVariant, pMediumLockList,
 
2526
                           &pProgress, false /* aWait */,
 
2527
                           NULL /* pfNeedsGlobalSaveSettings*/);
 
2528
    if (FAILED(rc))
 
2529
        delete pMediumLockList;
 
2530
    else
 
2531
        pProgress.queryInterfaceTo(aProgress);
 
2532
 
 
2533
    return rc;
 
2534
}
 
2535
 
 
2536
STDMETHODIMP Medium::MergeTo(IMedium *aTarget, IProgress **aProgress)
 
2537
{
 
2538
    CheckComArgNotNull(aTarget);
 
2539
    CheckComArgOutPointerValid(aProgress);
 
2540
    ComAssertRet(aTarget != this, E_INVALIDARG);
 
2541
 
 
2542
    AutoCaller autoCaller(this);
 
2543
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2544
 
 
2545
    ComObjPtr<Medium> pTarget = static_cast<Medium*>(aTarget);
 
2546
 
 
2547
    bool fMergeForward = false;
 
2548
    ComObjPtr<Medium> pParentForTarget;
 
2549
    MediaList childrenToReparent;
 
2550
    MediumLockList *pMediumLockList = NULL;
 
2551
 
 
2552
    HRESULT rc = S_OK;
 
2553
 
 
2554
    rc = prepareMergeTo(pTarget, NULL, NULL, true, fMergeForward,
 
2555
                        pParentForTarget, childrenToReparent, pMediumLockList);
 
2556
    if (FAILED(rc)) return rc;
 
2557
 
 
2558
    ComObjPtr <Progress> pProgress;
 
2559
 
 
2560
    rc = mergeTo(pTarget, fMergeForward, pParentForTarget, childrenToReparent,
 
2561
                 pMediumLockList, &pProgress, false /* aWait */,
 
2562
                 NULL /* pfNeedsGlobalSaveSettings */);
 
2563
    if (FAILED(rc))
 
2564
        cancelMergeTo(childrenToReparent, pMediumLockList);
 
2565
    else
 
2566
        pProgress.queryInterfaceTo(aProgress);
 
2567
 
 
2568
    return rc;
 
2569
}
 
2570
 
 
2571
STDMETHODIMP Medium::CloneTo(IMedium *aTarget,
 
2572
                             ULONG aVariant,
 
2573
                             IMedium *aParent,
 
2574
                             IProgress **aProgress)
 
2575
{
 
2576
    CheckComArgNotNull(aTarget);
 
2577
    CheckComArgOutPointerValid(aProgress);
 
2578
    ComAssertRet(aTarget != this, E_INVALIDARG);
 
2579
 
 
2580
    AutoCaller autoCaller(this);
 
2581
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2582
 
 
2583
    ComObjPtr<Medium> pTarget = static_cast<Medium*>(aTarget);
 
2584
    ComObjPtr<Medium> pParent;
 
2585
    if (aParent)
 
2586
        pParent = static_cast<Medium*>(aParent);
 
2587
 
 
2588
    HRESULT rc = S_OK;
 
2589
    ComObjPtr<Progress> pProgress;
 
2590
    Medium::Task *pTask = NULL;
 
2591
 
 
2592
    try
 
2593
    {
 
2594
        // locking: we need the tree lock first because we access parent pointers
 
2595
        AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
2596
        // and we need to write-lock the media involved
 
2597
        AutoMultiWriteLock3 alock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
 
2598
 
 
2599
        if (    pTarget->m->state != MediumState_NotCreated
 
2600
            &&  pTarget->m->state != MediumState_Created)
 
2601
            throw pTarget->setStateError();
 
2602
 
 
2603
        /* Build the source lock list. */
 
2604
        MediumLockList *pSourceMediumLockList(new MediumLockList());
 
2605
        rc = createMediumLockList(true /* fFailIfInaccessible */,
 
2606
                                  false /* fMediumLockWrite */,
 
2607
                                  NULL,
 
2608
                                  *pSourceMediumLockList);
 
2609
        if (FAILED(rc))
 
2610
        {
 
2611
            delete pSourceMediumLockList;
 
2612
            throw rc;
 
2613
        }
 
2614
 
 
2615
        /* Build the target lock list (including the to-be parent chain). */
 
2616
        MediumLockList *pTargetMediumLockList(new MediumLockList());
 
2617
        rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */,
 
2618
                                           true /* fMediumLockWrite */,
 
2619
                                           pParent,
 
2620
                                           *pTargetMediumLockList);
 
2621
        if (FAILED(rc))
 
2622
        {
 
2623
            delete pSourceMediumLockList;
 
2624
            delete pTargetMediumLockList;
 
2625
            throw rc;
 
2626
        }
 
2627
 
 
2628
        rc = pSourceMediumLockList->Lock();
 
2629
        if (FAILED(rc))
 
2630
        {
 
2631
            delete pSourceMediumLockList;
 
2632
            delete pTargetMediumLockList;
 
2633
            throw setError(rc,
 
2634
                           tr("Failed to lock source media '%s'"),
 
2635
                           getLocationFull().c_str());
 
2636
        }
 
2637
        rc = pTargetMediumLockList->Lock();
 
2638
        if (FAILED(rc))
 
2639
        {
 
2640
            delete pSourceMediumLockList;
 
2641
            delete pTargetMediumLockList;
 
2642
            throw setError(rc,
 
2643
                           tr("Failed to lock target media '%s'"),
 
2644
                           pTarget->getLocationFull().c_str());
 
2645
        }
 
2646
 
 
2647
        pProgress.createObject();
 
2648
        rc = pProgress->init(m->pVirtualBox,
 
2649
                             static_cast <IMedium *>(this),
 
2650
                             BstrFmt(tr("Creating clone medium '%s'"), pTarget->m->strLocationFull.c_str()).raw(),
 
2651
                             TRUE /* aCancelable */);
 
2652
        if (FAILED(rc))
 
2653
        {
 
2654
            delete pSourceMediumLockList;
 
2655
            delete pTargetMediumLockList;
 
2656
            throw rc;
 
2657
        }
 
2658
 
 
2659
        /* setup task object to carry out the operation asynchronously */
 
2660
        pTask = new Medium::CloneTask(this, pProgress, pTarget,
 
2661
                                      (MediumVariant_T)aVariant,
 
2662
                                      pParent, pSourceMediumLockList,
 
2663
                                      pTargetMediumLockList);
 
2664
        rc = pTask->rc();
 
2665
        AssertComRC(rc);
 
2666
        if (FAILED(rc))
 
2667
            throw rc;
 
2668
 
 
2669
        if (pTarget->m->state == MediumState_NotCreated)
 
2670
            pTarget->m->state = MediumState_Creating;
 
2671
    }
 
2672
    catch (HRESULT aRC) { rc = aRC; }
 
2673
 
 
2674
    if (SUCCEEDED(rc))
 
2675
    {
 
2676
        rc = startThread(pTask);
 
2677
 
 
2678
        if (SUCCEEDED(rc))
 
2679
            pProgress.queryInterfaceTo(aProgress);
 
2680
    }
 
2681
    else if (pTask != NULL)
 
2682
        delete pTask;
 
2683
 
 
2684
    return rc;
 
2685
}
 
2686
 
 
2687
STDMETHODIMP Medium::Compact(IProgress **aProgress)
 
2688
{
 
2689
    CheckComArgOutPointerValid(aProgress);
 
2690
 
 
2691
    AutoCaller autoCaller(this);
 
2692
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2693
 
 
2694
    HRESULT rc = S_OK;
 
2695
    ComObjPtr <Progress> pProgress;
 
2696
    Medium::Task *pTask = NULL;
 
2697
 
 
2698
    try
 
2699
    {
 
2700
        /* We need to lock both the current object, and the tree lock (would
 
2701
         * cause a lock order violation otherwise) for createMediumLockList. */
 
2702
        AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
 
2703
                                      this->lockHandle()
 
2704
                                      COMMA_LOCKVAL_SRC_POS);
 
2705
 
 
2706
        /* Build the medium lock list. */
 
2707
        MediumLockList *pMediumLockList(new MediumLockList());
 
2708
        rc = createMediumLockList(true /* fFailIfInaccessible */ ,
 
2709
                                  true /* fMediumLockWrite */,
 
2710
                                  NULL,
 
2711
                                  *pMediumLockList);
 
2712
        if (FAILED(rc))
 
2713
        {
 
2714
            delete pMediumLockList;
 
2715
            throw rc;
 
2716
        }
 
2717
 
 
2718
        rc = pMediumLockList->Lock();
 
2719
        if (FAILED(rc))
 
2720
        {
 
2721
            delete pMediumLockList;
 
2722
            throw setError(rc,
 
2723
                           tr("Failed to lock media when compacting '%s'"),
 
2724
                           getLocationFull().c_str());
 
2725
        }
 
2726
 
 
2727
        pProgress.createObject();
 
2728
        rc = pProgress->init(m->pVirtualBox,
 
2729
                             static_cast <IMedium *>(this),
 
2730
                             BstrFmt(tr("Compacting medium '%s'"), m->strLocationFull.c_str()).raw(),
 
2731
                             TRUE /* aCancelable */);
 
2732
        if (FAILED(rc))
 
2733
        {
 
2734
            delete pMediumLockList;
 
2735
            throw rc;
 
2736
        }
 
2737
 
 
2738
        /* setup task object to carry out the operation asynchronously */
 
2739
        pTask = new Medium::CompactTask(this, pProgress, pMediumLockList);
 
2740
        rc = pTask->rc();
 
2741
        AssertComRC(rc);
 
2742
        if (FAILED(rc))
 
2743
            throw rc;
 
2744
    }
 
2745
    catch (HRESULT aRC) { rc = aRC; }
 
2746
 
 
2747
    if (SUCCEEDED(rc))
 
2748
    {
 
2749
        rc = startThread(pTask);
 
2750
 
 
2751
        if (SUCCEEDED(rc))
 
2752
            pProgress.queryInterfaceTo(aProgress);
 
2753
    }
 
2754
    else if (pTask != NULL)
 
2755
        delete pTask;
 
2756
 
 
2757
    return rc;
 
2758
}
 
2759
 
 
2760
STDMETHODIMP Medium::Resize(LONG64 aLogicalSize, IProgress **aProgress)
 
2761
{
 
2762
    CheckComArgOutPointerValid(aProgress);
 
2763
 
 
2764
    AutoCaller autoCaller(this);
 
2765
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2766
 
 
2767
    HRESULT rc = S_OK;
 
2768
    ComObjPtr <Progress> pProgress;
 
2769
    Medium::Task *pTask = NULL;
 
2770
 
 
2771
    try
 
2772
    {
 
2773
        /* We need to lock both the current object, and the tree lock (would
 
2774
         * cause a lock order violation otherwise) for createMediumLockList. */
 
2775
        AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
 
2776
                                      this->lockHandle()
 
2777
                                      COMMA_LOCKVAL_SRC_POS);
 
2778
 
 
2779
        /* Build the medium lock list. */
 
2780
        MediumLockList *pMediumLockList(new MediumLockList());
 
2781
        rc = createMediumLockList(true /* fFailIfInaccessible */ ,
 
2782
                                  true /* fMediumLockWrite */,
 
2783
                                  NULL,
 
2784
                                  *pMediumLockList);
 
2785
        if (FAILED(rc))
 
2786
        {
 
2787
            delete pMediumLockList;
 
2788
            throw rc;
 
2789
        }
 
2790
 
 
2791
        rc = pMediumLockList->Lock();
 
2792
        if (FAILED(rc))
 
2793
        {
 
2794
            delete pMediumLockList;
 
2795
            throw setError(rc,
 
2796
                           tr("Failed to lock media when compacting '%s'"),
 
2797
                           getLocationFull().c_str());
 
2798
        }
 
2799
 
 
2800
        pProgress.createObject();
 
2801
        rc = pProgress->init(m->pVirtualBox,
 
2802
                             static_cast <IMedium *>(this),
 
2803
                             BstrFmt(tr("Compacting medium '%s'"), m->strLocationFull.c_str()).raw(),
 
2804
                             TRUE /* aCancelable */);
 
2805
        if (FAILED(rc))
 
2806
        {
 
2807
            delete pMediumLockList;
 
2808
            throw rc;
 
2809
        }
 
2810
 
 
2811
        /* setup task object to carry out the operation asynchronously */
 
2812
        pTask = new Medium::ResizeTask(this, aLogicalSize, pProgress, pMediumLockList);
 
2813
        rc = pTask->rc();
 
2814
        AssertComRC(rc);
 
2815
        if (FAILED(rc))
 
2816
            throw rc;
 
2817
    }
 
2818
    catch (HRESULT aRC) { rc = aRC; }
 
2819
 
 
2820
    if (SUCCEEDED(rc))
 
2821
    {
 
2822
        rc = startThread(pTask);
 
2823
 
 
2824
        if (SUCCEEDED(rc))
 
2825
            pProgress.queryInterfaceTo(aProgress);
 
2826
    }
 
2827
    else if (pTask != NULL)
 
2828
        delete pTask;
 
2829
 
 
2830
    return rc;
 
2831
}
 
2832
 
 
2833
STDMETHODIMP Medium::Reset(IProgress **aProgress)
 
2834
{
 
2835
    CheckComArgOutPointerValid(aProgress);
 
2836
 
 
2837
    AutoCaller autoCaller(this);
 
2838
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
2839
 
 
2840
    HRESULT rc = S_OK;
 
2841
    ComObjPtr <Progress> pProgress;
 
2842
    Medium::Task *pTask = NULL;
 
2843
 
 
2844
    try
 
2845
    {
 
2846
        /* canClose() needs the tree lock */
 
2847
        AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
 
2848
                                      this->lockHandle()
 
2849
                                      COMMA_LOCKVAL_SRC_POS);
 
2850
 
 
2851
        LogFlowThisFunc(("ENTER for medium %s\n", m->strLocationFull.c_str()));
 
2852
 
 
2853
        if (m->pParent.isNull())
 
2854
            throw setError(VBOX_E_NOT_SUPPORTED,
 
2855
                           tr("Medium type of '%s' is not differencing"),
 
2856
                           m->strLocationFull.c_str());
 
2857
 
 
2858
        rc = canClose();
 
2859
        if (FAILED(rc))
 
2860
            throw rc;
 
2861
 
 
2862
        /* Build the medium lock list. */
 
2863
        MediumLockList *pMediumLockList(new MediumLockList());
 
2864
        rc = createMediumLockList(true /* fFailIfInaccessible */,
 
2865
                                  true /* fMediumLockWrite */,
 
2866
                                  NULL,
 
2867
                                  *pMediumLockList);
 
2868
        if (FAILED(rc))
 
2869
        {
 
2870
            delete pMediumLockList;
 
2871
            throw rc;
 
2872
        }
 
2873
 
 
2874
        rc = pMediumLockList->Lock();
 
2875
        if (FAILED(rc))
 
2876
        {
 
2877
            delete pMediumLockList;
 
2878
            throw setError(rc,
 
2879
                           tr("Failed to lock media when resetting '%s'"),
 
2880
                           getLocationFull().c_str());
 
2881
        }
 
2882
 
 
2883
        pProgress.createObject();
 
2884
        rc = pProgress->init(m->pVirtualBox,
 
2885
                             static_cast<IMedium*>(this),
 
2886
                             BstrFmt(tr("Resetting differencing medium '%s'"), m->strLocationFull.c_str()).raw(),
 
2887
                             FALSE /* aCancelable */);
 
2888
        if (FAILED(rc))
 
2889
            throw rc;
 
2890
 
 
2891
        /* setup task object to carry out the operation asynchronously */
 
2892
        pTask = new Medium::ResetTask(this, pProgress, pMediumLockList);
 
2893
        rc = pTask->rc();
 
2894
        AssertComRC(rc);
 
2895
        if (FAILED(rc))
 
2896
            throw rc;
 
2897
    }
 
2898
    catch (HRESULT aRC) { rc = aRC; }
 
2899
 
 
2900
    if (SUCCEEDED(rc))
 
2901
    {
 
2902
        rc = startThread(pTask);
 
2903
 
 
2904
        if (SUCCEEDED(rc))
 
2905
            pProgress.queryInterfaceTo(aProgress);
 
2906
    }
 
2907
    else
 
2908
    {
 
2909
        /* Note: on success, the task will unlock this */
 
2910
        {
 
2911
            AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
2912
            HRESULT rc2 = UnlockWrite(NULL);
 
2913
            AssertComRC(rc2);
 
2914
        }
 
2915
        if (pTask != NULL)
 
2916
            delete pTask;
 
2917
    }
 
2918
 
 
2919
    LogFlowThisFunc(("LEAVE, rc=%Rhrc\n", rc));
 
2920
 
 
2921
    return rc;
 
2922
}
 
2923
 
 
2924
////////////////////////////////////////////////////////////////////////////////
 
2925
//
 
2926
// Medium public internal methods
 
2927
//
 
2928
////////////////////////////////////////////////////////////////////////////////
 
2929
 
 
2930
/**
 
2931
 * Internal method to return the medium's parent medium. Must have caller + locking!
 
2932
 * @return
 
2933
 */
 
2934
const ComObjPtr<Medium>& Medium::getParent() const
 
2935
{
 
2936
    return m->pParent;
 
2937
}
 
2938
 
 
2939
/**
 
2940
 * Internal method to return the medium's list of child media. Must have caller + locking!
 
2941
 * @return
 
2942
 */
 
2943
const MediaList& Medium::getChildren() const
 
2944
{
 
2945
    return m->llChildren;
 
2946
}
 
2947
 
 
2948
/**
 
2949
 * Internal method to return the medium's GUID. Must have caller + locking!
 
2950
 * @return
 
2951
 */
 
2952
const Guid& Medium::getId() const
 
2953
{
 
2954
    return m->id;
 
2955
}
 
2956
 
 
2957
/**
 
2958
 * Internal method to return the medium's state. Must have caller + locking!
 
2959
 * @return
 
2960
 */
 
2961
MediumState_T Medium::getState() const
 
2962
{
 
2963
    return m->state;
 
2964
}
 
2965
 
 
2966
/**
 
2967
 * Internal method to return the medium's variant. Must have caller + locking!
 
2968
 * @return
 
2969
 */
 
2970
MediumVariant_T Medium::getVariant() const
 
2971
{
 
2972
    return m->variant;
 
2973
}
 
2974
 
 
2975
/**
 
2976
 * Internal method which returns true if this medium represents a host drive.
 
2977
 * @return
 
2978
 */
 
2979
bool Medium::isHostDrive() const
 
2980
{
 
2981
    return m->hostDrive;
 
2982
}
 
2983
 
 
2984
/**
 
2985
 * Internal method to return the medium's full location. Must have caller + locking!
 
2986
 * @return
 
2987
 */
 
2988
const Utf8Str& Medium::getLocationFull() const
 
2989
{
 
2990
    return m->strLocationFull;
 
2991
}
 
2992
 
 
2993
/**
 
2994
 * Internal method to return the medium's format string. Must have caller + locking!
 
2995
 * @return
 
2996
 */
 
2997
const Utf8Str& Medium::getFormat() const
 
2998
{
 
2999
    return m->strFormat;
 
3000
}
 
3001
 
 
3002
/**
 
3003
 * Internal method to return the medium's format object. Must have caller + locking!
 
3004
 * @return
 
3005
 */
 
3006
const ComObjPtr<MediumFormat>& Medium::getMediumFormat() const
 
3007
{
 
3008
    return m->formatObj;
 
3009
}
 
3010
 
 
3011
/**
 
3012
 * Internal method that returns true if the medium is represented by a file on the host disk
 
3013
 * (and not iSCSI or something).
 
3014
 * @return
 
3015
 */
 
3016
bool Medium::isMediumFormatFile() const
 
3017
{
 
3018
    if (    m->formatObj
 
3019
         && (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
 
3020
       )
 
3021
        return true;
 
3022
    return false;
 
3023
}
 
3024
 
 
3025
/**
 
3026
 * Internal method to return the medium's size. Must have caller + locking!
 
3027
 * @return
 
3028
 */
 
3029
uint64_t Medium::getSize() const
 
3030
{
 
3031
    return m->size;
 
3032
}
 
3033
 
 
3034
/**
 
3035
 * Returns the medium device type. Must have caller + locking!
 
3036
 * @return
 
3037
 */
 
3038
DeviceType_T Medium::getDeviceType() const
 
3039
{
 
3040
    return m->devType;
 
3041
}
 
3042
 
 
3043
/**
 
3044
 * Returns the medium type. Must have caller + locking!
 
3045
 * @return
 
3046
 */
 
3047
MediumType_T Medium::getType() const
 
3048
{
 
3049
    return m->type;
 
3050
}
 
3051
 
 
3052
/**
 
3053
 * Returns a short version of the location attribute.
 
3054
 *
 
3055
 * @note Must be called from under this object's read or write lock.
 
3056
 */
 
3057
Utf8Str Medium::getName()
 
3058
{
 
3059
    Utf8Str name = RTPathFilename(m->strLocationFull.c_str());
 
3060
    return name;
 
3061
}
 
3062
 
 
3063
/**
 
3064
 * This adds the given UUID to the list of media registries in which this
 
3065
 * medium should be registered. The UUID can either be a machine UUID,
 
3066
 * to add a machine registry, or the global registry UUID as returned by
 
3067
 * VirtualBox::getGlobalRegistryId().
 
3068
 *
 
3069
 * Note that for hard disks, this method does nothing if the medium is
 
3070
 * already in another registry to avoid having hard disks in more than
 
3071
 * one registry, which causes trouble with keeping diff images in sync.
 
3072
 * See getFirstRegistryMachineId() for details.
 
3073
 *
 
3074
 * @param id
 
3075
 * @return true if the registry was added; false if the given id was already on the list.
 
3076
 */
 
3077
bool Medium::addRegistry(const Guid& id)
 
3078
{
 
3079
    AutoCaller autoCaller(this);
 
3080
    if (FAILED(autoCaller.rc())) return false;
 
3081
 
 
3082
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
3083
 
 
3084
    if (    m->devType == DeviceType_HardDisk
 
3085
         && m->llRegistryIDs.size() > 0
 
3086
       )
 
3087
        return false;
 
3088
 
 
3089
    // no need to add the UUID twice
 
3090
    for (GuidList::const_iterator it = m->llRegistryIDs.begin();
 
3091
         it != m->llRegistryIDs.end();
 
3092
         ++it)
 
3093
    {
 
3094
        if ((*it) == id)
 
3095
            return false;
 
3096
    }
 
3097
 
 
3098
    m->llRegistryIDs.push_back(id);
 
3099
    return true;
 
3100
}
 
3101
 
 
3102
/**
 
3103
 * Returns true if id is in the list of media registries for this medium.
 
3104
 * @param id
 
3105
 * @return
 
3106
 */
 
3107
bool Medium::isInRegistry(const Guid& id)
 
3108
{
 
3109
    AutoCaller autoCaller(this);
 
3110
    if (FAILED(autoCaller.rc())) return false;
 
3111
 
 
3112
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
3113
 
 
3114
    for (GuidList::const_iterator it = m->llRegistryIDs.begin();
 
3115
         it != m->llRegistryIDs.end();
 
3116
         ++it)
 
3117
    {
 
3118
        if (*it == id)
 
3119
            return true;
 
3120
    }
 
3121
 
 
3122
    return false;
 
3123
}
 
3124
 
 
3125
/**
 
3126
 * Internal method to return the medium's first registry machine (i.e. the machine in whose
 
3127
 * machine XML this medium is listed).
 
3128
 *
 
3129
 * Every medium must now (4.0) reside in at least one media registry, which is identified by
 
3130
 * a UUID. This is either a machine UUID if the machine is from 4.0 or newer, in which case
 
3131
 * machines have their own media registries, or it is the pseudo-UUID of the VirtualBox
 
3132
 * object if the machine is old and still needs the global registry in VirtualBox.xml.
 
3133
 *
 
3134
 * By definition, hard disks may only be in one media registry, in which all its children
 
3135
 * will be stored as well. Otherwise we run into problems with having keep multiple registries
 
3136
 * in sync. (This is the "cloned VM" case in which VM1 may link to the disks of VM2; in this
 
3137
 * case, only VM2's registry is used for the disk in question.)
 
3138
 *
 
3139
 * ISOs and RAWs, by contrast, can be in more than one repository to make things easier for
 
3140
 * the user.
 
3141
 *
 
3142
 * Must have caller + locking!
 
3143
 *
 
3144
 * @return
 
3145
 */
 
3146
const Guid& Medium::getFirstRegistryMachineId() const
 
3147
{
 
3148
    return m->llRegistryIDs.front();
 
3149
}
 
3150
 
 
3151
/**
 
3152
 * Adds all the IDs of the registries in which this medium is registered to the given list
 
3153
 * of UUIDs, but only if they are not on the list yet.
 
3154
 * @param llRegistryIDs
 
3155
 */
 
3156
HRESULT Medium::addToRegistryIDList(GuidList &llRegistryIDs)
 
3157
{
 
3158
    AutoCaller autoCaller(this);
 
3159
    if (FAILED(autoCaller.rc())) return false;
 
3160
 
 
3161
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
3162
 
 
3163
    for (GuidList::const_iterator it = m->llRegistryIDs.begin();
 
3164
         it != m->llRegistryIDs.end();
 
3165
         ++it)
 
3166
    {
 
3167
        m->pVirtualBox->addGuidToListUniquely(llRegistryIDs, *it);
 
3168
    }
 
3169
 
 
3170
    return S_OK;
 
3171
}
 
3172
 
 
3173
/**
 
3174
 * Adds the given machine and optionally the snapshot to the list of the objects
 
3175
 * this medium is attached to.
 
3176
 *
 
3177
 * @param aMachineId    Machine ID.
 
3178
 * @param aSnapshotId   Snapshot ID; when non-empty, adds a snapshot attachment.
 
3179
 */
 
3180
HRESULT Medium::addBackReference(const Guid &aMachineId,
 
3181
                                 const Guid &aSnapshotId /*= Guid::Empty*/)
 
3182
{
 
3183
    AssertReturn(!aMachineId.isEmpty(), E_FAIL);
 
3184
 
 
3185
    LogFlowThisFunc(("ENTER, aMachineId: {%RTuuid}, aSnapshotId: {%RTuuid}\n", aMachineId.raw(), aSnapshotId.raw()));
 
3186
 
 
3187
    AutoCaller autoCaller(this);
 
3188
    AssertComRCReturnRC(autoCaller.rc());
 
3189
 
 
3190
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
3191
 
 
3192
    switch (m->state)
 
3193
    {
 
3194
        case MediumState_Created:
 
3195
        case MediumState_Inaccessible:
 
3196
        case MediumState_LockedRead:
 
3197
        case MediumState_LockedWrite:
 
3198
            break;
 
3199
 
 
3200
        default:
 
3201
            return setStateError();
 
3202
    }
 
3203
 
 
3204
    if (m->numCreateDiffTasks > 0)
 
3205
        return setError(VBOX_E_OBJECT_IN_USE,
 
3206
                        tr("Cannot attach medium '%s' {%RTuuid}: %u differencing child media are being created"),
 
3207
                        m->strLocationFull.c_str(),
 
3208
                        m->id.raw(),
 
3209
                        m->numCreateDiffTasks);
 
3210
 
 
3211
    BackRefList::iterator it = std::find_if(m->backRefs.begin(),
 
3212
                                            m->backRefs.end(),
 
3213
                                            BackRef::EqualsTo(aMachineId));
 
3214
    if (it == m->backRefs.end())
 
3215
    {
 
3216
        BackRef ref(aMachineId, aSnapshotId);
 
3217
        m->backRefs.push_back(ref);
 
3218
 
 
3219
        return S_OK;
 
3220
    }
 
3221
 
 
3222
    // if the caller has not supplied a snapshot ID, then we're attaching
 
3223
    // to a machine a medium which represents the machine's current state,
 
3224
    // so set the flag
 
3225
    if (aSnapshotId.isEmpty())
 
3226
    {
 
3227
        /* sanity: no duplicate attachments */
 
3228
        AssertReturn(!it->fInCurState, E_FAIL);
 
3229
        it->fInCurState = true;
 
3230
 
 
3231
        return S_OK;
 
3232
    }
 
3233
 
 
3234
    // otherwise: a snapshot medium is being attached
 
3235
 
 
3236
    /* sanity: no duplicate attachments */
 
3237
    for (GuidList::const_iterator jt = it->llSnapshotIds.begin();
 
3238
         jt != it->llSnapshotIds.end();
 
3239
         ++jt)
 
3240
    {
 
3241
        const Guid &idOldSnapshot = *jt;
 
3242
 
 
3243
        if (idOldSnapshot == aSnapshotId)
 
3244
        {
 
3245
#ifdef DEBUG
 
3246
            dumpBackRefs();
 
3247
#endif
 
3248
            return setError(VBOX_E_OBJECT_IN_USE,
 
3249
                            tr("Cannot attach medium '%s' {%RTuuid} from snapshot '%RTuuid': medium is already in use by this snapshot!"),
 
3250
                            m->strLocationFull.c_str(),
 
3251
                            m->id.raw(),
 
3252
                            aSnapshotId.raw(),
 
3253
                            idOldSnapshot.raw());
 
3254
        }
 
3255
    }
 
3256
 
 
3257
    it->llSnapshotIds.push_back(aSnapshotId);
 
3258
    it->fInCurState = false;
 
3259
 
 
3260
    LogFlowThisFuncLeave();
 
3261
 
 
3262
    return S_OK;
 
3263
}
 
3264
 
 
3265
/**
 
3266
 * Removes the given machine and optionally the snapshot from the list of the
 
3267
 * objects this medium is attached to.
 
3268
 *
 
3269
 * @param aMachineId    Machine ID.
 
3270
 * @param aSnapshotId   Snapshot ID; when non-empty, removes the snapshot
 
3271
 *                      attachment.
 
3272
 */
 
3273
HRESULT Medium::removeBackReference(const Guid &aMachineId,
 
3274
                                    const Guid &aSnapshotId /*= Guid::Empty*/)
 
3275
{
 
3276
    AssertReturn(!aMachineId.isEmpty(), E_FAIL);
 
3277
 
 
3278
    AutoCaller autoCaller(this);
 
3279
    AssertComRCReturnRC(autoCaller.rc());
 
3280
 
 
3281
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
3282
 
 
3283
    BackRefList::iterator it =
 
3284
        std::find_if(m->backRefs.begin(), m->backRefs.end(),
 
3285
                     BackRef::EqualsTo(aMachineId));
 
3286
    AssertReturn(it != m->backRefs.end(), E_FAIL);
 
3287
 
 
3288
    if (aSnapshotId.isEmpty())
 
3289
    {
 
3290
        /* remove the current state attachment */
 
3291
        it->fInCurState = false;
 
3292
    }
 
3293
    else
 
3294
    {
 
3295
        /* remove the snapshot attachment */
 
3296
        GuidList::iterator jt = std::find(it->llSnapshotIds.begin(),
 
3297
                                          it->llSnapshotIds.end(),
 
3298
                                          aSnapshotId);
 
3299
 
 
3300
        AssertReturn(jt != it->llSnapshotIds.end(), E_FAIL);
 
3301
        it->llSnapshotIds.erase(jt);
 
3302
    }
 
3303
 
 
3304
    /* if the backref becomes empty, remove it */
 
3305
    if (it->fInCurState == false && it->llSnapshotIds.size() == 0)
 
3306
        m->backRefs.erase(it);
 
3307
 
 
3308
    return S_OK;
 
3309
}
 
3310
 
 
3311
/**
 
3312
 * Internal method to return the medium's list of backrefs. Must have caller + locking!
 
3313
 * @return
 
3314
 */
 
3315
const Guid* Medium::getFirstMachineBackrefId() const
 
3316
{
 
3317
    if (!m->backRefs.size())
 
3318
        return NULL;
 
3319
 
 
3320
    return &m->backRefs.front().machineId;
 
3321
}
 
3322
 
 
3323
const Guid* Medium::getFirstMachineBackrefSnapshotId() const
 
3324
{
 
3325
    if (!m->backRefs.size())
 
3326
        return NULL;
 
3327
 
 
3328
    const BackRef &ref = m->backRefs.front();
 
3329
    if (!ref.llSnapshotIds.size())
 
3330
        return NULL;
 
3331
 
 
3332
    return &ref.llSnapshotIds.front();
 
3333
}
 
3334
 
 
3335
#ifdef DEBUG
 
3336
/**
 
3337
 * Debugging helper that gets called after VirtualBox initialization that writes all
 
3338
 * machine backreferences to the debug log.
 
3339
 */
 
3340
void Medium::dumpBackRefs()
 
3341
{
 
3342
    AutoCaller autoCaller(this);
 
3343
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
3344
 
 
3345
    LogFlowThisFunc(("Dumping backrefs for medium '%s':\n", m->strLocationFull.c_str()));
 
3346
 
 
3347
    for (BackRefList::iterator it2 = m->backRefs.begin();
 
3348
         it2 != m->backRefs.end();
 
3349
         ++it2)
 
3350
    {
 
3351
        const BackRef &ref = *it2;
 
3352
        LogFlowThisFunc(("  Backref from machine {%RTuuid} (fInCurState: %d)\n", ref.machineId.raw(), ref.fInCurState));
 
3353
 
 
3354
        for (GuidList::const_iterator jt2 = it2->llSnapshotIds.begin();
 
3355
             jt2 != it2->llSnapshotIds.end();
 
3356
             ++jt2)
 
3357
        {
 
3358
            const Guid &id = *jt2;
 
3359
            LogFlowThisFunc(("  Backref from snapshot {%RTuuid}\n", id.raw()));
 
3360
        }
 
3361
    }
 
3362
}
 
3363
#endif
 
3364
 
 
3365
/**
 
3366
 * Checks if the given change of \a aOldPath to \a aNewPath affects the location
 
3367
 * of this media and updates it if necessary to reflect the new location.
 
3368
 *
 
3369
 * @param aOldPath  Old path (full).
 
3370
 * @param aNewPath  New path (full).
 
3371
 *
 
3372
 * @note Locks this object for writing.
 
3373
 */
 
3374
HRESULT Medium::updatePath(const Utf8Str &strOldPath, const Utf8Str &strNewPath)
 
3375
{
 
3376
    AssertReturn(!strOldPath.isEmpty(), E_FAIL);
 
3377
    AssertReturn(!strNewPath.isEmpty(), E_FAIL);
 
3378
 
 
3379
    AutoCaller autoCaller(this);
 
3380
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
3381
 
 
3382
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
3383
 
 
3384
    LogFlowThisFunc(("locationFull.before='%s'\n", m->strLocationFull.c_str()));
 
3385
 
 
3386
    const char *pcszMediumPath = m->strLocationFull.c_str();
 
3387
 
 
3388
    if (RTPathStartsWith(pcszMediumPath, strOldPath.c_str()))
 
3389
    {
 
3390
        Utf8Str newPath(strNewPath);
 
3391
        newPath.append(pcszMediumPath + strOldPath.length());
 
3392
        unconst(m->strLocationFull) = newPath;
 
3393
 
 
3394
        LogFlowThisFunc(("locationFull.after='%s'\n", m->strLocationFull.c_str()));
 
3395
    }
 
3396
 
 
3397
    return S_OK;
 
3398
}
 
3399
 
 
3400
/**
 
3401
 * Returns the base medium of the media chain this medium is part of.
 
3402
 *
 
3403
 * The base medium is found by walking up the parent-child relationship axis.
 
3404
 * If the medium doesn't have a parent (i.e. it's a base medium), it
 
3405
 * returns itself in response to this method.
 
3406
 *
 
3407
 * @param aLevel    Where to store the number of ancestors of this medium
 
3408
 *                  (zero for the base), may be @c NULL.
 
3409
 *
 
3410
 * @note Locks medium tree for reading.
 
3411
 */
 
3412
ComObjPtr<Medium> Medium::getBase(uint32_t *aLevel /*= NULL*/)
 
3413
{
 
3414
    ComObjPtr<Medium> pBase;
 
3415
    uint32_t level;
 
3416
 
 
3417
    AutoCaller autoCaller(this);
 
3418
    AssertReturn(autoCaller.isOk(), pBase);
 
3419
 
 
3420
    /* we access mParent */
 
3421
    AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
3422
 
 
3423
    pBase = this;
 
3424
    level = 0;
 
3425
 
 
3426
    if (m->pParent)
 
3427
    {
 
3428
        for (;;)
 
3429
        {
 
3430
            AutoCaller baseCaller(pBase);
 
3431
            AssertReturn(baseCaller.isOk(), pBase);
 
3432
 
 
3433
            if (pBase->m->pParent.isNull())
 
3434
                break;
 
3435
 
 
3436
            pBase = pBase->m->pParent;
 
3437
            ++level;
 
3438
        }
 
3439
    }
 
3440
 
 
3441
    if (aLevel != NULL)
 
3442
        *aLevel = level;
 
3443
 
 
3444
    return pBase;
 
3445
}
 
3446
 
 
3447
/**
 
3448
 * Returns @c true if this medium cannot be modified because it has
 
3449
 * dependents (children) or is part of the snapshot. Related to the medium
 
3450
 * type and posterity, not to the current media state.
 
3451
 *
 
3452
 * @note Locks this object and medium tree for reading.
 
3453
 */
 
3454
bool Medium::isReadOnly()
 
3455
{
 
3456
    AutoCaller autoCaller(this);
 
3457
    AssertComRCReturn(autoCaller.rc(), false);
 
3458
 
 
3459
    /* we access children */
 
3460
    AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
3461
 
 
3462
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
3463
 
 
3464
    switch (m->type)
 
3465
    {
 
3466
        case MediumType_Normal:
 
3467
        {
 
3468
            if (getChildren().size() != 0)
 
3469
                return true;
 
3470
 
 
3471
            for (BackRefList::const_iterator it = m->backRefs.begin();
 
3472
                 it != m->backRefs.end(); ++it)
 
3473
                if (it->llSnapshotIds.size() != 0)
 
3474
                    return true;
 
3475
 
 
3476
            return false;
 
3477
        }
 
3478
        case MediumType_Immutable:
 
3479
        case MediumType_MultiAttach:
 
3480
            return true;
 
3481
        case MediumType_Writethrough:
 
3482
        case MediumType_Shareable:
 
3483
        case MediumType_Readonly: /* explicit readonly media has no diffs */
 
3484
            return false;
 
3485
        default:
 
3486
            break;
 
3487
    }
 
3488
 
 
3489
    AssertFailedReturn(false);
 
3490
}
 
3491
 
 
3492
/**
 
3493
 * Saves medium data by appending a new child node to the given
 
3494
 * parent XML settings node.
 
3495
 *
 
3496
 * @param data      Settings struct to be updated.
 
3497
 * @param strHardDiskFolder Folder for which paths should be relative.
 
3498
 *
 
3499
 * @note Locks this object, medium tree and children for reading.
 
3500
 */
 
3501
HRESULT Medium::saveSettings(settings::Medium &data,
 
3502
                             const Utf8Str &strHardDiskFolder)
 
3503
{
 
3504
    AutoCaller autoCaller(this);
 
3505
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
3506
 
 
3507
    /* we access mParent */
 
3508
    AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
3509
 
 
3510
    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
3511
 
 
3512
    data.uuid = m->id;
 
3513
 
 
3514
    // make path relative if needed
 
3515
    if (    !strHardDiskFolder.isEmpty()
 
3516
         && RTPathStartsWith(m->strLocationFull.c_str(), strHardDiskFolder.c_str())
 
3517
       )
 
3518
        data.strLocation = m->strLocationFull.substr(strHardDiskFolder.length() + 1);
 
3519
    else
 
3520
        data.strLocation = m->strLocationFull;
 
3521
    data.strFormat = m->strFormat;
 
3522
 
 
3523
    /* optional, only for diffs, default is false */
 
3524
    if (m->pParent)
 
3525
        data.fAutoReset = m->autoReset;
 
3526
    else
 
3527
        data.fAutoReset = false;
 
3528
 
 
3529
    /* optional */
 
3530
    data.strDescription = m->strDescription;
 
3531
 
 
3532
    /* optional properties */
 
3533
    data.properties.clear();
 
3534
    for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
 
3535
         it != m->mapProperties.end();
 
3536
         ++it)
 
3537
    {
 
3538
        /* only save properties that have non-default values */
 
3539
        if (!it->second.isEmpty())
 
3540
        {
 
3541
            const Utf8Str &name = it->first;
 
3542
            const Utf8Str &value = it->second;
 
3543
            data.properties[name] = value;
 
3544
        }
 
3545
    }
 
3546
 
 
3547
    /* only for base media */
 
3548
    if (m->pParent.isNull())
 
3549
        data.hdType = m->type;
 
3550
 
 
3551
    /* save all children */
 
3552
    for (MediaList::const_iterator it = getChildren().begin();
 
3553
         it != getChildren().end();
 
3554
         ++it)
 
3555
    {
 
3556
        settings::Medium med;
 
3557
        HRESULT rc = (*it)->saveSettings(med, strHardDiskFolder);
 
3558
        AssertComRCReturnRC(rc);
 
3559
        data.llChildren.push_back(med);
 
3560
    }
 
3561
 
 
3562
    return S_OK;
 
3563
}
 
3564
 
 
3565
/**
 
3566
 * Constructs a medium lock list for this medium. The lock is not taken.
 
3567
 *
 
3568
 * @note Locks the medium tree for reading.
 
3569
 *
 
3570
 * @param fFailIfInaccessible If true, this fails with an error if a medium is inaccessible. If false,
 
3571
 *          inaccessible media are silently skipped and not locked (i.e. their state remains "Inaccessible");
 
3572
 *          this is necessary for a VM's removable media VM startup for which we do not want to fail.
 
3573
 * @param fMediumLockWrite  Whether to associate a write lock with this medium.
 
3574
 * @param pToBeParent       Medium which will become the parent of this medium.
 
3575
 * @param mediumLockList    Where to store the resulting list.
 
3576
 */
 
3577
HRESULT Medium::createMediumLockList(bool fFailIfInaccessible,
 
3578
                                     bool fMediumLockWrite,
 
3579
                                     Medium *pToBeParent,
 
3580
                                     MediumLockList &mediumLockList)
 
3581
{
 
3582
    AutoCaller autoCaller(this);
 
3583
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
3584
 
 
3585
    HRESULT rc = S_OK;
 
3586
 
 
3587
    /* we access parent medium objects */
 
3588
    AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
3589
 
 
3590
    /* paranoid sanity checking if the medium has a to-be parent medium */
 
3591
    if (pToBeParent)
 
3592
    {
 
3593
        AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
3594
        ComAssertRet(getParent().isNull(), E_FAIL);
 
3595
        ComAssertRet(getChildren().size() == 0, E_FAIL);
 
3596
    }
 
3597
 
 
3598
    ErrorInfoKeeper eik;
 
3599
    MultiResult mrc(S_OK);
 
3600
 
 
3601
    ComObjPtr<Medium> pMedium = this;
 
3602
    while (!pMedium.isNull())
 
3603
    {
 
3604
        // need write lock for RefreshState if medium is inaccessible
 
3605
        AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
 
3606
 
 
3607
        /* Accessibility check must be first, otherwise locking interferes
 
3608
         * with getting the medium state. Lock lists are not created for
 
3609
         * fun, and thus getting the medium status is no luxury. */
 
3610
        MediumState_T mediumState = pMedium->getState();
 
3611
        if (mediumState == MediumState_Inaccessible)
 
3612
        {
 
3613
            rc = pMedium->RefreshState(&mediumState);
 
3614
            if (FAILED(rc)) return rc;
 
3615
 
 
3616
            if (mediumState == MediumState_Inaccessible)
 
3617
            {
 
3618
                // ignore inaccessible ISO media and silently return S_OK,
 
3619
                // otherwise VM startup (esp. restore) may fail without good reason
 
3620
                if (!fFailIfInaccessible)
 
3621
                    return S_OK;
 
3622
 
 
3623
                // otherwise report an error
 
3624
                Bstr error;
 
3625
                rc = pMedium->COMGETTER(LastAccessError)(error.asOutParam());
 
3626
                if (FAILED(rc)) return rc;
 
3627
 
 
3628
                /* collect multiple errors */
 
3629
                eik.restore();
 
3630
                Assert(!error.isEmpty());
 
3631
                mrc = setError(E_FAIL,
 
3632
                               "%ls",
 
3633
                               error.raw());
 
3634
                    // error message will be something like
 
3635
                    // "Could not open the medium ... VD: error VERR_FILE_NOT_FOUND opening image file ... (VERR_FILE_NOT_FOUND).
 
3636
                eik.fetch();
 
3637
            }
 
3638
        }
 
3639
 
 
3640
        if (pMedium == this)
 
3641
            mediumLockList.Prepend(pMedium, fMediumLockWrite);
 
3642
        else
 
3643
            mediumLockList.Prepend(pMedium, false);
 
3644
 
 
3645
        pMedium = pMedium->getParent();
 
3646
        if (pMedium.isNull() && pToBeParent)
 
3647
        {
 
3648
            pMedium = pToBeParent;
 
3649
            pToBeParent = NULL;
 
3650
        }
 
3651
    }
 
3652
 
 
3653
    return mrc;
 
3654
}
 
3655
 
 
3656
/**
 
3657
 * Creates a new differencing storage unit using the format of the given target
 
3658
 * medium and the location. Note that @c aTarget must be NotCreated.
 
3659
 *
 
3660
 * The @a aMediumLockList parameter contains the associated medium lock list,
 
3661
 * which must be in locked state. If @a aWait is @c true then the caller is
 
3662
 * responsible for unlocking.
 
3663
 *
 
3664
 * If @a aProgress is not NULL but the object it points to is @c null then a
 
3665
 * new progress object will be created and assigned to @a *aProgress on
 
3666
 * success, otherwise the existing progress object is used. If @a aProgress is
 
3667
 * NULL, then no progress object is created/used at all.
 
3668
 *
 
3669
 * When @a aWait is @c false, this method will create a thread to perform the
 
3670
 * create operation asynchronously and will return immediately. Otherwise, it
 
3671
 * will perform the operation on the calling thread and will not return to the
 
3672
 * caller until the operation is completed. Note that @a aProgress cannot be
 
3673
 * NULL when @a aWait is @c false (this method will assert in this case).
 
3674
 *
 
3675
 * @param aTarget           Target medium.
 
3676
 * @param aVariant          Precise medium variant to create.
 
3677
 * @param aMediumLockList   List of media which should be locked.
 
3678
 * @param aProgress         Where to find/store a Progress object to track
 
3679
 *                          operation completion.
 
3680
 * @param aWait             @c true if this method should block instead of
 
3681
 *                          creating an asynchronous thread.
 
3682
 * @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs that will receive the registry IDs that need saving.
 
3683
 *                          This only works in "wait" mode; otherwise saveSettings is called automatically by the thread that
 
3684
 *                          was created, and this parameter is ignored.
 
3685
 *
 
3686
 * @note Locks this object and @a aTarget for writing.
 
3687
 */
 
3688
HRESULT Medium::createDiffStorage(ComObjPtr<Medium> &aTarget,
 
3689
                                  MediumVariant_T aVariant,
 
3690
                                  MediumLockList *aMediumLockList,
 
3691
                                  ComObjPtr<Progress> *aProgress,
 
3692
                                  bool aWait,
 
3693
                                  GuidList *pllRegistriesThatNeedSaving)
 
3694
{
 
3695
    AssertReturn(!aTarget.isNull(), E_FAIL);
 
3696
    AssertReturn(aMediumLockList, E_FAIL);
 
3697
    AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
 
3698
 
 
3699
    AutoCaller autoCaller(this);
 
3700
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
3701
 
 
3702
    AutoCaller targetCaller(aTarget);
 
3703
    if (FAILED(targetCaller.rc())) return targetCaller.rc();
 
3704
 
 
3705
    HRESULT rc = S_OK;
 
3706
    ComObjPtr<Progress> pProgress;
 
3707
    Medium::Task *pTask = NULL;
 
3708
 
 
3709
    try
 
3710
    {
 
3711
        AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
 
3712
 
 
3713
        ComAssertThrow(   m->type != MediumType_Writethrough
 
3714
                       && m->type != MediumType_Shareable
 
3715
                       && m->type != MediumType_Readonly, E_FAIL);
 
3716
        ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
 
3717
 
 
3718
        if (aTarget->m->state != MediumState_NotCreated)
 
3719
            throw aTarget->setStateError();
 
3720
 
 
3721
        /* Check that the medium is not attached to the current state of
 
3722
         * any VM referring to it. */
 
3723
        for (BackRefList::const_iterator it = m->backRefs.begin();
 
3724
             it != m->backRefs.end();
 
3725
             ++it)
 
3726
        {
 
3727
            if (it->fInCurState)
 
3728
            {
 
3729
                /* Note: when a VM snapshot is being taken, all normal media
 
3730
                 * attached to the VM in the current state will be, as an
 
3731
                 * exception, also associated with the snapshot which is about
 
3732
                 * to create (see SnapshotMachine::init()) before deassociating
 
3733
                 * them from the current state (which takes place only on
 
3734
                 * success in Machine::fixupHardDisks()), so that the size of
 
3735
                 * snapshotIds will be 1 in this case. The extra condition is
 
3736
                 * used to filter out this legal situation. */
 
3737
                if (it->llSnapshotIds.size() == 0)
 
3738
                    throw setError(VBOX_E_INVALID_OBJECT_STATE,
 
3739
                                   tr("Medium '%s' is attached to a virtual machine with UUID {%RTuuid}. No differencing media based on it may be created until it is detached"),
 
3740
                                   m->strLocationFull.c_str(), it->machineId.raw());
 
3741
 
 
3742
                Assert(it->llSnapshotIds.size() == 1);
 
3743
            }
 
3744
        }
 
3745
 
 
3746
        if (aProgress != NULL)
 
3747
        {
 
3748
            /* use the existing progress object... */
 
3749
            pProgress = *aProgress;
 
3750
 
 
3751
            /* ...but create a new one if it is null */
 
3752
            if (pProgress.isNull())
 
3753
            {
 
3754
                pProgress.createObject();
 
3755
                rc = pProgress->init(m->pVirtualBox,
 
3756
                                     static_cast<IMedium*>(this),
 
3757
                                     BstrFmt(tr("Creating differencing medium storage unit '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
 
3758
                                     TRUE /* aCancelable */);
 
3759
                if (FAILED(rc))
 
3760
                    throw rc;
 
3761
            }
 
3762
        }
 
3763
 
 
3764
        /* setup task object to carry out the operation sync/async */
 
3765
        pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
 
3766
                                           aMediumLockList,
 
3767
                                           aWait /* fKeepMediumLockList */);
 
3768
        rc = pTask->rc();
 
3769
        AssertComRC(rc);
 
3770
        if (FAILED(rc))
 
3771
             throw rc;
 
3772
 
 
3773
        /* register a task (it will deregister itself when done) */
 
3774
        ++m->numCreateDiffTasks;
 
3775
        Assert(m->numCreateDiffTasks != 0); /* overflow? */
 
3776
 
 
3777
        aTarget->m->state = MediumState_Creating;
 
3778
    }
 
3779
    catch (HRESULT aRC) { rc = aRC; }
 
3780
 
 
3781
    if (SUCCEEDED(rc))
 
3782
    {
 
3783
        if (aWait)
 
3784
            rc = runNow(pTask, pllRegistriesThatNeedSaving);
 
3785
        else
 
3786
            rc = startThread(pTask);
 
3787
 
 
3788
        if (SUCCEEDED(rc) && aProgress != NULL)
 
3789
            *aProgress = pProgress;
 
3790
    }
 
3791
    else if (pTask != NULL)
 
3792
        delete pTask;
 
3793
 
 
3794
    return rc;
 
3795
}
 
3796
 
 
3797
/**
 
3798
 * Returns a preferred format for differencing media.
 
3799
 */
 
3800
Utf8Str Medium::getPreferredDiffFormat()
 
3801
{
 
3802
    AutoCaller autoCaller(this);
 
3803
    AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
 
3804
 
 
3805
    /* check that our own format supports diffs */
 
3806
    if (!(m->formatObj->getCapabilities() & MediumFormatCapabilities_Differencing))
 
3807
    {
 
3808
        /* use the default format if not */
 
3809
        Utf8Str tmp;
 
3810
        m->pVirtualBox->getDefaultHardDiskFormat(tmp);
 
3811
        return tmp;
 
3812
    }
 
3813
 
 
3814
    /* m->strFormat is const, no need to lock */
 
3815
    return m->strFormat;
 
3816
}
 
3817
 
 
3818
/**
 
3819
 * Implementation for the public Medium::Close() with the exception of calling
 
3820
 * VirtualBox::saveSettings(), in case someone wants to call this for several
 
3821
 * media.
 
3822
 *
 
3823
 * After this returns with success, uninit() has been called on the medium, and
 
3824
 * the object is no longer usable ("not ready" state).
 
3825
 *
 
3826
 * @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs that will receive the registry IDs that need saving.
 
3827
 * @param autoCaller AutoCaller instance which must have been created on the caller's stack for this medium. This gets released here
 
3828
 *                   upon which the Medium instance gets uninitialized.
 
3829
 * @return
 
3830
 */
 
3831
HRESULT Medium::close(GuidList *pllRegistriesThatNeedSaving,
 
3832
                      AutoCaller &autoCaller)
 
3833
{
 
3834
    // we're accessing parent/child and backrefs, so lock the tree first, then ourselves
 
3835
    AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
 
3836
                                  this->lockHandle()
 
3837
                                  COMMA_LOCKVAL_SRC_POS);
 
3838
 
 
3839
    LogFlowFunc(("ENTER for %s\n", getLocationFull().c_str()));
 
3840
 
 
3841
    bool wasCreated = true;
 
3842
 
 
3843
    switch (m->state)
 
3844
    {
 
3845
        case MediumState_NotCreated:
 
3846
            wasCreated = false;
 
3847
            break;
 
3848
        case MediumState_Created:
 
3849
        case MediumState_Inaccessible:
 
3850
            break;
 
3851
        default:
 
3852
            return setStateError();
 
3853
    }
 
3854
 
 
3855
    if (m->backRefs.size() != 0)
 
3856
        return setError(VBOX_E_OBJECT_IN_USE,
 
3857
                        tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines"),
 
3858
                        m->strLocationFull.c_str(), m->backRefs.size());
 
3859
 
 
3860
    // perform extra media-dependent close checks
 
3861
    HRESULT rc = canClose();
 
3862
    if (FAILED(rc)) return rc;
 
3863
 
 
3864
    if (wasCreated)
 
3865
    {
 
3866
        // remove from the list of known media before performing actual
 
3867
        // uninitialization (to keep the media registry consistent on
 
3868
        // failure to do so)
 
3869
        rc = unregisterWithVirtualBox(pllRegistriesThatNeedSaving);
 
3870
        if (FAILED(rc)) return rc;
 
3871
    }
 
3872
 
 
3873
    // leave the AutoCaller, as otherwise uninit() will simply hang
 
3874
    autoCaller.release();
 
3875
 
 
3876
    // Keep the locks held until after uninit, as otherwise the consistency
 
3877
    // of the medium tree cannot be guaranteed.
 
3878
    uninit();
 
3879
 
 
3880
    LogFlowFuncLeave();
 
3881
 
 
3882
    return rc;
 
3883
}
 
3884
 
 
3885
/**
 
3886
 * Deletes the medium storage unit.
 
3887
 *
 
3888
 * If @a aProgress is not NULL but the object it points to is @c null then a new
 
3889
 * progress object will be created and assigned to @a *aProgress on success,
 
3890
 * otherwise the existing progress object is used. If Progress is NULL, then no
 
3891
 * progress object is created/used at all.
 
3892
 *
 
3893
 * When @a aWait is @c false, this method will create a thread to perform the
 
3894
 * delete operation asynchronously and will return immediately. Otherwise, it
 
3895
 * will perform the operation on the calling thread and will not return to the
 
3896
 * caller until the operation is completed. Note that @a aProgress cannot be
 
3897
 * NULL when @a aWait is @c false (this method will assert in this case).
 
3898
 *
 
3899
 * @param aProgress     Where to find/store a Progress object to track operation
 
3900
 *                      completion.
 
3901
 * @param aWait         @c true if this method should block instead of creating
 
3902
 *                      an asynchronous thread.
 
3903
 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
 
3904
 *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
 
3905
 *                This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
 
3906
 *                and this parameter is ignored.
 
3907
 *
 
3908
 * @note Locks mVirtualBox and this object for writing. Locks medium tree for
 
3909
 *       writing.
 
3910
 */
 
3911
HRESULT Medium::deleteStorage(ComObjPtr<Progress> *aProgress,
 
3912
                              bool aWait,
 
3913
                              GuidList *pllRegistriesThatNeedSaving)
 
3914
{
 
3915
    AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
 
3916
 
 
3917
    AutoCaller autoCaller(this);
 
3918
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
3919
 
 
3920
    HRESULT rc = S_OK;
 
3921
    ComObjPtr<Progress> pProgress;
 
3922
    Medium::Task *pTask = NULL;
 
3923
 
 
3924
    try
 
3925
    {
 
3926
        /* we're accessing the media tree, and canClose() needs it too */
 
3927
        AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),
 
3928
                                      this->lockHandle()
 
3929
                                      COMMA_LOCKVAL_SRC_POS);
 
3930
        LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() ));
 
3931
 
 
3932
        if (    !(m->formatObj->getCapabilities() & (   MediumFormatCapabilities_CreateDynamic
 
3933
                                                      | MediumFormatCapabilities_CreateFixed)))
 
3934
            throw setError(VBOX_E_NOT_SUPPORTED,
 
3935
                           tr("Medium format '%s' does not support storage deletion"),
 
3936
                           m->strFormat.c_str());
 
3937
 
 
3938
        /* Note that we are fine with Inaccessible state too: a) for symmetry
 
3939
         * with create calls and b) because it doesn't really harm to try, if
 
3940
         * it is really inaccessible, the delete operation will fail anyway.
 
3941
         * Accepting Inaccessible state is especially important because all
 
3942
         * registered media are initially Inaccessible upon VBoxSVC startup
 
3943
         * until COMGETTER(RefreshState) is called. Accept Deleting state
 
3944
         * because some callers need to put the medium in this state early
 
3945
         * to prevent races. */
 
3946
        switch (m->state)
 
3947
        {
 
3948
            case MediumState_Created:
 
3949
            case MediumState_Deleting:
 
3950
            case MediumState_Inaccessible:
 
3951
                break;
 
3952
            default:
 
3953
                throw setStateError();
 
3954
        }
 
3955
 
 
3956
        if (m->backRefs.size() != 0)
 
3957
        {
 
3958
            Utf8Str strMachines;
 
3959
            for (BackRefList::const_iterator it = m->backRefs.begin();
 
3960
                it != m->backRefs.end();
 
3961
                ++it)
 
3962
            {
 
3963
                const BackRef &b = *it;
 
3964
                if (strMachines.length())
 
3965
                    strMachines.append(", ");
 
3966
                strMachines.append(b.machineId.toString().c_str());
 
3967
            }
 
3968
#ifdef DEBUG
 
3969
            dumpBackRefs();
 
3970
#endif
 
3971
            throw setError(VBOX_E_OBJECT_IN_USE,
 
3972
                           tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"),
 
3973
                           m->strLocationFull.c_str(),
 
3974
                           m->backRefs.size(),
 
3975
                           strMachines.c_str());
 
3976
        }
 
3977
 
 
3978
        rc = canClose();
 
3979
        if (FAILED(rc))
 
3980
            throw rc;
 
3981
 
 
3982
        /* go to Deleting state, so that the medium is not actually locked */
 
3983
        if (m->state != MediumState_Deleting)
 
3984
        {
 
3985
            rc = markForDeletion();
 
3986
            if (FAILED(rc))
 
3987
                throw rc;
 
3988
        }
 
3989
 
 
3990
        /* Build the medium lock list. */
 
3991
        MediumLockList *pMediumLockList(new MediumLockList());
 
3992
        rc = createMediumLockList(true /* fFailIfInaccessible */,
 
3993
                                  true /* fMediumLockWrite */,
 
3994
                                  NULL,
 
3995
                                  *pMediumLockList);
 
3996
        if (FAILED(rc))
 
3997
        {
 
3998
            delete pMediumLockList;
 
3999
            throw rc;
 
4000
        }
 
4001
 
 
4002
        rc = pMediumLockList->Lock();
 
4003
        if (FAILED(rc))
 
4004
        {
 
4005
            delete pMediumLockList;
 
4006
            throw setError(rc,
 
4007
                           tr("Failed to lock media when deleting '%s'"),
 
4008
                           getLocationFull().c_str());
 
4009
        }
 
4010
 
 
4011
        /* try to remove from the list of known media before performing
 
4012
         * actual deletion (we favor the consistency of the media registry
 
4013
         * which would have been broken if unregisterWithVirtualBox() failed
 
4014
         * after we successfully deleted the storage) */
 
4015
        rc = unregisterWithVirtualBox(pllRegistriesThatNeedSaving);
 
4016
        if (FAILED(rc))
 
4017
            throw rc;
 
4018
        // no longer need lock
 
4019
        multilock.release();
 
4020
 
 
4021
        if (aProgress != NULL)
 
4022
        {
 
4023
            /* use the existing progress object... */
 
4024
            pProgress = *aProgress;
 
4025
 
 
4026
            /* ...but create a new one if it is null */
 
4027
            if (pProgress.isNull())
 
4028
            {
 
4029
                pProgress.createObject();
 
4030
                rc = pProgress->init(m->pVirtualBox,
 
4031
                                     static_cast<IMedium*>(this),
 
4032
                                     BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
 
4033
                                     FALSE /* aCancelable */);
 
4034
                if (FAILED(rc))
 
4035
                    throw rc;
 
4036
            }
 
4037
        }
 
4038
 
 
4039
        /* setup task object to carry out the operation sync/async */
 
4040
        pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList);
 
4041
        rc = pTask->rc();
 
4042
        AssertComRC(rc);
 
4043
        if (FAILED(rc))
 
4044
            throw rc;
 
4045
    }
 
4046
    catch (HRESULT aRC) { rc = aRC; }
 
4047
 
 
4048
    if (SUCCEEDED(rc))
 
4049
    {
 
4050
        if (aWait)
 
4051
            rc = runNow(pTask, NULL /* pfNeedsGlobalSaveSettings*/);
 
4052
        else
 
4053
            rc = startThread(pTask);
 
4054
 
 
4055
        if (SUCCEEDED(rc) && aProgress != NULL)
 
4056
            *aProgress = pProgress;
 
4057
 
 
4058
    }
 
4059
    else
 
4060
    {
 
4061
        if (pTask)
 
4062
            delete pTask;
 
4063
 
 
4064
        /* Undo deleting state if necessary. */
 
4065
        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4066
        unmarkForDeletion();
 
4067
    }
 
4068
 
 
4069
    return rc;
 
4070
}
 
4071
 
 
4072
/**
 
4073
 * Mark a medium for deletion.
 
4074
 *
 
4075
 * @note Caller must hold the write lock on this medium!
 
4076
 */
 
4077
HRESULT Medium::markForDeletion()
 
4078
{
 
4079
    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
 
4080
    switch (m->state)
 
4081
    {
 
4082
        case MediumState_Created:
 
4083
        case MediumState_Inaccessible:
 
4084
            m->preLockState = m->state;
 
4085
            m->state = MediumState_Deleting;
 
4086
            return S_OK;
 
4087
        default:
 
4088
            return setStateError();
 
4089
    }
 
4090
}
 
4091
 
 
4092
/**
 
4093
 * Removes the "mark for deletion".
 
4094
 *
 
4095
 * @note Caller must hold the write lock on this medium!
 
4096
 */
 
4097
HRESULT Medium::unmarkForDeletion()
 
4098
{
 
4099
    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
 
4100
    switch (m->state)
 
4101
    {
 
4102
        case MediumState_Deleting:
 
4103
            m->state = m->preLockState;
 
4104
            return S_OK;
 
4105
        default:
 
4106
            return setStateError();
 
4107
    }
 
4108
}
 
4109
 
 
4110
/**
 
4111
 * Mark a medium for deletion which is in locked state.
 
4112
 *
 
4113
 * @note Caller must hold the write lock on this medium!
 
4114
 */
 
4115
HRESULT Medium::markLockedForDeletion()
 
4116
{
 
4117
    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
 
4118
    if (   (   m->state == MediumState_LockedRead
 
4119
            || m->state == MediumState_LockedWrite)
 
4120
        && m->preLockState == MediumState_Created)
 
4121
    {
 
4122
        m->preLockState = MediumState_Deleting;
 
4123
        return S_OK;
 
4124
    }
 
4125
    else
 
4126
        return setStateError();
 
4127
}
 
4128
 
 
4129
/**
 
4130
 * Removes the "mark for deletion" for a medium in locked state.
 
4131
 *
 
4132
 * @note Caller must hold the write lock on this medium!
 
4133
 */
 
4134
HRESULT Medium::unmarkLockedForDeletion()
 
4135
{
 
4136
    ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
 
4137
    if (   (   m->state == MediumState_LockedRead
 
4138
            || m->state == MediumState_LockedWrite)
 
4139
        && m->preLockState == MediumState_Deleting)
 
4140
    {
 
4141
        m->preLockState = MediumState_Created;
 
4142
        return S_OK;
 
4143
    }
 
4144
    else
 
4145
        return setStateError();
 
4146
}
 
4147
 
 
4148
/**
 
4149
 * Prepares this (source) medium, target medium and all intermediate media
 
4150
 * for the merge operation.
 
4151
 *
 
4152
 * This method is to be called prior to calling the #mergeTo() to perform
 
4153
 * necessary consistency checks and place involved media to appropriate
 
4154
 * states. If #mergeTo() is not called or fails, the state modifications
 
4155
 * performed by this method must be undone by #cancelMergeTo().
 
4156
 *
 
4157
 * See #mergeTo() for more information about merging.
 
4158
 *
 
4159
 * @param pTarget       Target medium.
 
4160
 * @param aMachineId    Allowed machine attachment. NULL means do not check.
 
4161
 * @param aSnapshotId   Allowed snapshot attachment. NULL or empty UUID means
 
4162
 *                      do not check.
 
4163
 * @param fLockMedia    Flag whether to lock the medium lock list or not.
 
4164
 *                      If set to false and the medium lock list locking fails
 
4165
 *                      later you must call #cancelMergeTo().
 
4166
 * @param fMergeForward Resulting merge direction (out).
 
4167
 * @param pParentForTarget New parent for target medium after merge (out).
 
4168
 * @param aChildrenToReparent List of children of the source which will have
 
4169
 *                      to be reparented to the target after merge (out).
 
4170
 * @param aMediumLockList Medium locking information (out).
 
4171
 *
 
4172
 * @note Locks medium tree for reading. Locks this object, aTarget and all
 
4173
 *       intermediate media for writing.
 
4174
 */
 
4175
HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,
 
4176
                               const Guid *aMachineId,
 
4177
                               const Guid *aSnapshotId,
 
4178
                               bool fLockMedia,
 
4179
                               bool &fMergeForward,
 
4180
                               ComObjPtr<Medium> &pParentForTarget,
 
4181
                               MediaList &aChildrenToReparent,
 
4182
                               MediumLockList * &aMediumLockList)
 
4183
{
 
4184
    AssertReturn(pTarget != NULL, E_FAIL);
 
4185
    AssertReturn(pTarget != this, E_FAIL);
 
4186
 
 
4187
    AutoCaller autoCaller(this);
 
4188
    AssertComRCReturnRC(autoCaller.rc());
 
4189
 
 
4190
    AutoCaller targetCaller(pTarget);
 
4191
    AssertComRCReturnRC(targetCaller.rc());
 
4192
 
 
4193
    HRESULT rc = S_OK;
 
4194
    fMergeForward = false;
 
4195
    pParentForTarget.setNull();
 
4196
    aChildrenToReparent.clear();
 
4197
    Assert(aMediumLockList == NULL);
 
4198
    aMediumLockList = NULL;
 
4199
 
 
4200
    try
 
4201
    {
 
4202
        // locking: we need the tree lock first because we access parent pointers
 
4203
        AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
4204
 
 
4205
        /* more sanity checking and figuring out the merge direction */
 
4206
        ComObjPtr<Medium> pMedium = getParent();
 
4207
        while (!pMedium.isNull() && pMedium != pTarget)
 
4208
            pMedium = pMedium->getParent();
 
4209
        if (pMedium == pTarget)
 
4210
            fMergeForward = false;
 
4211
        else
 
4212
        {
 
4213
            pMedium = pTarget->getParent();
 
4214
            while (!pMedium.isNull() && pMedium != this)
 
4215
                pMedium = pMedium->getParent();
 
4216
            if (pMedium == this)
 
4217
                fMergeForward = true;
 
4218
            else
 
4219
            {
 
4220
                Utf8Str tgtLoc;
 
4221
                {
 
4222
                    AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
 
4223
                    tgtLoc = pTarget->getLocationFull();
 
4224
                }
 
4225
 
 
4226
                AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4227
                throw setError(VBOX_E_INVALID_OBJECT_STATE,
 
4228
                               tr("Media '%s' and '%s' are unrelated"),
 
4229
                               m->strLocationFull.c_str(), tgtLoc.c_str());
 
4230
            }
 
4231
        }
 
4232
 
 
4233
        /* Build the lock list. */
 
4234
        aMediumLockList = new MediumLockList();
 
4235
        if (fMergeForward)
 
4236
            rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */,
 
4237
                                               true /* fMediumLockWrite */,
 
4238
                                               NULL,
 
4239
                                               *aMediumLockList);
 
4240
        else
 
4241
            rc = createMediumLockList(true /* fFailIfInaccessible */,
 
4242
                                      false /* fMediumLockWrite */,
 
4243
                                      NULL,
 
4244
                                      *aMediumLockList);
 
4245
        if (FAILED(rc))
 
4246
            throw rc;
 
4247
 
 
4248
        /* Sanity checking, must be after lock list creation as it depends on
 
4249
         * valid medium states. The medium objects must be accessible. Only
 
4250
         * do this if immediate locking is requested, otherwise it fails when
 
4251
         * we construct a medium lock list for an already running VM. Snapshot
 
4252
         * deletion uses this to simplify its life. */
 
4253
        if (fLockMedia)
 
4254
        {
 
4255
            {
 
4256
                AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4257
                if (m->state != MediumState_Created)
 
4258
                    throw setStateError();
 
4259
            }
 
4260
            {
 
4261
                AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
 
4262
                if (pTarget->m->state != MediumState_Created)
 
4263
                    throw pTarget->setStateError();
 
4264
            }
 
4265
        }
 
4266
 
 
4267
        /* check medium attachment and other sanity conditions */
 
4268
        if (fMergeForward)
 
4269
        {
 
4270
            AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4271
            if (getChildren().size() > 1)
 
4272
            {
 
4273
                throw setError(VBOX_E_INVALID_OBJECT_STATE,
 
4274
                               tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
 
4275
                               m->strLocationFull.c_str(), getChildren().size());
 
4276
            }
 
4277
            /* One backreference is only allowed if the machine ID is not empty
 
4278
             * and it matches the machine the medium is attached to (including
 
4279
             * the snapshot ID if not empty). */
 
4280
            if (   m->backRefs.size() != 0
 
4281
                && (   !aMachineId
 
4282
                    || m->backRefs.size() != 1
 
4283
                    || aMachineId->isEmpty()
 
4284
                    || *getFirstMachineBackrefId() != *aMachineId
 
4285
                    || (   (!aSnapshotId || !aSnapshotId->isEmpty())
 
4286
                        && *getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
 
4287
                throw setError(VBOX_E_OBJECT_IN_USE,
 
4288
                               tr("Medium '%s' is attached to %d virtual machines"),
 
4289
                               m->strLocationFull.c_str(), m->backRefs.size());
 
4290
            if (m->type == MediumType_Immutable)
 
4291
                throw setError(VBOX_E_INVALID_OBJECT_STATE,
 
4292
                               tr("Medium '%s' is immutable"),
 
4293
                               m->strLocationFull.c_str());
 
4294
            if (m->type == MediumType_MultiAttach)
 
4295
                throw setError(VBOX_E_INVALID_OBJECT_STATE,
 
4296
                               tr("Medium '%s' is multi-attach"),
 
4297
                               m->strLocationFull.c_str());
 
4298
        }
 
4299
        else
 
4300
        {
 
4301
            AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
 
4302
            if (pTarget->getChildren().size() > 1)
 
4303
            {
 
4304
                throw setError(VBOX_E_OBJECT_IN_USE,
 
4305
                               tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
 
4306
                               pTarget->m->strLocationFull.c_str(),
 
4307
                               pTarget->getChildren().size());
 
4308
            }
 
4309
            if (pTarget->m->type == MediumType_Immutable)
 
4310
                throw setError(VBOX_E_INVALID_OBJECT_STATE,
 
4311
                               tr("Medium '%s' is immutable"),
 
4312
                               pTarget->m->strLocationFull.c_str());
 
4313
            if (pTarget->m->type == MediumType_MultiAttach)
 
4314
                throw setError(VBOX_E_INVALID_OBJECT_STATE,
 
4315
                               tr("Medium '%s' is multi-attach"),
 
4316
                               pTarget->m->strLocationFull.c_str());
 
4317
        }
 
4318
        ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
 
4319
        ComObjPtr<Medium> pLastIntermediate = pLast->getParent();
 
4320
        for (pLast = pLastIntermediate;
 
4321
             !pLast.isNull() && pLast != pTarget && pLast != this;
 
4322
             pLast = pLast->getParent())
 
4323
        {
 
4324
            AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
 
4325
            if (pLast->getChildren().size() > 1)
 
4326
            {
 
4327
                throw setError(VBOX_E_OBJECT_IN_USE,
 
4328
                               tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
 
4329
                               pLast->m->strLocationFull.c_str(),
 
4330
                               pLast->getChildren().size());
 
4331
            }
 
4332
            if (pLast->m->backRefs.size() != 0)
 
4333
                throw setError(VBOX_E_OBJECT_IN_USE,
 
4334
                               tr("Medium '%s' is attached to %d virtual machines"),
 
4335
                               pLast->m->strLocationFull.c_str(),
 
4336
                               pLast->m->backRefs.size());
 
4337
 
 
4338
        }
 
4339
 
 
4340
        /* Update medium states appropriately */
 
4341
        if (m->state == MediumState_Created)
 
4342
        {
 
4343
            rc = markForDeletion();
 
4344
            if (FAILED(rc))
 
4345
                throw rc;
 
4346
        }
 
4347
        else
 
4348
        {
 
4349
            if (fLockMedia)
 
4350
                throw setStateError();
 
4351
            else if (   m->state == MediumState_LockedWrite
 
4352
                     || m->state == MediumState_LockedRead)
 
4353
            {
 
4354
                /* Either mark it for deletion in locked state or allow
 
4355
                 * others to have done so. */
 
4356
                if (m->preLockState == MediumState_Created)
 
4357
                    markLockedForDeletion();
 
4358
                else if (m->preLockState != MediumState_Deleting)
 
4359
                    throw setStateError();
 
4360
            }
 
4361
            else
 
4362
                throw setStateError();
 
4363
        }
 
4364
 
 
4365
        if (fMergeForward)
 
4366
        {
 
4367
            /* we will need parent to reparent target */
 
4368
            pParentForTarget = m->pParent;
 
4369
        }
 
4370
        else
 
4371
        {
 
4372
            /* we will need to reparent children of the source */
 
4373
            for (MediaList::const_iterator it = getChildren().begin();
 
4374
                 it != getChildren().end();
 
4375
                 ++it)
 
4376
            {
 
4377
                pMedium = *it;
 
4378
                if (fLockMedia)
 
4379
                {
 
4380
                    rc = pMedium->LockWrite(NULL);
 
4381
                    if (FAILED(rc))
 
4382
                        throw rc;
 
4383
                }
 
4384
 
 
4385
                aChildrenToReparent.push_back(pMedium);
 
4386
            }
 
4387
        }
 
4388
        for (pLast = pLastIntermediate;
 
4389
             !pLast.isNull() && pLast != pTarget && pLast != this;
 
4390
             pLast = pLast->getParent())
 
4391
        {
 
4392
            AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
 
4393
            if (pLast->m->state == MediumState_Created)
 
4394
            {
 
4395
                rc = pLast->markForDeletion();
 
4396
                if (FAILED(rc))
 
4397
                    throw rc;
 
4398
            }
 
4399
            else
 
4400
                throw pLast->setStateError();
 
4401
        }
 
4402
 
 
4403
        /* Tweak the lock list in the backward merge case, as the target
 
4404
         * isn't marked to be locked for writing yet. */
 
4405
        if (!fMergeForward)
 
4406
        {
 
4407
            MediumLockList::Base::iterator lockListBegin =
 
4408
                aMediumLockList->GetBegin();
 
4409
            MediumLockList::Base::iterator lockListEnd =
 
4410
                aMediumLockList->GetEnd();
 
4411
            lockListEnd--;
 
4412
            for (MediumLockList::Base::iterator it = lockListBegin;
 
4413
                 it != lockListEnd;
 
4414
                 ++it)
 
4415
            {
 
4416
                MediumLock &mediumLock = *it;
 
4417
                if (mediumLock.GetMedium() == pTarget)
 
4418
                {
 
4419
                    HRESULT rc2 = mediumLock.UpdateLock(true);
 
4420
                    AssertComRC(rc2);
 
4421
                    break;
 
4422
                }
 
4423
            }
 
4424
        }
 
4425
 
 
4426
        if (fLockMedia)
 
4427
        {
 
4428
            rc = aMediumLockList->Lock();
 
4429
            if (FAILED(rc))
 
4430
            {
 
4431
                AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
 
4432
                throw setError(rc,
 
4433
                               tr("Failed to lock media when merging to '%s'"),
 
4434
                               pTarget->getLocationFull().c_str());
 
4435
            }
 
4436
        }
 
4437
    }
 
4438
    catch (HRESULT aRC) { rc = aRC; }
 
4439
 
 
4440
    if (FAILED(rc))
 
4441
    {
 
4442
        delete aMediumLockList;
 
4443
        aMediumLockList = NULL;
 
4444
    }
 
4445
 
 
4446
    return rc;
 
4447
}
 
4448
 
 
4449
/**
 
4450
 * Merges this medium to the specified medium which must be either its
 
4451
 * direct ancestor or descendant.
 
4452
 *
 
4453
 * Given this medium is SOURCE and the specified medium is TARGET, we will
 
4454
 * get two variants of the merge operation:
 
4455
 *
 
4456
 *                forward merge
 
4457
 *                ------------------------->
 
4458
 *  [Extra] <- SOURCE <- Intermediate <- TARGET
 
4459
 *  Any        Del       Del             LockWr
 
4460
 *
 
4461
 *
 
4462
 *                            backward merge
 
4463
 *                <-------------------------
 
4464
 *             TARGET <- Intermediate <- SOURCE <- [Extra]
 
4465
 *             LockWr    Del             Del       LockWr
 
4466
 *
 
4467
 * Each diagram shows the involved media on the media chain where
 
4468
 * SOURCE and TARGET belong. Under each medium there is a state value which
 
4469
 * the medium must have at a time of the mergeTo() call.
 
4470
 *
 
4471
 * The media in the square braces may be absent (e.g. when the forward
 
4472
 * operation takes place and SOURCE is the base medium, or when the backward
 
4473
 * merge operation takes place and TARGET is the last child in the chain) but if
 
4474
 * they present they are involved too as shown.
 
4475
 *
 
4476
 * Neither the source medium nor intermediate media may be attached to
 
4477
 * any VM directly or in the snapshot, otherwise this method will assert.
 
4478
 *
 
4479
 * The #prepareMergeTo() method must be called prior to this method to place all
 
4480
 * involved to necessary states and perform other consistency checks.
 
4481
 *
 
4482
 * If @a aWait is @c true then this method will perform the operation on the
 
4483
 * calling thread and will not return to the caller until the operation is
 
4484
 * completed. When this method succeeds, all intermediate medium objects in
 
4485
 * the chain will be uninitialized, the state of the target medium (and all
 
4486
 * involved extra media) will be restored. @a aMediumLockList will not be
 
4487
 * deleted, whether the operation is successful or not. The caller has to do
 
4488
 * this if appropriate. Note that this (source) medium is not uninitialized
 
4489
 * because of possible AutoCaller instances held by the caller of this method
 
4490
 * on the current thread. It's therefore the responsibility of the caller to
 
4491
 * call Medium::uninit() after releasing all callers.
 
4492
 *
 
4493
 * If @a aWait is @c false then this method will create a thread to perform the
 
4494
 * operation asynchronously and will return immediately. If the operation
 
4495
 * succeeds, the thread will uninitialize the source medium object and all
 
4496
 * intermediate medium objects in the chain, reset the state of the target
 
4497
 * medium (and all involved extra media) and delete @a aMediumLockList.
 
4498
 * If the operation fails, the thread will only reset the states of all
 
4499
 * involved media and delete @a aMediumLockList.
 
4500
 *
 
4501
 * When this method fails (regardless of the @a aWait mode), it is a caller's
 
4502
 * responsibility to undo state changes and delete @a aMediumLockList using
 
4503
 * #cancelMergeTo().
 
4504
 *
 
4505
 * If @a aProgress is not NULL but the object it points to is @c null then a new
 
4506
 * progress object will be created and assigned to @a *aProgress on success,
 
4507
 * otherwise the existing progress object is used. If Progress is NULL, then no
 
4508
 * progress object is created/used at all. Note that @a aProgress cannot be
 
4509
 * NULL when @a aWait is @c false (this method will assert in this case).
 
4510
 *
 
4511
 * @param pTarget       Target medium.
 
4512
 * @param fMergeForward Merge direction.
 
4513
 * @param pParentForTarget New parent for target medium after merge.
 
4514
 * @param aChildrenToReparent List of children of the source which will have
 
4515
 *                      to be reparented to the target after merge.
 
4516
 * @param aMediumLockList Medium locking information.
 
4517
 * @param aProgress     Where to find/store a Progress object to track operation
 
4518
 *                      completion.
 
4519
 * @param aWait         @c true if this method should block instead of creating
 
4520
 *                      an asynchronous thread.
 
4521
 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true
 
4522
 *                by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
 
4523
 *                This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,
 
4524
 *                and this parameter is ignored.
 
4525
 *
 
4526
 * @note Locks the tree lock for writing. Locks the media from the chain
 
4527
 *       for writing.
 
4528
 */
 
4529
HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget,
 
4530
                        bool fMergeForward,
 
4531
                        const ComObjPtr<Medium> &pParentForTarget,
 
4532
                        const MediaList &aChildrenToReparent,
 
4533
                        MediumLockList *aMediumLockList,
 
4534
                        ComObjPtr <Progress> *aProgress,
 
4535
                        bool aWait,
 
4536
                        GuidList *pllRegistriesThatNeedSaving)
 
4537
{
 
4538
    AssertReturn(pTarget != NULL, E_FAIL);
 
4539
    AssertReturn(pTarget != this, E_FAIL);
 
4540
    AssertReturn(aMediumLockList != NULL, E_FAIL);
 
4541
    AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
 
4542
 
 
4543
    AutoCaller autoCaller(this);
 
4544
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4545
 
 
4546
    AutoCaller targetCaller(pTarget);
 
4547
    AssertComRCReturnRC(targetCaller.rc());
 
4548
 
 
4549
    HRESULT rc = S_OK;
 
4550
    ComObjPtr <Progress> pProgress;
 
4551
    Medium::Task *pTask = NULL;
 
4552
 
 
4553
    try
 
4554
    {
 
4555
        if (aProgress != NULL)
 
4556
        {
 
4557
            /* use the existing progress object... */
 
4558
            pProgress = *aProgress;
 
4559
 
 
4560
            /* ...but create a new one if it is null */
 
4561
            if (pProgress.isNull())
 
4562
            {
 
4563
                Utf8Str tgtName;
 
4564
                {
 
4565
                    AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
 
4566
                    tgtName = pTarget->getName();
 
4567
                }
 
4568
 
 
4569
                AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4570
 
 
4571
                pProgress.createObject();
 
4572
                rc = pProgress->init(m->pVirtualBox,
 
4573
                                     static_cast<IMedium*>(this),
 
4574
                                     BstrFmt(tr("Merging medium '%s' to '%s'"),
 
4575
                                             getName().c_str(),
 
4576
                                             tgtName.c_str()).raw(),
 
4577
                                     TRUE /* aCancelable */);
 
4578
                if (FAILED(rc))
 
4579
                    throw rc;
 
4580
            }
 
4581
        }
 
4582
 
 
4583
        /* setup task object to carry out the operation sync/async */
 
4584
        pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
 
4585
                                      pParentForTarget, aChildrenToReparent,
 
4586
                                      pProgress, aMediumLockList,
 
4587
                                      aWait /* fKeepMediumLockList */);
 
4588
        rc = pTask->rc();
 
4589
        AssertComRC(rc);
 
4590
        if (FAILED(rc))
 
4591
            throw rc;
 
4592
    }
 
4593
    catch (HRESULT aRC) { rc = aRC; }
 
4594
 
 
4595
    if (SUCCEEDED(rc))
 
4596
    {
 
4597
        if (aWait)
 
4598
            rc = runNow(pTask, pllRegistriesThatNeedSaving);
 
4599
        else
 
4600
            rc = startThread(pTask);
 
4601
 
 
4602
        if (SUCCEEDED(rc) && aProgress != NULL)
 
4603
            *aProgress = pProgress;
 
4604
    }
 
4605
    else if (pTask != NULL)
 
4606
        delete pTask;
 
4607
 
 
4608
    return rc;
 
4609
}
 
4610
 
 
4611
/**
 
4612
 * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not
 
4613
 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
 
4614
 * the medium objects in @a aChildrenToReparent.
 
4615
 *
 
4616
 * @param aChildrenToReparent List of children of the source which will have
 
4617
 *                      to be reparented to the target after merge.
 
4618
 * @param aMediumLockList Medium locking information.
 
4619
 *
 
4620
 * @note Locks the media from the chain for writing.
 
4621
 */
 
4622
void Medium::cancelMergeTo(const MediaList &aChildrenToReparent,
 
4623
                           MediumLockList *aMediumLockList)
 
4624
{
 
4625
    AutoCaller autoCaller(this);
 
4626
    AssertComRCReturnVoid(autoCaller.rc());
 
4627
 
 
4628
    AssertReturnVoid(aMediumLockList != NULL);
 
4629
 
 
4630
    /* Revert media marked for deletion to previous state. */
 
4631
    HRESULT rc;
 
4632
    MediumLockList::Base::const_iterator mediumListBegin =
 
4633
        aMediumLockList->GetBegin();
 
4634
    MediumLockList::Base::const_iterator mediumListEnd =
 
4635
        aMediumLockList->GetEnd();
 
4636
    for (MediumLockList::Base::const_iterator it = mediumListBegin;
 
4637
         it != mediumListEnd;
 
4638
         ++it)
 
4639
    {
 
4640
        const MediumLock &mediumLock = *it;
 
4641
        const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
 
4642
        AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
 
4643
 
 
4644
        if (pMedium->m->state == MediumState_Deleting)
 
4645
        {
 
4646
            rc = pMedium->unmarkForDeletion();
 
4647
            AssertComRC(rc);
 
4648
        }
 
4649
    }
 
4650
 
 
4651
    /* the destructor will do the work */
 
4652
    delete aMediumLockList;
 
4653
 
 
4654
    /* unlock the children which had to be reparented */
 
4655
    for (MediaList::const_iterator it = aChildrenToReparent.begin();
 
4656
         it != aChildrenToReparent.end();
 
4657
         ++it)
 
4658
    {
 
4659
        const ComObjPtr<Medium> &pMedium = *it;
 
4660
 
 
4661
        AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
 
4662
        pMedium->UnlockWrite(NULL);
 
4663
    }
 
4664
}
 
4665
 
 
4666
/**
 
4667
 * Fix the parent UUID of all children to point to this medium as their
 
4668
 * parent.
 
4669
 */
 
4670
HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent)
 
4671
{
 
4672
    MediumLockList mediumLockList;
 
4673
    HRESULT rc = createMediumLockList(true /* fFailIfInaccessible */,
 
4674
                                      false /* fMediumLockWrite */,
 
4675
                                      this,
 
4676
                                      mediumLockList);
 
4677
    AssertComRCReturnRC(rc);
 
4678
 
 
4679
    try
 
4680
    {
 
4681
        PVBOXHDD hdd;
 
4682
        int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
 
4683
        ComAssertRCThrow(vrc, E_FAIL);
 
4684
 
 
4685
        try
 
4686
        {
 
4687
            MediumLockList::Base::iterator lockListBegin =
 
4688
                mediumLockList.GetBegin();
 
4689
            MediumLockList::Base::iterator lockListEnd =
 
4690
                mediumLockList.GetEnd();
 
4691
            for (MediumLockList::Base::iterator it = lockListBegin;
 
4692
                 it != lockListEnd;
 
4693
                 ++it)
 
4694
            {
 
4695
                MediumLock &mediumLock = *it;
 
4696
                const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
 
4697
                AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
 
4698
 
 
4699
                // open the medium
 
4700
                vrc = VDOpen(hdd,
 
4701
                             pMedium->m->strFormat.c_str(),
 
4702
                             pMedium->m->strLocationFull.c_str(),
 
4703
                             VD_OPEN_FLAGS_READONLY,
 
4704
                             pMedium->m->vdImageIfaces);
 
4705
                if (RT_FAILURE(vrc))
 
4706
                    throw vrc;
 
4707
            }
 
4708
 
 
4709
            for (MediaList::const_iterator it = childrenToReparent.begin();
 
4710
                 it != childrenToReparent.end();
 
4711
                 ++it)
 
4712
            {
 
4713
                /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
 
4714
                vrc = VDOpen(hdd,
 
4715
                             (*it)->m->strFormat.c_str(),
 
4716
                             (*it)->m->strLocationFull.c_str(),
 
4717
                             VD_OPEN_FLAGS_INFO,
 
4718
                             (*it)->m->vdImageIfaces);
 
4719
                if (RT_FAILURE(vrc))
 
4720
                    throw vrc;
 
4721
 
 
4722
                vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
 
4723
                if (RT_FAILURE(vrc))
 
4724
                    throw vrc;
 
4725
 
 
4726
                vrc = VDClose(hdd, false /* fDelete */);
 
4727
                if (RT_FAILURE(vrc))
 
4728
                    throw vrc;
 
4729
 
 
4730
                (*it)->UnlockWrite(NULL);
 
4731
            }
 
4732
        }
 
4733
        catch (HRESULT aRC) { rc = aRC; }
 
4734
        catch (int aVRC)
 
4735
        {
 
4736
            throw setError(E_FAIL,
 
4737
                            tr("Could not update medium UUID references to parent '%s' (%s)"),
 
4738
                            m->strLocationFull.c_str(),
 
4739
                            vdError(aVRC).c_str());
 
4740
        }
 
4741
 
 
4742
        VDDestroy(hdd);
 
4743
    }
 
4744
    catch (HRESULT aRC) { rc = aRC; }
 
4745
 
 
4746
    return rc;
 
4747
}
 
4748
 
 
4749
/**
 
4750
 * Used by IAppliance to export disk images.
 
4751
 *
 
4752
 * @param aFilename             Filename to create (UTF8).
 
4753
 * @param aFormat               Medium format for creating @a aFilename.
 
4754
 * @param aVariant              Which exact image format variant to use
 
4755
 *                              for the destination image.
 
4756
 * @param aVDImageIOCallbacks   Pointer to the callback table for a
 
4757
 *                              VDINTERFACEIO interface. May be NULL.
 
4758
 * @param aVDImageIOUser        Opaque data for the callbacks.
 
4759
 * @param aProgress             Progress object to use.
 
4760
 * @return
 
4761
 * @note The source format is defined by the Medium instance.
 
4762
 */
 
4763
HRESULT Medium::exportFile(const char *aFilename,
 
4764
                           const ComObjPtr<MediumFormat> &aFormat,
 
4765
                           MediumVariant_T aVariant,
 
4766
                           void *aVDImageIOCallbacks, void *aVDImageIOUser,
 
4767
                           const ComObjPtr<Progress> &aProgress)
 
4768
{
 
4769
    AssertPtrReturn(aFilename, E_INVALIDARG);
 
4770
    AssertReturn(!aFormat.isNull(), E_INVALIDARG);
 
4771
    AssertReturn(!aProgress.isNull(), E_INVALIDARG);
 
4772
 
 
4773
    AutoCaller autoCaller(this);
 
4774
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4775
 
 
4776
    HRESULT rc = S_OK;
 
4777
    Medium::Task *pTask = NULL;
 
4778
 
 
4779
    try
 
4780
    {
 
4781
        // locking: we need the tree lock first because we access parent pointers
 
4782
        AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
4783
        // and we need to write-lock the media involved
 
4784
        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4785
 
 
4786
        /* Build the source lock list. */
 
4787
        MediumLockList *pSourceMediumLockList(new MediumLockList());
 
4788
        rc = createMediumLockList(true /* fFailIfInaccessible */,
 
4789
                                  false /* fMediumLockWrite */,
 
4790
                                  NULL,
 
4791
                                  *pSourceMediumLockList);
 
4792
        if (FAILED(rc))
 
4793
        {
 
4794
            delete pSourceMediumLockList;
 
4795
            throw rc;
 
4796
        }
 
4797
 
 
4798
        rc = pSourceMediumLockList->Lock();
 
4799
        if (FAILED(rc))
 
4800
        {
 
4801
            delete pSourceMediumLockList;
 
4802
            throw setError(rc,
 
4803
                           tr("Failed to lock source media '%s'"),
 
4804
                           getLocationFull().c_str());
 
4805
        }
 
4806
 
 
4807
        /* setup task object to carry out the operation asynchronously */
 
4808
        pTask = new Medium::ExportTask(this, aProgress, aFilename, aFormat,
 
4809
                                       aVariant, aVDImageIOCallbacks,
 
4810
                                       aVDImageIOUser, pSourceMediumLockList);
 
4811
        rc = pTask->rc();
 
4812
        AssertComRC(rc);
 
4813
        if (FAILED(rc))
 
4814
            throw rc;
 
4815
    }
 
4816
    catch (HRESULT aRC) { rc = aRC; }
 
4817
 
 
4818
    if (SUCCEEDED(rc))
 
4819
        rc = startThread(pTask);
 
4820
    else if (pTask != NULL)
 
4821
        delete pTask;
 
4822
 
 
4823
    return rc;
 
4824
}
 
4825
 
 
4826
/**
 
4827
 * Used by IAppliance to import disk images.
 
4828
 *
 
4829
 * @param aFilename             Filename to read (UTF8).
 
4830
 * @param aFormat               Medium format for reading @a aFilename.
 
4831
 * @param aVariant              Which exact image format variant to use
 
4832
 *                              for the destination image.
 
4833
 * @param aVDImageIOCallbacks   Pointer to the callback table for a
 
4834
 *                              VDINTERFACEIO interface. May be NULL.
 
4835
 * @param aVDImageIOUser        Opaque data for the callbacks.
 
4836
 * @param aParent               Parent medium. May be NULL.
 
4837
 * @param aProgress             Progress object to use.
 
4838
 * @return
 
4839
 * @note The destination format is defined by the Medium instance.
 
4840
 */
 
4841
HRESULT Medium::importFile(const char *aFilename,
 
4842
                           const ComObjPtr<MediumFormat> &aFormat,
 
4843
                           MediumVariant_T aVariant,
 
4844
                           void *aVDImageIOCallbacks, void *aVDImageIOUser,
 
4845
                           const ComObjPtr<Medium> &aParent,
 
4846
                           const ComObjPtr<Progress> &aProgress)
 
4847
{
 
4848
    AssertPtrReturn(aFilename, E_INVALIDARG);
 
4849
    AssertReturn(!aFormat.isNull(), E_INVALIDARG);
 
4850
    AssertReturn(!aProgress.isNull(), E_INVALIDARG);
 
4851
 
 
4852
    AutoCaller autoCaller(this);
 
4853
    if (FAILED(autoCaller.rc())) return autoCaller.rc();
 
4854
 
 
4855
    HRESULT rc = S_OK;
 
4856
    Medium::Task *pTask = NULL;
 
4857
 
 
4858
    try
 
4859
    {
 
4860
        // locking: we need the tree lock first because we access parent pointers
 
4861
        AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
4862
        // and we need to write-lock the media involved
 
4863
        AutoMultiWriteLock2 alock(this, aParent COMMA_LOCKVAL_SRC_POS);
 
4864
 
 
4865
        if (   m->state != MediumState_NotCreated
 
4866
            && m->state != MediumState_Created)
 
4867
            throw setStateError();
 
4868
 
 
4869
        /* Build the target lock list. */
 
4870
        MediumLockList *pTargetMediumLockList(new MediumLockList());
 
4871
        rc = createMediumLockList(true /* fFailIfInaccessible */,
 
4872
                                  true /* fMediumLockWrite */,
 
4873
                                  aParent,
 
4874
                                  *pTargetMediumLockList);
 
4875
        if (FAILED(rc))
 
4876
        {
 
4877
            delete pTargetMediumLockList;
 
4878
            throw rc;
 
4879
        }
 
4880
 
 
4881
        rc = pTargetMediumLockList->Lock();
 
4882
        if (FAILED(rc))
 
4883
        {
 
4884
            delete pTargetMediumLockList;
 
4885
            throw setError(rc,
 
4886
                           tr("Failed to lock target media '%s'"),
 
4887
                           getLocationFull().c_str());
 
4888
        }
 
4889
 
 
4890
        /* setup task object to carry out the operation asynchronously */
 
4891
        pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat,
 
4892
                                       aVariant, aVDImageIOCallbacks,
 
4893
                                       aVDImageIOUser, aParent,
 
4894
                                       pTargetMediumLockList);
 
4895
        rc = pTask->rc();
 
4896
        AssertComRC(rc);
 
4897
        if (FAILED(rc))
 
4898
            throw rc;
 
4899
 
 
4900
        if (m->state == MediumState_NotCreated)
 
4901
            m->state = MediumState_Creating;
 
4902
    }
 
4903
    catch (HRESULT aRC) { rc = aRC; }
 
4904
 
 
4905
    if (SUCCEEDED(rc))
 
4906
        rc = startThread(pTask);
 
4907
    else if (pTask != NULL)
 
4908
        delete pTask;
 
4909
 
 
4910
    return rc;
 
4911
}
 
4912
 
 
4913
////////////////////////////////////////////////////////////////////////////////
 
4914
//
 
4915
// Private methods
 
4916
//
 
4917
////////////////////////////////////////////////////////////////////////////////
 
4918
 
 
4919
/**
 
4920
 * Queries information from the medium.
 
4921
 *
 
4922
 * As a result of this call, the accessibility state and data members such as
 
4923
 * size and description will be updated with the current information.
 
4924
 *
 
4925
 * @note This method may block during a system I/O call that checks storage
 
4926
 *       accessibility.
 
4927
 *
 
4928
 * @note Locks medium tree for reading and writing (for new diff media checked
 
4929
 *       for the first time). Locks mParent for reading. Locks this object for
 
4930
 *       writing.
 
4931
 *
 
4932
 * @param fSetImageId Whether to reset the UUID contained in the image file to the UUID in the medium instance data (see SetIDs())
 
4933
 * @param fSetParentId Whether to reset the parent UUID contained in the image file to the parent UUID in the medium instance data (see SetIDs())
 
4934
 * @return
 
4935
 */
 
4936
HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId)
 
4937
{
 
4938
    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
 
4939
 
 
4940
    if (   m->state != MediumState_Created
 
4941
        && m->state != MediumState_Inaccessible
 
4942
        && m->state != MediumState_LockedRead)
 
4943
        return E_FAIL;
 
4944
 
 
4945
    HRESULT rc = S_OK;
 
4946
 
 
4947
    int vrc = VINF_SUCCESS;
 
4948
 
 
4949
    /* check if a blocking queryInfo() call is in progress on some other thread,
 
4950
     * and wait for it to finish if so instead of querying data ourselves */
 
4951
    if (m->queryInfoRunning)
 
4952
    {
 
4953
        Assert(   m->state == MediumState_LockedRead
 
4954
               || m->state == MediumState_LockedWrite);
 
4955
 
 
4956
        alock.leave();
 
4957
        vrc = RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT);
 
4958
        alock.enter();
 
4959
 
 
4960
        AssertRC(vrc);
 
4961
 
 
4962
        return S_OK;
 
4963
    }
 
4964
 
 
4965
    bool success = false;
 
4966
    Utf8Str lastAccessError;
 
4967
 
 
4968
    /* are we dealing with a new medium constructed using the existing
 
4969
     * location? */
 
4970
    bool isImport = m->id.isEmpty();
 
4971
    unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
 
4972
 
 
4973
    /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
 
4974
     * media because that would prevent necessary modifications
 
4975
     * when opening media of some third-party formats for the first
 
4976
     * time in VirtualBox (such as VMDK for which VDOpen() needs to
 
4977
     * generate an UUID if it is missing) */
 
4978
    if (    (m->hddOpenMode == OpenReadOnly)
 
4979
         || m->type == MediumType_Readonly
 
4980
         || !isImport
 
4981
       )
 
4982
        uOpenFlags |= VD_OPEN_FLAGS_READONLY;
 
4983
 
 
4984
    /* Open shareable medium with the appropriate flags */
 
4985
    if (m->type == MediumType_Shareable)
 
4986
        uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
 
4987
 
 
4988
    /* Lock the medium, which makes the behavior much more consistent */
 
4989
    if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
 
4990
        rc = LockRead(NULL);
 
4991
    else
 
4992
        rc = LockWrite(NULL);
 
4993
    if (FAILED(rc)) return rc;
 
4994
 
 
4995
    /* Copies of the input state fields which are not read-only,
 
4996
     * as we're dropping the lock. CAUTION: be extremely careful what
 
4997
     * you do with the contents of this medium object, as you will
 
4998
     * create races if there are concurrent changes. */
 
4999
    Utf8Str format(m->strFormat);
 
5000
    Utf8Str location(m->strLocationFull);
 
5001
    ComObjPtr<MediumFormat> formatObj = m->formatObj;
 
5002
 
 
5003
    /* "Output" values which can't be set because the lock isn't held
 
5004
     * at the time the values are determined. */
 
5005
    Guid mediumId = m->id;
 
5006
    uint64_t mediumSize = 0;
 
5007
    uint64_t mediumLogicalSize = 0;
 
5008
 
 
5009
    /* Flag whether a base image has a non-zero parent UUID and thus
 
5010
     * need repairing after it was closed again. */
 
5011
    bool fRepairImageZeroParentUuid = false;
 
5012
 
 
5013
    /* leave the lock before a lengthy operation */
 
5014
    vrc = RTSemEventMultiReset(m->queryInfoSem);
 
5015
    AssertRCReturn(vrc, E_FAIL);
 
5016
    m->queryInfoRunning = true;
 
5017
    alock.leave();
 
5018
 
 
5019
    try
 
5020
    {
 
5021
        /* skip accessibility checks for host drives */
 
5022
        if (m->hostDrive)
 
5023
        {
 
5024
            success = true;
 
5025
            throw S_OK;
 
5026
        }
 
5027
 
 
5028
        PVBOXHDD hdd;
 
5029
        vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
 
5030
        ComAssertRCThrow(vrc, E_FAIL);
 
5031
 
 
5032
        try
 
5033
        {
 
5034
            /** @todo This kind of opening of media is assuming that diff
 
5035
             * media can be opened as base media. Should be documented that
 
5036
             * it must work for all medium format backends. */
 
5037
            vrc = VDOpen(hdd,
 
5038
                         format.c_str(),
 
5039
                         location.c_str(),
 
5040
                         uOpenFlags,
 
5041
                         m->vdImageIfaces);
 
5042
            if (RT_FAILURE(vrc))
 
5043
            {
 
5044
                lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
 
5045
                                             location.c_str(), vdError(vrc).c_str());
 
5046
                throw S_OK;
 
5047
            }
 
5048
 
 
5049
            if (formatObj->getCapabilities() & MediumFormatCapabilities_Uuid)
 
5050
            {
 
5051
                /* Modify the UUIDs if necessary. The associated fields are
 
5052
                 * not modified by other code, so no need to copy. */
 
5053
                if (fSetImageId)
 
5054
                {
 
5055
                    vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
 
5056
                    ComAssertRCThrow(vrc, E_FAIL);
 
5057
                }
 
5058
                if (fSetParentId)
 
5059
                {
 
5060
                    vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
 
5061
                    ComAssertRCThrow(vrc, E_FAIL);
 
5062
                }
 
5063
                /* zap the information, these are no long-term members */
 
5064
                unconst(m->uuidImage).clear();
 
5065
                unconst(m->uuidParentImage).clear();
 
5066
 
 
5067
                /* check the UUID */
 
5068
                RTUUID uuid;
 
5069
                vrc = VDGetUuid(hdd, 0, &uuid);
 
5070
                ComAssertRCThrow(vrc, E_FAIL);
 
5071
 
 
5072
                if (isImport)
 
5073
                {
 
5074
                    mediumId = uuid;
 
5075
 
 
5076
                    if (mediumId.isEmpty() && (m->hddOpenMode == OpenReadOnly))
 
5077
                        // only when importing a VDMK that has no UUID, create one in memory
 
5078
                        mediumId.create();
 
5079
                }
 
5080
                else
 
5081
                {
 
5082
                    Assert(!mediumId.isEmpty());
 
5083
 
 
5084
                    if (mediumId != uuid)
 
5085
                    {
 
5086
                        lastAccessError = Utf8StrFmt(
 
5087
                                tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
 
5088
                                &uuid,
 
5089
                                location.c_str(),
 
5090
                                mediumId.raw(),
 
5091
                                m->pVirtualBox->settingsFilePath().c_str());
 
5092
                        throw S_OK;
 
5093
                    }
 
5094
                }
 
5095
            }
 
5096
            else
 
5097
            {
 
5098
                /* the backend does not support storing UUIDs within the
 
5099
                 * underlying storage so use what we store in XML */
 
5100
 
 
5101
                /* generate an UUID for an imported UUID-less medium */
 
5102
                if (isImport)
 
5103
                {
 
5104
                    if (fSetImageId)
 
5105
                        mediumId = m->uuidImage;
 
5106
                    else
 
5107
                        mediumId.create();
 
5108
                }
 
5109
            }
 
5110
 
 
5111
            /* get the medium variant */
 
5112
            unsigned uImageFlags;
 
5113
            vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
 
5114
            ComAssertRCThrow(vrc, E_FAIL);
 
5115
            m->variant = (MediumVariant_T)uImageFlags;
 
5116
 
 
5117
            /* check/get the parent uuid and update corresponding state */
 
5118
            if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
 
5119
            {
 
5120
                RTUUID parentId;
 
5121
                vrc = VDGetParentUuid(hdd, 0, &parentId);
 
5122
                ComAssertRCThrow(vrc, E_FAIL);
 
5123
 
 
5124
                /* streamOptimized VMDK images are only accepted as base
 
5125
                 * images, as this allows automatic repair of OVF appliances.
 
5126
                 * Since such images don't support random writes they will not
 
5127
                 * be created for diff images. Only an overly smart user might
 
5128
                 * manually create this case. Too bad for him. */
 
5129
                if (   isImport
 
5130
                    && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
 
5131
                {
 
5132
                    /* the parent must be known to us. Note that we freely
 
5133
                     * call locking methods of mVirtualBox and parent, as all
 
5134
                     * relevant locks must be already held. There may be no
 
5135
                     * concurrent access to the just opened medium on other
 
5136
                     * threads yet (and init() will fail if this method reports
 
5137
                     * MediumState_Inaccessible) */
 
5138
 
 
5139
                    Guid id = parentId;
 
5140
                    ComObjPtr<Medium> pParent;
 
5141
                    rc = m->pVirtualBox->findHardDiskById(id, false /* aSetError */, &pParent);
 
5142
                    if (FAILED(rc))
 
5143
                    {
 
5144
                        lastAccessError = Utf8StrFmt(
 
5145
                                tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
 
5146
                                &parentId, location.c_str(),
 
5147
                                m->pVirtualBox->settingsFilePath().c_str());
 
5148
                        throw S_OK;
 
5149
                    }
 
5150
 
 
5151
                    /* we set mParent & children() */
 
5152
                    AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
5153
 
 
5154
                    Assert(m->pParent.isNull());
 
5155
                    m->pParent = pParent;
 
5156
                    m->pParent->m->llChildren.push_back(this);
 
5157
                }
 
5158
                else
 
5159
                {
 
5160
                    /* we access mParent */
 
5161
                    AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
5162
 
 
5163
                    /* check that parent UUIDs match. Note that there's no need
 
5164
                     * for the parent's AutoCaller (our lifetime is bound to
 
5165
                     * it) */
 
5166
 
 
5167
                    if (m->pParent.isNull())
 
5168
                    {
 
5169
                        /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
 
5170
                         * and 3.1.0-3.1.8 there are base images out there
 
5171
                         * which have a non-zero parent UUID. No point in
 
5172
                         * complaining about them, instead automatically
 
5173
                         * repair the problem. Later we can bring back the
 
5174
                         * error message, but we should wait until really
 
5175
                         * most users have repaired their images, either with
 
5176
                         * VBoxFixHdd or this way. */
 
5177
#if 1
 
5178
                        fRepairImageZeroParentUuid = true;
 
5179
#else /* 0 */
 
5180
                        lastAccessError = Utf8StrFmt(
 
5181
                                tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
 
5182
                                location.c_str(),
 
5183
                                m->pVirtualBox->settingsFilePath().c_str());
 
5184
                        throw S_OK;
 
5185
#endif /* 0 */
 
5186
                    }
 
5187
 
 
5188
                    AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
 
5189
                    if (   !fRepairImageZeroParentUuid
 
5190
                        && m->pParent->getState() != MediumState_Inaccessible
 
5191
                        && m->pParent->getId() != parentId)
 
5192
                    {
 
5193
                        lastAccessError = Utf8StrFmt(
 
5194
                                tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
 
5195
                                &parentId, location.c_str(),
 
5196
                                m->pParent->getId().raw(),
 
5197
                                m->pVirtualBox->settingsFilePath().c_str());
 
5198
                        throw S_OK;
 
5199
                    }
 
5200
 
 
5201
                    /// @todo NEWMEDIA what to do if the parent is not
 
5202
                    /// accessible while the diff is? Probably nothing. The
 
5203
                    /// real code will detect the mismatch anyway.
 
5204
                }
 
5205
            }
 
5206
 
 
5207
            mediumSize = VDGetFileSize(hdd, 0);
 
5208
            mediumLogicalSize = VDGetSize(hdd, 0);
 
5209
 
 
5210
            success = true;
 
5211
        }
 
5212
        catch (HRESULT aRC)
 
5213
        {
 
5214
            rc = aRC;
 
5215
        }
 
5216
 
 
5217
        VDDestroy(hdd);
 
5218
    }
 
5219
    catch (HRESULT aRC)
 
5220
    {
 
5221
        rc = aRC;
 
5222
    }
 
5223
 
 
5224
    alock.enter();
 
5225
 
 
5226
    if (isImport)
 
5227
        unconst(m->id) = mediumId;
 
5228
 
 
5229
    if (success)
 
5230
    {
 
5231
        m->size = mediumSize;
 
5232
        m->logicalSize = mediumLogicalSize;
 
5233
        m->strLastAccessError.setNull();
 
5234
    }
 
5235
    else
 
5236
    {
 
5237
        m->strLastAccessError = lastAccessError;
 
5238
        LogWarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
 
5239
                        location.c_str(), m->strLastAccessError.c_str(),
 
5240
                        rc, vrc));
 
5241
    }
 
5242
 
 
5243
    /* inform other callers if there are any */
 
5244
    RTSemEventMultiSignal(m->queryInfoSem);
 
5245
    m->queryInfoRunning = false;
 
5246
 
 
5247
    /* Set the proper state according to the result of the check */
 
5248
    if (success)
 
5249
        m->preLockState = MediumState_Created;
 
5250
    else
 
5251
        m->preLockState = MediumState_Inaccessible;
 
5252
 
 
5253
    HRESULT rc2;
 
5254
    if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
 
5255
        rc2 = UnlockRead(NULL);
 
5256
    else
 
5257
        rc2 = UnlockWrite(NULL);
 
5258
    if (SUCCEEDED(rc) && FAILED(rc2))
 
5259
        rc = rc2;
 
5260
    if (FAILED(rc)) return rc;
 
5261
 
 
5262
    /* If this is a base image which incorrectly has a parent UUID set,
 
5263
     * repair the image now by zeroing the parent UUID. This is only done
 
5264
     * when we have structural information from a config file, on import
 
5265
     * this is not possible. If someone would accidentally call openMedium
 
5266
     * with a diff image before the base is registered this would destroy
 
5267
     * the diff. Not acceptable. */
 
5268
    if (fRepairImageZeroParentUuid)
 
5269
    {
 
5270
        rc = LockWrite(NULL);
 
5271
        if (FAILED(rc)) return rc;
 
5272
 
 
5273
        alock.leave();
 
5274
 
 
5275
        try
 
5276
        {
 
5277
            PVBOXHDD hdd;
 
5278
            vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
 
5279
            ComAssertRCThrow(vrc, E_FAIL);
 
5280
 
 
5281
            try
 
5282
            {
 
5283
                vrc = VDOpen(hdd,
 
5284
                             format.c_str(),
 
5285
                             location.c_str(),
 
5286
                             uOpenFlags & ~VD_OPEN_FLAGS_READONLY,
 
5287
                             m->vdImageIfaces);
 
5288
                if (RT_FAILURE(vrc))
 
5289
                    throw S_OK;
 
5290
 
 
5291
                RTUUID zeroParentUuid;
 
5292
                RTUuidClear(&zeroParentUuid);
 
5293
                vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
 
5294
                ComAssertRCThrow(vrc, E_FAIL);
 
5295
            }
 
5296
            catch (HRESULT aRC)
 
5297
            {
 
5298
                rc = aRC;
 
5299
            }
 
5300
 
 
5301
            VDDestroy(hdd);
 
5302
        }
 
5303
        catch (HRESULT aRC)
 
5304
        {
 
5305
            rc = aRC;
 
5306
        }
 
5307
 
 
5308
        alock.enter();
 
5309
 
 
5310
        rc = UnlockWrite(NULL);
 
5311
        if (SUCCEEDED(rc) && FAILED(rc2))
 
5312
            rc = rc2;
 
5313
        if (FAILED(rc)) return rc;
 
5314
    }
 
5315
 
 
5316
    return rc;
 
5317
}
 
5318
 
 
5319
/**
 
5320
 * Performs extra checks if the medium can be closed and returns S_OK in
 
5321
 * this case. Otherwise, returns a respective error message. Called by
 
5322
 * Close() under the medium tree lock and the medium lock.
 
5323
 *
 
5324
 * @note Also reused by Medium::Reset().
 
5325
 *
 
5326
 * @note Caller must hold the media tree write lock!
 
5327
 */
 
5328
HRESULT Medium::canClose()
 
5329
{
 
5330
    Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
 
5331
 
 
5332
    if (getChildren().size() != 0)
 
5333
        return setError(VBOX_E_OBJECT_IN_USE,
 
5334
                        tr("Cannot close medium '%s' because it has %d child media"),
 
5335
                        m->strLocationFull.c_str(), getChildren().size());
 
5336
 
 
5337
    return S_OK;
 
5338
}
 
5339
 
 
5340
/**
 
5341
 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
 
5342
 *
 
5343
 * This calls either VirtualBox::unregisterImage or VirtualBox::unregisterHardDisk depending
 
5344
 * on the device type of this medium.
 
5345
 *
 
5346
 * @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs that will receive the registry IDs that need saving.
 
5347
 *
 
5348
 * @note Caller must have locked the media tree lock for writing!
 
5349
 */
 
5350
HRESULT Medium::unregisterWithVirtualBox(GuidList *pllRegistriesThatNeedSaving)
 
5351
{
 
5352
    /* Note that we need to de-associate ourselves from the parent to let
 
5353
     * unregisterHardDisk() properly save the registry */
 
5354
 
 
5355
    /* we modify mParent and access children */
 
5356
    Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
 
5357
 
 
5358
    Medium *pParentBackup = m->pParent;
 
5359
    AssertReturn(getChildren().size() == 0, E_FAIL);
 
5360
    if (m->pParent)
 
5361
        deparent();
 
5362
 
 
5363
    HRESULT rc = E_FAIL;
 
5364
    switch (m->devType)
 
5365
    {
 
5366
        case DeviceType_DVD:
 
5367
        case DeviceType_Floppy:
 
5368
            rc = m->pVirtualBox->unregisterImage(this,
 
5369
                                                 m->devType,
 
5370
                                                 pllRegistriesThatNeedSaving);
 
5371
        break;
 
5372
 
 
5373
        case DeviceType_HardDisk:
 
5374
            rc = m->pVirtualBox->unregisterHardDisk(this, pllRegistriesThatNeedSaving);
 
5375
        break;
 
5376
 
 
5377
        default:
 
5378
        break;
 
5379
    }
 
5380
 
 
5381
    if (FAILED(rc))
 
5382
    {
 
5383
        if (pParentBackup)
 
5384
        {
 
5385
            // re-associate with the parent as we are still relatives in the registry
 
5386
            m->pParent = pParentBackup;
 
5387
            m->pParent->m->llChildren.push_back(this);
 
5388
        }
 
5389
    }
 
5390
 
 
5391
    return rc;
 
5392
}
 
5393
 
 
5394
/**
 
5395
 * Sets the extended error info according to the current media state.
 
5396
 *
 
5397
 * @note Must be called from under this object's write or read lock.
 
5398
 */
 
5399
HRESULT Medium::setStateError()
 
5400
{
 
5401
    HRESULT rc = E_FAIL;
 
5402
 
 
5403
    switch (m->state)
 
5404
    {
 
5405
        case MediumState_NotCreated:
 
5406
        {
 
5407
            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
 
5408
                          tr("Storage for the medium '%s' is not created"),
 
5409
                          m->strLocationFull.c_str());
 
5410
            break;
 
5411
        }
 
5412
        case MediumState_Created:
 
5413
        {
 
5414
            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
 
5415
                          tr("Storage for the medium '%s' is already created"),
 
5416
                          m->strLocationFull.c_str());
 
5417
            break;
 
5418
        }
 
5419
        case MediumState_LockedRead:
 
5420
        {
 
5421
            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
 
5422
                          tr("Medium '%s' is locked for reading by another task"),
 
5423
                          m->strLocationFull.c_str());
 
5424
            break;
 
5425
        }
 
5426
        case MediumState_LockedWrite:
 
5427
        {
 
5428
            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
 
5429
                          tr("Medium '%s' is locked for writing by another task"),
 
5430
                          m->strLocationFull.c_str());
 
5431
            break;
 
5432
        }
 
5433
        case MediumState_Inaccessible:
 
5434
        {
 
5435
            /* be in sync with Console::powerUpThread() */
 
5436
            if (!m->strLastAccessError.isEmpty())
 
5437
                rc = setError(VBOX_E_INVALID_OBJECT_STATE,
 
5438
                              tr("Medium '%s' is not accessible. %s"),
 
5439
                              m->strLocationFull.c_str(), m->strLastAccessError.c_str());
 
5440
            else
 
5441
                rc = setError(VBOX_E_INVALID_OBJECT_STATE,
 
5442
                              tr("Medium '%s' is not accessible"),
 
5443
                              m->strLocationFull.c_str());
 
5444
            break;
 
5445
        }
 
5446
        case MediumState_Creating:
 
5447
        {
 
5448
            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
 
5449
                          tr("Storage for the medium '%s' is being created"),
 
5450
                          m->strLocationFull.c_str());
 
5451
            break;
 
5452
        }
 
5453
        case MediumState_Deleting:
 
5454
        {
 
5455
            rc = setError(VBOX_E_INVALID_OBJECT_STATE,
 
5456
                          tr("Storage for the medium '%s' is being deleted"),
 
5457
                          m->strLocationFull.c_str());
 
5458
            break;
 
5459
        }
 
5460
        default:
 
5461
        {
 
5462
            AssertFailed();
 
5463
            break;
 
5464
        }
 
5465
    }
 
5466
 
 
5467
    return rc;
 
5468
}
 
5469
 
 
5470
/**
 
5471
 * Sets the value of m->strLocationFull. The given location must be a fully
 
5472
 * qualified path; relative paths are not supported here.
 
5473
 *
 
5474
 * As a special exception, if the specified location is a file path that ends with '/'
 
5475
 * then the file name part will be generated by this method automatically in the format
 
5476
 * '{<uuid>}.<ext>' where <uuid> is a fresh UUID that this method will generate
 
5477
 * and assign to this medium, and <ext> is the default extension for this
 
5478
 * medium's storage format. Note that this procedure requires the media state to
 
5479
 * be NotCreated and will return a failure otherwise.
 
5480
 *
 
5481
 * @param aLocation Location of the storage unit. If the location is a FS-path,
 
5482
 *                  then it can be relative to the VirtualBox home directory.
 
5483
 * @param aFormat   Optional fallback format if it is an import and the format
 
5484
 *                  cannot be determined.
 
5485
 *
 
5486
 * @note Must be called from under this object's write lock.
 
5487
 */
 
5488
HRESULT Medium::setLocation(const Utf8Str &aLocation,
 
5489
                            const Utf8Str &aFormat /* = Utf8Str::Empty */)
 
5490
{
 
5491
    AssertReturn(!aLocation.isEmpty(), E_FAIL);
 
5492
 
 
5493
    AutoCaller autoCaller(this);
 
5494
    AssertComRCReturnRC(autoCaller.rc());
 
5495
 
 
5496
    /* formatObj may be null only when initializing from an existing path and
 
5497
     * no format is known yet */
 
5498
    AssertReturn(    (!m->strFormat.isEmpty() && !m->formatObj.isNull())
 
5499
                  || (    autoCaller.state() == InInit
 
5500
                       && m->state != MediumState_NotCreated
 
5501
                       && m->id.isEmpty()
 
5502
                       && m->strFormat.isEmpty()
 
5503
                       && m->formatObj.isNull()),
 
5504
                 E_FAIL);
 
5505
 
 
5506
    /* are we dealing with a new medium constructed using the existing
 
5507
     * location? */
 
5508
    bool isImport = m->strFormat.isEmpty();
 
5509
 
 
5510
    if (   isImport
 
5511
        || (   (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
 
5512
            && !m->hostDrive))
 
5513
    {
 
5514
        Guid id;
 
5515
 
 
5516
        Utf8Str locationFull(aLocation);
 
5517
 
 
5518
        if (m->state == MediumState_NotCreated)
 
5519
        {
 
5520
            /* must be a file (formatObj must be already known) */
 
5521
            Assert(m->formatObj->getCapabilities() & MediumFormatCapabilities_File);
 
5522
 
 
5523
            if (RTPathFilename(aLocation.c_str()) == NULL)
 
5524
            {
 
5525
                /* no file name is given (either an empty string or ends with a
 
5526
                 * slash), generate a new UUID + file name if the state allows
 
5527
                 * this */
 
5528
 
 
5529
                ComAssertMsgRet(!m->formatObj->getFileExtensions().empty(),
 
5530
                                ("Must be at least one extension if it is MediumFormatCapabilities_File\n"),
 
5531
                                E_FAIL);
 
5532
 
 
5533
                Utf8Str strExt = m->formatObj->getFileExtensions().front();
 
5534
                ComAssertMsgRet(!strExt.isEmpty(),
 
5535
                                ("Default extension must not be empty\n"),
 
5536
                                E_FAIL);
 
5537
 
 
5538
                id.create();
 
5539
 
 
5540
                locationFull = Utf8StrFmt("%s{%RTuuid}.%s",
 
5541
                                          aLocation.c_str(), id.raw(), strExt.c_str());
 
5542
            }
 
5543
        }
 
5544
 
 
5545
        // we must always have full paths now
 
5546
        if (!RTPathStartsWithRoot(locationFull.c_str()))
 
5547
            return setError(VBOX_E_FILE_ERROR,
 
5548
                            tr("The given path '%s' is not fully qualified"),
 
5549
                            locationFull.c_str());
 
5550
 
 
5551
        /* detect the backend from the storage unit if importing */
 
5552
        if (isImport)
 
5553
        {
 
5554
            VDTYPE enmType = VDTYPE_INVALID;
 
5555
            char *backendName = NULL;
 
5556
 
 
5557
            int vrc = VINF_SUCCESS;
 
5558
 
 
5559
            /* is it a file? */
 
5560
            {
 
5561
                RTFILE file;
 
5562
                vrc = RTFileOpen(&file, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
 
5563
                if (RT_SUCCESS(vrc))
 
5564
                    RTFileClose(file);
 
5565
            }
 
5566
            if (RT_SUCCESS(vrc))
 
5567
            {
 
5568
                vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
 
5569
                                  locationFull.c_str(), &backendName, &enmType);
 
5570
            }
 
5571
            else if (vrc != VERR_FILE_NOT_FOUND && vrc != VERR_PATH_NOT_FOUND)
 
5572
            {
 
5573
                /* assume it's not a file, restore the original location */
 
5574
                locationFull = aLocation;
 
5575
                vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
 
5576
                                  locationFull.c_str(), &backendName, &enmType);
 
5577
            }
 
5578
 
 
5579
            if (RT_FAILURE(vrc))
 
5580
            {
 
5581
                if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
 
5582
                    return setError(VBOX_E_FILE_ERROR,
 
5583
                                    tr("Could not find file for the medium '%s' (%Rrc)"),
 
5584
                                    locationFull.c_str(), vrc);
 
5585
                else if (aFormat.isEmpty())
 
5586
                    return setError(VBOX_E_IPRT_ERROR,
 
5587
                                    tr("Could not get the storage format of the medium '%s' (%Rrc)"),
 
5588
                                    locationFull.c_str(), vrc);
 
5589
                else
 
5590
                {
 
5591
                    HRESULT rc = setFormat(aFormat);
 
5592
                    /* setFormat() must not fail since we've just used the backend so
 
5593
                     * the format object must be there */
 
5594
                    AssertComRCReturnRC(rc);
 
5595
                }
 
5596
            }
 
5597
            else if (   enmType == VDTYPE_INVALID
 
5598
                     || m->devType != convertToDeviceType(enmType))
 
5599
            {
 
5600
                /*
 
5601
                 * The user tried to use a image as a device which is not supported
 
5602
                 * by the backend.
 
5603
                 */
 
5604
                return setError(E_FAIL,
 
5605
                                tr("The medium '%s' can't be used as the requested device type"),
 
5606
                                locationFull.c_str());
 
5607
            }
 
5608
            else
 
5609
            {
 
5610
                ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
 
5611
 
 
5612
                HRESULT rc = setFormat(backendName);
 
5613
                RTStrFree(backendName);
 
5614
 
 
5615
                /* setFormat() must not fail since we've just used the backend so
 
5616
                 * the format object must be there */
 
5617
                AssertComRCReturnRC(rc);
 
5618
            }
 
5619
        }
 
5620
 
 
5621
        m->strLocationFull = locationFull;
 
5622
 
 
5623
        /* is it still a file? */
 
5624
        if (    (m->formatObj->getCapabilities() & MediumFormatCapabilities_File)
 
5625
             && (m->state == MediumState_NotCreated)
 
5626
           )
 
5627
            /* assign a new UUID (this UUID will be used when calling
 
5628
             * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
 
5629
             * also do that if we didn't generate it to make sure it is
 
5630
             * either generated by us or reset to null */
 
5631
            unconst(m->id) = id;
 
5632
    }
 
5633
    else
 
5634
        m->strLocationFull = aLocation;
 
5635
 
 
5636
    return S_OK;
 
5637
}
 
5638
 
 
5639
/**
 
5640
 * Checks that the format ID is valid and sets it on success.
 
5641
 *
 
5642
 * Note that this method will caller-reference the format object on success!
 
5643
 * This reference must be released somewhere to let the MediumFormat object be
 
5644
 * uninitialized.
 
5645
 *
 
5646
 * @note Must be called from under this object's write lock.
 
5647
 */
 
5648
HRESULT Medium::setFormat(const Utf8Str &aFormat)
 
5649
{
 
5650
    /* get the format object first */
 
5651
    {
 
5652
        SystemProperties *pSysProps = m->pVirtualBox->getSystemProperties();
 
5653
        AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
 
5654
 
 
5655
        unconst(m->formatObj) = pSysProps->mediumFormat(aFormat);
 
5656
        if (m->formatObj.isNull())
 
5657
            return setError(E_INVALIDARG,
 
5658
                            tr("Invalid medium storage format '%s'"),
 
5659
                            aFormat.c_str());
 
5660
 
 
5661
        /* reference the format permanently to prevent its unexpected
 
5662
         * uninitialization */
 
5663
        HRESULT rc = m->formatObj->addCaller();
 
5664
        AssertComRCReturnRC(rc);
 
5665
 
 
5666
        /* get properties (preinsert them as keys in the map). Note that the
 
5667
         * map doesn't grow over the object life time since the set of
 
5668
         * properties is meant to be constant. */
 
5669
 
 
5670
        Assert(m->mapProperties.empty());
 
5671
 
 
5672
        for (MediumFormat::PropertyList::const_iterator it = m->formatObj->getProperties().begin();
 
5673
             it != m->formatObj->getProperties().end();
 
5674
             ++it)
 
5675
        {
 
5676
            m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
 
5677
        }
 
5678
    }
 
5679
 
 
5680
    unconst(m->strFormat) = aFormat;
 
5681
 
 
5682
    return S_OK;
 
5683
}
 
5684
 
 
5685
/**
 
5686
 * Converts the Medium device type to the VD type.
 
5687
 */
 
5688
VDTYPE Medium::convertDeviceType()
 
5689
{
 
5690
    VDTYPE enmType;
 
5691
 
 
5692
    switch (m->devType)
 
5693
    {
 
5694
        case DeviceType_HardDisk:
 
5695
            enmType = VDTYPE_HDD;
 
5696
            break;
 
5697
        case DeviceType_DVD:
 
5698
            enmType = VDTYPE_DVD;
 
5699
            break;
 
5700
        case DeviceType_Floppy:
 
5701
            enmType = VDTYPE_FLOPPY;
 
5702
            break;
 
5703
        default:
 
5704
            ComAssertFailedRet(VDTYPE_INVALID);
 
5705
    }
 
5706
 
 
5707
    return enmType;
 
5708
}
 
5709
 
 
5710
/**
 
5711
 * Converts from the VD type to the medium type.
 
5712
 */
 
5713
DeviceType_T Medium::convertToDeviceType(VDTYPE enmType)
 
5714
{
 
5715
    DeviceType_T devType;
 
5716
 
 
5717
    switch (enmType)
 
5718
    {
 
5719
        case VDTYPE_HDD:
 
5720
            devType = DeviceType_HardDisk;
 
5721
            break;
 
5722
        case VDTYPE_DVD:
 
5723
            devType = DeviceType_DVD;
 
5724
            break;
 
5725
        case VDTYPE_FLOPPY:
 
5726
            devType = DeviceType_Floppy;
 
5727
            break;
 
5728
        default:
 
5729
            ComAssertFailedRet(DeviceType_Null);
 
5730
    }
 
5731
 
 
5732
    return devType;
 
5733
}
 
5734
 
 
5735
/**
 
5736
 * Returns the last error message collected by the vdErrorCall callback and
 
5737
 * resets it.
 
5738
 *
 
5739
 * The error message is returned prepended with a dot and a space, like this:
 
5740
 * <code>
 
5741
 *   ". <error_text> (%Rrc)"
 
5742
 * </code>
 
5743
 * to make it easily appendable to a more general error message. The @c %Rrc
 
5744
 * format string is given @a aVRC as an argument.
 
5745
 *
 
5746
 * If there is no last error message collected by vdErrorCall or if it is a
 
5747
 * null or empty string, then this function returns the following text:
 
5748
 * <code>
 
5749
 *   " (%Rrc)"
 
5750
 * </code>
 
5751
 *
 
5752
 * @note Doesn't do any object locking; it is assumed that the caller makes sure
 
5753
 *       the callback isn't called by more than one thread at a time.
 
5754
 *
 
5755
 * @param aVRC  VBox error code to use when no error message is provided.
 
5756
 */
 
5757
Utf8Str Medium::vdError(int aVRC)
 
5758
{
 
5759
    Utf8Str error;
 
5760
 
 
5761
    if (m->vdError.isEmpty())
 
5762
        error = Utf8StrFmt(" (%Rrc)", aVRC);
 
5763
    else
 
5764
        error = Utf8StrFmt(".\n%s", m->vdError.c_str());
 
5765
 
 
5766
    m->vdError.setNull();
 
5767
 
 
5768
    return error;
 
5769
}
 
5770
 
 
5771
/**
 
5772
 * Error message callback.
 
5773
 *
 
5774
 * Puts the reported error message to the m->vdError field.
 
5775
 *
 
5776
 * @note Doesn't do any object locking; it is assumed that the caller makes sure
 
5777
 *       the callback isn't called by more than one thread at a time.
 
5778
 *
 
5779
 * @param   pvUser          The opaque data passed on container creation.
 
5780
 * @param   rc              The VBox error code.
 
5781
 * @param   RT_SRC_POS_DECL Use RT_SRC_POS.
 
5782
 * @param   pszFormat       Error message format string.
 
5783
 * @param   va              Error message arguments.
 
5784
 */
 
5785
/*static*/
 
5786
DECLCALLBACK(void) Medium::vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
 
5787
                                       const char *pszFormat, va_list va)
 
5788
{
 
5789
    NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
 
5790
 
 
5791
    Medium *that = static_cast<Medium*>(pvUser);
 
5792
    AssertReturnVoid(that != NULL);
 
5793
 
 
5794
    if (that->m->vdError.isEmpty())
 
5795
        that->m->vdError =
 
5796
            Utf8StrFmt("%s (%Rrc)", Utf8Str(pszFormat, va).c_str(), rc);
 
5797
    else
 
5798
        that->m->vdError =
 
5799
            Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.c_str(),
 
5800
                       Utf8Str(pszFormat, va).c_str(), rc);
 
5801
}
 
5802
 
 
5803
/* static */
 
5804
DECLCALLBACK(bool) Medium::vdConfigAreKeysValid(void *pvUser,
 
5805
                                                const char * /* pszzValid */)
 
5806
{
 
5807
    Medium *that = static_cast<Medium*>(pvUser);
 
5808
    AssertReturn(that != NULL, false);
 
5809
 
 
5810
    /* we always return true since the only keys we have are those found in
 
5811
     * VDBACKENDINFO */
 
5812
    return true;
 
5813
}
 
5814
 
 
5815
/* static */
 
5816
DECLCALLBACK(int) Medium::vdConfigQuerySize(void *pvUser,
 
5817
                                            const char *pszName,
 
5818
                                            size_t *pcbValue)
 
5819
{
 
5820
    AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
 
5821
 
 
5822
    Medium *that = static_cast<Medium*>(pvUser);
 
5823
    AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
 
5824
 
 
5825
    settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
 
5826
    if (it == that->m->mapProperties.end())
 
5827
        return VERR_CFGM_VALUE_NOT_FOUND;
 
5828
 
 
5829
    /* we interpret null values as "no value" in Medium */
 
5830
    if (it->second.isEmpty())
 
5831
        return VERR_CFGM_VALUE_NOT_FOUND;
 
5832
 
 
5833
    *pcbValue = it->second.length() + 1 /* include terminator */;
 
5834
 
 
5835
    return VINF_SUCCESS;
 
5836
}
 
5837
 
 
5838
/* static */
 
5839
DECLCALLBACK(int) Medium::vdConfigQuery(void *pvUser,
 
5840
                                        const char *pszName,
 
5841
                                        char *pszValue,
 
5842
                                        size_t cchValue)
 
5843
{
 
5844
    AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
 
5845
 
 
5846
    Medium *that = static_cast<Medium*>(pvUser);
 
5847
    AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
 
5848
 
 
5849
    settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
 
5850
    if (it == that->m->mapProperties.end())
 
5851
        return VERR_CFGM_VALUE_NOT_FOUND;
 
5852
 
 
5853
    /* we interpret null values as "no value" in Medium */
 
5854
    if (it->second.isEmpty())
 
5855
        return VERR_CFGM_VALUE_NOT_FOUND;
 
5856
 
 
5857
    const Utf8Str &value = it->second;
 
5858
    if (value.length() >= cchValue)
 
5859
        return VERR_CFGM_NOT_ENOUGH_SPACE;
 
5860
 
 
5861
    memcpy(pszValue, value.c_str(), value.length() + 1);
 
5862
 
 
5863
    return VINF_SUCCESS;
 
5864
}
 
5865
 
 
5866
DECLCALLBACK(int) Medium::vdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
 
5867
{
 
5868
    PVDSOCKETINT pSocketInt = NULL;
 
5869
 
 
5870
    if ((fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT) != 0)
 
5871
        return VERR_NOT_SUPPORTED;
 
5872
 
 
5873
    pSocketInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
 
5874
    if (!pSocketInt)
 
5875
        return VERR_NO_MEMORY;
 
5876
 
 
5877
    pSocketInt->hSocket = NIL_RTSOCKET;
 
5878
    *pSock = pSocketInt;
 
5879
    return VINF_SUCCESS;
 
5880
}
 
5881
 
 
5882
DECLCALLBACK(int) Medium::vdTcpSocketDestroy(VDSOCKET Sock)
 
5883
{
 
5884
    PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
 
5885
 
 
5886
    if (pSocketInt->hSocket != NIL_RTSOCKET)
 
5887
        RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
 
5888
 
 
5889
    RTMemFree(pSocketInt);
 
5890
 
 
5891
    return VINF_SUCCESS;
 
5892
}
 
5893
 
 
5894
DECLCALLBACK(int) Medium::vdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort)
 
5895
{
 
5896
    PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
 
5897
 
 
5898
    return RTTcpClientConnect(pszAddress, uPort, &pSocketInt->hSocket);
 
5899
}
 
5900
 
 
5901
DECLCALLBACK(int) Medium::vdTcpClientClose(VDSOCKET Sock)
 
5902
{
 
5903
    int rc = VINF_SUCCESS;
 
5904
    PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
 
5905
 
 
5906
    rc = RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
 
5907
    pSocketInt->hSocket = NIL_RTSOCKET;
 
5908
    return rc;
 
5909
}
 
5910
 
 
5911
DECLCALLBACK(bool) Medium::vdTcpIsClientConnected(VDSOCKET Sock)
 
5912
{
 
5913
    PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
 
5914
    return pSocketInt->hSocket != NIL_RTSOCKET;
 
5915
}
 
5916
 
 
5917
DECLCALLBACK(int) Medium::vdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
 
5918
{
 
5919
    PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
 
5920
    return RTTcpSelectOne(pSocketInt->hSocket, cMillies);
 
5921
}
 
5922
 
 
5923
DECLCALLBACK(int) Medium::vdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
 
5924
{
 
5925
    PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
 
5926
    return RTTcpRead(pSocketInt->hSocket, pvBuffer, cbBuffer, pcbRead);
 
5927
}
 
5928
 
 
5929
DECLCALLBACK(int) Medium::vdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
 
5930
{
 
5931
    PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
 
5932
    return RTTcpWrite(pSocketInt->hSocket, pvBuffer, cbBuffer);
 
5933
}
 
5934
 
 
5935
DECLCALLBACK(int) Medium::vdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
 
5936
{
 
5937
    PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
 
5938
    return RTTcpSgWrite(pSocketInt->hSocket, pSgBuf);
 
5939
}
 
5940
 
 
5941
DECLCALLBACK(int) Medium::vdTcpFlush(VDSOCKET Sock)
 
5942
{
 
5943
    PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
 
5944
    return RTTcpFlush(pSocketInt->hSocket);
 
5945
}
 
5946
 
 
5947
DECLCALLBACK(int) Medium::vdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
 
5948
{
 
5949
    PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
 
5950
    return RTTcpSetSendCoalescing(pSocketInt->hSocket, fEnable);
 
5951
}
 
5952
 
 
5953
DECLCALLBACK(int) Medium::vdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
 
5954
{
 
5955
    PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
 
5956
    return RTTcpGetLocalAddress(pSocketInt->hSocket, pAddr);
 
5957
}
 
5958
 
 
5959
DECLCALLBACK(int) Medium::vdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
 
5960
{
 
5961
    PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
 
5962
    return RTTcpGetPeerAddress(pSocketInt->hSocket, pAddr);
 
5963
}
 
5964
 
 
5965
/**
 
5966
 * Starts a new thread driven by the appropriate Medium::Task::handler() method.
 
5967
 *
 
5968
 * @note When the task is executed by this method, IProgress::notifyComplete()
 
5969
 *       is automatically called for the progress object associated with this
 
5970
 *       task when the task is finished to signal the operation completion for
 
5971
 *       other threads asynchronously waiting for it.
 
5972
 */
 
5973
HRESULT Medium::startThread(Medium::Task *pTask)
 
5974
{
 
5975
#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
 
5976
    /* Extreme paranoia: The calling thread should not hold the medium
 
5977
     * tree lock or any medium lock. Since there is no separate lock class
 
5978
     * for medium objects be even more strict: no other object locks. */
 
5979
    Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
 
5980
    Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
 
5981
#endif
 
5982
 
 
5983
    /// @todo use a more descriptive task name
 
5984
    int vrc = RTThreadCreate(NULL, Medium::Task::fntMediumTask, pTask,
 
5985
                             0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
 
5986
                             "Medium::Task");
 
5987
    if (RT_FAILURE(vrc))
 
5988
    {
 
5989
        delete pTask;
 
5990
        return setError(E_FAIL, "Could not create Medium::Task thread (%Rrc)\n",  vrc);
 
5991
    }
 
5992
 
 
5993
    return S_OK;
 
5994
}
 
5995
 
 
5996
/**
 
5997
 * Runs Medium::Task::handler() on the current thread instead of creating
 
5998
 * a new one.
 
5999
 *
 
6000
 * This call implies that it is made on another temporary thread created for
 
6001
 * some asynchronous task. Avoid calling it from a normal thread since the task
 
6002
 * operations are potentially lengthy and will block the calling thread in this
 
6003
 * case.
 
6004
 *
 
6005
 * @note When the task is executed by this method, IProgress::notifyComplete()
 
6006
 *       is not called for the progress object associated with this task when
 
6007
 *       the task is finished. Instead, the result of the operation is returned
 
6008
 *       by this method directly and it's the caller's responsibility to
 
6009
 *       complete the progress object in this case.
 
6010
 */
 
6011
HRESULT Medium::runNow(Medium::Task *pTask,
 
6012
                       GuidList *pllRegistriesThatNeedSaving)
 
6013
{
 
6014
#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
 
6015
    /* Extreme paranoia: The calling thread should not hold the medium
 
6016
     * tree lock or any medium lock. Since there is no separate lock class
 
6017
     * for medium objects be even more strict: no other object locks. */
 
6018
    Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
 
6019
    Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
 
6020
#endif
 
6021
 
 
6022
    pTask->m_pllRegistriesThatNeedSaving = pllRegistriesThatNeedSaving;
 
6023
 
 
6024
    /* NIL_RTTHREAD indicates synchronous call. */
 
6025
    return (HRESULT)Medium::Task::fntMediumTask(NIL_RTTHREAD, pTask);
 
6026
}
 
6027
 
 
6028
/**
 
6029
 * Implementation code for the "create base" task.
 
6030
 *
 
6031
 * This only gets started from Medium::CreateBaseStorage() and always runs
 
6032
 * asynchronously. As a result, we always save the VirtualBox.xml file when
 
6033
 * we're done here.
 
6034
 *
 
6035
 * @param task
 
6036
 * @return
 
6037
 */
 
6038
HRESULT Medium::taskCreateBaseHandler(Medium::CreateBaseTask &task)
 
6039
{
 
6040
    HRESULT rc = S_OK;
 
6041
 
 
6042
    /* these parameters we need after creation */
 
6043
    uint64_t size = 0, logicalSize = 0;
 
6044
    MediumVariant_T variant = MediumVariant_Standard;
 
6045
    bool fGenerateUuid = false;
 
6046
 
 
6047
    try
 
6048
    {
 
6049
        AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
 
6050
 
 
6051
        /* The object may request a specific UUID (through a special form of
 
6052
        * the setLocation() argument). Otherwise we have to generate it */
 
6053
        Guid id = m->id;
 
6054
        fGenerateUuid = id.isEmpty();
 
6055
        if (fGenerateUuid)
 
6056
        {
 
6057
            id.create();
 
6058
            /* VirtualBox::registerHardDisk() will need UUID */
 
6059
            unconst(m->id) = id;
 
6060
        }
 
6061
 
 
6062
        Utf8Str format(m->strFormat);
 
6063
        Utf8Str location(m->strLocationFull);
 
6064
        uint64_t capabilities = m->formatObj->getCapabilities();
 
6065
        ComAssertThrow(capabilities & (  VD_CAP_CREATE_FIXED
 
6066
                                       | VD_CAP_CREATE_DYNAMIC), E_FAIL);
 
6067
        Assert(m->state == MediumState_Creating);
 
6068
 
 
6069
        PVBOXHDD hdd;
 
6070
        int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
 
6071
        ComAssertRCThrow(vrc, E_FAIL);
 
6072
 
 
6073
        /* unlock before the potentially lengthy operation */
 
6074
        thisLock.release();
 
6075
 
 
6076
        try
 
6077
        {
 
6078
            /* ensure the directory exists */
 
6079
            rc = VirtualBox::ensureFilePathExists(location);
 
6080
            if (FAILED(rc))
 
6081
                throw rc;
 
6082
 
 
6083
            VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
 
6084
 
 
6085
            vrc = VDCreateBase(hdd,
 
6086
                               format.c_str(),
 
6087
                               location.c_str(),
 
6088
                               task.mSize,
 
6089
                               task.mVariant,
 
6090
                               NULL,
 
6091
                               &geo,
 
6092
                               &geo,
 
6093
                               id.raw(),
 
6094
                               VD_OPEN_FLAGS_NORMAL,
 
6095
                               m->vdImageIfaces,
 
6096
                               task.mVDOperationIfaces);
 
6097
            if (RT_FAILURE(vrc))
 
6098
                throw setError(VBOX_E_FILE_ERROR,
 
6099
                               tr("Could not create the medium storage unit '%s'%s"),
 
6100
                               location.c_str(), vdError(vrc).c_str());
 
6101
 
 
6102
            size = VDGetFileSize(hdd, 0);
 
6103
            logicalSize = VDGetSize(hdd, 0);
 
6104
            unsigned uImageFlags;
 
6105
            vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
 
6106
            if (RT_SUCCESS(vrc))
 
6107
                variant = (MediumVariant_T)uImageFlags;
 
6108
        }
 
6109
        catch (HRESULT aRC) { rc = aRC; }
 
6110
 
 
6111
        VDDestroy(hdd);
 
6112
    }
 
6113
    catch (HRESULT aRC) { rc = aRC; }
 
6114
 
 
6115
    if (SUCCEEDED(rc))
 
6116
    {
 
6117
        /* register with mVirtualBox as the last step and move to
 
6118
         * Created state only on success (leaving an orphan file is
 
6119
         * better than breaking media registry consistency) */
 
6120
        AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
6121
        rc = m->pVirtualBox->registerHardDisk(this, NULL /* pllRegistriesThatNeedSaving */);
 
6122
    }
 
6123
 
 
6124
    // reenter the lock before changing state
 
6125
    AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
 
6126
 
 
6127
    if (SUCCEEDED(rc))
 
6128
    {
 
6129
        m->state = MediumState_Created;
 
6130
 
 
6131
        m->size = size;
 
6132
        m->logicalSize = logicalSize;
 
6133
        m->variant = variant;
 
6134
    }
 
6135
    else
 
6136
    {
 
6137
        /* back to NotCreated on failure */
 
6138
        m->state = MediumState_NotCreated;
 
6139
 
 
6140
        /* reset UUID to prevent it from being reused next time */
 
6141
        if (fGenerateUuid)
 
6142
            unconst(m->id).clear();
 
6143
    }
 
6144
 
 
6145
    return rc;
 
6146
}
 
6147
 
 
6148
/**
 
6149
 * Implementation code for the "create diff" task.
 
6150
 *
 
6151
 * This task always gets started from Medium::createDiffStorage() and can run
 
6152
 * synchronously or asynchronously depending on the "wait" parameter passed to
 
6153
 * that function. If we run synchronously, the caller expects the bool
 
6154
 * *pfNeedsGlobalSaveSettings to be set before returning; otherwise (in asynchronous
 
6155
 * mode), we save the settings ourselves.
 
6156
 *
 
6157
 * @param task
 
6158
 * @return
 
6159
 */
 
6160
HRESULT Medium::taskCreateDiffHandler(Medium::CreateDiffTask &task)
 
6161
{
 
6162
    HRESULT rc = S_OK;
 
6163
 
 
6164
    const ComObjPtr<Medium> &pTarget = task.mTarget;
 
6165
 
 
6166
    uint64_t size = 0, logicalSize = 0;
 
6167
    MediumVariant_T variant = MediumVariant_Standard;
 
6168
    bool fGenerateUuid = false;
 
6169
 
 
6170
    GuidList llRegistriesThatNeedSaving;
 
6171
 
 
6172
    try
 
6173
    {
 
6174
        /* Lock both in {parent,child} order. */
 
6175
        AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
 
6176
 
 
6177
        /* The object may request a specific UUID (through a special form of
 
6178
         * the setLocation() argument). Otherwise we have to generate it */
 
6179
        Guid targetId = pTarget->m->id;
 
6180
        fGenerateUuid = targetId.isEmpty();
 
6181
        if (fGenerateUuid)
 
6182
        {
 
6183
            targetId.create();
 
6184
            /* VirtualBox::registerHardDisk() will need UUID */
 
6185
            unconst(pTarget->m->id) = targetId;
 
6186
        }
 
6187
 
 
6188
        Guid id = m->id;
 
6189
 
 
6190
        Utf8Str targetFormat(pTarget->m->strFormat);
 
6191
        Utf8Str targetLocation(pTarget->m->strLocationFull);
 
6192
        uint64_t capabilities = pTarget->m->formatObj->getCapabilities();
 
6193
        ComAssertThrow(capabilities & VD_CAP_CREATE_DYNAMIC, E_FAIL);
 
6194
 
 
6195
        Assert(pTarget->m->state == MediumState_Creating);
 
6196
        Assert(m->state == MediumState_LockedRead);
 
6197
 
 
6198
        PVBOXHDD hdd;
 
6199
        int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
 
6200
        ComAssertRCThrow(vrc, E_FAIL);
 
6201
 
 
6202
        /* the two media are now protected by their non-default states;
 
6203
         * unlock the media before the potentially lengthy operation */
 
6204
        mediaLock.release();
 
6205
 
 
6206
        try
 
6207
        {
 
6208
            /* Open all media in the target chain but the last. */
 
6209
            MediumLockList::Base::const_iterator targetListBegin =
 
6210
                task.mpMediumLockList->GetBegin();
 
6211
            MediumLockList::Base::const_iterator targetListEnd =
 
6212
                task.mpMediumLockList->GetEnd();
 
6213
            for (MediumLockList::Base::const_iterator it = targetListBegin;
 
6214
                 it != targetListEnd;
 
6215
                 ++it)
 
6216
            {
 
6217
                const MediumLock &mediumLock = *it;
 
6218
                const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
 
6219
 
 
6220
                AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
 
6221
 
 
6222
                /* Skip over the target diff medium */
 
6223
                if (pMedium->m->state == MediumState_Creating)
 
6224
                    continue;
 
6225
 
 
6226
                /* sanity check */
 
6227
                Assert(pMedium->m->state == MediumState_LockedRead);
 
6228
 
 
6229
                /* Open all media in appropriate mode. */
 
6230
                vrc = VDOpen(hdd,
 
6231
                             pMedium->m->strFormat.c_str(),
 
6232
                             pMedium->m->strLocationFull.c_str(),
 
6233
                             VD_OPEN_FLAGS_READONLY,
 
6234
                             pMedium->m->vdImageIfaces);
 
6235
                if (RT_FAILURE(vrc))
 
6236
                    throw setError(VBOX_E_FILE_ERROR,
 
6237
                                   tr("Could not open the medium storage unit '%s'%s"),
 
6238
                                   pMedium->m->strLocationFull.c_str(),
 
6239
                                   vdError(vrc).c_str());
 
6240
            }
 
6241
 
 
6242
            /* ensure the target directory exists */
 
6243
            rc = VirtualBox::ensureFilePathExists(targetLocation);
 
6244
            if (FAILED(rc))
 
6245
                throw rc;
 
6246
 
 
6247
            vrc = VDCreateDiff(hdd,
 
6248
                               targetFormat.c_str(),
 
6249
                               targetLocation.c_str(),
 
6250
                               task.mVariant | VD_IMAGE_FLAGS_DIFF,
 
6251
                               NULL,
 
6252
                               targetId.raw(),
 
6253
                               id.raw(),
 
6254
                               VD_OPEN_FLAGS_NORMAL,
 
6255
                               pTarget->m->vdImageIfaces,
 
6256
                               task.mVDOperationIfaces);
 
6257
            if (RT_FAILURE(vrc))
 
6258
                throw setError(VBOX_E_FILE_ERROR,
 
6259
                                tr("Could not create the differencing medium storage unit '%s'%s"),
 
6260
                                targetLocation.c_str(), vdError(vrc).c_str());
 
6261
 
 
6262
            size = VDGetFileSize(hdd, VD_LAST_IMAGE);
 
6263
            logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
 
6264
            unsigned uImageFlags;
 
6265
            vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
 
6266
            if (RT_SUCCESS(vrc))
 
6267
                variant = (MediumVariant_T)uImageFlags;
 
6268
        }
 
6269
        catch (HRESULT aRC) { rc = aRC; }
 
6270
 
 
6271
        VDDestroy(hdd);
 
6272
    }
 
6273
    catch (HRESULT aRC) { rc = aRC; }
 
6274
 
 
6275
    if (SUCCEEDED(rc))
 
6276
    {
 
6277
        AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
6278
 
 
6279
        Assert(pTarget->m->pParent.isNull());
 
6280
 
 
6281
        /* associate the child with the parent */
 
6282
        pTarget->m->pParent = this;
 
6283
        m->llChildren.push_back(pTarget);
 
6284
 
 
6285
        /** @todo r=klaus neither target nor base() are locked,
 
6286
            * potential race! */
 
6287
        /* diffs for immutable media are auto-reset by default */
 
6288
        pTarget->m->autoReset = (getBase()->m->type == MediumType_Immutable);
 
6289
 
 
6290
        /* register with mVirtualBox as the last step and move to
 
6291
         * Created state only on success (leaving an orphan file is
 
6292
         * better than breaking media registry consistency) */
 
6293
        rc = m->pVirtualBox->registerHardDisk(pTarget, &llRegistriesThatNeedSaving);
 
6294
 
 
6295
        if (FAILED(rc))
 
6296
            /* break the parent association on failure to register */
 
6297
            deparent();
 
6298
    }
 
6299
 
 
6300
    AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
 
6301
 
 
6302
    if (SUCCEEDED(rc))
 
6303
    {
 
6304
        pTarget->m->state = MediumState_Created;
 
6305
 
 
6306
        pTarget->m->size = size;
 
6307
        pTarget->m->logicalSize = logicalSize;
 
6308
        pTarget->m->variant = variant;
 
6309
    }
 
6310
    else
 
6311
    {
 
6312
        /* back to NotCreated on failure */
 
6313
        pTarget->m->state = MediumState_NotCreated;
 
6314
 
 
6315
        pTarget->m->autoReset = false;
 
6316
 
 
6317
        /* reset UUID to prevent it from being reused next time */
 
6318
        if (fGenerateUuid)
 
6319
            unconst(pTarget->m->id).clear();
 
6320
    }
 
6321
 
 
6322
    // deregister the task registered in createDiffStorage()
 
6323
    Assert(m->numCreateDiffTasks != 0);
 
6324
    --m->numCreateDiffTasks;
 
6325
 
 
6326
    if (task.isAsync())
 
6327
    {
 
6328
        mediaLock.release();
 
6329
        m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
 
6330
    }
 
6331
    else
 
6332
        // synchronous mode: report save settings result to caller
 
6333
        if (task.m_pllRegistriesThatNeedSaving)
 
6334
            *task.m_pllRegistriesThatNeedSaving = llRegistriesThatNeedSaving;
 
6335
 
 
6336
    /* Note that in sync mode, it's the caller's responsibility to
 
6337
     * unlock the medium. */
 
6338
 
 
6339
    return rc;
 
6340
}
 
6341
 
 
6342
/**
 
6343
 * Implementation code for the "merge" task.
 
6344
 *
 
6345
 * This task always gets started from Medium::mergeTo() and can run
 
6346
 * synchronously or asynchronously depending on the "wait" parameter passed to
 
6347
 * that function. If we run synchronously, the caller expects the bool
 
6348
 * *pfNeedsGlobalSaveSettings to be set before returning; otherwise (in asynchronous
 
6349
 * mode), we save the settings ourselves.
 
6350
 *
 
6351
 * @param task
 
6352
 * @return
 
6353
 */
 
6354
HRESULT Medium::taskMergeHandler(Medium::MergeTask &task)
 
6355
{
 
6356
    HRESULT rc = S_OK;
 
6357
 
 
6358
    const ComObjPtr<Medium> &pTarget = task.mTarget;
 
6359
 
 
6360
    try
 
6361
    {
 
6362
        PVBOXHDD hdd;
 
6363
        int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
 
6364
        ComAssertRCThrow(vrc, E_FAIL);
 
6365
 
 
6366
        try
 
6367
        {
 
6368
            // Similar code appears in SessionMachine::onlineMergeMedium, so
 
6369
            // if you make any changes below check whether they are applicable
 
6370
            // in that context as well.
 
6371
 
 
6372
            unsigned uTargetIdx = VD_LAST_IMAGE;
 
6373
            unsigned uSourceIdx = VD_LAST_IMAGE;
 
6374
            /* Open all media in the chain. */
 
6375
            MediumLockList::Base::iterator lockListBegin =
 
6376
                task.mpMediumLockList->GetBegin();
 
6377
            MediumLockList::Base::iterator lockListEnd =
 
6378
                task.mpMediumLockList->GetEnd();
 
6379
            unsigned i = 0;
 
6380
            for (MediumLockList::Base::iterator it = lockListBegin;
 
6381
                 it != lockListEnd;
 
6382
                 ++it)
 
6383
            {
 
6384
                MediumLock &mediumLock = *it;
 
6385
                const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
 
6386
 
 
6387
                if (pMedium == this)
 
6388
                    uSourceIdx = i;
 
6389
                else if (pMedium == pTarget)
 
6390
                    uTargetIdx = i;
 
6391
 
 
6392
                AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
 
6393
 
 
6394
                /*
 
6395
                 * complex sanity (sane complexity)
 
6396
                 *
 
6397
                 * The current medium must be in the Deleting (medium is merged)
 
6398
                 * or LockedRead (parent medium) state if it is not the target.
 
6399
                 * If it is the target it must be in the LockedWrite state.
 
6400
                 */
 
6401
                Assert(   (   pMedium != pTarget
 
6402
                           && (   pMedium->m->state == MediumState_Deleting
 
6403
                               || pMedium->m->state == MediumState_LockedRead))
 
6404
                       || (   pMedium == pTarget
 
6405
                           && pMedium->m->state == MediumState_LockedWrite));
 
6406
 
 
6407
                /*
 
6408
                 * Medium must be the target, in the LockedRead state
 
6409
                 * or Deleting state where it is not allowed to be attached
 
6410
                 * to a virtual machine.
 
6411
                 */
 
6412
                Assert(   pMedium == pTarget
 
6413
                       || pMedium->m->state == MediumState_LockedRead
 
6414
                       || (   pMedium->m->backRefs.size() == 0
 
6415
                           && pMedium->m->state == MediumState_Deleting));
 
6416
                /* The source medium must be in Deleting state. */
 
6417
                Assert(  pMedium != this
 
6418
                       || pMedium->m->state == MediumState_Deleting);
 
6419
 
 
6420
                unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
 
6421
 
 
6422
                if (   pMedium->m->state == MediumState_LockedRead
 
6423
                    || pMedium->m->state == MediumState_Deleting)
 
6424
                    uOpenFlags = VD_OPEN_FLAGS_READONLY;
 
6425
                if (pMedium->m->type == MediumType_Shareable)
 
6426
                    uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
 
6427
 
 
6428
                /* Open the medium */
 
6429
                vrc = VDOpen(hdd,
 
6430
                             pMedium->m->strFormat.c_str(),
 
6431
                             pMedium->m->strLocationFull.c_str(),
 
6432
                             uOpenFlags,
 
6433
                             pMedium->m->vdImageIfaces);
 
6434
                if (RT_FAILURE(vrc))
 
6435
                    throw vrc;
 
6436
 
 
6437
                i++;
 
6438
            }
 
6439
 
 
6440
            ComAssertThrow(   uSourceIdx != VD_LAST_IMAGE
 
6441
                           && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
 
6442
 
 
6443
            vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
 
6444
                          task.mVDOperationIfaces);
 
6445
            if (RT_FAILURE(vrc))
 
6446
                throw vrc;
 
6447
 
 
6448
            /* update parent UUIDs */
 
6449
            if (!task.mfMergeForward)
 
6450
            {
 
6451
                /* we need to update UUIDs of all source's children
 
6452
                 * which cannot be part of the container at once so
 
6453
                 * add each one in there individually */
 
6454
                if (task.mChildrenToReparent.size() > 0)
 
6455
                {
 
6456
                    for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
 
6457
                         it != task.mChildrenToReparent.end();
 
6458
                         ++it)
 
6459
                    {
 
6460
                        /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
 
6461
                        vrc = VDOpen(hdd,
 
6462
                                     (*it)->m->strFormat.c_str(),
 
6463
                                     (*it)->m->strLocationFull.c_str(),
 
6464
                                     VD_OPEN_FLAGS_INFO,
 
6465
                                     (*it)->m->vdImageIfaces);
 
6466
                        if (RT_FAILURE(vrc))
 
6467
                            throw vrc;
 
6468
 
 
6469
                        vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
 
6470
                                              pTarget->m->id.raw());
 
6471
                        if (RT_FAILURE(vrc))
 
6472
                            throw vrc;
 
6473
 
 
6474
                        vrc = VDClose(hdd, false /* fDelete */);
 
6475
                        if (RT_FAILURE(vrc))
 
6476
                            throw vrc;
 
6477
 
 
6478
                        (*it)->UnlockWrite(NULL);
 
6479
                    }
 
6480
                }
 
6481
            }
 
6482
        }
 
6483
        catch (HRESULT aRC) { rc = aRC; }
 
6484
        catch (int aVRC)
 
6485
        {
 
6486
            throw setError(VBOX_E_FILE_ERROR,
 
6487
                            tr("Could not merge the medium '%s' to '%s'%s"),
 
6488
                            m->strLocationFull.c_str(),
 
6489
                            pTarget->m->strLocationFull.c_str(),
 
6490
                            vdError(aVRC).c_str());
 
6491
        }
 
6492
 
 
6493
        VDDestroy(hdd);
 
6494
    }
 
6495
    catch (HRESULT aRC) { rc = aRC; }
 
6496
 
 
6497
    HRESULT rc2;
 
6498
 
 
6499
    if (SUCCEEDED(rc))
 
6500
    {
 
6501
        /* all media but the target were successfully deleted by
 
6502
         * VDMerge; reparent the last one and uninitialize deleted media. */
 
6503
 
 
6504
        AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
6505
 
 
6506
        if (task.mfMergeForward)
 
6507
        {
 
6508
            /* first, unregister the target since it may become a base
 
6509
             * medium which needs re-registration */
 
6510
            rc2 = m->pVirtualBox->unregisterHardDisk(pTarget, NULL /*&fNeedsGlobalSaveSettings*/);
 
6511
            AssertComRC(rc2);
 
6512
 
 
6513
            /* then, reparent it and disconnect the deleted branch at
 
6514
             * both ends (chain->parent() is source's parent) */
 
6515
            pTarget->deparent();
 
6516
            pTarget->m->pParent = task.mParentForTarget;
 
6517
            if (pTarget->m->pParent)
 
6518
            {
 
6519
                pTarget->m->pParent->m->llChildren.push_back(pTarget);
 
6520
                deparent();
 
6521
            }
 
6522
 
 
6523
            /* then, register again */
 
6524
            rc2 = m->pVirtualBox->registerHardDisk(pTarget, NULL /* pllRegistriesThatNeedSaving */ );
 
6525
            AssertComRC(rc2);
 
6526
        }
 
6527
        else
 
6528
        {
 
6529
            Assert(pTarget->getChildren().size() == 1);
 
6530
            Medium *targetChild = pTarget->getChildren().front();
 
6531
 
 
6532
            /* disconnect the deleted branch at the elder end */
 
6533
            targetChild->deparent();
 
6534
 
 
6535
            /* reparent source's children and disconnect the deleted
 
6536
             * branch at the younger end */
 
6537
            if (task.mChildrenToReparent.size() > 0)
 
6538
            {
 
6539
                /* obey {parent,child} lock order */
 
6540
                AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
 
6541
 
 
6542
                for (MediaList::const_iterator it = task.mChildrenToReparent.begin();
 
6543
                     it != task.mChildrenToReparent.end();
 
6544
                     it++)
 
6545
                {
 
6546
                    Medium *pMedium = *it;
 
6547
                    AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
 
6548
 
 
6549
                    pMedium->deparent();  // removes pMedium from source
 
6550
                    pMedium->setParent(pTarget);
 
6551
                }
 
6552
            }
 
6553
        }
 
6554
 
 
6555
        /* unregister and uninitialize all media removed by the merge */
 
6556
        MediumLockList::Base::iterator lockListBegin =
 
6557
            task.mpMediumLockList->GetBegin();
 
6558
        MediumLockList::Base::iterator lockListEnd =
 
6559
            task.mpMediumLockList->GetEnd();
 
6560
        for (MediumLockList::Base::iterator it = lockListBegin;
 
6561
             it != lockListEnd;
 
6562
             )
 
6563
        {
 
6564
            MediumLock &mediumLock = *it;
 
6565
            /* Create a real copy of the medium pointer, as the medium
 
6566
             * lock deletion below would invalidate the referenced object. */
 
6567
            const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
 
6568
 
 
6569
            /* The target and all media not merged (readonly) are skipped */
 
6570
            if (   pMedium == pTarget
 
6571
                || pMedium->m->state == MediumState_LockedRead)
 
6572
            {
 
6573
                ++it;
 
6574
                continue;
 
6575
            }
 
6576
 
 
6577
            rc2 = pMedium->m->pVirtualBox->unregisterHardDisk(pMedium,
 
6578
                                                              NULL /*pfNeedsGlobalSaveSettings*/);
 
6579
            AssertComRC(rc2);
 
6580
 
 
6581
            /* now, uninitialize the deleted medium (note that
 
6582
             * due to the Deleting state, uninit() will not touch
 
6583
             * the parent-child relationship so we need to
 
6584
             * uninitialize each disk individually) */
 
6585
 
 
6586
            /* note that the operation initiator medium (which is
 
6587
             * normally also the source medium) is a special case
 
6588
             * -- there is one more caller added by Task to it which
 
6589
             * we must release. Also, if we are in sync mode, the
 
6590
             * caller may still hold an AutoCaller instance for it
 
6591
             * and therefore we cannot uninit() it (it's therefore
 
6592
             * the caller's responsibility) */
 
6593
            if (pMedium == this)
 
6594
            {
 
6595
                Assert(getChildren().size() == 0);
 
6596
                Assert(m->backRefs.size() == 0);
 
6597
                task.mMediumCaller.release();
 
6598
            }
 
6599
 
 
6600
            /* Delete the medium lock list entry, which also releases the
 
6601
             * caller added by MergeChain before uninit() and updates the
 
6602
             * iterator to point to the right place. */
 
6603
            rc2 = task.mpMediumLockList->RemoveByIterator(it);
 
6604
            AssertComRC(rc2);
 
6605
 
 
6606
            if (task.isAsync() || pMedium != this)
 
6607
                pMedium->uninit();
 
6608
        }
 
6609
    }
 
6610
 
 
6611
    if (task.isAsync())
 
6612
    {
 
6613
        // in asynchronous mode, save settings now
 
6614
        GuidList llRegistriesThatNeedSaving;
 
6615
        addToRegistryIDList(llRegistriesThatNeedSaving);
 
6616
        rc = m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
 
6617
    }
 
6618
    else
 
6619
        // synchronous mode: report save settings result to caller
 
6620
        if (task.m_pllRegistriesThatNeedSaving)
 
6621
            pTarget->addToRegistryIDList(*task.m_pllRegistriesThatNeedSaving);
 
6622
 
 
6623
    if (FAILED(rc))
 
6624
    {
 
6625
        /* Here we come if either VDMerge() failed (in which case we
 
6626
         * assume that it tried to do everything to make a further
 
6627
         * retry possible -- e.g. not deleted intermediate media
 
6628
         * and so on) or VirtualBox::saveSettings() failed (where we
 
6629
         * should have the original tree but with intermediate storage
 
6630
         * units deleted by VDMerge()). We have to only restore states
 
6631
         * (through the MergeChain dtor) unless we are run synchronously
 
6632
         * in which case it's the responsibility of the caller as stated
 
6633
         * in the mergeTo() docs. The latter also implies that we
 
6634
         * don't own the merge chain, so release it in this case. */
 
6635
        if (task.isAsync())
 
6636
        {
 
6637
            Assert(task.mChildrenToReparent.size() == 0);
 
6638
            cancelMergeTo(task.mChildrenToReparent, task.mpMediumLockList);
 
6639
        }
 
6640
    }
 
6641
 
 
6642
    return rc;
 
6643
}
 
6644
 
 
6645
/**
 
6646
 * Implementation code for the "clone" task.
 
6647
 *
 
6648
 * This only gets started from Medium::CloneTo() and always runs asynchronously.
 
6649
 * As a result, we always save the VirtualBox.xml file when we're done here.
 
6650
 *
 
6651
 * @param task
 
6652
 * @return
 
6653
 */
 
6654
HRESULT Medium::taskCloneHandler(Medium::CloneTask &task)
 
6655
{
 
6656
    HRESULT rc = S_OK;
 
6657
 
 
6658
    const ComObjPtr<Medium> &pTarget = task.mTarget;
 
6659
    const ComObjPtr<Medium> &pParent = task.mParent;
 
6660
 
 
6661
    bool fCreatingTarget = false;
 
6662
 
 
6663
    uint64_t size = 0, logicalSize = 0;
 
6664
    MediumVariant_T variant = MediumVariant_Standard;
 
6665
    bool fGenerateUuid = false;
 
6666
 
 
6667
    try
 
6668
    {
 
6669
        /* Lock all in {parent,child} order. The lock is also used as a
 
6670
         * signal from the task initiator (which releases it only after
 
6671
         * RTThreadCreate()) that we can start the job. */
 
6672
        AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
 
6673
 
 
6674
        fCreatingTarget = pTarget->m->state == MediumState_Creating;
 
6675
 
 
6676
        /* The object may request a specific UUID (through a special form of
 
6677
         * the setLocation() argument). Otherwise we have to generate it */
 
6678
        Guid targetId = pTarget->m->id;
 
6679
        fGenerateUuid = targetId.isEmpty();
 
6680
        if (fGenerateUuid)
 
6681
        {
 
6682
            targetId.create();
 
6683
            /* VirtualBox::registerHardDisk() will need UUID */
 
6684
            unconst(pTarget->m->id) = targetId;
 
6685
        }
 
6686
 
 
6687
        PVBOXHDD hdd;
 
6688
        int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
 
6689
        ComAssertRCThrow(vrc, E_FAIL);
 
6690
 
 
6691
        try
 
6692
        {
 
6693
            /* Open all media in the source chain. */
 
6694
            MediumLockList::Base::const_iterator sourceListBegin =
 
6695
                task.mpSourceMediumLockList->GetBegin();
 
6696
            MediumLockList::Base::const_iterator sourceListEnd =
 
6697
                task.mpSourceMediumLockList->GetEnd();
 
6698
            for (MediumLockList::Base::const_iterator it = sourceListBegin;
 
6699
                 it != sourceListEnd;
 
6700
                 ++it)
 
6701
            {
 
6702
                const MediumLock &mediumLock = *it;
 
6703
                const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
 
6704
                AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
 
6705
 
 
6706
                /* sanity check */
 
6707
                Assert(pMedium->m->state == MediumState_LockedRead);
 
6708
 
 
6709
                /** Open all media in read-only mode. */
 
6710
                vrc = VDOpen(hdd,
 
6711
                             pMedium->m->strFormat.c_str(),
 
6712
                             pMedium->m->strLocationFull.c_str(),
 
6713
                             VD_OPEN_FLAGS_READONLY,
 
6714
                             pMedium->m->vdImageIfaces);
 
6715
                if (RT_FAILURE(vrc))
 
6716
                    throw setError(VBOX_E_FILE_ERROR,
 
6717
                                   tr("Could not open the medium storage unit '%s'%s"),
 
6718
                                   pMedium->m->strLocationFull.c_str(),
 
6719
                                   vdError(vrc).c_str());
 
6720
            }
 
6721
 
 
6722
            Utf8Str targetFormat(pTarget->m->strFormat);
 
6723
            Utf8Str targetLocation(pTarget->m->strLocationFull);
 
6724
 
 
6725
            Assert(   pTarget->m->state == MediumState_Creating
 
6726
                   || pTarget->m->state == MediumState_LockedWrite);
 
6727
            Assert(m->state == MediumState_LockedRead);
 
6728
            Assert(   pParent.isNull()
 
6729
                   || pParent->m->state == MediumState_LockedRead);
 
6730
 
 
6731
            /* unlock before the potentially lengthy operation */
 
6732
            thisLock.release();
 
6733
 
 
6734
            /* ensure the target directory exists */
 
6735
            rc = VirtualBox::ensureFilePathExists(targetLocation);
 
6736
            if (FAILED(rc))
 
6737
                throw rc;
 
6738
 
 
6739
            PVBOXHDD targetHdd;
 
6740
            vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &targetHdd);
 
6741
            ComAssertRCThrow(vrc, E_FAIL);
 
6742
 
 
6743
            try
 
6744
            {
 
6745
                /* Open all media in the target chain. */
 
6746
                MediumLockList::Base::const_iterator targetListBegin =
 
6747
                    task.mpTargetMediumLockList->GetBegin();
 
6748
                MediumLockList::Base::const_iterator targetListEnd =
 
6749
                    task.mpTargetMediumLockList->GetEnd();
 
6750
                for (MediumLockList::Base::const_iterator it = targetListBegin;
 
6751
                     it != targetListEnd;
 
6752
                     ++it)
 
6753
                {
 
6754
                    const MediumLock &mediumLock = *it;
 
6755
                    const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
 
6756
 
 
6757
                    /* If the target medium is not created yet there's no
 
6758
                     * reason to open it. */
 
6759
                    if (pMedium == pTarget && fCreatingTarget)
 
6760
                        continue;
 
6761
 
 
6762
                    AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
 
6763
 
 
6764
                    /* sanity check */
 
6765
                    Assert(    pMedium->m->state == MediumState_LockedRead
 
6766
                            || pMedium->m->state == MediumState_LockedWrite);
 
6767
 
 
6768
                    unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
 
6769
                    if (pMedium->m->state != MediumState_LockedWrite)
 
6770
                        uOpenFlags = VD_OPEN_FLAGS_READONLY;
 
6771
                    if (pMedium->m->type == MediumType_Shareable)
 
6772
                        uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
 
6773
 
 
6774
                    /* Open all media in appropriate mode. */
 
6775
                    vrc = VDOpen(targetHdd,
 
6776
                                 pMedium->m->strFormat.c_str(),
 
6777
                                 pMedium->m->strLocationFull.c_str(),
 
6778
                                 uOpenFlags,
 
6779
                                 pMedium->m->vdImageIfaces);
 
6780
                    if (RT_FAILURE(vrc))
 
6781
                        throw setError(VBOX_E_FILE_ERROR,
 
6782
                                       tr("Could not open the medium storage unit '%s'%s"),
 
6783
                                       pMedium->m->strLocationFull.c_str(),
 
6784
                                       vdError(vrc).c_str());
 
6785
                }
 
6786
 
 
6787
                /** @todo r=klaus target isn't locked, race getting the state */
 
6788
                vrc = VDCopy(hdd,
 
6789
                             VD_LAST_IMAGE,
 
6790
                             targetHdd,
 
6791
                             targetFormat.c_str(),
 
6792
                             (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
 
6793
                             false /* fMoveByRename */,
 
6794
                             0 /* cbSize */,
 
6795
                             task.mVariant,
 
6796
                             targetId.raw(),
 
6797
                             VD_OPEN_FLAGS_NORMAL,
 
6798
                             NULL /* pVDIfsOperation */,
 
6799
                             pTarget->m->vdImageIfaces,
 
6800
                             task.mVDOperationIfaces);
 
6801
                if (RT_FAILURE(vrc))
 
6802
                    throw setError(VBOX_E_FILE_ERROR,
 
6803
                                   tr("Could not create the clone medium '%s'%s"),
 
6804
                                   targetLocation.c_str(), vdError(vrc).c_str());
 
6805
 
 
6806
                size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
 
6807
                logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
 
6808
                unsigned uImageFlags;
 
6809
                vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
 
6810
                if (RT_SUCCESS(vrc))
 
6811
                    variant = (MediumVariant_T)uImageFlags;
 
6812
            }
 
6813
            catch (HRESULT aRC) { rc = aRC; }
 
6814
 
 
6815
            VDDestroy(targetHdd);
 
6816
        }
 
6817
        catch (HRESULT aRC) { rc = aRC; }
 
6818
 
 
6819
        VDDestroy(hdd);
 
6820
    }
 
6821
    catch (HRESULT aRC) { rc = aRC; }
 
6822
 
 
6823
    /* Only do the parent changes for newly created media. */
 
6824
    if (SUCCEEDED(rc) && fCreatingTarget)
 
6825
    {
 
6826
        /* we set mParent & children() */
 
6827
        AutoWriteLock alock2(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
6828
 
 
6829
        Assert(pTarget->m->pParent.isNull());
 
6830
 
 
6831
        if (pParent)
 
6832
        {
 
6833
            /* associate the clone with the parent and deassociate
 
6834
             * from VirtualBox */
 
6835
            pTarget->m->pParent = pParent;
 
6836
            pParent->m->llChildren.push_back(pTarget);
 
6837
 
 
6838
            /* register with mVirtualBox as the last step and move to
 
6839
             * Created state only on success (leaving an orphan file is
 
6840
             * better than breaking media registry consistency) */
 
6841
            rc = pParent->m->pVirtualBox->registerHardDisk(pTarget, NULL /* pllRegistriesThatNeedSaving */);
 
6842
 
 
6843
            if (FAILED(rc))
 
6844
                /* break parent association on failure to register */
 
6845
                pTarget->deparent();     // removes target from parent
 
6846
        }
 
6847
        else
 
6848
        {
 
6849
            /* just register  */
 
6850
            rc = m->pVirtualBox->registerHardDisk(pTarget, NULL /* pllRegistriesThatNeedSaving */);
 
6851
        }
 
6852
    }
 
6853
 
 
6854
    if (fCreatingTarget)
 
6855
    {
 
6856
        AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
 
6857
 
 
6858
        if (SUCCEEDED(rc))
 
6859
        {
 
6860
            pTarget->m->state = MediumState_Created;
 
6861
 
 
6862
            pTarget->m->size = size;
 
6863
            pTarget->m->logicalSize = logicalSize;
 
6864
            pTarget->m->variant = variant;
 
6865
        }
 
6866
        else
 
6867
        {
 
6868
            /* back to NotCreated on failure */
 
6869
            pTarget->m->state = MediumState_NotCreated;
 
6870
 
 
6871
            /* reset UUID to prevent it from being reused next time */
 
6872
            if (fGenerateUuid)
 
6873
                unconst(pTarget->m->id).clear();
 
6874
        }
 
6875
    }
 
6876
 
 
6877
    // now, at the end of this task (always asynchronous), save the settings
 
6878
    {
 
6879
        // save the settings
 
6880
        GuidList llRegistriesThatNeedSaving;
 
6881
        addToRegistryIDList(llRegistriesThatNeedSaving);
 
6882
        rc = m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
 
6883
    }
 
6884
 
 
6885
    /* Everything is explicitly unlocked when the task exits,
 
6886
     * as the task destruction also destroys the source chain. */
 
6887
 
 
6888
    /* Make sure the source chain is released early. It could happen
 
6889
     * that we get a deadlock in Appliance::Import when Medium::Close
 
6890
     * is called & the source chain is released at the same time. */
 
6891
    task.mpSourceMediumLockList->Clear();
 
6892
 
 
6893
    return rc;
 
6894
}
 
6895
 
 
6896
/**
 
6897
 * Implementation code for the "delete" task.
 
6898
 *
 
6899
 * This task always gets started from Medium::deleteStorage() and can run
 
6900
 * synchronously or asynchronously depending on the "wait" parameter passed to
 
6901
 * that function.
 
6902
 *
 
6903
 * @param task
 
6904
 * @return
 
6905
 */
 
6906
HRESULT Medium::taskDeleteHandler(Medium::DeleteTask &task)
 
6907
{
 
6908
    NOREF(task);
 
6909
    HRESULT rc = S_OK;
 
6910
 
 
6911
    try
 
6912
    {
 
6913
        /* The lock is also used as a signal from the task initiator (which
 
6914
         * releases it only after RTThreadCreate()) that we can start the job */
 
6915
        AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
 
6916
 
 
6917
        PVBOXHDD hdd;
 
6918
        int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
 
6919
        ComAssertRCThrow(vrc, E_FAIL);
 
6920
 
 
6921
        Utf8Str format(m->strFormat);
 
6922
        Utf8Str location(m->strLocationFull);
 
6923
 
 
6924
        /* unlock before the potentially lengthy operation */
 
6925
        Assert(m->state == MediumState_Deleting);
 
6926
        thisLock.release();
 
6927
 
 
6928
        try
 
6929
        {
 
6930
            vrc = VDOpen(hdd,
 
6931
                         format.c_str(),
 
6932
                         location.c_str(),
 
6933
                         VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
 
6934
                         m->vdImageIfaces);
 
6935
            if (RT_SUCCESS(vrc))
 
6936
                vrc = VDClose(hdd, true /* fDelete */);
 
6937
 
 
6938
            if (RT_FAILURE(vrc))
 
6939
                throw setError(VBOX_E_FILE_ERROR,
 
6940
                               tr("Could not delete the medium storage unit '%s'%s"),
 
6941
                               location.c_str(), vdError(vrc).c_str());
 
6942
 
 
6943
        }
 
6944
        catch (HRESULT aRC) { rc = aRC; }
 
6945
 
 
6946
        VDDestroy(hdd);
 
6947
    }
 
6948
    catch (HRESULT aRC) { rc = aRC; }
 
6949
 
 
6950
    AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
 
6951
 
 
6952
    /* go to the NotCreated state even on failure since the storage
 
6953
     * may have been already partially deleted and cannot be used any
 
6954
     * more. One will be able to manually re-open the storage if really
 
6955
     * needed to re-register it. */
 
6956
    m->state = MediumState_NotCreated;
 
6957
 
 
6958
    /* Reset UUID to prevent Create* from reusing it again */
 
6959
    unconst(m->id).clear();
 
6960
 
 
6961
    return rc;
 
6962
}
 
6963
 
 
6964
/**
 
6965
 * Implementation code for the "reset" task.
 
6966
 *
 
6967
 * This always gets started asynchronously from Medium::Reset().
 
6968
 *
 
6969
 * @param task
 
6970
 * @return
 
6971
 */
 
6972
HRESULT Medium::taskResetHandler(Medium::ResetTask &task)
 
6973
{
 
6974
    HRESULT rc = S_OK;
 
6975
 
 
6976
    uint64_t size = 0, logicalSize = 0;
 
6977
    MediumVariant_T variant = MediumVariant_Standard;
 
6978
 
 
6979
    try
 
6980
    {
 
6981
        /* The lock is also used as a signal from the task initiator (which
 
6982
         * releases it only after RTThreadCreate()) that we can start the job */
 
6983
        AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
 
6984
 
 
6985
        /// @todo Below we use a pair of delete/create operations to reset
 
6986
        /// the diff contents but the most efficient way will of course be
 
6987
        /// to add a VDResetDiff() API call
 
6988
 
 
6989
        PVBOXHDD hdd;
 
6990
        int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
 
6991
        ComAssertRCThrow(vrc, E_FAIL);
 
6992
 
 
6993
        Guid id = m->id;
 
6994
        Utf8Str format(m->strFormat);
 
6995
        Utf8Str location(m->strLocationFull);
 
6996
 
 
6997
        Medium *pParent = m->pParent;
 
6998
        Guid parentId = pParent->m->id;
 
6999
        Utf8Str parentFormat(pParent->m->strFormat);
 
7000
        Utf8Str parentLocation(pParent->m->strLocationFull);
 
7001
 
 
7002
        Assert(m->state == MediumState_LockedWrite);
 
7003
 
 
7004
        /* unlock before the potentially lengthy operation */
 
7005
        thisLock.release();
 
7006
 
 
7007
        try
 
7008
        {
 
7009
            /* Open all media in the target chain but the last. */
 
7010
            MediumLockList::Base::const_iterator targetListBegin =
 
7011
                task.mpMediumLockList->GetBegin();
 
7012
            MediumLockList::Base::const_iterator targetListEnd =
 
7013
                task.mpMediumLockList->GetEnd();
 
7014
            for (MediumLockList::Base::const_iterator it = targetListBegin;
 
7015
                 it != targetListEnd;
 
7016
                 ++it)
 
7017
            {
 
7018
                const MediumLock &mediumLock = *it;
 
7019
                const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
 
7020
 
 
7021
                AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
 
7022
 
 
7023
                /* sanity check, "this" is checked above */
 
7024
                Assert(   pMedium == this
 
7025
                       || pMedium->m->state == MediumState_LockedRead);
 
7026
 
 
7027
                /* Open all media in appropriate mode. */
 
7028
                vrc = VDOpen(hdd,
 
7029
                             pMedium->m->strFormat.c_str(),
 
7030
                             pMedium->m->strLocationFull.c_str(),
 
7031
                             VD_OPEN_FLAGS_READONLY,
 
7032
                             pMedium->m->vdImageIfaces);
 
7033
                if (RT_FAILURE(vrc))
 
7034
                    throw setError(VBOX_E_FILE_ERROR,
 
7035
                                   tr("Could not open the medium storage unit '%s'%s"),
 
7036
                                   pMedium->m->strLocationFull.c_str(),
 
7037
                                   vdError(vrc).c_str());
 
7038
 
 
7039
                /* Done when we hit the media which should be reset */
 
7040
                if (pMedium == this)
 
7041
                    break;
 
7042
            }
 
7043
 
 
7044
            /* first, delete the storage unit */
 
7045
            vrc = VDClose(hdd, true /* fDelete */);
 
7046
            if (RT_FAILURE(vrc))
 
7047
                throw setError(VBOX_E_FILE_ERROR,
 
7048
                               tr("Could not delete the medium storage unit '%s'%s"),
 
7049
                               location.c_str(), vdError(vrc).c_str());
 
7050
 
 
7051
            /* next, create it again */
 
7052
            vrc = VDOpen(hdd,
 
7053
                         parentFormat.c_str(),
 
7054
                         parentLocation.c_str(),
 
7055
                         VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO,
 
7056
                         m->vdImageIfaces);
 
7057
            if (RT_FAILURE(vrc))
 
7058
                throw setError(VBOX_E_FILE_ERROR,
 
7059
                               tr("Could not open the medium storage unit '%s'%s"),
 
7060
                               parentLocation.c_str(), vdError(vrc).c_str());
 
7061
 
 
7062
            vrc = VDCreateDiff(hdd,
 
7063
                               format.c_str(),
 
7064
                               location.c_str(),
 
7065
                               /// @todo use the same medium variant as before
 
7066
                               VD_IMAGE_FLAGS_NONE,
 
7067
                               NULL,
 
7068
                               id.raw(),
 
7069
                               parentId.raw(),
 
7070
                               VD_OPEN_FLAGS_NORMAL,
 
7071
                               m->vdImageIfaces,
 
7072
                               task.mVDOperationIfaces);
 
7073
            if (RT_FAILURE(vrc))
 
7074
                throw setError(VBOX_E_FILE_ERROR,
 
7075
                               tr("Could not create the differencing medium storage unit '%s'%s"),
 
7076
                               location.c_str(), vdError(vrc).c_str());
 
7077
 
 
7078
            size = VDGetFileSize(hdd, VD_LAST_IMAGE);
 
7079
            logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
 
7080
            unsigned uImageFlags;
 
7081
            vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
 
7082
            if (RT_SUCCESS(vrc))
 
7083
                variant = (MediumVariant_T)uImageFlags;
 
7084
        }
 
7085
        catch (HRESULT aRC) { rc = aRC; }
 
7086
 
 
7087
        VDDestroy(hdd);
 
7088
    }
 
7089
    catch (HRESULT aRC) { rc = aRC; }
 
7090
 
 
7091
    AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
 
7092
 
 
7093
    m->size = size;
 
7094
    m->logicalSize = logicalSize;
 
7095
    m->variant = variant;
 
7096
 
 
7097
    if (task.isAsync())
 
7098
    {
 
7099
        /* unlock ourselves when done */
 
7100
        HRESULT rc2 = UnlockWrite(NULL);
 
7101
        AssertComRC(rc2);
 
7102
    }
 
7103
 
 
7104
    /* Note that in sync mode, it's the caller's responsibility to
 
7105
     * unlock the medium. */
 
7106
 
 
7107
    return rc;
 
7108
}
 
7109
 
 
7110
/**
 
7111
 * Implementation code for the "compact" task.
 
7112
 *
 
7113
 * @param task
 
7114
 * @return
 
7115
 */
 
7116
HRESULT Medium::taskCompactHandler(Medium::CompactTask &task)
 
7117
{
 
7118
    HRESULT rc = S_OK;
 
7119
 
 
7120
    /* Lock all in {parent,child} order. The lock is also used as a
 
7121
     * signal from the task initiator (which releases it only after
 
7122
     * RTThreadCreate()) that we can start the job. */
 
7123
    AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
 
7124
 
 
7125
    try
 
7126
    {
 
7127
        PVBOXHDD hdd;
 
7128
        int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
 
7129
        ComAssertRCThrow(vrc, E_FAIL);
 
7130
 
 
7131
        try
 
7132
        {
 
7133
            /* Open all media in the chain. */
 
7134
            MediumLockList::Base::const_iterator mediumListBegin =
 
7135
                task.mpMediumLockList->GetBegin();
 
7136
            MediumLockList::Base::const_iterator mediumListEnd =
 
7137
                task.mpMediumLockList->GetEnd();
 
7138
            MediumLockList::Base::const_iterator mediumListLast =
 
7139
                mediumListEnd;
 
7140
            mediumListLast--;
 
7141
            for (MediumLockList::Base::const_iterator it = mediumListBegin;
 
7142
                 it != mediumListEnd;
 
7143
                 ++it)
 
7144
            {
 
7145
                const MediumLock &mediumLock = *it;
 
7146
                const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
 
7147
                AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
 
7148
 
 
7149
                /* sanity check */
 
7150
                if (it == mediumListLast)
 
7151
                    Assert(pMedium->m->state == MediumState_LockedWrite);
 
7152
                else
 
7153
                    Assert(pMedium->m->state == MediumState_LockedRead);
 
7154
 
 
7155
                /* Open all media but last in read-only mode. Do not handle
 
7156
                 * shareable media, as compaction and sharing are mutually
 
7157
                 * exclusive. */
 
7158
                vrc = VDOpen(hdd,
 
7159
                             pMedium->m->strFormat.c_str(),
 
7160
                             pMedium->m->strLocationFull.c_str(),
 
7161
                             (it == mediumListLast) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
 
7162
                             pMedium->m->vdImageIfaces);
 
7163
                if (RT_FAILURE(vrc))
 
7164
                    throw setError(VBOX_E_FILE_ERROR,
 
7165
                                   tr("Could not open the medium storage unit '%s'%s"),
 
7166
                                   pMedium->m->strLocationFull.c_str(),
 
7167
                                   vdError(vrc).c_str());
 
7168
            }
 
7169
 
 
7170
            Assert(m->state == MediumState_LockedWrite);
 
7171
 
 
7172
            Utf8Str location(m->strLocationFull);
 
7173
 
 
7174
            /* unlock before the potentially lengthy operation */
 
7175
            thisLock.release();
 
7176
 
 
7177
            vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
 
7178
            if (RT_FAILURE(vrc))
 
7179
            {
 
7180
                if (vrc == VERR_NOT_SUPPORTED)
 
7181
                    throw setError(VBOX_E_NOT_SUPPORTED,
 
7182
                                   tr("Compacting is not yet supported for medium '%s'"),
 
7183
                                   location.c_str());
 
7184
                else if (vrc == VERR_NOT_IMPLEMENTED)
 
7185
                    throw setError(E_NOTIMPL,
 
7186
                                   tr("Compacting is not implemented, medium '%s'"),
 
7187
                                   location.c_str());
 
7188
                else
 
7189
                    throw setError(VBOX_E_FILE_ERROR,
 
7190
                                   tr("Could not compact medium '%s'%s"),
 
7191
                                   location.c_str(),
 
7192
                                   vdError(vrc).c_str());
 
7193
            }
 
7194
        }
 
7195
        catch (HRESULT aRC) { rc = aRC; }
 
7196
 
 
7197
        VDDestroy(hdd);
 
7198
    }
 
7199
    catch (HRESULT aRC) { rc = aRC; }
 
7200
 
 
7201
    /* Everything is explicitly unlocked when the task exits,
 
7202
     * as the task destruction also destroys the media chain. */
 
7203
 
 
7204
    return rc;
 
7205
}
 
7206
 
 
7207
/**
 
7208
 * Implementation code for the "resize" task.
 
7209
 *
 
7210
 * @param task
 
7211
 * @return
 
7212
 */
 
7213
HRESULT Medium::taskResizeHandler(Medium::ResizeTask &task)
 
7214
{
 
7215
    HRESULT rc = S_OK;
 
7216
 
 
7217
    /* Lock all in {parent,child} order. The lock is also used as a
 
7218
     * signal from the task initiator (which releases it only after
 
7219
     * RTThreadCreate()) that we can start the job. */
 
7220
    AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
 
7221
 
 
7222
    try
 
7223
    {
 
7224
        PVBOXHDD hdd;
 
7225
        int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
 
7226
        ComAssertRCThrow(vrc, E_FAIL);
 
7227
 
 
7228
        try
 
7229
        {
 
7230
            /* Open all media in the chain. */
 
7231
            MediumLockList::Base::const_iterator mediumListBegin =
 
7232
                task.mpMediumLockList->GetBegin();
 
7233
            MediumLockList::Base::const_iterator mediumListEnd =
 
7234
                task.mpMediumLockList->GetEnd();
 
7235
            MediumLockList::Base::const_iterator mediumListLast =
 
7236
                mediumListEnd;
 
7237
            mediumListLast--;
 
7238
            for (MediumLockList::Base::const_iterator it = mediumListBegin;
 
7239
                 it != mediumListEnd;
 
7240
                 ++it)
 
7241
            {
 
7242
                const MediumLock &mediumLock = *it;
 
7243
                const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
 
7244
                AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
 
7245
 
 
7246
                /* sanity check */
 
7247
                if (it == mediumListLast)
 
7248
                    Assert(pMedium->m->state == MediumState_LockedWrite);
 
7249
                else
 
7250
                    Assert(pMedium->m->state == MediumState_LockedRead);
 
7251
 
 
7252
                /* Open all media but last in read-only mode. Do not handle
 
7253
                 * shareable media, as compaction and sharing are mutually
 
7254
                 * exclusive. */
 
7255
                vrc = VDOpen(hdd,
 
7256
                             pMedium->m->strFormat.c_str(),
 
7257
                             pMedium->m->strLocationFull.c_str(),
 
7258
                             (it == mediumListLast) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY,
 
7259
                             pMedium->m->vdImageIfaces);
 
7260
                if (RT_FAILURE(vrc))
 
7261
                    throw setError(VBOX_E_FILE_ERROR,
 
7262
                                   tr("Could not open the medium storage unit '%s'%s"),
 
7263
                                   pMedium->m->strLocationFull.c_str(),
 
7264
                                   vdError(vrc).c_str());
 
7265
            }
 
7266
 
 
7267
            Assert(m->state == MediumState_LockedWrite);
 
7268
 
 
7269
            Utf8Str location(m->strLocationFull);
 
7270
 
 
7271
            /* unlock before the potentially lengthy operation */
 
7272
            thisLock.release();
 
7273
 
 
7274
            VDGEOMETRY geo = {0, 0, 0}; /* auto */
 
7275
            vrc = VDResize(hdd, task.mSize, &geo, &geo, task.mVDOperationIfaces);
 
7276
            if (RT_FAILURE(vrc))
 
7277
            {
 
7278
                if (vrc == VERR_NOT_SUPPORTED)
 
7279
                    throw setError(VBOX_E_NOT_SUPPORTED,
 
7280
                                   tr("Compacting is not yet supported for medium '%s'"),
 
7281
                                   location.c_str());
 
7282
                else if (vrc == VERR_NOT_IMPLEMENTED)
 
7283
                    throw setError(E_NOTIMPL,
 
7284
                                   tr("Compacting is not implemented, medium '%s'"),
 
7285
                                   location.c_str());
 
7286
                else
 
7287
                    throw setError(VBOX_E_FILE_ERROR,
 
7288
                                   tr("Could not compact medium '%s'%s"),
 
7289
                                   location.c_str(),
 
7290
                                   vdError(vrc).c_str());
 
7291
            }
 
7292
        }
 
7293
        catch (HRESULT aRC) { rc = aRC; }
 
7294
 
 
7295
        VDDestroy(hdd);
 
7296
    }
 
7297
    catch (HRESULT aRC) { rc = aRC; }
 
7298
 
 
7299
    /* Everything is explicitly unlocked when the task exits,
 
7300
     * as the task destruction also destroys the media chain. */
 
7301
 
 
7302
    return rc;
 
7303
}
 
7304
 
 
7305
/**
 
7306
 * Implementation code for the "export" task.
 
7307
 *
 
7308
 * This only gets started from Medium::exportFile() and always runs
 
7309
 * asynchronously. It doesn't touch anything configuration related, so
 
7310
 * we never save the VirtualBox.xml file here.
 
7311
 *
 
7312
 * @param task
 
7313
 * @return
 
7314
 */
 
7315
HRESULT Medium::taskExportHandler(Medium::ExportTask &task)
 
7316
{
 
7317
    HRESULT rc = S_OK;
 
7318
 
 
7319
    try
 
7320
    {
 
7321
        /* Lock all in {parent,child} order. The lock is also used as a
 
7322
         * signal from the task initiator (which releases it only after
 
7323
         * RTThreadCreate()) that we can start the job. */
 
7324
        AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
 
7325
 
 
7326
        PVBOXHDD hdd;
 
7327
        int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
 
7328
        ComAssertRCThrow(vrc, E_FAIL);
 
7329
 
 
7330
        try
 
7331
        {
 
7332
            /* Open all media in the source chain. */
 
7333
            MediumLockList::Base::const_iterator sourceListBegin =
 
7334
                task.mpSourceMediumLockList->GetBegin();
 
7335
            MediumLockList::Base::const_iterator sourceListEnd =
 
7336
                task.mpSourceMediumLockList->GetEnd();
 
7337
            for (MediumLockList::Base::const_iterator it = sourceListBegin;
 
7338
                 it != sourceListEnd;
 
7339
                 ++it)
 
7340
            {
 
7341
                const MediumLock &mediumLock = *it;
 
7342
                const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
 
7343
                AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
 
7344
 
 
7345
                /* sanity check */
 
7346
                Assert(pMedium->m->state == MediumState_LockedRead);
 
7347
 
 
7348
                /* Open all media in read-only mode. */
 
7349
                vrc = VDOpen(hdd,
 
7350
                             pMedium->m->strFormat.c_str(),
 
7351
                             pMedium->m->strLocationFull.c_str(),
 
7352
                             VD_OPEN_FLAGS_READONLY,
 
7353
                             pMedium->m->vdImageIfaces);
 
7354
                if (RT_FAILURE(vrc))
 
7355
                    throw setError(VBOX_E_FILE_ERROR,
 
7356
                                   tr("Could not open the medium storage unit '%s'%s"),
 
7357
                                   pMedium->m->strLocationFull.c_str(),
 
7358
                                   vdError(vrc).c_str());
 
7359
            }
 
7360
 
 
7361
            Utf8Str targetFormat(task.mFormat->getId());
 
7362
            Utf8Str targetLocation(task.mFilename);
 
7363
 
 
7364
            Assert(m->state == MediumState_LockedRead);
 
7365
 
 
7366
            /* unlock before the potentially lengthy operation */
 
7367
            thisLock.release();
 
7368
 
 
7369
            /* ensure the target directory exists */
 
7370
            rc = VirtualBox::ensureFilePathExists(targetLocation);
 
7371
            if (FAILED(rc))
 
7372
                throw rc;
 
7373
 
 
7374
            PVBOXHDD targetHdd;
 
7375
            vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &targetHdd);
 
7376
            ComAssertRCThrow(vrc, E_FAIL);
 
7377
 
 
7378
            try
 
7379
            {
 
7380
                vrc = VDCopy(hdd,
 
7381
                             VD_LAST_IMAGE,
 
7382
                             targetHdd,
 
7383
                             targetFormat.c_str(),
 
7384
                             targetLocation.c_str(),
 
7385
                             false /* fMoveByRename */,
 
7386
                             0 /* cbSize */,
 
7387
                             task.mVariant,
 
7388
                             NULL /* pDstUuid */,
 
7389
                             VD_OPEN_FLAGS_NORMAL,
 
7390
                             NULL /* pVDIfsOperation */,
 
7391
                             task.mVDImageIfaces,
 
7392
                             task.mVDOperationIfaces);
 
7393
                if (RT_FAILURE(vrc))
 
7394
                    throw setError(VBOX_E_FILE_ERROR,
 
7395
                                   tr("Could not create the clone medium '%s'%s"),
 
7396
                                   targetLocation.c_str(), vdError(vrc).c_str());
 
7397
            }
 
7398
            catch (HRESULT aRC) { rc = aRC; }
 
7399
 
 
7400
            VDDestroy(targetHdd);
 
7401
        }
 
7402
        catch (HRESULT aRC) { rc = aRC; }
 
7403
 
 
7404
        VDDestroy(hdd);
 
7405
    }
 
7406
    catch (HRESULT aRC) { rc = aRC; }
 
7407
 
 
7408
    /* Everything is explicitly unlocked when the task exits,
 
7409
     * as the task destruction also destroys the source chain. */
 
7410
 
 
7411
    /* Make sure the source chain is released early, otherwise it can
 
7412
     * lead to deadlocks with concurrent IAppliance activities. */
 
7413
    task.mpSourceMediumLockList->Clear();
 
7414
 
 
7415
    return rc;
 
7416
}
 
7417
 
 
7418
/**
 
7419
 * Implementation code for the "import" task.
 
7420
 *
 
7421
 * This only gets started from Medium::importFile() and always runs
 
7422
 * asynchronously. It potentially touches the media registry, so we
 
7423
 * always save the VirtualBox.xml file when we're done here.
 
7424
 *
 
7425
 * @param task
 
7426
 * @return
 
7427
 */
 
7428
HRESULT Medium::taskImportHandler(Medium::ImportTask &task)
 
7429
{
 
7430
    HRESULT rc = S_OK;
 
7431
 
 
7432
    const ComObjPtr<Medium> &pParent = task.mParent;
 
7433
 
 
7434
    bool fCreatingTarget = false;
 
7435
 
 
7436
    uint64_t size = 0, logicalSize = 0;
 
7437
    MediumVariant_T variant = MediumVariant_Standard;
 
7438
    bool fGenerateUuid = false;
 
7439
 
 
7440
    try
 
7441
    {
 
7442
        /* Lock all in {parent,child} order. The lock is also used as a
 
7443
         * signal from the task initiator (which releases it only after
 
7444
         * RTThreadCreate()) that we can start the job. */
 
7445
        AutoMultiWriteLock2 thisLock(this, pParent COMMA_LOCKVAL_SRC_POS);
 
7446
 
 
7447
        fCreatingTarget = m->state == MediumState_Creating;
 
7448
 
 
7449
        /* The object may request a specific UUID (through a special form of
 
7450
         * the setLocation() argument). Otherwise we have to generate it */
 
7451
        Guid targetId = m->id;
 
7452
        fGenerateUuid = targetId.isEmpty();
 
7453
        if (fGenerateUuid)
 
7454
        {
 
7455
            targetId.create();
 
7456
            /* VirtualBox::registerHardDisk() will need UUID */
 
7457
            unconst(m->id) = targetId;
 
7458
        }
 
7459
 
 
7460
 
 
7461
        PVBOXHDD hdd;
 
7462
        int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);
 
7463
        ComAssertRCThrow(vrc, E_FAIL);
 
7464
 
 
7465
        try
 
7466
        {
 
7467
            /* Open source medium. */
 
7468
            vrc = VDOpen(hdd,
 
7469
                         task.mFormat->getId().c_str(),
 
7470
                         task.mFilename.c_str(),
 
7471
                         VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL,
 
7472
                         task.mVDImageIfaces);
 
7473
            if (RT_FAILURE(vrc))
 
7474
                throw setError(VBOX_E_FILE_ERROR,
 
7475
                               tr("Could not open the medium storage unit '%s'%s"),
 
7476
                               task.mFilename.c_str(),
 
7477
                               vdError(vrc).c_str());
 
7478
 
 
7479
            Utf8Str targetFormat(m->strFormat);
 
7480
            Utf8Str targetLocation(m->strLocationFull);
 
7481
 
 
7482
            Assert(   m->state == MediumState_Creating
 
7483
                   || m->state == MediumState_LockedWrite);
 
7484
            Assert(   pParent.isNull()
 
7485
                   || pParent->m->state == MediumState_LockedRead);
 
7486
 
 
7487
            /* unlock before the potentially lengthy operation */
 
7488
            thisLock.release();
 
7489
 
 
7490
            /* ensure the target directory exists */
 
7491
            rc = VirtualBox::ensureFilePathExists(targetLocation);
 
7492
            if (FAILED(rc))
 
7493
                throw rc;
 
7494
 
 
7495
            PVBOXHDD targetHdd;
 
7496
            vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &targetHdd);
 
7497
            ComAssertRCThrow(vrc, E_FAIL);
 
7498
 
 
7499
            try
 
7500
            {
 
7501
                /* Open all media in the target chain. */
 
7502
                MediumLockList::Base::const_iterator targetListBegin =
 
7503
                    task.mpTargetMediumLockList->GetBegin();
 
7504
                MediumLockList::Base::const_iterator targetListEnd =
 
7505
                    task.mpTargetMediumLockList->GetEnd();
 
7506
                for (MediumLockList::Base::const_iterator it = targetListBegin;
 
7507
                     it != targetListEnd;
 
7508
                     ++it)
 
7509
                {
 
7510
                    const MediumLock &mediumLock = *it;
 
7511
                    const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
 
7512
 
 
7513
                    /* If the target medium is not created yet there's no
 
7514
                     * reason to open it. */
 
7515
                    if (pMedium == this && fCreatingTarget)
 
7516
                        continue;
 
7517
 
 
7518
                    AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
 
7519
 
 
7520
                    /* sanity check */
 
7521
                    Assert(    pMedium->m->state == MediumState_LockedRead
 
7522
                            || pMedium->m->state == MediumState_LockedWrite);
 
7523
 
 
7524
                    unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
 
7525
                    if (pMedium->m->state != MediumState_LockedWrite)
 
7526
                        uOpenFlags = VD_OPEN_FLAGS_READONLY;
 
7527
                    if (pMedium->m->type == MediumType_Shareable)
 
7528
                        uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
 
7529
 
 
7530
                    /* Open all media in appropriate mode. */
 
7531
                    vrc = VDOpen(targetHdd,
 
7532
                                 pMedium->m->strFormat.c_str(),
 
7533
                                 pMedium->m->strLocationFull.c_str(),
 
7534
                                 uOpenFlags,
 
7535
                                 pMedium->m->vdImageIfaces);
 
7536
                    if (RT_FAILURE(vrc))
 
7537
                        throw setError(VBOX_E_FILE_ERROR,
 
7538
                                       tr("Could not open the medium storage unit '%s'%s"),
 
7539
                                       pMedium->m->strLocationFull.c_str(),
 
7540
                                       vdError(vrc).c_str());
 
7541
                }
 
7542
 
 
7543
                /** @todo r=klaus target isn't locked, race getting the state */
 
7544
                vrc = VDCopy(hdd,
 
7545
                             VD_LAST_IMAGE,
 
7546
                             targetHdd,
 
7547
                             targetFormat.c_str(),
 
7548
                             (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
 
7549
                             false /* fMoveByRename */,
 
7550
                             0 /* cbSize */,
 
7551
                             task.mVariant,
 
7552
                             targetId.raw(),
 
7553
                             VD_OPEN_FLAGS_NORMAL,
 
7554
                             NULL /* pVDIfsOperation */,
 
7555
                             m->vdImageIfaces,
 
7556
                             task.mVDOperationIfaces);
 
7557
                if (RT_FAILURE(vrc))
 
7558
                    throw setError(VBOX_E_FILE_ERROR,
 
7559
                                   tr("Could not create the clone medium '%s'%s"),
 
7560
                                   targetLocation.c_str(), vdError(vrc).c_str());
 
7561
 
 
7562
                size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
 
7563
                logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
 
7564
                unsigned uImageFlags;
 
7565
                vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
 
7566
                if (RT_SUCCESS(vrc))
 
7567
                    variant = (MediumVariant_T)uImageFlags;
 
7568
            }
 
7569
            catch (HRESULT aRC) { rc = aRC; }
 
7570
 
 
7571
            VDDestroy(targetHdd);
 
7572
        }
 
7573
        catch (HRESULT aRC) { rc = aRC; }
 
7574
 
 
7575
        VDDestroy(hdd);
 
7576
    }
 
7577
    catch (HRESULT aRC) { rc = aRC; }
 
7578
 
 
7579
    /* Only do the parent changes for newly created media. */
 
7580
    if (SUCCEEDED(rc) && fCreatingTarget)
 
7581
    {
 
7582
        /* we set mParent & children() */
 
7583
        AutoWriteLock alock2(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
 
7584
 
 
7585
        Assert(m->pParent.isNull());
 
7586
 
 
7587
        if (pParent)
 
7588
        {
 
7589
            /* associate the clone with the parent and deassociate
 
7590
             * from VirtualBox */
 
7591
            m->pParent = pParent;
 
7592
            pParent->m->llChildren.push_back(this);
 
7593
 
 
7594
            /* register with mVirtualBox as the last step and move to
 
7595
             * Created state only on success (leaving an orphan file is
 
7596
             * better than breaking media registry consistency) */
 
7597
            rc = pParent->m->pVirtualBox->registerHardDisk(this, NULL /* llRegistriesThatNeedSaving */);
 
7598
 
 
7599
            if (FAILED(rc))
 
7600
                /* break parent association on failure to register */
 
7601
                this->deparent();     // removes target from parent
 
7602
        }
 
7603
        else
 
7604
        {
 
7605
            /* just register  */
 
7606
            rc = m->pVirtualBox->registerHardDisk(this, NULL /* pllRegistriesThatNeedSaving */);
 
7607
        }
 
7608
    }
 
7609
 
 
7610
    if (fCreatingTarget)
 
7611
    {
 
7612
        AutoWriteLock mLock(this COMMA_LOCKVAL_SRC_POS);
 
7613
 
 
7614
        if (SUCCEEDED(rc))
 
7615
        {
 
7616
            m->state = MediumState_Created;
 
7617
 
 
7618
            m->size = size;
 
7619
            m->logicalSize = logicalSize;
 
7620
            m->variant = variant;
 
7621
        }
 
7622
        else
 
7623
        {
 
7624
            /* back to NotCreated on failure */
 
7625
            m->state = MediumState_NotCreated;
 
7626
 
 
7627
            /* reset UUID to prevent it from being reused next time */
 
7628
            if (fGenerateUuid)
 
7629
                unconst(m->id).clear();
 
7630
        }
 
7631
    }
 
7632
 
 
7633
    // now, at the end of this task (always asynchronous), save the settings
 
7634
    {
 
7635
        // save the settings
 
7636
        GuidList llRegistriesThatNeedSaving;
 
7637
        addToRegistryIDList(llRegistriesThatNeedSaving);
 
7638
        rc = m->pVirtualBox->saveRegistries(llRegistriesThatNeedSaving);
 
7639
    }
 
7640
 
 
7641
    /* Everything is explicitly unlocked when the task exits,
 
7642
     * as the task destruction also destroys the target chain. */
 
7643
 
 
7644
    /* Make sure the target chain is released early, otherwise it can
 
7645
     * lead to deadlocks with concurrent IAppliance activities. */
 
7646
    task.mpTargetMediumLockList->Clear();
 
7647
 
 
7648
    return rc;
 
7649
}
 
7650
 
 
7651
/* vi: set tabstop=4 shiftwidth=4 expandtab: */