Changeset 33908 in vbox
- Timestamp:
- Nov 9, 2010 3:37:30 PM (15 years ago)
- svn:sync-xref-src-repo-rev:
- 67564
- Location:
- trunk/src/VBox/Main
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/MediumImpl.cpp
r33760 r33908 2943 2943 2944 2944 /** 2945 * Returns the medium device type. Must have caller + locking! 2946 * @return 2947 */ 2948 DeviceType_T Medium::getDeviceType() const 2949 { 2950 return m->devType; 2951 } 2952 2953 /** 2954 * Returns the medium type. Must have caller + locking! 2955 * @return 2956 */ 2957 MediumType_T Medium::getType() const 2958 { 2959 return m->type; 2960 } 2961 2962 /** 2963 * Returns a short version of the location attribute. 2964 * 2965 * @note Must be called from under this object's read or write lock. 2966 */ 2967 Utf8Str Medium::getName() 2968 { 2969 Utf8Str name = RTPathFilename(m->strLocationFull.c_str()); 2970 return name; 2971 } 2972 2973 /** 2974 * This adds the given UUID to the list of media registries in which this 2975 * medium should be registered. The UUID can either be a machine UUID, 2976 * to add a machine registry, or the global registry UUID as returned by 2977 * VirtualBox::getGlobalRegistryId(). 2978 * 2979 * Note that for hard disks, this method does nothing if the medium is 2980 * already in another registry to avoid having hard disks in more than 2981 * one registry, which causes trouble with keeping diff images in sync. 2982 * See getFirstRegistryMachineId() for details. 2983 * 2984 * @param id 2985 * @param pfNeedsSaveSettings If != NULL, is set to true if a new reference was added and saveSettings for either the machine or global XML is needed. 2986 * @return true if the registry was added. 2987 */ 2988 bool Medium::addRegistry(const Guid& id, 2989 bool *pfNeedsSaveSettings) 2990 { 2991 AutoCaller autoCaller(this); 2992 if (FAILED(autoCaller.rc())) return false; 2993 2994 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2995 2996 if ( m->devType == DeviceType_HardDisk 2997 && m->llRegistryIDs.size() > 0 2998 ) 2999 return false; 3000 3001 // no need to add the UUID twice 3002 for (GuidList::const_iterator it = m->llRegistryIDs.begin(); 3003 it != m->llRegistryIDs.end(); 3004 ++it) 3005 { 3006 if ((*it) == id) 3007 return false; 3008 } 3009 3010 m->llRegistryIDs.push_back(id); 3011 if (pfNeedsSaveSettings) 3012 *pfNeedsSaveSettings = true; 3013 return true; 3014 } 3015 3016 /** 3017 * Returns true if id is in the list of media registries for this medium. 3018 * @param id 3019 * @return 3020 */ 3021 bool Medium::isInRegistry(const Guid& id) 3022 { 3023 AutoCaller autoCaller(this); 3024 if (FAILED(autoCaller.rc())) return false; 3025 3026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3027 3028 for (GuidList::const_iterator it = m->llRegistryIDs.begin(); 3029 it != m->llRegistryIDs.end(); 3030 ++it) 3031 { 3032 if (*it == id) 3033 return true; 3034 } 3035 3036 return false; 3037 } 3038 3039 /** 3040 * Internal method to return the medium's first registry machine (i.e. the machine in whose 3041 * machine XML this medium is listed). 3042 * 3043 * Every medium must now (4.0) reside in at least one media registry, which is identified by 3044 * a UUID. This is either a machine UUID if the machine is from 4.0 or newer, in which case 3045 * machines have their own media registries, or it is the pseudo-UUID of the VirtualBox 3046 * object if the machine is old and still needs the global registry in VirtualBox.xml. 3047 * 3048 * By definition, hard disks may only be in one media registry, in which all its children 3049 * will be stored as well. Otherwise we run into problems with having keep multiple registries 3050 * in sync. (This is the "cloned VM" case in which VM1 may link to the disks of VM2; in this 3051 * case, only VM2's registry is used for the disk in question.) 3052 * 3053 * ISOs and RAWs, by contrast, can be in more than one repository to make things easier for 3054 * the user. 3055 * 3056 * Must have caller + locking! 3057 * 3058 * @return 3059 */ 3060 const Guid& Medium::getFirstRegistryMachineId() const 3061 { 3062 return m->llRegistryIDs.front(); 3063 } 3064 3065 /** 2945 3066 * Adds the given machine and optionally the snapshot to the list of the objects 2946 3067 * this medium is attached to. … … 3425 3546 3426 3547 /** 3548 * Creates a new differencing storage unit using the format of the given target 3549 * medium and the location. Note that @c aTarget must be NotCreated. 3550 * 3551 * The @a aMediumLockList parameter contains the associated medium lock list, 3552 * which must be in locked state. If @a aWait is @c true then the caller is 3553 * responsible for unlocking. 3554 * 3555 * If @a aProgress is not NULL but the object it points to is @c null then a 3556 * new progress object will be created and assigned to @a *aProgress on 3557 * success, otherwise the existing progress object is used. If @a aProgress is 3558 * NULL, then no progress object is created/used at all. 3559 * 3560 * When @a aWait is @c false, this method will create a thread to perform the 3561 * create operation asynchronously and will return immediately. Otherwise, it 3562 * will perform the operation on the calling thread and will not return to the 3563 * caller until the operation is completed. Note that @a aProgress cannot be 3564 * NULL when @a aWait is @c false (this method will assert in this case). 3565 * 3566 * @param aTarget Target medium. 3567 * @param aVariant Precise medium variant to create. 3568 * @param aMediumLockList List of media which should be locked. 3569 * @param aProgress Where to find/store a Progress object to track 3570 * operation completion. 3571 * @param aWait @c true if this method should block instead of 3572 * creating an asynchronous thread. 3573 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been 3574 * initialized to false and that will be set to true 3575 * by this function if the caller should invoke 3576 * VirtualBox::saveSettings() because the global 3577 * settings have changed. This only works in "wait" 3578 * mode; otherwise saveSettings is called 3579 * automatically by the thread that was created, 3580 * and this parameter is ignored. 3581 * 3582 * @note Locks this object and @a aTarget for writing. 3583 */ 3584 HRESULT Medium::createDiffStorage(ComObjPtr<Medium> &aTarget, 3585 MediumVariant_T aVariant, 3586 MediumLockList *aMediumLockList, 3587 ComObjPtr<Progress> *aProgress, 3588 bool aWait, 3589 bool *pfNeedsGlobalSaveSettings) 3590 { 3591 AssertReturn(!aTarget.isNull(), E_FAIL); 3592 AssertReturn(aMediumLockList, E_FAIL); 3593 AssertReturn(aProgress != NULL || aWait == true, E_FAIL); 3594 3595 AutoCaller autoCaller(this); 3596 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3597 3598 AutoCaller targetCaller(aTarget); 3599 if (FAILED(targetCaller.rc())) return targetCaller.rc(); 3600 3601 HRESULT rc = S_OK; 3602 ComObjPtr<Progress> pProgress; 3603 Medium::Task *pTask = NULL; 3604 3605 try 3606 { 3607 AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS); 3608 3609 ComAssertThrow( m->type != MediumType_Writethrough 3610 && m->type != MediumType_Shareable 3611 && m->type != MediumType_Readonly, E_FAIL); 3612 ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL); 3613 3614 if (aTarget->m->state != MediumState_NotCreated) 3615 throw aTarget->setStateError(); 3616 3617 /* Check that the medium is not attached to the current state of 3618 * any VM referring to it. */ 3619 for (BackRefList::const_iterator it = m->backRefs.begin(); 3620 it != m->backRefs.end(); 3621 ++it) 3622 { 3623 if (it->fInCurState) 3624 { 3625 /* Note: when a VM snapshot is being taken, all normal media 3626 * attached to the VM in the current state will be, as an 3627 * exception, also associated with the snapshot which is about 3628 * to create (see SnapshotMachine::init()) before deassociating 3629 * them from the current state (which takes place only on 3630 * success in Machine::fixupHardDisks()), so that the size of 3631 * snapshotIds will be 1 in this case. The extra condition is 3632 * used to filter out this legal situation. */ 3633 if (it->llSnapshotIds.size() == 0) 3634 throw setError(VBOX_E_INVALID_OBJECT_STATE, 3635 tr("Medium '%s' is attached to a virtual machine with UUID {%RTuuid}. No differencing media based on it may be created until it is detached"), 3636 m->strLocationFull.c_str(), it->machineId.raw()); 3637 3638 Assert(it->llSnapshotIds.size() == 1); 3639 } 3640 } 3641 3642 if (aProgress != NULL) 3643 { 3644 /* use the existing progress object... */ 3645 pProgress = *aProgress; 3646 3647 /* ...but create a new one if it is null */ 3648 if (pProgress.isNull()) 3649 { 3650 pProgress.createObject(); 3651 rc = pProgress->init(m->pVirtualBox, 3652 static_cast<IMedium*>(this), 3653 BstrFmt(tr("Creating differencing medium storage unit '%s'"), aTarget->m->strLocationFull.c_str()).raw(), 3654 TRUE /* aCancelable */); 3655 if (FAILED(rc)) 3656 throw rc; 3657 } 3658 } 3659 3660 /* setup task object to carry out the operation sync/async */ 3661 pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant, 3662 aMediumLockList, 3663 aWait /* fKeepMediumLockList */); 3664 rc = pTask->rc(); 3665 AssertComRC(rc); 3666 if (FAILED(rc)) 3667 throw rc; 3668 3669 /* register a task (it will deregister itself when done) */ 3670 ++m->numCreateDiffTasks; 3671 Assert(m->numCreateDiffTasks != 0); /* overflow? */ 3672 3673 aTarget->m->state = MediumState_Creating; 3674 } 3675 catch (HRESULT aRC) { rc = aRC; } 3676 3677 if (SUCCEEDED(rc)) 3678 { 3679 if (aWait) 3680 rc = runNow(pTask, pfNeedsGlobalSaveSettings); 3681 else 3682 rc = startThread(pTask); 3683 3684 if (SUCCEEDED(rc) && aProgress != NULL) 3685 *aProgress = pProgress; 3686 } 3687 else if (pTask != NULL) 3688 delete pTask; 3689 3690 return rc; 3691 } 3692 3693 /** 3427 3694 * Returns a preferred format for differencing media. 3428 3695 */ … … 3446 3713 3447 3714 /** 3448 * Returns the medium device type. Must have caller + locking! 3715 * Implementation for the public Medium::Close() with the exception of calling 3716 * VirtualBox::saveSettings(), in case someone wants to call this for several 3717 * media. 3718 * 3719 * After this returns with success, uninit() has been called on the medium, and 3720 * the object is no longer usable ("not ready" state). 3721 * 3722 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true 3723 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed. 3724 * This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created, 3725 * and this parameter is ignored. 3726 * @param autoCaller AutoCaller instance which must have been created on the caller's stack for this medium. This gets released here 3727 * upon which the Medium instance gets uninitialized. 3449 3728 * @return 3450 3729 */ 3451 DeviceType_T Medium::getDeviceType() const 3452 { 3453 return m->devType; 3454 } 3455 3456 /** 3457 * Returns the medium type. Must have caller + locking! 3730 HRESULT Medium::close(bool *pfNeedsGlobalSaveSettings, AutoCaller &autoCaller) 3731 { 3732 // we're accessing parent/child and backrefs, so lock the tree first, then ourselves 3733 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(), 3734 this->lockHandle() 3735 COMMA_LOCKVAL_SRC_POS); 3736 3737 LogFlowFunc(("ENTER for %s\n", getLocationFull().c_str())); 3738 3739 bool wasCreated = true; 3740 3741 switch (m->state) 3742 { 3743 case MediumState_NotCreated: 3744 wasCreated = false; 3745 break; 3746 case MediumState_Created: 3747 case MediumState_Inaccessible: 3748 break; 3749 default: 3750 return setStateError(); 3751 } 3752 3753 if (m->backRefs.size() != 0) 3754 return setError(VBOX_E_OBJECT_IN_USE, 3755 tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines"), 3756 m->strLocationFull.c_str(), m->backRefs.size()); 3757 3758 // perform extra media-dependent close checks 3759 HRESULT rc = canClose(); 3760 if (FAILED(rc)) return rc; 3761 3762 if (wasCreated) 3763 { 3764 // remove from the list of known media before performing actual 3765 // uninitialization (to keep the media registry consistent on 3766 // failure to do so) 3767 rc = unregisterWithVirtualBox(pfNeedsGlobalSaveSettings); 3768 if (FAILED(rc)) return rc; 3769 } 3770 3771 // leave the AutoCaller, as otherwise uninit() will simply hang 3772 autoCaller.release(); 3773 3774 // Keep the locks held until after uninit, as otherwise the consistency 3775 // of the medium tree cannot be guaranteed. 3776 uninit(); 3777 3778 LogFlowFuncLeave(); 3779 3780 return rc; 3781 } 3782 3783 /** 3784 * Deletes the medium storage unit. 3785 * 3786 * If @a aProgress is not NULL but the object it points to is @c null then a new 3787 * progress object will be created and assigned to @a *aProgress on success, 3788 * otherwise the existing progress object is used. If Progress is NULL, then no 3789 * progress object is created/used at all. 3790 * 3791 * When @a aWait is @c false, this method will create a thread to perform the 3792 * delete operation asynchronously and will return immediately. Otherwise, it 3793 * will perform the operation on the calling thread and will not return to the 3794 * caller until the operation is completed. Note that @a aProgress cannot be 3795 * NULL when @a aWait is @c false (this method will assert in this case). 3796 * 3797 * @param aProgress Where to find/store a Progress object to track operation 3798 * completion. 3799 * @param aWait @c true if this method should block instead of creating 3800 * an asynchronous thread. 3801 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true 3802 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed. 3803 * This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created, 3804 * and this parameter is ignored. 3805 * 3806 * @note Locks mVirtualBox and this object for writing. Locks medium tree for 3807 * writing. 3808 */ 3809 HRESULT Medium::deleteStorage(ComObjPtr<Progress> *aProgress, 3810 bool aWait, 3811 bool *pfNeedsGlobalSaveSettings) 3812 { 3813 AssertReturn(aProgress != NULL || aWait == true, E_FAIL); 3814 3815 AutoCaller autoCaller(this); 3816 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3817 3818 HRESULT rc = S_OK; 3819 ComObjPtr<Progress> pProgress; 3820 Medium::Task *pTask = NULL; 3821 3822 try 3823 { 3824 /* we're accessing the media tree, and canClose() needs it too */ 3825 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(), 3826 this->lockHandle() 3827 COMMA_LOCKVAL_SRC_POS); 3828 LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() )); 3829 3830 if ( !(m->formatObj->getCapabilities() & ( MediumFormatCapabilities_CreateDynamic 3831 | MediumFormatCapabilities_CreateFixed))) 3832 throw setError(VBOX_E_NOT_SUPPORTED, 3833 tr("Medium format '%s' does not support storage deletion"), 3834 m->strFormat.c_str()); 3835 3836 /* Note that we are fine with Inaccessible state too: a) for symmetry 3837 * with create calls and b) because it doesn't really harm to try, if 3838 * it is really inaccessible, the delete operation will fail anyway. 3839 * Accepting Inaccessible state is especially important because all 3840 * registered media are initially Inaccessible upon VBoxSVC startup 3841 * until COMGETTER(RefreshState) is called. Accept Deleting state 3842 * because some callers need to put the medium in this state early 3843 * to prevent races. */ 3844 switch (m->state) 3845 { 3846 case MediumState_Created: 3847 case MediumState_Deleting: 3848 case MediumState_Inaccessible: 3849 break; 3850 default: 3851 throw setStateError(); 3852 } 3853 3854 if (m->backRefs.size() != 0) 3855 { 3856 Utf8Str strMachines; 3857 for (BackRefList::const_iterator it = m->backRefs.begin(); 3858 it != m->backRefs.end(); 3859 ++it) 3860 { 3861 const BackRef &b = *it; 3862 if (strMachines.length()) 3863 strMachines.append(", "); 3864 strMachines.append(b.machineId.toString().c_str()); 3865 } 3866 #ifdef DEBUG 3867 dumpBackRefs(); 3868 #endif 3869 throw setError(VBOX_E_OBJECT_IN_USE, 3870 tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"), 3871 m->strLocationFull.c_str(), 3872 m->backRefs.size(), 3873 strMachines.c_str()); 3874 } 3875 3876 rc = canClose(); 3877 if (FAILED(rc)) 3878 throw rc; 3879 3880 /* go to Deleting state, so that the medium is not actually locked */ 3881 if (m->state != MediumState_Deleting) 3882 { 3883 rc = markForDeletion(); 3884 if (FAILED(rc)) 3885 throw rc; 3886 } 3887 3888 /* Build the medium lock list. */ 3889 MediumLockList *pMediumLockList(new MediumLockList()); 3890 rc = createMediumLockList(true /* fFailIfInaccessible */, 3891 true /* fMediumLockWrite */, 3892 NULL, 3893 *pMediumLockList); 3894 if (FAILED(rc)) 3895 { 3896 delete pMediumLockList; 3897 throw rc; 3898 } 3899 3900 rc = pMediumLockList->Lock(); 3901 if (FAILED(rc)) 3902 { 3903 delete pMediumLockList; 3904 throw setError(rc, 3905 tr("Failed to lock media when deleting '%s'"), 3906 getLocationFull().c_str()); 3907 } 3908 3909 /* try to remove from the list of known media before performing 3910 * actual deletion (we favor the consistency of the media registry 3911 * which would have been broken if unregisterWithVirtualBox() failed 3912 * after we successfully deleted the storage) */ 3913 rc = unregisterWithVirtualBox(pfNeedsGlobalSaveSettings); 3914 if (FAILED(rc)) 3915 throw rc; 3916 // no longer need lock 3917 multilock.release(); 3918 3919 if (aProgress != NULL) 3920 { 3921 /* use the existing progress object... */ 3922 pProgress = *aProgress; 3923 3924 /* ...but create a new one if it is null */ 3925 if (pProgress.isNull()) 3926 { 3927 pProgress.createObject(); 3928 rc = pProgress->init(m->pVirtualBox, 3929 static_cast<IMedium*>(this), 3930 BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(), 3931 FALSE /* aCancelable */); 3932 if (FAILED(rc)) 3933 throw rc; 3934 } 3935 } 3936 3937 /* setup task object to carry out the operation sync/async */ 3938 pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList); 3939 rc = pTask->rc(); 3940 AssertComRC(rc); 3941 if (FAILED(rc)) 3942 throw rc; 3943 } 3944 catch (HRESULT aRC) { rc = aRC; } 3945 3946 if (SUCCEEDED(rc)) 3947 { 3948 if (aWait) 3949 rc = runNow(pTask, NULL /* pfNeedsGlobalSaveSettings*/); 3950 else 3951 rc = startThread(pTask); 3952 3953 if (SUCCEEDED(rc) && aProgress != NULL) 3954 *aProgress = pProgress; 3955 3956 } 3957 else 3958 { 3959 if (pTask) 3960 delete pTask; 3961 3962 /* Undo deleting state if necessary. */ 3963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3964 unmarkForDeletion(); 3965 } 3966 3967 return rc; 3968 } 3969 3970 /** 3971 * Mark a medium for deletion. 3972 * 3973 * @note Caller must hold the write lock on this medium! 3974 */ 3975 HRESULT Medium::markForDeletion() 3976 { 3977 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL); 3978 switch (m->state) 3979 { 3980 case MediumState_Created: 3981 case MediumState_Inaccessible: 3982 m->preLockState = m->state; 3983 m->state = MediumState_Deleting; 3984 return S_OK; 3985 default: 3986 return setStateError(); 3987 } 3988 } 3989 3990 /** 3991 * Removes the "mark for deletion". 3992 * 3993 * @note Caller must hold the write lock on this medium! 3994 */ 3995 HRESULT Medium::unmarkForDeletion() 3996 { 3997 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL); 3998 switch (m->state) 3999 { 4000 case MediumState_Deleting: 4001 m->state = m->preLockState; 4002 return S_OK; 4003 default: 4004 return setStateError(); 4005 } 4006 } 4007 4008 /** 4009 * Mark a medium for deletion which is in locked state. 4010 * 4011 * @note Caller must hold the write lock on this medium! 4012 */ 4013 HRESULT Medium::markLockedForDeletion() 4014 { 4015 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL); 4016 if ( ( m->state == MediumState_LockedRead 4017 || m->state == MediumState_LockedWrite) 4018 && m->preLockState == MediumState_Created) 4019 { 4020 m->preLockState = MediumState_Deleting; 4021 return S_OK; 4022 } 4023 else 4024 return setStateError(); 4025 } 4026 4027 /** 4028 * Removes the "mark for deletion" for a medium in locked state. 4029 * 4030 * @note Caller must hold the write lock on this medium! 4031 */ 4032 HRESULT Medium::unmarkLockedForDeletion() 4033 { 4034 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL); 4035 if ( ( m->state == MediumState_LockedRead 4036 || m->state == MediumState_LockedWrite) 4037 && m->preLockState == MediumState_Deleting) 4038 { 4039 m->preLockState = MediumState_Created; 4040 return S_OK; 4041 } 4042 else 4043 return setStateError(); 4044 } 4045 4046 /** 4047 * Prepares this (source) medium, target medium and all intermediate media 4048 * for the merge operation. 4049 * 4050 * This method is to be called prior to calling the #mergeTo() to perform 4051 * necessary consistency checks and place involved media to appropriate 4052 * states. If #mergeTo() is not called or fails, the state modifications 4053 * performed by this method must be undone by #cancelMergeTo(). 4054 * 4055 * See #mergeTo() for more information about merging. 4056 * 4057 * @param pTarget Target medium. 4058 * @param aMachineId Allowed machine attachment. NULL means do not check. 4059 * @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means 4060 * do not check. 4061 * @param fLockMedia Flag whether to lock the medium lock list or not. 4062 * If set to false and the medium lock list locking fails 4063 * later you must call #cancelMergeTo(). 4064 * @param fMergeForward Resulting merge direction (out). 4065 * @param pParentForTarget New parent for target medium after merge (out). 4066 * @param aChildrenToReparent List of children of the source which will have 4067 * to be reparented to the target after merge (out). 4068 * @param aMediumLockList Medium locking information (out). 4069 * 4070 * @note Locks medium tree for reading. Locks this object, aTarget and all 4071 * intermediate media for writing. 4072 */ 4073 HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget, 4074 const Guid *aMachineId, 4075 const Guid *aSnapshotId, 4076 bool fLockMedia, 4077 bool &fMergeForward, 4078 ComObjPtr<Medium> &pParentForTarget, 4079 MediaList &aChildrenToReparent, 4080 MediumLockList * &aMediumLockList) 4081 { 4082 AssertReturn(pTarget != NULL, E_FAIL); 4083 AssertReturn(pTarget != this, E_FAIL); 4084 4085 AutoCaller autoCaller(this); 4086 AssertComRCReturnRC(autoCaller.rc()); 4087 4088 AutoCaller targetCaller(pTarget); 4089 AssertComRCReturnRC(targetCaller.rc()); 4090 4091 HRESULT rc = S_OK; 4092 fMergeForward = false; 4093 pParentForTarget.setNull(); 4094 aChildrenToReparent.clear(); 4095 Assert(aMediumLockList == NULL); 4096 aMediumLockList = NULL; 4097 4098 try 4099 { 4100 // locking: we need the tree lock first because we access parent pointers 4101 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 4102 4103 /* more sanity checking and figuring out the merge direction */ 4104 ComObjPtr<Medium> pMedium = getParent(); 4105 while (!pMedium.isNull() && pMedium != pTarget) 4106 pMedium = pMedium->getParent(); 4107 if (pMedium == pTarget) 4108 fMergeForward = false; 4109 else 4110 { 4111 pMedium = pTarget->getParent(); 4112 while (!pMedium.isNull() && pMedium != this) 4113 pMedium = pMedium->getParent(); 4114 if (pMedium == this) 4115 fMergeForward = true; 4116 else 4117 { 4118 Utf8Str tgtLoc; 4119 { 4120 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS); 4121 tgtLoc = pTarget->getLocationFull(); 4122 } 4123 4124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4125 throw setError(VBOX_E_INVALID_OBJECT_STATE, 4126 tr("Media '%s' and '%s' are unrelated"), 4127 m->strLocationFull.c_str(), tgtLoc.c_str()); 4128 } 4129 } 4130 4131 /* Build the lock list. */ 4132 aMediumLockList = new MediumLockList(); 4133 if (fMergeForward) 4134 rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */, 4135 true /* fMediumLockWrite */, 4136 NULL, 4137 *aMediumLockList); 4138 else 4139 rc = createMediumLockList(true /* fFailIfInaccessible */, 4140 false /* fMediumLockWrite */, 4141 NULL, 4142 *aMediumLockList); 4143 if (FAILED(rc)) 4144 throw rc; 4145 4146 /* Sanity checking, must be after lock list creation as it depends on 4147 * valid medium states. The medium objects must be accessible. Only 4148 * do this if immediate locking is requested, otherwise it fails when 4149 * we construct a medium lock list for an already running VM. Snapshot 4150 * deletion uses this to simplify its life. */ 4151 if (fLockMedia) 4152 { 4153 { 4154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4155 if (m->state != MediumState_Created) 4156 throw setStateError(); 4157 } 4158 { 4159 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS); 4160 if (pTarget->m->state != MediumState_Created) 4161 throw pTarget->setStateError(); 4162 } 4163 } 4164 4165 /* check medium attachment and other sanity conditions */ 4166 if (fMergeForward) 4167 { 4168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4169 if (getChildren().size() > 1) 4170 { 4171 throw setError(VBOX_E_INVALID_OBJECT_STATE, 4172 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"), 4173 m->strLocationFull.c_str(), getChildren().size()); 4174 } 4175 /* One backreference is only allowed if the machine ID is not empty 4176 * and it matches the machine the medium is attached to (including 4177 * the snapshot ID if not empty). */ 4178 if ( m->backRefs.size() != 0 4179 && ( !aMachineId 4180 || m->backRefs.size() != 1 4181 || aMachineId->isEmpty() 4182 || *getFirstMachineBackrefId() != *aMachineId 4183 || ( (!aSnapshotId || !aSnapshotId->isEmpty()) 4184 && *getFirstMachineBackrefSnapshotId() != *aSnapshotId))) 4185 throw setError(VBOX_E_OBJECT_IN_USE, 4186 tr("Medium '%s' is attached to %d virtual machines"), 4187 m->strLocationFull.c_str(), m->backRefs.size()); 4188 if (m->type == MediumType_Immutable) 4189 throw setError(VBOX_E_INVALID_OBJECT_STATE, 4190 tr("Medium '%s' is immutable"), 4191 m->strLocationFull.c_str()); 4192 } 4193 else 4194 { 4195 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS); 4196 if (pTarget->getChildren().size() > 1) 4197 { 4198 throw setError(VBOX_E_OBJECT_IN_USE, 4199 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"), 4200 pTarget->m->strLocationFull.c_str(), 4201 pTarget->getChildren().size()); 4202 } 4203 if (pTarget->m->type == MediumType_Immutable) 4204 throw setError(VBOX_E_INVALID_OBJECT_STATE, 4205 tr("Medium '%s' is immutable"), 4206 pTarget->m->strLocationFull.c_str()); 4207 } 4208 ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this); 4209 ComObjPtr<Medium> pLastIntermediate = pLast->getParent(); 4210 for (pLast = pLastIntermediate; 4211 !pLast.isNull() && pLast != pTarget && pLast != this; 4212 pLast = pLast->getParent()) 4213 { 4214 AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS); 4215 if (pLast->getChildren().size() > 1) 4216 { 4217 throw setError(VBOX_E_OBJECT_IN_USE, 4218 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"), 4219 pLast->m->strLocationFull.c_str(), 4220 pLast->getChildren().size()); 4221 } 4222 if (pLast->m->backRefs.size() != 0) 4223 throw setError(VBOX_E_OBJECT_IN_USE, 4224 tr("Medium '%s' is attached to %d virtual machines"), 4225 pLast->m->strLocationFull.c_str(), 4226 pLast->m->backRefs.size()); 4227 4228 } 4229 4230 /* Update medium states appropriately */ 4231 if (m->state == MediumState_Created) 4232 { 4233 rc = markForDeletion(); 4234 if (FAILED(rc)) 4235 throw rc; 4236 } 4237 else 4238 { 4239 if (fLockMedia) 4240 throw setStateError(); 4241 else if ( m->state == MediumState_LockedWrite 4242 || m->state == MediumState_LockedRead) 4243 { 4244 /* Either mark it for deletion in locked state or allow 4245 * others to have done so. */ 4246 if (m->preLockState == MediumState_Created) 4247 markLockedForDeletion(); 4248 else if (m->preLockState != MediumState_Deleting) 4249 throw setStateError(); 4250 } 4251 else 4252 throw setStateError(); 4253 } 4254 4255 if (fMergeForward) 4256 { 4257 /* we will need parent to reparent target */ 4258 pParentForTarget = m->pParent; 4259 } 4260 else 4261 { 4262 /* we will need to reparent children of the source */ 4263 for (MediaList::const_iterator it = getChildren().begin(); 4264 it != getChildren().end(); 4265 ++it) 4266 { 4267 pMedium = *it; 4268 if (fLockMedia) 4269 { 4270 rc = pMedium->LockWrite(NULL); 4271 if (FAILED(rc)) 4272 throw rc; 4273 } 4274 4275 aChildrenToReparent.push_back(pMedium); 4276 } 4277 } 4278 for (pLast = pLastIntermediate; 4279 !pLast.isNull() && pLast != pTarget && pLast != this; 4280 pLast = pLast->getParent()) 4281 { 4282 AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS); 4283 if (pLast->m->state == MediumState_Created) 4284 { 4285 rc = pLast->markForDeletion(); 4286 if (FAILED(rc)) 4287 throw rc; 4288 } 4289 else 4290 throw pLast->setStateError(); 4291 } 4292 4293 /* Tweak the lock list in the backward merge case, as the target 4294 * isn't marked to be locked for writing yet. */ 4295 if (!fMergeForward) 4296 { 4297 MediumLockList::Base::iterator lockListBegin = 4298 aMediumLockList->GetBegin(); 4299 MediumLockList::Base::iterator lockListEnd = 4300 aMediumLockList->GetEnd(); 4301 lockListEnd--; 4302 for (MediumLockList::Base::iterator it = lockListBegin; 4303 it != lockListEnd; 4304 ++it) 4305 { 4306 MediumLock &mediumLock = *it; 4307 if (mediumLock.GetMedium() == pTarget) 4308 { 4309 HRESULT rc2 = mediumLock.UpdateLock(true); 4310 AssertComRC(rc2); 4311 break; 4312 } 4313 } 4314 } 4315 4316 if (fLockMedia) 4317 { 4318 rc = aMediumLockList->Lock(); 4319 if (FAILED(rc)) 4320 { 4321 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS); 4322 throw setError(rc, 4323 tr("Failed to lock media when merging to '%s'"), 4324 pTarget->getLocationFull().c_str()); 4325 } 4326 } 4327 } 4328 catch (HRESULT aRC) { rc = aRC; } 4329 4330 if (FAILED(rc)) 4331 { 4332 delete aMediumLockList; 4333 aMediumLockList = NULL; 4334 } 4335 4336 return rc; 4337 } 4338 4339 /** 4340 * Merges this medium to the specified medium which must be either its 4341 * direct ancestor or descendant. 4342 * 4343 * Given this medium is SOURCE and the specified medium is TARGET, we will 4344 * get two variants of the merge operation: 4345 * 4346 * forward merge 4347 * -------------------------> 4348 * [Extra] <- SOURCE <- Intermediate <- TARGET 4349 * Any Del Del LockWr 4350 * 4351 * 4352 * backward merge 4353 * <------------------------- 4354 * TARGET <- Intermediate <- SOURCE <- [Extra] 4355 * LockWr Del Del LockWr 4356 * 4357 * Each diagram shows the involved media on the media chain where 4358 * SOURCE and TARGET belong. Under each medium there is a state value which 4359 * the medium must have at a time of the mergeTo() call. 4360 * 4361 * The media in the square braces may be absent (e.g. when the forward 4362 * operation takes place and SOURCE is the base medium, or when the backward 4363 * merge operation takes place and TARGET is the last child in the chain) but if 4364 * they present they are involved too as shown. 4365 * 4366 * Neither the source medium nor intermediate media may be attached to 4367 * any VM directly or in the snapshot, otherwise this method will assert. 4368 * 4369 * The #prepareMergeTo() method must be called prior to this method to place all 4370 * involved to necessary states and perform other consistency checks. 4371 * 4372 * If @a aWait is @c true then this method will perform the operation on the 4373 * calling thread and will not return to the caller until the operation is 4374 * completed. When this method succeeds, all intermediate medium objects in 4375 * the chain will be uninitialized, the state of the target medium (and all 4376 * involved extra media) will be restored. @a aMediumLockList will not be 4377 * deleted, whether the operation is successful or not. The caller has to do 4378 * this if appropriate. Note that this (source) medium is not uninitialized 4379 * because of possible AutoCaller instances held by the caller of this method 4380 * on the current thread. It's therefore the responsibility of the caller to 4381 * call Medium::uninit() after releasing all callers. 4382 * 4383 * If @a aWait is @c false then this method will create a thread to perform the 4384 * operation asynchronously and will return immediately. If the operation 4385 * succeeds, the thread will uninitialize the source medium object and all 4386 * intermediate medium objects in the chain, reset the state of the target 4387 * medium (and all involved extra media) and delete @a aMediumLockList. 4388 * If the operation fails, the thread will only reset the states of all 4389 * involved media and delete @a aMediumLockList. 4390 * 4391 * When this method fails (regardless of the @a aWait mode), it is a caller's 4392 * responsibility to undo state changes and delete @a aMediumLockList using 4393 * #cancelMergeTo(). 4394 * 4395 * If @a aProgress is not NULL but the object it points to is @c null then a new 4396 * progress object will be created and assigned to @a *aProgress on success, 4397 * otherwise the existing progress object is used. If Progress is NULL, then no 4398 * progress object is created/used at all. Note that @a aProgress cannot be 4399 * NULL when @a aWait is @c false (this method will assert in this case). 4400 * 4401 * @param pTarget Target medium. 4402 * @param fMergeForward Merge direction. 4403 * @param pParentForTarget New parent for target medium after merge. 4404 * @param aChildrenToReparent List of children of the source which will have 4405 * to be reparented to the target after merge. 4406 * @param aMediumLockList Medium locking information. 4407 * @param aProgress Where to find/store a Progress object to track operation 4408 * completion. 4409 * @param aWait @c true if this method should block instead of creating 4410 * an asynchronous thread. 4411 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true 4412 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed. 4413 * This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created, 4414 * and this parameter is ignored. 4415 * 4416 * @note Locks the tree lock for writing. Locks the media from the chain 4417 * for writing. 4418 */ 4419 HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget, 4420 bool fMergeForward, 4421 const ComObjPtr<Medium> &pParentForTarget, 4422 const MediaList &aChildrenToReparent, 4423 MediumLockList *aMediumLockList, 4424 ComObjPtr <Progress> *aProgress, 4425 bool aWait, 4426 bool *pfNeedsGlobalSaveSettings) 4427 { 4428 AssertReturn(pTarget != NULL, E_FAIL); 4429 AssertReturn(pTarget != this, E_FAIL); 4430 AssertReturn(aMediumLockList != NULL, E_FAIL); 4431 AssertReturn(aProgress != NULL || aWait == true, E_FAIL); 4432 4433 AutoCaller autoCaller(this); 4434 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4435 4436 AutoCaller targetCaller(pTarget); 4437 AssertComRCReturnRC(targetCaller.rc()); 4438 4439 HRESULT rc = S_OK; 4440 ComObjPtr <Progress> pProgress; 4441 Medium::Task *pTask = NULL; 4442 4443 try 4444 { 4445 if (aProgress != NULL) 4446 { 4447 /* use the existing progress object... */ 4448 pProgress = *aProgress; 4449 4450 /* ...but create a new one if it is null */ 4451 if (pProgress.isNull()) 4452 { 4453 Utf8Str tgtName; 4454 { 4455 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS); 4456 tgtName = pTarget->getName(); 4457 } 4458 4459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4460 4461 pProgress.createObject(); 4462 rc = pProgress->init(m->pVirtualBox, 4463 static_cast<IMedium*>(this), 4464 BstrFmt(tr("Merging medium '%s' to '%s'"), 4465 getName().c_str(), 4466 tgtName.c_str()).raw(), 4467 TRUE /* aCancelable */); 4468 if (FAILED(rc)) 4469 throw rc; 4470 } 4471 } 4472 4473 /* setup task object to carry out the operation sync/async */ 4474 pTask = new Medium::MergeTask(this, pTarget, fMergeForward, 4475 pParentForTarget, aChildrenToReparent, 4476 pProgress, aMediumLockList, 4477 aWait /* fKeepMediumLockList */); 4478 rc = pTask->rc(); 4479 AssertComRC(rc); 4480 if (FAILED(rc)) 4481 throw rc; 4482 } 4483 catch (HRESULT aRC) { rc = aRC; } 4484 4485 if (SUCCEEDED(rc)) 4486 { 4487 if (aWait) 4488 rc = runNow(pTask, pfNeedsGlobalSaveSettings); 4489 else 4490 rc = startThread(pTask); 4491 4492 if (SUCCEEDED(rc) && aProgress != NULL) 4493 *aProgress = pProgress; 4494 } 4495 else if (pTask != NULL) 4496 delete pTask; 4497 4498 return rc; 4499 } 4500 4501 /** 4502 * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not 4503 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks 4504 * the medium objects in @a aChildrenToReparent. 4505 * 4506 * @param aChildrenToReparent List of children of the source which will have 4507 * to be reparented to the target after merge. 4508 * @param aMediumLockList Medium locking information. 4509 * 4510 * @note Locks the media from the chain for writing. 4511 */ 4512 void Medium::cancelMergeTo(const MediaList &aChildrenToReparent, 4513 MediumLockList *aMediumLockList) 4514 { 4515 AutoCaller autoCaller(this); 4516 AssertComRCReturnVoid(autoCaller.rc()); 4517 4518 AssertReturnVoid(aMediumLockList != NULL); 4519 4520 /* Revert media marked for deletion to previous state. */ 4521 HRESULT rc; 4522 MediumLockList::Base::const_iterator mediumListBegin = 4523 aMediumLockList->GetBegin(); 4524 MediumLockList::Base::const_iterator mediumListEnd = 4525 aMediumLockList->GetEnd(); 4526 for (MediumLockList::Base::const_iterator it = mediumListBegin; 4527 it != mediumListEnd; 4528 ++it) 4529 { 4530 const MediumLock &mediumLock = *it; 4531 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium(); 4532 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS); 4533 4534 if (pMedium->m->state == MediumState_Deleting) 4535 { 4536 rc = pMedium->unmarkForDeletion(); 4537 AssertComRC(rc); 4538 } 4539 } 4540 4541 /* the destructor will do the work */ 4542 delete aMediumLockList; 4543 4544 /* unlock the children which had to be reparented */ 4545 for (MediaList::const_iterator it = aChildrenToReparent.begin(); 4546 it != aChildrenToReparent.end(); 4547 ++it) 4548 { 4549 const ComObjPtr<Medium> &pMedium = *it; 4550 4551 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS); 4552 pMedium->UnlockWrite(NULL); 4553 } 4554 } 4555 4556 /** 4557 * Fix the parent UUID of all children to point to this medium as their 4558 * parent. 4559 */ 4560 HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent) 4561 { 4562 MediumLockList mediumLockList; 4563 HRESULT rc = createMediumLockList(true /* fFailIfInaccessible */, 4564 false /* fMediumLockWrite */, 4565 this, 4566 mediumLockList); 4567 AssertComRCReturnRC(rc); 4568 4569 try 4570 { 4571 PVBOXHDD hdd; 4572 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd); 4573 ComAssertRCThrow(vrc, E_FAIL); 4574 4575 try 4576 { 4577 MediumLockList::Base::iterator lockListBegin = 4578 mediumLockList.GetBegin(); 4579 MediumLockList::Base::iterator lockListEnd = 4580 mediumLockList.GetEnd(); 4581 for (MediumLockList::Base::iterator it = lockListBegin; 4582 it != lockListEnd; 4583 ++it) 4584 { 4585 MediumLock &mediumLock = *it; 4586 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium(); 4587 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS); 4588 4589 // open the medium 4590 vrc = VDOpen(hdd, 4591 pMedium->m->strFormat.c_str(), 4592 pMedium->m->strLocationFull.c_str(), 4593 VD_OPEN_FLAGS_READONLY, 4594 pMedium->m->vdImageIfaces); 4595 if (RT_FAILURE(vrc)) 4596 throw vrc; 4597 } 4598 4599 for (MediaList::const_iterator it = childrenToReparent.begin(); 4600 it != childrenToReparent.end(); 4601 ++it) 4602 { 4603 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */ 4604 vrc = VDOpen(hdd, 4605 (*it)->m->strFormat.c_str(), 4606 (*it)->m->strLocationFull.c_str(), 4607 VD_OPEN_FLAGS_INFO, 4608 (*it)->m->vdImageIfaces); 4609 if (RT_FAILURE(vrc)) 4610 throw vrc; 4611 4612 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw()); 4613 if (RT_FAILURE(vrc)) 4614 throw vrc; 4615 4616 vrc = VDClose(hdd, false /* fDelete */); 4617 if (RT_FAILURE(vrc)) 4618 throw vrc; 4619 4620 (*it)->UnlockWrite(NULL); 4621 } 4622 } 4623 catch (HRESULT aRC) { rc = aRC; } 4624 catch (int aVRC) 4625 { 4626 throw setError(E_FAIL, 4627 tr("Could not update medium UUID references to parent '%s' (%s)"), 4628 m->strLocationFull.c_str(), 4629 vdError(aVRC).c_str()); 4630 } 4631 4632 VDDestroy(hdd); 4633 } 4634 catch (HRESULT aRC) { rc = aRC; } 4635 4636 return rc; 4637 } 4638 4639 /** 4640 * Used by IAppliance to export disk images. 4641 * 4642 * @param aFilename Filename to create (UTF8). 4643 * @param aFormat Medium format for creating @a aFilename. 4644 * @param aVariant Which exact image format variant to use 4645 * for the destination image. 4646 * @param aVDImageIOCallbacks Pointer to the callback table for a 4647 * VDINTERFACEIO interface. May be NULL. 4648 * @param aVDImageIOUser Opaque data for the callbacks. 4649 * @param aProgress Progress object to use. 3458 4650 * @return 3459 */ 3460 MediumType_T Medium::getType() const 3461 { 3462 return m->type; 3463 } 3464 3465 /** 3466 * Returns a short version of the location attribute. 3467 * 3468 * @note Must be called from under this object's read or write lock. 3469 */ 3470 Utf8Str Medium::getName() 3471 { 3472 Utf8Str name = RTPathFilename(m->strLocationFull.c_str()); 3473 return name; 3474 } 3475 3476 /** 3477 * This adds the given UUID to the list of media registries in which this 3478 * medium should be registered. The UUID can either be a machine UUID, 3479 * to add a machine registry, or the global registry UUID as returned by 3480 * VirtualBox::getGlobalRegistryId(). 3481 * 3482 * Note that for hard disks, this method does nothing if the medium is 3483 * already in another registry to avoid having hard disks in more than 3484 * one registry, which causes trouble with keeping diff images in sync. 3485 * See getFirstRegistryMachineId() for details. 3486 * 3487 * @param id 3488 * @param pfNeedsSaveSettings If != NULL, is set to true if a new reference was added and saveSettings for either the machine or global XML is needed. 3489 * @return true if the registry was added. 3490 */ 3491 bool Medium::addRegistry(const Guid& id, 3492 bool *pfNeedsSaveSettings) 3493 { 4651 * @note The source format is defined by the Medium instance. 4652 */ 4653 HRESULT Medium::exportFile(const char *aFilename, 4654 const ComObjPtr<MediumFormat> &aFormat, 4655 MediumVariant_T aVariant, 4656 void *aVDImageIOCallbacks, void *aVDImageIOUser, 4657 const ComObjPtr<Progress> &aProgress) 4658 { 4659 AssertPtrReturn(aFilename, E_INVALIDARG); 4660 AssertReturn(!aFormat.isNull(), E_INVALIDARG); 4661 AssertReturn(!aProgress.isNull(), E_INVALIDARG); 4662 3494 4663 AutoCaller autoCaller(this); 3495 if (FAILED(autoCaller.rc())) return false; 3496 4664 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4665 4666 HRESULT rc = S_OK; 4667 Medium::Task *pTask = NULL; 4668 4669 try 4670 { 4671 // locking: we need the tree lock first because we access parent pointers 4672 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 4673 // and we need to write-lock the media involved 4674 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4675 4676 /* Build the source lock list. */ 4677 MediumLockList *pSourceMediumLockList(new MediumLockList()); 4678 rc = createMediumLockList(true /* fFailIfInaccessible */, 4679 false /* fMediumLockWrite */, 4680 NULL, 4681 *pSourceMediumLockList); 4682 if (FAILED(rc)) 4683 { 4684 delete pSourceMediumLockList; 4685 throw rc; 4686 } 4687 4688 rc = pSourceMediumLockList->Lock(); 4689 if (FAILED(rc)) 4690 { 4691 delete pSourceMediumLockList; 4692 throw setError(rc, 4693 tr("Failed to lock source media '%s'"), 4694 getLocationFull().c_str()); 4695 } 4696 4697 /* setup task object to carry out the operation asynchronously */ 4698 pTask = new Medium::ExportTask(this, aProgress, aFilename, aFormat, 4699 aVariant, aVDImageIOCallbacks, 4700 aVDImageIOUser, pSourceMediumLockList); 4701 rc = pTask->rc(); 4702 AssertComRC(rc); 4703 if (FAILED(rc)) 4704 throw rc; 4705 } 4706 catch (HRESULT aRC) { rc = aRC; } 4707 4708 if (SUCCEEDED(rc)) 4709 rc = startThread(pTask); 4710 else if (pTask != NULL) 4711 delete pTask; 4712 4713 return rc; 4714 } 4715 4716 /** 4717 * Used by IAppliance to import disk images. 4718 * 4719 * @param aFilename Filename to read (UTF8). 4720 * @param aFormat Medium format for reading @a aFilename. 4721 * @param aVariant Which exact image format variant to use 4722 * for the destination image. 4723 * @param aVDImageIOCallbacks Pointer to the callback table for a 4724 * VDINTERFACEIO interface. May be NULL. 4725 * @param aVDImageIOUser Opaque data for the callbacks. 4726 * @param aParent Parent medium. May be NULL. 4727 * @param aProgress Progress object to use. 4728 * @return 4729 * @note The destination format is defined by the Medium instance. 4730 */ 4731 HRESULT Medium::importFile(const char *aFilename, 4732 const ComObjPtr<MediumFormat> &aFormat, 4733 MediumVariant_T aVariant, 4734 void *aVDImageIOCallbacks, void *aVDImageIOUser, 4735 const ComObjPtr<Medium> &aParent, 4736 const ComObjPtr<Progress> &aProgress) 4737 { 4738 AssertPtrReturn(aFilename, E_INVALIDARG); 4739 AssertReturn(!aFormat.isNull(), E_INVALIDARG); 4740 AssertReturn(!aProgress.isNull(), E_INVALIDARG); 4741 4742 AutoCaller autoCaller(this); 4743 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4744 4745 HRESULT rc = S_OK; 4746 Medium::Task *pTask = NULL; 4747 4748 try 4749 { 4750 // locking: we need the tree lock first because we access parent pointers 4751 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 4752 // and we need to write-lock the media involved 4753 AutoMultiWriteLock2 alock(this, aParent COMMA_LOCKVAL_SRC_POS); 4754 4755 if ( m->state != MediumState_NotCreated 4756 && m->state != MediumState_Created) 4757 throw setStateError(); 4758 4759 /* Build the target lock list. */ 4760 MediumLockList *pTargetMediumLockList(new MediumLockList()); 4761 rc = createMediumLockList(true /* fFailIfInaccessible */, 4762 true /* fMediumLockWrite */, 4763 aParent, 4764 *pTargetMediumLockList); 4765 if (FAILED(rc)) 4766 { 4767 delete pTargetMediumLockList; 4768 throw rc; 4769 } 4770 4771 rc = pTargetMediumLockList->Lock(); 4772 if (FAILED(rc)) 4773 { 4774 delete pTargetMediumLockList; 4775 throw setError(rc, 4776 tr("Failed to lock target media '%s'"), 4777 getLocationFull().c_str()); 4778 } 4779 4780 /* setup task object to carry out the operation asynchronously */ 4781 pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat, 4782 aVariant, aVDImageIOCallbacks, 4783 aVDImageIOUser, aParent, 4784 pTargetMediumLockList); 4785 rc = pTask->rc(); 4786 AssertComRC(rc); 4787 if (FAILED(rc)) 4788 throw rc; 4789 4790 if (m->state == MediumState_NotCreated) 4791 m->state = MediumState_Creating; 4792 } 4793 catch (HRESULT aRC) { rc = aRC; } 4794 4795 if (SUCCEEDED(rc)) 4796 rc = startThread(pTask); 4797 else if (pTask != NULL) 4798 delete pTask; 4799 4800 return rc; 4801 } 4802 4803 //////////////////////////////////////////////////////////////////////////////// 4804 // 4805 // Private methods 4806 // 4807 //////////////////////////////////////////////////////////////////////////////// 4808 4809 /** 4810 * Queries information from the medium. 4811 * 4812 * As a result of this call, the accessibility state and data members such as 4813 * size and description will be updated with the current information. 4814 * 4815 * @note This method may block during a system I/O call that checks storage 4816 * accessibility. 4817 * 4818 * @note Locks medium tree for reading and writing (for new diff media checked 4819 * for the first time). Locks mParent for reading. Locks this object for 4820 * writing. 4821 * 4822 * @param fSetImageId Whether to reset the UUID contained in the image file to the UUID in the medium instance data (see SetIDs()) 4823 * @param fSetParentId Whether to reset the parent UUID contained in the image file to the parent UUID in the medium instance data (see SetIDs()) 4824 * @return 4825 */ 4826 HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId) 4827 { 3497 4828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3498 4829 3499 if ( m->devType == DeviceType_HardDisk 3500 && m->llRegistryIDs.size() > 0 4830 if ( m->state != MediumState_Created 4831 && m->state != MediumState_Inaccessible 4832 && m->state != MediumState_LockedRead) 4833 return E_FAIL; 4834 4835 HRESULT rc = S_OK; 4836 4837 int vrc = VINF_SUCCESS; 4838 4839 /* check if a blocking queryInfo() call is in progress on some other thread, 4840 * and wait for it to finish if so instead of querying data ourselves */ 4841 if (m->queryInfoRunning) 4842 { 4843 Assert( m->state == MediumState_LockedRead 4844 || m->state == MediumState_LockedWrite); 4845 4846 alock.leave(); 4847 vrc = RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT); 4848 alock.enter(); 4849 4850 AssertRC(vrc); 4851 4852 return S_OK; 4853 } 4854 4855 bool success = false; 4856 Utf8Str lastAccessError; 4857 4858 /* are we dealing with a new medium constructed using the existing 4859 * location? */ 4860 bool isImport = m->id.isEmpty(); 4861 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO; 4862 4863 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new 4864 * media because that would prevent necessary modifications 4865 * when opening media of some third-party formats for the first 4866 * time in VirtualBox (such as VMDK for which VDOpen() needs to 4867 * generate an UUID if it is missing) */ 4868 if ( (m->hddOpenMode == OpenReadOnly) 4869 || m->type == MediumType_Readonly 4870 || !isImport 3501 4871 ) 3502 return false; 3503 3504 // no need to add the UUID twice 3505 for (GuidList::const_iterator it = m->llRegistryIDs.begin(); 3506 it != m->llRegistryIDs.end(); 3507 ++it) 3508 { 3509 if ((*it) == id) 3510 return false; 3511 } 3512 3513 m->llRegistryIDs.push_back(id); 3514 if (pfNeedsSaveSettings) 3515 *pfNeedsSaveSettings = true; 3516 return true; 3517 } 3518 3519 /** 3520 * Returns true if id is in the list of media registries for this medium. 3521 * @param id 3522 * @return 3523 */ 3524 bool Medium::isInRegistry(const Guid& id) 3525 { 3526 AutoCaller autoCaller(this); 3527 if (FAILED(autoCaller.rc())) return false; 3528 3529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3530 3531 for (GuidList::const_iterator it = m->llRegistryIDs.begin(); 3532 it != m->llRegistryIDs.end(); 3533 ++it) 3534 { 3535 if (*it == id) 3536 return true; 3537 } 3538 3539 return false; 3540 } 3541 3542 /** 3543 * Internal method to return the medium's first registry machine (i.e. the machine in whose 3544 * machine XML this medium is listed). 3545 * 3546 * Every medium must now (4.0) reside in at least one media registry, which is identified by 3547 * a UUID. This is either a machine UUID if the machine is from 4.0 or newer, in which case 3548 * machines have their own media registries, or it is the pseudo-UUID of the VirtualBox 3549 * object if the machine is old and still needs the global registry in VirtualBox.xml. 3550 * 3551 * By definition, hard disks may only be in one media registry, in which all its children 3552 * will be stored as well. Otherwise we run into problems with having keep multiple registries 3553 * in sync. (This is the "cloned VM" case in which VM1 may link to the disks of VM2; in this 3554 * case, only VM2's registry is used for the disk in question.) 3555 * 3556 * ISOs and RAWs, by contrast, can be in more than one repository to make things easier for 3557 * the user. 3558 * 3559 * Must have caller + locking! 3560 * 3561 * @return 3562 */ 3563 const Guid& Medium::getFirstRegistryMachineId() const 3564 { 3565 return m->llRegistryIDs.front(); 4872 uOpenFlags |= VD_OPEN_FLAGS_READONLY; 4873 4874 /* Open shareable medium with the appropriate flags */ 4875 if (m->type == MediumType_Shareable) 4876 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE; 4877 4878 /* Lock the medium, which makes the behavior much more consistent */ 4879 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE)) 4880 rc = LockRead(NULL); 4881 else 4882 rc = LockWrite(NULL); 4883 if (FAILED(rc)) return rc; 4884 4885 /* Copies of the input state fields which are not read-only, 4886 * as we're dropping the lock. CAUTION: be extremely careful what 4887 * you do with the contents of this medium object, as you will 4888 * create races if there are concurrent changes. */ 4889 Utf8Str format(m->strFormat); 4890 Utf8Str location(m->strLocationFull); 4891 ComObjPtr<MediumFormat> formatObj = m->formatObj; 4892 4893 /* "Output" values which can't be set because the lock isn't held 4894 * at the time the values are determined. */ 4895 Guid mediumId = m->id; 4896 uint64_t mediumSize = 0; 4897 uint64_t mediumLogicalSize = 0; 4898 4899 /* Flag whether a base image has a non-zero parent UUID and thus 4900 * need repairing after it was closed again. */ 4901 bool fRepairImageZeroParentUuid = false; 4902 4903 /* leave the lock before a lengthy operation */ 4904 vrc = RTSemEventMultiReset(m->queryInfoSem); 4905 AssertRCReturn(vrc, E_FAIL); 4906 m->queryInfoRunning = true; 4907 alock.leave(); 4908 4909 try 4910 { 4911 /* skip accessibility checks for host drives */ 4912 if (m->hostDrive) 4913 { 4914 success = true; 4915 throw S_OK; 4916 } 4917 4918 PVBOXHDD hdd; 4919 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd); 4920 ComAssertRCThrow(vrc, E_FAIL); 4921 4922 try 4923 { 4924 /** @todo This kind of opening of media is assuming that diff 4925 * media can be opened as base media. Should be documented that 4926 * it must work for all medium format backends. */ 4927 vrc = VDOpen(hdd, 4928 format.c_str(), 4929 location.c_str(), 4930 uOpenFlags, 4931 m->vdImageIfaces); 4932 if (RT_FAILURE(vrc)) 4933 { 4934 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"), 4935 location.c_str(), vdError(vrc).c_str()); 4936 throw S_OK; 4937 } 4938 4939 if (formatObj->getCapabilities() & MediumFormatCapabilities_Uuid) 4940 { 4941 /* Modify the UUIDs if necessary. The associated fields are 4942 * not modified by other code, so no need to copy. */ 4943 if (fSetImageId) 4944 { 4945 vrc = VDSetUuid(hdd, 0, m->uuidImage.raw()); 4946 ComAssertRCThrow(vrc, E_FAIL); 4947 } 4948 if (fSetParentId) 4949 { 4950 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw()); 4951 ComAssertRCThrow(vrc, E_FAIL); 4952 } 4953 /* zap the information, these are no long-term members */ 4954 unconst(m->uuidImage).clear(); 4955 unconst(m->uuidParentImage).clear(); 4956 4957 /* check the UUID */ 4958 RTUUID uuid; 4959 vrc = VDGetUuid(hdd, 0, &uuid); 4960 ComAssertRCThrow(vrc, E_FAIL); 4961 4962 if (isImport) 4963 { 4964 mediumId = uuid; 4965 4966 if (mediumId.isEmpty() && (m->hddOpenMode == OpenReadOnly)) 4967 // only when importing a VDMK that has no UUID, create one in memory 4968 mediumId.create(); 4969 } 4970 else 4971 { 4972 Assert(!mediumId.isEmpty()); 4973 4974 if (mediumId != uuid) 4975 { 4976 lastAccessError = Utf8StrFmt( 4977 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"), 4978 &uuid, 4979 location.c_str(), 4980 mediumId.raw(), 4981 m->pVirtualBox->settingsFilePath().c_str()); 4982 throw S_OK; 4983 } 4984 } 4985 } 4986 else 4987 { 4988 /* the backend does not support storing UUIDs within the 4989 * underlying storage so use what we store in XML */ 4990 4991 /* generate an UUID for an imported UUID-less medium */ 4992 if (isImport) 4993 { 4994 if (fSetImageId) 4995 mediumId = m->uuidImage; 4996 else 4997 mediumId.create(); 4998 } 4999 } 5000 5001 /* get the medium variant */ 5002 unsigned uImageFlags; 5003 vrc = VDGetImageFlags(hdd, 0, &uImageFlags); 5004 ComAssertRCThrow(vrc, E_FAIL); 5005 m->variant = (MediumVariant_T)uImageFlags; 5006 5007 /* check/get the parent uuid and update corresponding state */ 5008 if (uImageFlags & VD_IMAGE_FLAGS_DIFF) 5009 { 5010 RTUUID parentId; 5011 vrc = VDGetParentUuid(hdd, 0, &parentId); 5012 ComAssertRCThrow(vrc, E_FAIL); 5013 5014 /* streamOptimized VMDK images are only accepted as base 5015 * images, as this allows automatic repair of OVF appliances. 5016 * Since such images don't support random writes they will not 5017 * be created for diff images. Only an overly smart user might 5018 * manually create this case. Too bad for him. */ 5019 if ( isImport 5020 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)) 5021 { 5022 /* the parent must be known to us. Note that we freely 5023 * call locking methods of mVirtualBox and parent, as all 5024 * relevant locks must be already held. There may be no 5025 * concurrent access to the just opened medium on other 5026 * threads yet (and init() will fail if this method reports 5027 * MediumState_Inaccessible) */ 5028 5029 Guid id = parentId; 5030 ComObjPtr<Medium> pParent; 5031 rc = m->pVirtualBox->findHardDiskById(id, false /* aSetError */, &pParent); 5032 if (FAILED(rc)) 5033 { 5034 lastAccessError = Utf8StrFmt( 5035 tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"), 5036 &parentId, location.c_str(), 5037 m->pVirtualBox->settingsFilePath().c_str()); 5038 throw S_OK; 5039 } 5040 5041 /* we set mParent & children() */ 5042 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 5043 5044 Assert(m->pParent.isNull()); 5045 m->pParent = pParent; 5046 m->pParent->m->llChildren.push_back(this); 5047 } 5048 else 5049 { 5050 /* we access mParent */ 5051 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 5052 5053 /* check that parent UUIDs match. Note that there's no need 5054 * for the parent's AutoCaller (our lifetime is bound to 5055 * it) */ 5056 5057 if (m->pParent.isNull()) 5058 { 5059 /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14 5060 * and 3.1.0-3.1.8 there are base images out there 5061 * which have a non-zero parent UUID. No point in 5062 * complaining about them, instead automatically 5063 * repair the problem. Later we can bring back the 5064 * error message, but we should wait until really 5065 * most users have repaired their images, either with 5066 * VBoxFixHdd or this way. */ 5067 #if 1 5068 fRepairImageZeroParentUuid = true; 5069 #else /* 0 */ 5070 lastAccessError = Utf8StrFmt( 5071 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"), 5072 location.c_str(), 5073 m->pVirtualBox->settingsFilePath().c_str()); 5074 throw S_OK; 5075 #endif /* 0 */ 5076 } 5077 5078 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS); 5079 if ( !fRepairImageZeroParentUuid 5080 && m->pParent->getState() != MediumState_Inaccessible 5081 && m->pParent->getId() != parentId) 5082 { 5083 lastAccessError = Utf8StrFmt( 5084 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"), 5085 &parentId, location.c_str(), 5086 m->pParent->getId().raw(), 5087 m->pVirtualBox->settingsFilePath().c_str()); 5088 throw S_OK; 5089 } 5090 5091 /// @todo NEWMEDIA what to do if the parent is not 5092 /// accessible while the diff is? Probably nothing. The 5093 /// real code will detect the mismatch anyway. 5094 } 5095 } 5096 5097 mediumSize = VDGetFileSize(hdd, 0); 5098 mediumLogicalSize = VDGetSize(hdd, 0); 5099 5100 success = true; 5101 } 5102 catch (HRESULT aRC) 5103 { 5104 rc = aRC; 5105 } 5106 5107 VDDestroy(hdd); 5108 } 5109 catch (HRESULT aRC) 5110 { 5111 rc = aRC; 5112 } 5113 5114 alock.enter(); 5115 5116 if (isImport) 5117 unconst(m->id) = mediumId; 5118 5119 if (success) 5120 { 5121 m->size = mediumSize; 5122 m->logicalSize = mediumLogicalSize; 5123 m->strLastAccessError.setNull(); 5124 } 5125 else 5126 { 5127 m->strLastAccessError = lastAccessError; 5128 LogWarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n", 5129 location.c_str(), m->strLastAccessError.c_str(), 5130 rc, vrc)); 5131 } 5132 5133 /* inform other callers if there are any */ 5134 RTSemEventMultiSignal(m->queryInfoSem); 5135 m->queryInfoRunning = false; 5136 5137 /* Set the proper state according to the result of the check */ 5138 if (success) 5139 m->preLockState = MediumState_Created; 5140 else 5141 m->preLockState = MediumState_Inaccessible; 5142 5143 HRESULT rc2; 5144 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE)) 5145 rc2 = UnlockRead(NULL); 5146 else 5147 rc2 = UnlockWrite(NULL); 5148 if (SUCCEEDED(rc) && FAILED(rc2)) 5149 rc = rc2; 5150 if (FAILED(rc)) return rc; 5151 5152 /* If this is a base image which incorrectly has a parent UUID set, 5153 * repair the image now by zeroing the parent UUID. This is only done 5154 * when we have structural information from a config file, on import 5155 * this is not possible. If someone would accidentally call openMedium 5156 * with a diff image before the base is registered this would destroy 5157 * the diff. Not acceptable. */ 5158 if (fRepairImageZeroParentUuid) 5159 { 5160 rc = LockWrite(NULL); 5161 if (FAILED(rc)) return rc; 5162 5163 alock.leave(); 5164 5165 try 5166 { 5167 PVBOXHDD hdd; 5168 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd); 5169 ComAssertRCThrow(vrc, E_FAIL); 5170 5171 try 5172 { 5173 vrc = VDOpen(hdd, 5174 format.c_str(), 5175 location.c_str(), 5176 uOpenFlags & ~VD_OPEN_FLAGS_READONLY, 5177 m->vdImageIfaces); 5178 if (RT_FAILURE(vrc)) 5179 throw S_OK; 5180 5181 RTUUID zeroParentUuid; 5182 RTUuidClear(&zeroParentUuid); 5183 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid); 5184 ComAssertRCThrow(vrc, E_FAIL); 5185 } 5186 catch (HRESULT aRC) 5187 { 5188 rc = aRC; 5189 } 5190 5191 VDDestroy(hdd); 5192 } 5193 catch (HRESULT aRC) 5194 { 5195 rc = aRC; 5196 } 5197 5198 alock.enter(); 5199 5200 rc = UnlockWrite(NULL); 5201 if (SUCCEEDED(rc) && FAILED(rc2)) 5202 rc = rc2; 5203 if (FAILED(rc)) return rc; 5204 } 5205 5206 return rc; 5207 } 5208 5209 /** 5210 * Performs extra checks if the medium can be closed and returns S_OK in 5211 * this case. Otherwise, returns a respective error message. Called by 5212 * Close() under the medium tree lock and the medium lock. 5213 * 5214 * @note Also reused by Medium::Reset(). 5215 * 5216 * @note Caller must hold the media tree write lock! 5217 */ 5218 HRESULT Medium::canClose() 5219 { 5220 Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread()); 5221 5222 if (getChildren().size() != 0) 5223 return setError(VBOX_E_OBJECT_IN_USE, 5224 tr("Cannot close medium '%s' because it has %d child media"), 5225 m->strLocationFull.c_str(), getChildren().size()); 5226 5227 return S_OK; 5228 } 5229 5230 /** 5231 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock. 5232 * 5233 * This calls either VirtualBox::unregisterImage or VirtualBox::unregisterHardDisk depending 5234 * on the device type of this medium. 5235 * 5236 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true 5237 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed. 5238 * 5239 * @note Caller must have locked the media tree lock for writing! 5240 */ 5241 HRESULT Medium::unregisterWithVirtualBox(bool *pfNeedsGlobalSaveSettings) 5242 { 5243 /* Note that we need to de-associate ourselves from the parent to let 5244 * unregisterHardDisk() properly save the registry */ 5245 5246 /* we modify mParent and access children */ 5247 Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread()); 5248 5249 Medium *pParentBackup = m->pParent; 5250 AssertReturn(getChildren().size() == 0, E_FAIL); 5251 if (m->pParent) 5252 deparent(); 5253 5254 HRESULT rc = E_FAIL; 5255 switch (m->devType) 5256 { 5257 case DeviceType_DVD: 5258 rc = m->pVirtualBox->unregisterImage(this, DeviceType_DVD, pfNeedsGlobalSaveSettings); 5259 break; 5260 5261 case DeviceType_Floppy: 5262 rc = m->pVirtualBox->unregisterImage(this, DeviceType_Floppy, pfNeedsGlobalSaveSettings); 5263 break; 5264 5265 case DeviceType_HardDisk: 5266 rc = m->pVirtualBox->unregisterHardDisk(this, pfNeedsGlobalSaveSettings); 5267 break; 5268 5269 default: 5270 break; 5271 } 5272 5273 if (FAILED(rc)) 5274 { 5275 if (pParentBackup) 5276 { 5277 // re-associate with the parent as we are still relatives in the registry 5278 m->pParent = pParentBackup; 5279 m->pParent->m->llChildren.push_back(this); 5280 } 5281 } 5282 5283 return rc; 5284 } 5285 5286 /** 5287 * Sets the extended error info according to the current media state. 5288 * 5289 * @note Must be called from under this object's write or read lock. 5290 */ 5291 HRESULT Medium::setStateError() 5292 { 5293 HRESULT rc = E_FAIL; 5294 5295 switch (m->state) 5296 { 5297 case MediumState_NotCreated: 5298 { 5299 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 5300 tr("Storage for the medium '%s' is not created"), 5301 m->strLocationFull.c_str()); 5302 break; 5303 } 5304 case MediumState_Created: 5305 { 5306 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 5307 tr("Storage for the medium '%s' is already created"), 5308 m->strLocationFull.c_str()); 5309 break; 5310 } 5311 case MediumState_LockedRead: 5312 { 5313 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 5314 tr("Medium '%s' is locked for reading by another task"), 5315 m->strLocationFull.c_str()); 5316 break; 5317 } 5318 case MediumState_LockedWrite: 5319 { 5320 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 5321 tr("Medium '%s' is locked for writing by another task"), 5322 m->strLocationFull.c_str()); 5323 break; 5324 } 5325 case MediumState_Inaccessible: 5326 { 5327 /* be in sync with Console::powerUpThread() */ 5328 if (!m->strLastAccessError.isEmpty()) 5329 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 5330 tr("Medium '%s' is not accessible. %s"), 5331 m->strLocationFull.c_str(), m->strLastAccessError.c_str()); 5332 else 5333 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 5334 tr("Medium '%s' is not accessible"), 5335 m->strLocationFull.c_str()); 5336 break; 5337 } 5338 case MediumState_Creating: 5339 { 5340 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 5341 tr("Storage for the medium '%s' is being created"), 5342 m->strLocationFull.c_str()); 5343 break; 5344 } 5345 case MediumState_Deleting: 5346 { 5347 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 5348 tr("Storage for the medium '%s' is being deleted"), 5349 m->strLocationFull.c_str()); 5350 break; 5351 } 5352 default: 5353 { 5354 AssertFailed(); 5355 break; 5356 } 5357 } 5358 5359 return rc; 3566 5360 } 3567 5361 … … 3736 5530 3737 5531 /** 3738 * Queries information from the medium. 3739 * 3740 * As a result of this call, the accessibility state and data members such as 3741 * size and description will be updated with the current information. 3742 * 3743 * @note This method may block during a system I/O call that checks storage 3744 * accessibility. 3745 * 3746 * @note Locks medium tree for reading and writing (for new diff media checked 3747 * for the first time). Locks mParent for reading. Locks this object for 3748 * writing. 3749 * 3750 * @param fSetImageId Whether to reset the UUID contained in the image file to the UUID in the medium instance data (see SetIDs()) 3751 * @param fSetParentId Whether to reset the parent UUID contained in the image file to the parent UUID in the medium instance data (see SetIDs()) 3752 * @return 3753 */ 3754 HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId) 3755 { 3756 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3757 3758 if ( m->state != MediumState_Created 3759 && m->state != MediumState_Inaccessible 3760 && m->state != MediumState_LockedRead) 3761 return E_FAIL; 3762 3763 HRESULT rc = S_OK; 3764 3765 int vrc = VINF_SUCCESS; 3766 3767 /* check if a blocking queryInfo() call is in progress on some other thread, 3768 * and wait for it to finish if so instead of querying data ourselves */ 3769 if (m->queryInfoRunning) 3770 { 3771 Assert( m->state == MediumState_LockedRead 3772 || m->state == MediumState_LockedWrite); 3773 3774 alock.leave(); 3775 vrc = RTSemEventMultiWait(m->queryInfoSem, RT_INDEFINITE_WAIT); 3776 alock.enter(); 3777 3778 AssertRC(vrc); 3779 3780 return S_OK; 3781 } 3782 3783 bool success = false; 3784 Utf8Str lastAccessError; 3785 3786 /* are we dealing with a new medium constructed using the existing 3787 * location? */ 3788 bool isImport = m->id.isEmpty(); 3789 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO; 3790 3791 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new 3792 * media because that would prevent necessary modifications 3793 * when opening media of some third-party formats for the first 3794 * time in VirtualBox (such as VMDK for which VDOpen() needs to 3795 * generate an UUID if it is missing) */ 3796 if ( (m->hddOpenMode == OpenReadOnly) 3797 || m->type == MediumType_Readonly 3798 || !isImport 3799 ) 3800 uOpenFlags |= VD_OPEN_FLAGS_READONLY; 3801 3802 /* Open shareable medium with the appropriate flags */ 3803 if (m->type == MediumType_Shareable) 3804 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE; 3805 3806 /* Lock the medium, which makes the behavior much more consistent */ 3807 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE)) 3808 rc = LockRead(NULL); 3809 else 3810 rc = LockWrite(NULL); 3811 if (FAILED(rc)) return rc; 3812 3813 /* Copies of the input state fields which are not read-only, 3814 * as we're dropping the lock. CAUTION: be extremely careful what 3815 * you do with the contents of this medium object, as you will 3816 * create races if there are concurrent changes. */ 3817 Utf8Str format(m->strFormat); 3818 Utf8Str location(m->strLocationFull); 3819 ComObjPtr<MediumFormat> formatObj = m->formatObj; 3820 3821 /* "Output" values which can't be set because the lock isn't held 3822 * at the time the values are determined. */ 3823 Guid mediumId = m->id; 3824 uint64_t mediumSize = 0; 3825 uint64_t mediumLogicalSize = 0; 3826 3827 /* Flag whether a base image has a non-zero parent UUID and thus 3828 * need repairing after it was closed again. */ 3829 bool fRepairImageZeroParentUuid = false; 3830 3831 /* leave the lock before a lengthy operation */ 3832 vrc = RTSemEventMultiReset(m->queryInfoSem); 3833 AssertRCReturn(vrc, E_FAIL); 3834 m->queryInfoRunning = true; 3835 alock.leave(); 3836 3837 try 3838 { 3839 /* skip accessibility checks for host drives */ 3840 if (m->hostDrive) 3841 { 3842 success = true; 3843 throw S_OK; 3844 } 3845 3846 PVBOXHDD hdd; 3847 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd); 3848 ComAssertRCThrow(vrc, E_FAIL); 3849 3850 try 3851 { 3852 /** @todo This kind of opening of media is assuming that diff 3853 * media can be opened as base media. Should be documented that 3854 * it must work for all medium format backends. */ 3855 vrc = VDOpen(hdd, 3856 format.c_str(), 3857 location.c_str(), 3858 uOpenFlags, 3859 m->vdImageIfaces); 3860 if (RT_FAILURE(vrc)) 3861 { 3862 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"), 3863 location.c_str(), vdError(vrc).c_str()); 3864 throw S_OK; 3865 } 3866 3867 if (formatObj->getCapabilities() & MediumFormatCapabilities_Uuid) 3868 { 3869 /* Modify the UUIDs if necessary. The associated fields are 3870 * not modified by other code, so no need to copy. */ 3871 if (fSetImageId) 3872 { 3873 vrc = VDSetUuid(hdd, 0, m->uuidImage.raw()); 3874 ComAssertRCThrow(vrc, E_FAIL); 3875 } 3876 if (fSetParentId) 3877 { 3878 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw()); 3879 ComAssertRCThrow(vrc, E_FAIL); 3880 } 3881 /* zap the information, these are no long-term members */ 3882 unconst(m->uuidImage).clear(); 3883 unconst(m->uuidParentImage).clear(); 3884 3885 /* check the UUID */ 3886 RTUUID uuid; 3887 vrc = VDGetUuid(hdd, 0, &uuid); 3888 ComAssertRCThrow(vrc, E_FAIL); 3889 3890 if (isImport) 3891 { 3892 mediumId = uuid; 3893 3894 if (mediumId.isEmpty() && (m->hddOpenMode == OpenReadOnly)) 3895 // only when importing a VDMK that has no UUID, create one in memory 3896 mediumId.create(); 3897 } 3898 else 3899 { 3900 Assert(!mediumId.isEmpty()); 3901 3902 if (mediumId != uuid) 3903 { 3904 lastAccessError = Utf8StrFmt( 3905 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"), 3906 &uuid, 3907 location.c_str(), 3908 mediumId.raw(), 3909 m->pVirtualBox->settingsFilePath().c_str()); 3910 throw S_OK; 3911 } 3912 } 3913 } 3914 else 3915 { 3916 /* the backend does not support storing UUIDs within the 3917 * underlying storage so use what we store in XML */ 3918 3919 /* generate an UUID for an imported UUID-less medium */ 3920 if (isImport) 3921 { 3922 if (fSetImageId) 3923 mediumId = m->uuidImage; 3924 else 3925 mediumId.create(); 3926 } 3927 } 3928 3929 /* get the medium variant */ 3930 unsigned uImageFlags; 3931 vrc = VDGetImageFlags(hdd, 0, &uImageFlags); 3932 ComAssertRCThrow(vrc, E_FAIL); 3933 m->variant = (MediumVariant_T)uImageFlags; 3934 3935 /* check/get the parent uuid and update corresponding state */ 3936 if (uImageFlags & VD_IMAGE_FLAGS_DIFF) 3937 { 3938 RTUUID parentId; 3939 vrc = VDGetParentUuid(hdd, 0, &parentId); 3940 ComAssertRCThrow(vrc, E_FAIL); 3941 3942 /* streamOptimized VMDK images are only accepted as base 3943 * images, as this allows automatic repair of OVF appliances. 3944 * Since such images don't support random writes they will not 3945 * be created for diff images. Only an overly smart user might 3946 * manually create this case. Too bad for him. */ 3947 if ( isImport 3948 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)) 3949 { 3950 /* the parent must be known to us. Note that we freely 3951 * call locking methods of mVirtualBox and parent, as all 3952 * relevant locks must be already held. There may be no 3953 * concurrent access to the just opened medium on other 3954 * threads yet (and init() will fail if this method reports 3955 * MediumState_Inaccessible) */ 3956 3957 Guid id = parentId; 3958 ComObjPtr<Medium> pParent; 3959 rc = m->pVirtualBox->findHardDiskById(id, false /* aSetError */, &pParent); 3960 if (FAILED(rc)) 3961 { 3962 lastAccessError = Utf8StrFmt( 3963 tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"), 3964 &parentId, location.c_str(), 3965 m->pVirtualBox->settingsFilePath().c_str()); 3966 throw S_OK; 3967 } 3968 3969 /* we set mParent & children() */ 3970 AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 3971 3972 Assert(m->pParent.isNull()); 3973 m->pParent = pParent; 3974 m->pParent->m->llChildren.push_back(this); 3975 } 3976 else 3977 { 3978 /* we access mParent */ 3979 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 3980 3981 /* check that parent UUIDs match. Note that there's no need 3982 * for the parent's AutoCaller (our lifetime is bound to 3983 * it) */ 3984 3985 if (m->pParent.isNull()) 3986 { 3987 /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14 3988 * and 3.1.0-3.1.8 there are base images out there 3989 * which have a non-zero parent UUID. No point in 3990 * complaining about them, instead automatically 3991 * repair the problem. Later we can bring back the 3992 * error message, but we should wait until really 3993 * most users have repaired their images, either with 3994 * VBoxFixHdd or this way. */ 3995 #if 1 3996 fRepairImageZeroParentUuid = true; 3997 #else /* 0 */ 3998 lastAccessError = Utf8StrFmt( 3999 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"), 4000 location.c_str(), 4001 m->pVirtualBox->settingsFilePath().c_str()); 4002 throw S_OK; 4003 #endif /* 0 */ 4004 } 4005 4006 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS); 4007 if ( !fRepairImageZeroParentUuid 4008 && m->pParent->getState() != MediumState_Inaccessible 4009 && m->pParent->getId() != parentId) 4010 { 4011 lastAccessError = Utf8StrFmt( 4012 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"), 4013 &parentId, location.c_str(), 4014 m->pParent->getId().raw(), 4015 m->pVirtualBox->settingsFilePath().c_str()); 4016 throw S_OK; 4017 } 4018 4019 /// @todo NEWMEDIA what to do if the parent is not 4020 /// accessible while the diff is? Probably nothing. The 4021 /// real code will detect the mismatch anyway. 4022 } 4023 } 4024 4025 mediumSize = VDGetFileSize(hdd, 0); 4026 mediumLogicalSize = VDGetSize(hdd, 0); 4027 4028 success = true; 4029 } 4030 catch (HRESULT aRC) 4031 { 4032 rc = aRC; 4033 } 4034 4035 VDDestroy(hdd); 4036 } 4037 catch (HRESULT aRC) 4038 { 4039 rc = aRC; 4040 } 4041 4042 alock.enter(); 4043 4044 if (isImport) 4045 unconst(m->id) = mediumId; 4046 4047 if (success) 4048 { 4049 m->size = mediumSize; 4050 m->logicalSize = mediumLogicalSize; 4051 m->strLastAccessError.setNull(); 4052 } 4053 else 4054 { 4055 m->strLastAccessError = lastAccessError; 4056 LogWarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n", 4057 location.c_str(), m->strLastAccessError.c_str(), 4058 rc, vrc)); 4059 } 4060 4061 /* inform other callers if there are any */ 4062 RTSemEventMultiSignal(m->queryInfoSem); 4063 m->queryInfoRunning = false; 4064 4065 /* Set the proper state according to the result of the check */ 4066 if (success) 4067 m->preLockState = MediumState_Created; 4068 else 4069 m->preLockState = MediumState_Inaccessible; 4070 4071 HRESULT rc2; 4072 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE)) 4073 rc2 = UnlockRead(NULL); 4074 else 4075 rc2 = UnlockWrite(NULL); 4076 if (SUCCEEDED(rc) && FAILED(rc2)) 4077 rc = rc2; 4078 if (FAILED(rc)) return rc; 4079 4080 /* If this is a base image which incorrectly has a parent UUID set, 4081 * repair the image now by zeroing the parent UUID. This is only done 4082 * when we have structural information from a config file, on import 4083 * this is not possible. If someone would accidentally call openMedium 4084 * with a diff image before the base is registered this would destroy 4085 * the diff. Not acceptable. */ 4086 if (fRepairImageZeroParentUuid) 4087 { 4088 rc = LockWrite(NULL); 4089 if (FAILED(rc)) return rc; 4090 4091 alock.leave(); 4092 4093 try 4094 { 4095 PVBOXHDD hdd; 4096 vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd); 4097 ComAssertRCThrow(vrc, E_FAIL); 4098 4099 try 4100 { 4101 vrc = VDOpen(hdd, 4102 format.c_str(), 4103 location.c_str(), 4104 uOpenFlags & ~VD_OPEN_FLAGS_READONLY, 4105 m->vdImageIfaces); 4106 if (RT_FAILURE(vrc)) 4107 throw S_OK; 4108 4109 RTUUID zeroParentUuid; 4110 RTUuidClear(&zeroParentUuid); 4111 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid); 4112 ComAssertRCThrow(vrc, E_FAIL); 4113 } 4114 catch (HRESULT aRC) 4115 { 4116 rc = aRC; 4117 } 4118 4119 VDDestroy(hdd); 4120 } 4121 catch (HRESULT aRC) 4122 { 4123 rc = aRC; 4124 } 4125 4126 alock.enter(); 4127 4128 rc = UnlockWrite(NULL); 4129 if (SUCCEEDED(rc) && FAILED(rc2)) 4130 rc = rc2; 4131 if (FAILED(rc)) return rc; 4132 } 4133 4134 return rc; 4135 } 4136 4137 /** 4138 * Sets the extended error info according to the current media state. 4139 * 4140 * @note Must be called from under this object's write or read lock. 4141 */ 4142 HRESULT Medium::setStateError() 4143 { 4144 HRESULT rc = E_FAIL; 4145 4146 switch (m->state) 4147 { 4148 case MediumState_NotCreated: 4149 { 4150 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 4151 tr("Storage for the medium '%s' is not created"), 4152 m->strLocationFull.c_str()); 4153 break; 4154 } 4155 case MediumState_Created: 4156 { 4157 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 4158 tr("Storage for the medium '%s' is already created"), 4159 m->strLocationFull.c_str()); 4160 break; 4161 } 4162 case MediumState_LockedRead: 4163 { 4164 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 4165 tr("Medium '%s' is locked for reading by another task"), 4166 m->strLocationFull.c_str()); 4167 break; 4168 } 4169 case MediumState_LockedWrite: 4170 { 4171 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 4172 tr("Medium '%s' is locked for writing by another task"), 4173 m->strLocationFull.c_str()); 4174 break; 4175 } 4176 case MediumState_Inaccessible: 4177 { 4178 /* be in sync with Console::powerUpThread() */ 4179 if (!m->strLastAccessError.isEmpty()) 4180 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 4181 tr("Medium '%s' is not accessible. %s"), 4182 m->strLocationFull.c_str(), m->strLastAccessError.c_str()); 4183 else 4184 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 4185 tr("Medium '%s' is not accessible"), 4186 m->strLocationFull.c_str()); 4187 break; 4188 } 4189 case MediumState_Creating: 4190 { 4191 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 4192 tr("Storage for the medium '%s' is being created"), 4193 m->strLocationFull.c_str()); 4194 break; 4195 } 4196 case MediumState_Deleting: 4197 { 4198 rc = setError(VBOX_E_INVALID_OBJECT_STATE, 4199 tr("Storage for the medium '%s' is being deleted"), 4200 m->strLocationFull.c_str()); 4201 break; 4202 } 4203 default: 4204 { 4205 AssertFailed(); 4206 break; 4207 } 4208 } 4209 4210 return rc; 5532 * Checks that the format ID is valid and sets it on success. 5533 * 5534 * Note that this method will caller-reference the format object on success! 5535 * This reference must be released somewhere to let the MediumFormat object be 5536 * uninitialized. 5537 * 5538 * @note Must be called from under this object's write lock. 5539 */ 5540 HRESULT Medium::setFormat(const Utf8Str &aFormat) 5541 { 5542 /* get the format object first */ 5543 { 5544 SystemProperties *pSysProps = m->pVirtualBox->getSystemProperties(); 5545 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS); 5546 5547 unconst(m->formatObj) = pSysProps->mediumFormat(aFormat); 5548 if (m->formatObj.isNull()) 5549 return setError(E_INVALIDARG, 5550 tr("Invalid medium storage format '%s'"), 5551 aFormat.c_str()); 5552 5553 /* reference the format permanently to prevent its unexpected 5554 * uninitialization */ 5555 HRESULT rc = m->formatObj->addCaller(); 5556 AssertComRCReturnRC(rc); 5557 5558 /* get properties (preinsert them as keys in the map). Note that the 5559 * map doesn't grow over the object life time since the set of 5560 * properties is meant to be constant. */ 5561 5562 Assert(m->mapProperties.empty()); 5563 5564 for (MediumFormat::PropertyList::const_iterator it = m->formatObj->getProperties().begin(); 5565 it != m->formatObj->getProperties().end(); 5566 ++it) 5567 { 5568 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty)); 5569 } 5570 } 5571 5572 unconst(m->strFormat) = aFormat; 5573 5574 return S_OK; 4211 5575 } 4212 5576 … … 4262 5626 4263 5627 /** 4264 * Implementation for the public Medium::Close() with the exception of calling4265 * VirtualBox::saveSettings(), in case someone wants to call this for several4266 * media.4267 *4268 * After this returns with success, uninit() has been called on the medium, and4269 * the object is no longer usable ("not ready" state).4270 *4271 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true4272 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.4273 * This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,4274 * and this parameter is ignored.4275 * @param autoCaller AutoCaller instance which must have been created on the caller's stack for this medium. This gets released here4276 * upon which the Medium instance gets uninitialized.4277 * @return4278 */4279 HRESULT Medium::close(bool *pfNeedsGlobalSaveSettings, AutoCaller &autoCaller)4280 {4281 // we're accessing parent/child and backrefs, so lock the tree first, then ourselves4282 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),4283 this->lockHandle()4284 COMMA_LOCKVAL_SRC_POS);4285 4286 LogFlowFunc(("ENTER for %s\n", getLocationFull().c_str()));4287 4288 bool wasCreated = true;4289 4290 switch (m->state)4291 {4292 case MediumState_NotCreated:4293 wasCreated = false;4294 break;4295 case MediumState_Created:4296 case MediumState_Inaccessible:4297 break;4298 default:4299 return setStateError();4300 }4301 4302 if (m->backRefs.size() != 0)4303 return setError(VBOX_E_OBJECT_IN_USE,4304 tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines"),4305 m->strLocationFull.c_str(), m->backRefs.size());4306 4307 // perform extra media-dependent close checks4308 HRESULT rc = canClose();4309 if (FAILED(rc)) return rc;4310 4311 if (wasCreated)4312 {4313 // remove from the list of known media before performing actual4314 // uninitialization (to keep the media registry consistent on4315 // failure to do so)4316 rc = unregisterWithVirtualBox(pfNeedsGlobalSaveSettings);4317 if (FAILED(rc)) return rc;4318 }4319 4320 // leave the AutoCaller, as otherwise uninit() will simply hang4321 autoCaller.release();4322 4323 // Keep the locks held until after uninit, as otherwise the consistency4324 // of the medium tree cannot be guaranteed.4325 uninit();4326 4327 LogFlowFuncLeave();4328 4329 return rc;4330 }4331 4332 /**4333 * Deletes the medium storage unit.4334 *4335 * If @a aProgress is not NULL but the object it points to is @c null then a new4336 * progress object will be created and assigned to @a *aProgress on success,4337 * otherwise the existing progress object is used. If Progress is NULL, then no4338 * progress object is created/used at all.4339 *4340 * When @a aWait is @c false, this method will create a thread to perform the4341 * delete operation asynchronously and will return immediately. Otherwise, it4342 * will perform the operation on the calling thread and will not return to the4343 * caller until the operation is completed. Note that @a aProgress cannot be4344 * NULL when @a aWait is @c false (this method will assert in this case).4345 *4346 * @param aProgress Where to find/store a Progress object to track operation4347 * completion.4348 * @param aWait @c true if this method should block instead of creating4349 * an asynchronous thread.4350 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true4351 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.4352 * This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,4353 * and this parameter is ignored.4354 *4355 * @note Locks mVirtualBox and this object for writing. Locks medium tree for4356 * writing.4357 */4358 HRESULT Medium::deleteStorage(ComObjPtr<Progress> *aProgress,4359 bool aWait,4360 bool *pfNeedsGlobalSaveSettings)4361 {4362 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);4363 4364 AutoCaller autoCaller(this);4365 if (FAILED(autoCaller.rc())) return autoCaller.rc();4366 4367 HRESULT rc = S_OK;4368 ComObjPtr<Progress> pProgress;4369 Medium::Task *pTask = NULL;4370 4371 try4372 {4373 /* we're accessing the media tree, and canClose() needs it too */4374 AutoMultiWriteLock2 multilock(&m->pVirtualBox->getMediaTreeLockHandle(),4375 this->lockHandle()4376 COMMA_LOCKVAL_SRC_POS);4377 LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() ));4378 4379 if ( !(m->formatObj->getCapabilities() & ( MediumFormatCapabilities_CreateDynamic4380 | MediumFormatCapabilities_CreateFixed)))4381 throw setError(VBOX_E_NOT_SUPPORTED,4382 tr("Medium format '%s' does not support storage deletion"),4383 m->strFormat.c_str());4384 4385 /* Note that we are fine with Inaccessible state too: a) for symmetry4386 * with create calls and b) because it doesn't really harm to try, if4387 * it is really inaccessible, the delete operation will fail anyway.4388 * Accepting Inaccessible state is especially important because all4389 * registered media are initially Inaccessible upon VBoxSVC startup4390 * until COMGETTER(RefreshState) is called. Accept Deleting state4391 * because some callers need to put the medium in this state early4392 * to prevent races. */4393 switch (m->state)4394 {4395 case MediumState_Created:4396 case MediumState_Deleting:4397 case MediumState_Inaccessible:4398 break;4399 default:4400 throw setStateError();4401 }4402 4403 if (m->backRefs.size() != 0)4404 {4405 Utf8Str strMachines;4406 for (BackRefList::const_iterator it = m->backRefs.begin();4407 it != m->backRefs.end();4408 ++it)4409 {4410 const BackRef &b = *it;4411 if (strMachines.length())4412 strMachines.append(", ");4413 strMachines.append(b.machineId.toString().c_str());4414 }4415 #ifdef DEBUG4416 dumpBackRefs();4417 #endif4418 throw setError(VBOX_E_OBJECT_IN_USE,4419 tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"),4420 m->strLocationFull.c_str(),4421 m->backRefs.size(),4422 strMachines.c_str());4423 }4424 4425 rc = canClose();4426 if (FAILED(rc))4427 throw rc;4428 4429 /* go to Deleting state, so that the medium is not actually locked */4430 if (m->state != MediumState_Deleting)4431 {4432 rc = markForDeletion();4433 if (FAILED(rc))4434 throw rc;4435 }4436 4437 /* Build the medium lock list. */4438 MediumLockList *pMediumLockList(new MediumLockList());4439 rc = createMediumLockList(true /* fFailIfInaccessible */,4440 true /* fMediumLockWrite */,4441 NULL,4442 *pMediumLockList);4443 if (FAILED(rc))4444 {4445 delete pMediumLockList;4446 throw rc;4447 }4448 4449 rc = pMediumLockList->Lock();4450 if (FAILED(rc))4451 {4452 delete pMediumLockList;4453 throw setError(rc,4454 tr("Failed to lock media when deleting '%s'"),4455 getLocationFull().c_str());4456 }4457 4458 /* try to remove from the list of known media before performing4459 * actual deletion (we favor the consistency of the media registry4460 * which would have been broken if unregisterWithVirtualBox() failed4461 * after we successfully deleted the storage) */4462 rc = unregisterWithVirtualBox(pfNeedsGlobalSaveSettings);4463 if (FAILED(rc))4464 throw rc;4465 // no longer need lock4466 multilock.release();4467 4468 if (aProgress != NULL)4469 {4470 /* use the existing progress object... */4471 pProgress = *aProgress;4472 4473 /* ...but create a new one if it is null */4474 if (pProgress.isNull())4475 {4476 pProgress.createObject();4477 rc = pProgress->init(m->pVirtualBox,4478 static_cast<IMedium*>(this),4479 BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),4480 FALSE /* aCancelable */);4481 if (FAILED(rc))4482 throw rc;4483 }4484 }4485 4486 /* setup task object to carry out the operation sync/async */4487 pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList);4488 rc = pTask->rc();4489 AssertComRC(rc);4490 if (FAILED(rc))4491 throw rc;4492 }4493 catch (HRESULT aRC) { rc = aRC; }4494 4495 if (SUCCEEDED(rc))4496 {4497 if (aWait)4498 rc = runNow(pTask, NULL /* pfNeedsGlobalSaveSettings*/);4499 else4500 rc = startThread(pTask);4501 4502 if (SUCCEEDED(rc) && aProgress != NULL)4503 *aProgress = pProgress;4504 4505 }4506 else4507 {4508 if (pTask)4509 delete pTask;4510 4511 /* Undo deleting state if necessary. */4512 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);4513 unmarkForDeletion();4514 }4515 4516 return rc;4517 }4518 4519 /**4520 * Mark a medium for deletion.4521 *4522 * @note Caller must hold the write lock on this medium!4523 */4524 HRESULT Medium::markForDeletion()4525 {4526 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);4527 switch (m->state)4528 {4529 case MediumState_Created:4530 case MediumState_Inaccessible:4531 m->preLockState = m->state;4532 m->state = MediumState_Deleting;4533 return S_OK;4534 default:4535 return setStateError();4536 }4537 }4538 4539 /**4540 * Removes the "mark for deletion".4541 *4542 * @note Caller must hold the write lock on this medium!4543 */4544 HRESULT Medium::unmarkForDeletion()4545 {4546 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);4547 switch (m->state)4548 {4549 case MediumState_Deleting:4550 m->state = m->preLockState;4551 return S_OK;4552 default:4553 return setStateError();4554 }4555 }4556 4557 /**4558 * Mark a medium for deletion which is in locked state.4559 *4560 * @note Caller must hold the write lock on this medium!4561 */4562 HRESULT Medium::markLockedForDeletion()4563 {4564 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);4565 if ( ( m->state == MediumState_LockedRead4566 || m->state == MediumState_LockedWrite)4567 && m->preLockState == MediumState_Created)4568 {4569 m->preLockState = MediumState_Deleting;4570 return S_OK;4571 }4572 else4573 return setStateError();4574 }4575 4576 /**4577 * Removes the "mark for deletion" for a medium in locked state.4578 *4579 * @note Caller must hold the write lock on this medium!4580 */4581 HRESULT Medium::unmarkLockedForDeletion()4582 {4583 ComAssertRet(this->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);4584 if ( ( m->state == MediumState_LockedRead4585 || m->state == MediumState_LockedWrite)4586 && m->preLockState == MediumState_Deleting)4587 {4588 m->preLockState = MediumState_Created;4589 return S_OK;4590 }4591 else4592 return setStateError();4593 }4594 4595 /**4596 * Creates a new differencing storage unit using the format of the given target4597 * medium and the location. Note that @c aTarget must be NotCreated.4598 *4599 * The @a aMediumLockList parameter contains the associated medium lock list,4600 * which must be in locked state. If @a aWait is @c true then the caller is4601 * responsible for unlocking.4602 *4603 * If @a aProgress is not NULL but the object it points to is @c null then a4604 * new progress object will be created and assigned to @a *aProgress on4605 * success, otherwise the existing progress object is used. If @a aProgress is4606 * NULL, then no progress object is created/used at all.4607 *4608 * When @a aWait is @c false, this method will create a thread to perform the4609 * create operation asynchronously and will return immediately. Otherwise, it4610 * will perform the operation on the calling thread and will not return to the4611 * caller until the operation is completed. Note that @a aProgress cannot be4612 * NULL when @a aWait is @c false (this method will assert in this case).4613 *4614 * @param aTarget Target medium.4615 * @param aVariant Precise medium variant to create.4616 * @param aMediumLockList List of media which should be locked.4617 * @param aProgress Where to find/store a Progress object to track4618 * operation completion.4619 * @param aWait @c true if this method should block instead of4620 * creating an asynchronous thread.4621 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been4622 * initialized to false and that will be set to true4623 * by this function if the caller should invoke4624 * VirtualBox::saveSettings() because the global4625 * settings have changed. This only works in "wait"4626 * mode; otherwise saveSettings is called4627 * automatically by the thread that was created,4628 * and this parameter is ignored.4629 *4630 * @note Locks this object and @a aTarget for writing.4631 */4632 HRESULT Medium::createDiffStorage(ComObjPtr<Medium> &aTarget,4633 MediumVariant_T aVariant,4634 MediumLockList *aMediumLockList,4635 ComObjPtr<Progress> *aProgress,4636 bool aWait,4637 bool *pfNeedsGlobalSaveSettings)4638 {4639 AssertReturn(!aTarget.isNull(), E_FAIL);4640 AssertReturn(aMediumLockList, E_FAIL);4641 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);4642 4643 AutoCaller autoCaller(this);4644 if (FAILED(autoCaller.rc())) return autoCaller.rc();4645 4646 AutoCaller targetCaller(aTarget);4647 if (FAILED(targetCaller.rc())) return targetCaller.rc();4648 4649 HRESULT rc = S_OK;4650 ComObjPtr<Progress> pProgress;4651 Medium::Task *pTask = NULL;4652 4653 try4654 {4655 AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);4656 4657 ComAssertThrow( m->type != MediumType_Writethrough4658 && m->type != MediumType_Shareable4659 && m->type != MediumType_Readonly, E_FAIL);4660 ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);4661 4662 if (aTarget->m->state != MediumState_NotCreated)4663 throw aTarget->setStateError();4664 4665 /* Check that the medium is not attached to the current state of4666 * any VM referring to it. */4667 for (BackRefList::const_iterator it = m->backRefs.begin();4668 it != m->backRefs.end();4669 ++it)4670 {4671 if (it->fInCurState)4672 {4673 /* Note: when a VM snapshot is being taken, all normal media4674 * attached to the VM in the current state will be, as an4675 * exception, also associated with the snapshot which is about4676 * to create (see SnapshotMachine::init()) before deassociating4677 * them from the current state (which takes place only on4678 * success in Machine::fixupHardDisks()), so that the size of4679 * snapshotIds will be 1 in this case. The extra condition is4680 * used to filter out this legal situation. */4681 if (it->llSnapshotIds.size() == 0)4682 throw setError(VBOX_E_INVALID_OBJECT_STATE,4683 tr("Medium '%s' is attached to a virtual machine with UUID {%RTuuid}. No differencing media based on it may be created until it is detached"),4684 m->strLocationFull.c_str(), it->machineId.raw());4685 4686 Assert(it->llSnapshotIds.size() == 1);4687 }4688 }4689 4690 if (aProgress != NULL)4691 {4692 /* use the existing progress object... */4693 pProgress = *aProgress;4694 4695 /* ...but create a new one if it is null */4696 if (pProgress.isNull())4697 {4698 pProgress.createObject();4699 rc = pProgress->init(m->pVirtualBox,4700 static_cast<IMedium*>(this),4701 BstrFmt(tr("Creating differencing medium storage unit '%s'"), aTarget->m->strLocationFull.c_str()).raw(),4702 TRUE /* aCancelable */);4703 if (FAILED(rc))4704 throw rc;4705 }4706 }4707 4708 /* setup task object to carry out the operation sync/async */4709 pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,4710 aMediumLockList,4711 aWait /* fKeepMediumLockList */);4712 rc = pTask->rc();4713 AssertComRC(rc);4714 if (FAILED(rc))4715 throw rc;4716 4717 /* register a task (it will deregister itself when done) */4718 ++m->numCreateDiffTasks;4719 Assert(m->numCreateDiffTasks != 0); /* overflow? */4720 4721 aTarget->m->state = MediumState_Creating;4722 }4723 catch (HRESULT aRC) { rc = aRC; }4724 4725 if (SUCCEEDED(rc))4726 {4727 if (aWait)4728 rc = runNow(pTask, pfNeedsGlobalSaveSettings);4729 else4730 rc = startThread(pTask);4731 4732 if (SUCCEEDED(rc) && aProgress != NULL)4733 *aProgress = pProgress;4734 }4735 else if (pTask != NULL)4736 delete pTask;4737 4738 return rc;4739 }4740 4741 /**4742 * Prepares this (source) medium, target medium and all intermediate media4743 * for the merge operation.4744 *4745 * This method is to be called prior to calling the #mergeTo() to perform4746 * necessary consistency checks and place involved media to appropriate4747 * states. If #mergeTo() is not called or fails, the state modifications4748 * performed by this method must be undone by #cancelMergeTo().4749 *4750 * See #mergeTo() for more information about merging.4751 *4752 * @param pTarget Target medium.4753 * @param aMachineId Allowed machine attachment. NULL means do not check.4754 * @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means4755 * do not check.4756 * @param fLockMedia Flag whether to lock the medium lock list or not.4757 * If set to false and the medium lock list locking fails4758 * later you must call #cancelMergeTo().4759 * @param fMergeForward Resulting merge direction (out).4760 * @param pParentForTarget New parent for target medium after merge (out).4761 * @param aChildrenToReparent List of children of the source which will have4762 * to be reparented to the target after merge (out).4763 * @param aMediumLockList Medium locking information (out).4764 *4765 * @note Locks medium tree for reading. Locks this object, aTarget and all4766 * intermediate media for writing.4767 */4768 HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget,4769 const Guid *aMachineId,4770 const Guid *aSnapshotId,4771 bool fLockMedia,4772 bool &fMergeForward,4773 ComObjPtr<Medium> &pParentForTarget,4774 MediaList &aChildrenToReparent,4775 MediumLockList * &aMediumLockList)4776 {4777 AssertReturn(pTarget != NULL, E_FAIL);4778 AssertReturn(pTarget != this, E_FAIL);4779 4780 AutoCaller autoCaller(this);4781 AssertComRCReturnRC(autoCaller.rc());4782 4783 AutoCaller targetCaller(pTarget);4784 AssertComRCReturnRC(targetCaller.rc());4785 4786 HRESULT rc = S_OK;4787 fMergeForward = false;4788 pParentForTarget.setNull();4789 aChildrenToReparent.clear();4790 Assert(aMediumLockList == NULL);4791 aMediumLockList = NULL;4792 4793 try4794 {4795 // locking: we need the tree lock first because we access parent pointers4796 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);4797 4798 /* more sanity checking and figuring out the merge direction */4799 ComObjPtr<Medium> pMedium = getParent();4800 while (!pMedium.isNull() && pMedium != pTarget)4801 pMedium = pMedium->getParent();4802 if (pMedium == pTarget)4803 fMergeForward = false;4804 else4805 {4806 pMedium = pTarget->getParent();4807 while (!pMedium.isNull() && pMedium != this)4808 pMedium = pMedium->getParent();4809 if (pMedium == this)4810 fMergeForward = true;4811 else4812 {4813 Utf8Str tgtLoc;4814 {4815 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);4816 tgtLoc = pTarget->getLocationFull();4817 }4818 4819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);4820 throw setError(VBOX_E_INVALID_OBJECT_STATE,4821 tr("Media '%s' and '%s' are unrelated"),4822 m->strLocationFull.c_str(), tgtLoc.c_str());4823 }4824 }4825 4826 /* Build the lock list. */4827 aMediumLockList = new MediumLockList();4828 if (fMergeForward)4829 rc = pTarget->createMediumLockList(true /* fFailIfInaccessible */,4830 true /* fMediumLockWrite */,4831 NULL,4832 *aMediumLockList);4833 else4834 rc = createMediumLockList(true /* fFailIfInaccessible */,4835 false /* fMediumLockWrite */,4836 NULL,4837 *aMediumLockList);4838 if (FAILED(rc))4839 throw rc;4840 4841 /* Sanity checking, must be after lock list creation as it depends on4842 * valid medium states. The medium objects must be accessible. Only4843 * do this if immediate locking is requested, otherwise it fails when4844 * we construct a medium lock list for an already running VM. Snapshot4845 * deletion uses this to simplify its life. */4846 if (fLockMedia)4847 {4848 {4849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);4850 if (m->state != MediumState_Created)4851 throw setStateError();4852 }4853 {4854 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);4855 if (pTarget->m->state != MediumState_Created)4856 throw pTarget->setStateError();4857 }4858 }4859 4860 /* check medium attachment and other sanity conditions */4861 if (fMergeForward)4862 {4863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);4864 if (getChildren().size() > 1)4865 {4866 throw setError(VBOX_E_INVALID_OBJECT_STATE,4867 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),4868 m->strLocationFull.c_str(), getChildren().size());4869 }4870 /* One backreference is only allowed if the machine ID is not empty4871 * and it matches the machine the medium is attached to (including4872 * the snapshot ID if not empty). */4873 if ( m->backRefs.size() != 04874 && ( !aMachineId4875 || m->backRefs.size() != 14876 || aMachineId->isEmpty()4877 || *getFirstMachineBackrefId() != *aMachineId4878 || ( (!aSnapshotId || !aSnapshotId->isEmpty())4879 && *getFirstMachineBackrefSnapshotId() != *aSnapshotId)))4880 throw setError(VBOX_E_OBJECT_IN_USE,4881 tr("Medium '%s' is attached to %d virtual machines"),4882 m->strLocationFull.c_str(), m->backRefs.size());4883 if (m->type == MediumType_Immutable)4884 throw setError(VBOX_E_INVALID_OBJECT_STATE,4885 tr("Medium '%s' is immutable"),4886 m->strLocationFull.c_str());4887 }4888 else4889 {4890 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);4891 if (pTarget->getChildren().size() > 1)4892 {4893 throw setError(VBOX_E_OBJECT_IN_USE,4894 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),4895 pTarget->m->strLocationFull.c_str(),4896 pTarget->getChildren().size());4897 }4898 if (pTarget->m->type == MediumType_Immutable)4899 throw setError(VBOX_E_INVALID_OBJECT_STATE,4900 tr("Medium '%s' is immutable"),4901 pTarget->m->strLocationFull.c_str());4902 }4903 ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);4904 ComObjPtr<Medium> pLastIntermediate = pLast->getParent();4905 for (pLast = pLastIntermediate;4906 !pLast.isNull() && pLast != pTarget && pLast != this;4907 pLast = pLast->getParent())4908 {4909 AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);4910 if (pLast->getChildren().size() > 1)4911 {4912 throw setError(VBOX_E_OBJECT_IN_USE,4913 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),4914 pLast->m->strLocationFull.c_str(),4915 pLast->getChildren().size());4916 }4917 if (pLast->m->backRefs.size() != 0)4918 throw setError(VBOX_E_OBJECT_IN_USE,4919 tr("Medium '%s' is attached to %d virtual machines"),4920 pLast->m->strLocationFull.c_str(),4921 pLast->m->backRefs.size());4922 4923 }4924 4925 /* Update medium states appropriately */4926 if (m->state == MediumState_Created)4927 {4928 rc = markForDeletion();4929 if (FAILED(rc))4930 throw rc;4931 }4932 else4933 {4934 if (fLockMedia)4935 throw setStateError();4936 else if ( m->state == MediumState_LockedWrite4937 || m->state == MediumState_LockedRead)4938 {4939 /* Either mark it for deletion in locked state or allow4940 * others to have done so. */4941 if (m->preLockState == MediumState_Created)4942 markLockedForDeletion();4943 else if (m->preLockState != MediumState_Deleting)4944 throw setStateError();4945 }4946 else4947 throw setStateError();4948 }4949 4950 if (fMergeForward)4951 {4952 /* we will need parent to reparent target */4953 pParentForTarget = m->pParent;4954 }4955 else4956 {4957 /* we will need to reparent children of the source */4958 for (MediaList::const_iterator it = getChildren().begin();4959 it != getChildren().end();4960 ++it)4961 {4962 pMedium = *it;4963 if (fLockMedia)4964 {4965 rc = pMedium->LockWrite(NULL);4966 if (FAILED(rc))4967 throw rc;4968 }4969 4970 aChildrenToReparent.push_back(pMedium);4971 }4972 }4973 for (pLast = pLastIntermediate;4974 !pLast.isNull() && pLast != pTarget && pLast != this;4975 pLast = pLast->getParent())4976 {4977 AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);4978 if (pLast->m->state == MediumState_Created)4979 {4980 rc = pLast->markForDeletion();4981 if (FAILED(rc))4982 throw rc;4983 }4984 else4985 throw pLast->setStateError();4986 }4987 4988 /* Tweak the lock list in the backward merge case, as the target4989 * isn't marked to be locked for writing yet. */4990 if (!fMergeForward)4991 {4992 MediumLockList::Base::iterator lockListBegin =4993 aMediumLockList->GetBegin();4994 MediumLockList::Base::iterator lockListEnd =4995 aMediumLockList->GetEnd();4996 lockListEnd--;4997 for (MediumLockList::Base::iterator it = lockListBegin;4998 it != lockListEnd;4999 ++it)5000 {5001 MediumLock &mediumLock = *it;5002 if (mediumLock.GetMedium() == pTarget)5003 {5004 HRESULT rc2 = mediumLock.UpdateLock(true);5005 AssertComRC(rc2);5006 break;5007 }5008 }5009 }5010 5011 if (fLockMedia)5012 {5013 rc = aMediumLockList->Lock();5014 if (FAILED(rc))5015 {5016 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);5017 throw setError(rc,5018 tr("Failed to lock media when merging to '%s'"),5019 pTarget->getLocationFull().c_str());5020 }5021 }5022 }5023 catch (HRESULT aRC) { rc = aRC; }5024 5025 if (FAILED(rc))5026 {5027 delete aMediumLockList;5028 aMediumLockList = NULL;5029 }5030 5031 return rc;5032 }5033 5034 /**5035 * Merges this medium to the specified medium which must be either its5036 * direct ancestor or descendant.5037 *5038 * Given this medium is SOURCE and the specified medium is TARGET, we will5039 * get two variants of the merge operation:5040 *5041 * forward merge5042 * ------------------------->5043 * [Extra] <- SOURCE <- Intermediate <- TARGET5044 * Any Del Del LockWr5045 *5046 *5047 * backward merge5048 * <-------------------------5049 * TARGET <- Intermediate <- SOURCE <- [Extra]5050 * LockWr Del Del LockWr5051 *5052 * Each diagram shows the involved media on the media chain where5053 * SOURCE and TARGET belong. Under each medium there is a state value which5054 * the medium must have at a time of the mergeTo() call.5055 *5056 * The media in the square braces may be absent (e.g. when the forward5057 * operation takes place and SOURCE is the base medium, or when the backward5058 * merge operation takes place and TARGET is the last child in the chain) but if5059 * they present they are involved too as shown.5060 *5061 * Neither the source medium nor intermediate media may be attached to5062 * any VM directly or in the snapshot, otherwise this method will assert.5063 *5064 * The #prepareMergeTo() method must be called prior to this method to place all5065 * involved to necessary states and perform other consistency checks.5066 *5067 * If @a aWait is @c true then this method will perform the operation on the5068 * calling thread and will not return to the caller until the operation is5069 * completed. When this method succeeds, all intermediate medium objects in5070 * the chain will be uninitialized, the state of the target medium (and all5071 * involved extra media) will be restored. @a aMediumLockList will not be5072 * deleted, whether the operation is successful or not. The caller has to do5073 * this if appropriate. Note that this (source) medium is not uninitialized5074 * because of possible AutoCaller instances held by the caller of this method5075 * on the current thread. It's therefore the responsibility of the caller to5076 * call Medium::uninit() after releasing all callers.5077 *5078 * If @a aWait is @c false then this method will create a thread to perform the5079 * operation asynchronously and will return immediately. If the operation5080 * succeeds, the thread will uninitialize the source medium object and all5081 * intermediate medium objects in the chain, reset the state of the target5082 * medium (and all involved extra media) and delete @a aMediumLockList.5083 * If the operation fails, the thread will only reset the states of all5084 * involved media and delete @a aMediumLockList.5085 *5086 * When this method fails (regardless of the @a aWait mode), it is a caller's5087 * responsibility to undo state changes and delete @a aMediumLockList using5088 * #cancelMergeTo().5089 *5090 * If @a aProgress is not NULL but the object it points to is @c null then a new5091 * progress object will be created and assigned to @a *aProgress on success,5092 * otherwise the existing progress object is used. If Progress is NULL, then no5093 * progress object is created/used at all. Note that @a aProgress cannot be5094 * NULL when @a aWait is @c false (this method will assert in this case).5095 *5096 * @param pTarget Target medium.5097 * @param fMergeForward Merge direction.5098 * @param pParentForTarget New parent for target medium after merge.5099 * @param aChildrenToReparent List of children of the source which will have5100 * to be reparented to the target after merge.5101 * @param aMediumLockList Medium locking information.5102 * @param aProgress Where to find/store a Progress object to track operation5103 * completion.5104 * @param aWait @c true if this method should block instead of creating5105 * an asynchronous thread.5106 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true5107 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.5108 * This only works in "wait" mode; otherwise saveSettings gets called automatically by the thread that was created,5109 * and this parameter is ignored.5110 *5111 * @note Locks the tree lock for writing. Locks the media from the chain5112 * for writing.5113 */5114 HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget,5115 bool fMergeForward,5116 const ComObjPtr<Medium> &pParentForTarget,5117 const MediaList &aChildrenToReparent,5118 MediumLockList *aMediumLockList,5119 ComObjPtr <Progress> *aProgress,5120 bool aWait,5121 bool *pfNeedsGlobalSaveSettings)5122 {5123 AssertReturn(pTarget != NULL, E_FAIL);5124 AssertReturn(pTarget != this, E_FAIL);5125 AssertReturn(aMediumLockList != NULL, E_FAIL);5126 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);5127 5128 AutoCaller autoCaller(this);5129 if (FAILED(autoCaller.rc())) return autoCaller.rc();5130 5131 AutoCaller targetCaller(pTarget);5132 AssertComRCReturnRC(targetCaller.rc());5133 5134 HRESULT rc = S_OK;5135 ComObjPtr <Progress> pProgress;5136 Medium::Task *pTask = NULL;5137 5138 try5139 {5140 if (aProgress != NULL)5141 {5142 /* use the existing progress object... */5143 pProgress = *aProgress;5144 5145 /* ...but create a new one if it is null */5146 if (pProgress.isNull())5147 {5148 Utf8Str tgtName;5149 {5150 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);5151 tgtName = pTarget->getName();5152 }5153 5154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);5155 5156 pProgress.createObject();5157 rc = pProgress->init(m->pVirtualBox,5158 static_cast<IMedium*>(this),5159 BstrFmt(tr("Merging medium '%s' to '%s'"),5160 getName().c_str(),5161 tgtName.c_str()).raw(),5162 TRUE /* aCancelable */);5163 if (FAILED(rc))5164 throw rc;5165 }5166 }5167 5168 /* setup task object to carry out the operation sync/async */5169 pTask = new Medium::MergeTask(this, pTarget, fMergeForward,5170 pParentForTarget, aChildrenToReparent,5171 pProgress, aMediumLockList,5172 aWait /* fKeepMediumLockList */);5173 rc = pTask->rc();5174 AssertComRC(rc);5175 if (FAILED(rc))5176 throw rc;5177 }5178 catch (HRESULT aRC) { rc = aRC; }5179 5180 if (SUCCEEDED(rc))5181 {5182 if (aWait)5183 rc = runNow(pTask, pfNeedsGlobalSaveSettings);5184 else5185 rc = startThread(pTask);5186 5187 if (SUCCEEDED(rc) && aProgress != NULL)5188 *aProgress = pProgress;5189 }5190 else if (pTask != NULL)5191 delete pTask;5192 5193 return rc;5194 }5195 5196 /**5197 * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not5198 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks5199 * the medium objects in @a aChildrenToReparent.5200 *5201 * @param aChildrenToReparent List of children of the source which will have5202 * to be reparented to the target after merge.5203 * @param aMediumLockList Medium locking information.5204 *5205 * @note Locks the media from the chain for writing.5206 */5207 void Medium::cancelMergeTo(const MediaList &aChildrenToReparent,5208 MediumLockList *aMediumLockList)5209 {5210 AutoCaller autoCaller(this);5211 AssertComRCReturnVoid(autoCaller.rc());5212 5213 AssertReturnVoid(aMediumLockList != NULL);5214 5215 /* Revert media marked for deletion to previous state. */5216 HRESULT rc;5217 MediumLockList::Base::const_iterator mediumListBegin =5218 aMediumLockList->GetBegin();5219 MediumLockList::Base::const_iterator mediumListEnd =5220 aMediumLockList->GetEnd();5221 for (MediumLockList::Base::const_iterator it = mediumListBegin;5222 it != mediumListEnd;5223 ++it)5224 {5225 const MediumLock &mediumLock = *it;5226 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();5227 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);5228 5229 if (pMedium->m->state == MediumState_Deleting)5230 {5231 rc = pMedium->unmarkForDeletion();5232 AssertComRC(rc);5233 }5234 }5235 5236 /* the destructor will do the work */5237 delete aMediumLockList;5238 5239 /* unlock the children which had to be reparented */5240 for (MediaList::const_iterator it = aChildrenToReparent.begin();5241 it != aChildrenToReparent.end();5242 ++it)5243 {5244 const ComObjPtr<Medium> &pMedium = *it;5245 5246 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);5247 pMedium->UnlockWrite(NULL);5248 }5249 }5250 5251 5252 HRESULT Medium::exportFile(const char *aFilename,5253 const ComObjPtr<MediumFormat> &aFormat,5254 MediumVariant_T aVariant,5255 void *aVDImageIOCallbacks, void *aVDImageIOUser,5256 const ComObjPtr<Progress> &aProgress)5257 {5258 AssertPtrReturn(aFilename, E_INVALIDARG);5259 AssertReturn(!aFormat.isNull(), E_INVALIDARG);5260 AssertReturn(!aProgress.isNull(), E_INVALIDARG);5261 5262 AutoCaller autoCaller(this);5263 if (FAILED(autoCaller.rc())) return autoCaller.rc();5264 5265 HRESULT rc = S_OK;5266 Medium::Task *pTask = NULL;5267 5268 try5269 {5270 // locking: we need the tree lock first because we access parent pointers5271 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);5272 // and we need to write-lock the media involved5273 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);5274 5275 /* Build the source lock list. */5276 MediumLockList *pSourceMediumLockList(new MediumLockList());5277 rc = createMediumLockList(true /* fFailIfInaccessible */,5278 false /* fMediumLockWrite */,5279 NULL,5280 *pSourceMediumLockList);5281 if (FAILED(rc))5282 {5283 delete pSourceMediumLockList;5284 throw rc;5285 }5286 5287 rc = pSourceMediumLockList->Lock();5288 if (FAILED(rc))5289 {5290 delete pSourceMediumLockList;5291 throw setError(rc,5292 tr("Failed to lock source media '%s'"),5293 getLocationFull().c_str());5294 }5295 5296 /* setup task object to carry out the operation asynchronously */5297 pTask = new Medium::ExportTask(this, aProgress, aFilename, aFormat,5298 aVariant, aVDImageIOCallbacks,5299 aVDImageIOUser, pSourceMediumLockList);5300 rc = pTask->rc();5301 AssertComRC(rc);5302 if (FAILED(rc))5303 throw rc;5304 }5305 catch (HRESULT aRC) { rc = aRC; }5306 5307 if (SUCCEEDED(rc))5308 rc = startThread(pTask);5309 else if (pTask != NULL)5310 delete pTask;5311 5312 return rc;5313 }5314 5315 HRESULT Medium::importFile(const char *aFilename,5316 const ComObjPtr<MediumFormat> &aFormat,5317 MediumVariant_T aVariant,5318 void *aVDImageIOCallbacks, void *aVDImageIOUser,5319 const ComObjPtr<Medium> &aParent,5320 const ComObjPtr<Progress> &aProgress)5321 {5322 AssertPtrReturn(aFilename, E_INVALIDARG);5323 AssertReturn(!aFormat.isNull(), E_INVALIDARG);5324 AssertReturn(!aProgress.isNull(), E_INVALIDARG);5325 5326 AutoCaller autoCaller(this);5327 if (FAILED(autoCaller.rc())) return autoCaller.rc();5328 5329 HRESULT rc = S_OK;5330 Medium::Task *pTask = NULL;5331 5332 try5333 {5334 // locking: we need the tree lock first because we access parent pointers5335 AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);5336 // and we need to write-lock the media involved5337 AutoMultiWriteLock2 alock(this, aParent COMMA_LOCKVAL_SRC_POS);5338 5339 if ( m->state != MediumState_NotCreated5340 && m->state != MediumState_Created)5341 throw setStateError();5342 5343 /* Build the target lock list. */5344 MediumLockList *pTargetMediumLockList(new MediumLockList());5345 rc = createMediumLockList(true /* fFailIfInaccessible */,5346 true /* fMediumLockWrite */,5347 aParent,5348 *pTargetMediumLockList);5349 if (FAILED(rc))5350 {5351 delete pTargetMediumLockList;5352 throw rc;5353 }5354 5355 rc = pTargetMediumLockList->Lock();5356 if (FAILED(rc))5357 {5358 delete pTargetMediumLockList;5359 throw setError(rc,5360 tr("Failed to lock target media '%s'"),5361 getLocationFull().c_str());5362 }5363 5364 /* setup task object to carry out the operation asynchronously */5365 pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat,5366 aVariant, aVDImageIOCallbacks,5367 aVDImageIOUser, aParent,5368 pTargetMediumLockList);5369 rc = pTask->rc();5370 AssertComRC(rc);5371 if (FAILED(rc))5372 throw rc;5373 5374 if (m->state == MediumState_NotCreated)5375 m->state = MediumState_Creating;5376 }5377 catch (HRESULT aRC) { rc = aRC; }5378 5379 if (SUCCEEDED(rc))5380 rc = startThread(pTask);5381 else if (pTask != NULL)5382 delete pTask;5383 5384 return rc;5385 }5386 5387 ////////////////////////////////////////////////////////////////////////////////5388 //5389 // Private methods5390 //5391 ////////////////////////////////////////////////////////////////////////////////5392 5393 /**5394 * Performs extra checks if the medium can be closed and returns S_OK in5395 * this case. Otherwise, returns a respective error message. Called by5396 * Close() under the medium tree lock and the medium lock.5397 *5398 * @note Also reused by Medium::Reset().5399 *5400 * @note Caller must hold the media tree write lock!5401 */5402 HRESULT Medium::canClose()5403 {5404 Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());5405 5406 if (getChildren().size() != 0)5407 return setError(VBOX_E_OBJECT_IN_USE,5408 tr("Cannot close medium '%s' because it has %d child media"),5409 m->strLocationFull.c_str(), getChildren().size());5410 5411 return S_OK;5412 }5413 5414 /**5415 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.5416 *5417 * This calls either VirtualBox::unregisterImage or VirtualBox::unregisterHardDisk depending5418 * on the device type of this medium.5419 *5420 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true5421 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.5422 *5423 * @note Caller must have locked the media tree lock for writing!5424 */5425 HRESULT Medium::unregisterWithVirtualBox(bool *pfNeedsGlobalSaveSettings)5426 {5427 /* Note that we need to de-associate ourselves from the parent to let5428 * unregisterHardDisk() properly save the registry */5429 5430 /* we modify mParent and access children */5431 Assert(m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread());5432 5433 Medium *pParentBackup = m->pParent;5434 AssertReturn(getChildren().size() == 0, E_FAIL);5435 if (m->pParent)5436 deparent();5437 5438 HRESULT rc = E_FAIL;5439 switch (m->devType)5440 {5441 case DeviceType_DVD:5442 rc = m->pVirtualBox->unregisterImage(this, DeviceType_DVD, pfNeedsGlobalSaveSettings);5443 break;5444 5445 case DeviceType_Floppy:5446 rc = m->pVirtualBox->unregisterImage(this, DeviceType_Floppy, pfNeedsGlobalSaveSettings);5447 break;5448 5449 case DeviceType_HardDisk:5450 rc = m->pVirtualBox->unregisterHardDisk(this, pfNeedsGlobalSaveSettings);5451 break;5452 5453 default:5454 break;5455 }5456 5457 if (FAILED(rc))5458 {5459 if (pParentBackup)5460 {5461 // re-associate with the parent as we are still relatives in the registry5462 m->pParent = pParentBackup;5463 m->pParent->m->llChildren.push_back(this);5464 }5465 }5466 5467 return rc;5468 }5469 5470 /**5471 * Checks that the format ID is valid and sets it on success.5472 *5473 * Note that this method will caller-reference the format object on success!5474 * This reference must be released somewhere to let the MediumFormat object be5475 * uninitialized.5476 *5477 * @note Must be called from under this object's write lock.5478 */5479 HRESULT Medium::setFormat(const Utf8Str &aFormat)5480 {5481 /* get the format object first */5482 {5483 SystemProperties *pSysProps = m->pVirtualBox->getSystemProperties();5484 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);5485 5486 unconst(m->formatObj) = pSysProps->mediumFormat(aFormat);5487 if (m->formatObj.isNull())5488 return setError(E_INVALIDARG,5489 tr("Invalid medium storage format '%s'"),5490 aFormat.c_str());5491 5492 /* reference the format permanently to prevent its unexpected5493 * uninitialization */5494 HRESULT rc = m->formatObj->addCaller();5495 AssertComRCReturnRC(rc);5496 5497 /* get properties (preinsert them as keys in the map). Note that the5498 * map doesn't grow over the object life time since the set of5499 * properties is meant to be constant. */5500 5501 Assert(m->mapProperties.empty());5502 5503 for (MediumFormat::PropertyList::const_iterator it = m->formatObj->getProperties().begin();5504 it != m->formatObj->getProperties().end();5505 ++it)5506 {5507 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));5508 }5509 }5510 5511 unconst(m->strFormat) = aFormat;5512 5513 return S_OK;5514 }5515 5516 /**5517 5628 * Returns the last error message collected by the vdErrorCall callback and 5518 5629 * resets it. … … 5744 5855 } 5745 5856 5746 5747 5857 /** 5748 5858 * Starts a new thread driven by the appropriate Medium::Task::handler() method. … … 5774 5884 5775 5885 return S_OK; 5776 }5777 5778 /**5779 * Fix the parent UUID of all children to point to this medium as their5780 * parent.5781 */5782 HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent)5783 {5784 MediumLockList mediumLockList;5785 HRESULT rc = createMediumLockList(true /* fFailIfInaccessible */,5786 false /* fMediumLockWrite */,5787 this,5788 mediumLockList);5789 AssertComRCReturnRC(rc);5790 5791 try5792 {5793 PVBOXHDD hdd;5794 int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd);5795 ComAssertRCThrow(vrc, E_FAIL);5796 5797 try5798 {5799 MediumLockList::Base::iterator lockListBegin =5800 mediumLockList.GetBegin();5801 MediumLockList::Base::iterator lockListEnd =5802 mediumLockList.GetEnd();5803 for (MediumLockList::Base::iterator it = lockListBegin;5804 it != lockListEnd;5805 ++it)5806 {5807 MediumLock &mediumLock = *it;5808 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();5809 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);5810 5811 // open the medium5812 vrc = VDOpen(hdd,5813 pMedium->m->strFormat.c_str(),5814 pMedium->m->strLocationFull.c_str(),5815 VD_OPEN_FLAGS_READONLY,5816 pMedium->m->vdImageIfaces);5817 if (RT_FAILURE(vrc))5818 throw vrc;5819 }5820 5821 for (MediaList::const_iterator it = childrenToReparent.begin();5822 it != childrenToReparent.end();5823 ++it)5824 {5825 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */5826 vrc = VDOpen(hdd,5827 (*it)->m->strFormat.c_str(),5828 (*it)->m->strLocationFull.c_str(),5829 VD_OPEN_FLAGS_INFO,5830 (*it)->m->vdImageIfaces);5831 if (RT_FAILURE(vrc))5832 throw vrc;5833 5834 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());5835 if (RT_FAILURE(vrc))5836 throw vrc;5837 5838 vrc = VDClose(hdd, false /* fDelete */);5839 if (RT_FAILURE(vrc))5840 throw vrc;5841 5842 (*it)->UnlockWrite(NULL);5843 }5844 }5845 catch (HRESULT aRC) { rc = aRC; }5846 catch (int aVRC)5847 {5848 throw setError(E_FAIL,5849 tr("Could not update medium UUID references to parent '%s' (%s)"),5850 m->strLocationFull.c_str(),5851 vdError(aVRC).c_str());5852 }5853 5854 VDDestroy(hdd);5855 }5856 catch (HRESULT aRC) { rc = aRC; }5857 5858 return rc;5859 5886 } 5860 5887 -
trunk/src/VBox/Main/include/MediumImpl.h
r33567 r33908 213 213 bool aWait, 214 214 bool *pfNeedsGlobalSaveSettings); 215 Utf8Str getPreferredDiffFormat(); 215 216 216 217 HRESULT close(bool *pfNeedsGlobalSaveSettings, AutoCaller &autoCaller); … … 242 243 HRESULT fixParentUuidOfChildren(const MediaList &childrenToReparent); 243 244 244 /**245 * Used by IAppliance to export disk images.246 *247 * @param aFilename Filename to create (UTF8).248 * @param aFormat Medium format for creating @a aFilename.249 * @param aVariant Which exact image format variant to use250 * for the destination image.251 * @param aVDImageIOCallbacks Pointer to the callback table for a252 * VDINTERFACEIO interface. May be NULL.253 * @param aVDImageIOUser Opaque data for the callbacks.254 * @param aProgress Progress object to use.255 * @return256 * @note The source format is defined by the Medium instance.257 */258 245 HRESULT exportFile(const char *aFilename, 259 246 const ComObjPtr<MediumFormat> &aFormat, … … 261 248 void *aVDImageIOCallbacks, void *aVDImageIOUser, 262 249 const ComObjPtr<Progress> &aProgress); 263 /**264 * Used by IAppliance to import disk images.265 *266 * @param aFilename Filename to read (UTF8).267 * @param aFormat Medium format for reading @a aFilename.268 * @param aVariant Which exact image format variant to use269 * for the destination image.270 * @param aVDImageIOCallbacks Pointer to the callback table for a271 * VDINTERFACEIO interface. May be NULL.272 * @param aVDImageIOUser Opaque data for the callbacks.273 * @param aParent Parent medium. May be NULL.274 * @param aProgress Progress object to use.275 * @return276 * @note The destination format is defined by the Medium instance.277 */278 250 HRESULT importFile(const char *aFilename, 279 251 const ComObjPtr<MediumFormat> &aFormat, … … 283 255 const ComObjPtr<Progress> &aProgress); 284 256 285 /** Returns a preferred format for a differencing hard disk. */286 Utf8Str getPreferredDiffFormat();287 288 257 private: 289 258 … … 298 267 HRESULT setFormat(const Utf8Str &aFormat); 299 268 300 Utf8Str vdError(int aVRC);301 302 269 VDTYPE convertDeviceType(); 303 270 DeviceType_T convertToDeviceType(VDTYPE enmType); 271 272 Utf8Str vdError(int aVRC); 304 273 305 274 static DECLCALLBACK(void) vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
Note:
See TracChangeset
for help on using the changeset viewer.