VirtualBox

source: vbox/trunk/src/VBox/Main/MediumImpl.cpp@ 24612

最後變更 在這個檔案從24612是 24563,由 vboxsync 提交於 15 年 前

Main/MediumImpl: dead code

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette