~ubuntu-branches/ubuntu/raring/virtualbox-ose/raring

« back to all changes in this revision

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