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

« back to all changes in this revision

Viewing changes to src/VBox/Main/HardDiskImpl.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Felix Geyer
  • Date: 2009-12-18 16:44:29 UTC
  • mfrom: (0.3.3 upstream) (0.4.6 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091218164429-jd34ccexpv5na11a
Tags: 3.1.2-dfsg-1ubuntu1
* Merge from Debian unstable (LP: #498219), remaining changes:
  - Disable update action
    - debian/patches/u01-disable-update-action.dpatch
  - VirtualBox should go in Accessories, not in System tools (LP: #288590)
    - debian/virtualbox-ose-qt.files/virtualbox-ose.desktop
  - Add Apport hook
    - debian/virtualbox-ose.files/source_virtualbox-ose.py
    - debian/virtualbox-ose.install
  - Add Launchpad integration
    - debian/control
    - debian/lpi-bug.xpm
    - debian/patches/u02-lp-integration.dpatch
* Fixes the following bugs:
  - Kernel module fails to build with Linux >= 2.6.32 (LP: #474625)
  - X.Org drivers need to be rebuilt against X-Server 1.7 (LP: #495935)
  - The *-source packages try to build the kernel modules even though the
    kernel headers aren't available (LP: #473334)
* Replace *-source packages with transitional packages for *-dkms.
* Adapt u01-disable-update-action.dpatch and u02-lp-integration.dpatch for
  new upstream version.

Show diffs side-by-side

added added

removed removed

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