Changeset 5101 in vbox
- Timestamp:
- Sep 28, 2007 4:13:06 PM (17 years ago)
- Location:
- trunk/src/VBox
- Files:
-
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Storage/VBoxHDD-new.cpp
r4798 r5101 31 31 #include <iprt/asm.h> 32 32 #include <iprt/ldr.h> 33 #include <iprt/dir.h> 34 #include <iprt/path.h> 33 35 34 36 #include "VBoxHDD-newInternal.h" … … 501 503 * @todo: find out what to do if filenames are case sensitive. 502 504 */ 503 cbPluginName = RTStrAPrintf(&pszPluginName, " VBoxHDD%s", pszBackend);505 cbPluginName = RTStrAPrintf(&pszPluginName, "%s%s", VBOX_HDDFORMAT_PLUGIN_PREFIX, pszBackend); 504 506 if (cbPluginName == -1) 505 507 { … … 577 579 578 580 /** 581 * Try to get the backend name which can use this image. 582 * 583 * @returns VBox status code. 584 * @param pszFilename Name of the image file for which the backend is queried. 585 * @param ppszFormat Where to store the name of the plugin. 586 */ 587 588 VBOXDDU_DECL(int) VDGetFormat(const char *pszFilename, char **ppszFormat) 589 { 590 char pszProgramPath[1024]; /* Far too much I think but to be on the safe side. */ 591 char *pszPluginFilter; 592 PRTDIR pPluginDir = NULL; 593 PRTDIRENTRY pPluginDirEntry = NULL; 594 unsigned cbPluginDirEntry; 595 int rc = VERR_NOT_SUPPORTED; 596 bool fPluginFound = false; 597 598 if (!ppszFormat) 599 return VERR_INVALID_PARAMETER; 600 601 memset(pszProgramPath, 0, 1024); 602 rc = RTPathProgram(pszProgramPath, 1024); 603 if (VBOX_FAILURE(rc)) 604 { 605 return rc; 606 } 607 608 /* To get all entries with VBoxHDD as prefix. */ 609 rc = RTStrAPrintf(&pszPluginFilter, "%s/%s*", pszProgramPath, VBOX_HDDFORMAT_PLUGIN_PREFIX); 610 if (VBOX_FAILURE(rc)) 611 { 612 RTStrFree(pszProgramPath); 613 rc = VERR_NO_MEMORY; 614 return rc; 615 } 616 617 /* The plugins are in the same directory as the program. */ 618 rc = RTDirOpenFiltered(&pPluginDir, pszPluginFilter, RTDIRFILTER_WINNT); 619 if (VBOX_FAILURE(rc)) 620 goto out; 621 622 pPluginDirEntry = (PRTDIRENTRY)RTMemAllocZ(sizeof(RTDIRENTRY)); 623 if (!pPluginDir) 624 { 625 rc = VERR_NO_MEMORY; 626 goto out; 627 } 628 629 while ((rc = RTDirRead(pPluginDir, pPluginDirEntry, &cbPluginDirEntry)) != VERR_NO_MORE_FILES) 630 { 631 RTLDRMOD hPlugin = NIL_RTLDRMOD; 632 PFNVBOXHDDFORMATLOAD pfnHDDFormatLoad = NULL; 633 PVBOXHDDBACKEND pBackend = NULL; 634 635 if (rc == VERR_BUFFER_OVERFLOW) 636 { 637 /* allocate new buffer. */ 638 RTMemFree(pPluginDirEntry); 639 pPluginDirEntry = (PRTDIRENTRY)RTMemAllocZ(cbPluginDirEntry); 640 /* Retry. */ 641 rc = RTDirRead(pPluginDir, pPluginDirEntry, &cbPluginDirEntry); 642 if (VBOX_FAILURE(rc)) 643 break; 644 } 645 else if (VBOX_FAILURE(rc)) 646 break; 647 648 /* We got the new entry. */ 649 if (pPluginDirEntry->enmType != RTDIRENTRYTYPE_FILE) 650 continue; 651 652 rc = RTLdrLoad(pPluginDirEntry->szName, &hPlugin); 653 if (VBOX_SUCCESS(rc)) 654 { 655 rc = RTLdrGetSymbol(hPlugin, VBOX_HDDFORMAT_LOAD_NAME, (void**)&pfnHDDFormatLoad); 656 if (VBOX_FAILURE(rc) || !pfnHDDFormatLoad) 657 { 658 Log(("%s: Error resolving the entry point %s, rc = %d, pfnHDDFormat = %p\n", VBOX_HDDFORMAT_LOAD_NAME, rc, pfnHDDFormatLoad)); 659 if (VBOX_SUCCESS(rc)) 660 rc = VERR_SYMBOL_NOT_FOUND; 661 } 662 else 663 { 664 /* Get the function table. */ 665 pBackend = (PVBOXHDDBACKEND)RTMemAllocZ(sizeof(VBOXHDDBACKEND)); 666 if (!pBackend) 667 { 668 rc = VERR_NO_MEMORY; 669 } 670 else 671 { 672 pBackend->cbSize = sizeof(VBOXHDDBACKEND); 673 rc = pfnHDDFormatLoad(pBackend); 674 if (VBOX_FAILURE(rc)) 675 { 676 RTMemFree(pBackend); 677 pBackend = NULL; 678 } 679 680 /* Check if the plugin can handle this file. */ 681 rc = pBackend->pfnCheckIfValid(pszFilename); 682 if (VBOX_FAILURE(rc)) 683 { 684 RTMemFree(pBackend); 685 RTLdrClose(hPlugin); 686 } 687 else 688 { 689 RTMemFree(pBackend); 690 RTLdrClose(hPlugin); 691 fPluginFound = true; 692 693 /* Report the format name. */ 694 char *pszName = pPluginDirEntry->szName + VBOX_HDDFORMAT_PLUGIN_PREFIX_LENGTH; /* Point to the rest after the prefix. */ 695 char *pszFormat = NULL; 696 unsigned cbFormat = 0; 697 698 while((*pszName != '.') && (*pszName != '\0')) 699 { 700 cbFormat++; 701 pszName++; 702 } 703 704 pszName = pPluginDirEntry->szName + VBOX_HDDFORMAT_PLUGIN_PREFIX_LENGTH; 705 706 /* Copy the name into the new string. */ 707 pszFormat = (char *)RTMemAllocZ(cbFormat+1); 708 709 if (!pszFormat) 710 { 711 rc = VERR_NO_MEMORY; 712 break; 713 } 714 715 memcpy(pszFormat, pszName, cbFormat); 716 717 *ppszFormat = pszFormat; 718 719 break; 720 } 721 } 722 } 723 } 724 } 725 726 out: 727 if (pPluginDirEntry) 728 RTMemFree(pPluginDirEntry); 729 if (pPluginDir) 730 RTDirClose(pPluginDir); 731 732 RTStrFree(pszPluginFilter); 733 RTStrFree(pszProgramPath); 734 735 if ((fPluginFound == true) && (*ppszFormat != NULL)) 736 rc = VINF_SUCCESS; 737 738 return rc; 739 } 740 741 /** 579 742 * Destroys the VBox HDD container. 580 743 * If container has opened image files they will be closed. … … 584 747 VBOXDDU_DECL(void) VDDestroy(PVBOXHDD pDisk) 585 748 { 586 LogFlow(("%s: pDisk=%#p\n", pDisk));749 LogFlow(("%s: pDisk=%#p\n", __FUNCTION__, pDisk)); 587 750 /* sanity check */ 588 751 Assert(pDisk); -
trunk/src/VBox/Devices/Storage/VmdkHDDCore.cpp
r4522 r5101 3482 3482 /* cbSize */ 3483 3483 sizeof(VBOXHDDBACKEND), 3484 /* pfnCheckIfValid */ 3485 NULL, 3484 3486 /* pfnOpen */ 3485 3487 vmdkOpen, -
trunk/src/VBox/Main/ConsoleImpl2.cpp
r5060 r5101 727 727 STR_CONV(); 728 728 rc = CFGMR3InsertString(pCfg, "Path", psz); RC_CHECK(); 729 STR_FREE(); 730 } 731 else if (hddType == HardDiskStorageType_CustomHardDisk) 732 { 733 ComPtr<ICustomHardDisk> customHardDisk = hardDisk; 734 AssertBreak (!customHardDisk.isNull(), hrc = E_FAIL); 735 736 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1); RC_CHECK(); 737 rc = CFGMR3InsertString(pLunL1, "Driver", "VD"); RC_CHECK(); 738 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK(); 739 hrc = customHardDisk->COMGETTER(Location)(&str); H(); 740 STR_CONV(); 741 rc = CFGMR3InsertString(pCfg, "Path", psz); RC_CHECK(); 742 STR_FREE(); 743 hrc = customHardDisk->COMGETTER(Format)(&str); H(); 744 STR_CONV(); 745 rc = CFGMR3InsertString(pCfg, "Format", psz); RC_CHECK(); 729 746 STR_FREE(); 730 747 } -
trunk/src/VBox/Main/HardDiskImpl.cpp
r5027 r5101 1088 1088 HRESULT firstRC = S_OK; 1089 1089 com::ErrorInfoKeeper firstErr (true /* aIsNull */); 1090 1091 if (haveFirst == false) 1092 { 1093 /* Try if a plugin supports this format. */ 1094 ComObjPtr <HCustomHardDisk> obj; 1095 obj.createObject(); 1096 rc = obj->init (aVirtualBox, NULL, aLocation, 1097 FALSE /* aRegistered */); 1098 if (SUCCEEDED (rc)) 1099 { 1100 hardDisk = obj; 1101 return rc; 1102 } 1103 } 1090 1104 1091 1105 for (size_t i = 0; i < ELEMENTS (storageTypes); ++ i) … … 4212 4226 } 4213 4227 4228 //////////////////////////////////////////////////////////////////////////////// 4229 // HCustomHardDisk class 4230 //////////////////////////////////////////////////////////////////////////////// 4231 4232 // constructor / destructor 4233 //////////////////////////////////////////////////////////////////////////////// 4234 4235 HRESULT HCustomHardDisk::FinalConstruct() 4236 { 4237 HRESULT rc = HardDisk::FinalConstruct(); 4238 if (FAILED (rc)) 4239 return rc; 4240 4241 mState = NotCreated; 4242 4243 mStateCheckSem = NIL_RTSEMEVENTMULTI; 4244 mStateCheckWaiters = 0; 4245 4246 mSize = 0; 4247 mActualSize = 0; 4248 mContainer = NULL; 4249 4250 ComAssertRCRet (rc, E_FAIL); 4251 4252 return S_OK; 4253 } 4254 4255 void HCustomHardDisk::FinalRelease() 4256 { 4257 if (mContainer != NULL) 4258 VDDestroy (mContainer); 4259 4260 HardDisk::FinalRelease(); 4261 } 4262 4263 // public initializer/uninitializer for internal purposes only 4264 //////////////////////////////////////////////////////////////////////////////// 4265 4266 // public methods for internal purposes only 4267 ///////////////////////////////////////////////////////////////////////////// 4268 4269 /** 4270 * Initializes the custom hard disk object by reading its properties from 4271 * the given configuration node. The created hard disk will be marked as 4272 * registered on success. 4273 * 4274 * @param aHDNode <HardDisk> node 4275 * @param aCustomNode <VirtualDiskImage> node 4276 */ 4277 HRESULT HCustomHardDisk::init (VirtualBox *aVirtualBox, HardDisk *aParent, 4278 CFGNODE aHDNode, CFGNODE aCustomNode) 4279 { 4280 LogFlowThisFunc (("aHDNode=%p, aCustomNode=%p\n", aHDNode, aCustomNode)); 4281 4282 AssertReturn (aHDNode && aCustomNode, E_FAIL); 4283 4284 AutoLock alock (this); 4285 ComAssertRet (!isReady(), E_UNEXPECTED); 4286 4287 mStorageType = HardDiskStorageType_CustomHardDisk; 4288 4289 HRESULT rc = S_OK; 4290 int vrc = VINF_SUCCESS; 4291 do 4292 { 4293 rc = protectedInit (aVirtualBox, aParent); 4294 CheckComRCBreakRC (rc); 4295 4296 /* set ready to let protectedUninit() be called on failure */ 4297 setReady (true); 4298 4299 /* location (required) */ 4300 Bstr location; 4301 CFGLDRQueryBSTR (aCustomNode, "location", location.asOutParam()); 4302 4303 /* format (required) */ 4304 Bstr format; 4305 char *pszFormat; 4306 CFGLDRQueryBSTR (aCustomNode, "format", format.asOutParam()); 4307 vrc = RTStrUcs2ToUtf8(&pszFormat, format); 4308 4309 /* initialize the container */ 4310 vrc = VDCreate (pszFormat, VDError, this, &mContainer); 4311 ComAssertRCRet (vrc, E_FAIL); 4312 4313 RTStrFree(pszFormat); 4314 4315 rc = setLocation (location); 4316 CheckComRCBreakRC (rc); 4317 4318 mFormat = format; 4319 4320 /* load basic settings and children */ 4321 rc = loadSettings (aHDNode); 4322 CheckComRCBreakRC (rc); 4323 4324 mState = Created; 4325 mRegistered = TRUE; 4326 4327 /* Don't call queryInformation() for registered hard disks to 4328 * prevent the calling thread (i.e. the VirtualBox server startup 4329 * thread) from an unexpected freeze. The vital mId property (UUID) 4330 * is read from the registry file in loadSettings(). To get the rest, 4331 * the user will have to call COMGETTER(Accessible) manually. */ 4332 } 4333 while (0); 4334 4335 if (FAILED (rc)) 4336 uninit(); 4337 4338 return rc; 4339 } 4340 4341 /** 4342 * Initializes the custom hard disk object using the given image file name. 4343 * 4344 * @param aVirtualBox VirtualBox parent. 4345 * @param aParent Currently, must always be @c NULL. 4346 * @param aLocation Location of the virtual disk, or @c NULL to create an 4347 * image-less object. 4348 * @param aRegistered Whether to mark this disk as registered or not 4349 * (ignored when @a aFilePath is @c NULL, assuming @c FALSE) 4350 */ 4351 HRESULT HCustomHardDisk::init (VirtualBox *aVirtualBox, HardDisk *aParent, 4352 const BSTR aLocation, BOOL aRegistered /* = FALSE */) 4353 { 4354 LogFlowThisFunc (("aLocation='%ls', aRegistered=%d\n", aLocation, aRegistered)); 4355 4356 AssertReturn (aParent == NULL, E_FAIL); 4357 4358 AutoLock alock (this); 4359 ComAssertRet (!isReady(), E_UNEXPECTED); 4360 4361 mStorageType = HardDiskStorageType_CustomHardDisk; 4362 4363 HRESULT rc = S_OK; 4364 4365 do 4366 { 4367 rc = protectedInit (aVirtualBox, aParent); 4368 CheckComRCBreakRC (rc); 4369 4370 /* set ready to let protectedUninit() be called on failure */ 4371 setReady (true); 4372 4373 rc = setLocation (aLocation); 4374 CheckComRCBreakRC (rc); 4375 4376 /* currently, all custom hard disks are writethrough */ 4377 mType = HardDiskType_WritethroughHardDisk; 4378 4379 Assert (mId.isEmpty()); 4380 4381 if (aLocation && *aLocation) 4382 { 4383 int vrc; 4384 char *pszLocation = NULL; 4385 char *pszFormat = NULL; 4386 4387 mRegistered = aRegistered; 4388 mState = Created; 4389 4390 vrc = RTStrUcs2ToUtf8(&pszLocation, aLocation); 4391 if (VBOX_FAILURE(vrc)) 4392 { 4393 rc = E_FAIL; 4394 } 4395 4396 vrc = VDGetFormat(pszLocation, &pszFormat); 4397 if (VBOX_FAILURE(vrc)) 4398 { 4399 if (vrc == VERR_NOT_SUPPORTED) 4400 { 4401 /* @todo: show a understandable error string. */ 4402 } 4403 rc = E_FAIL; 4404 } 4405 4406 mFormat = Bstr(pszFormat); 4407 4408 /* Create the corresponding container. */ 4409 vrc = VDCreate (pszFormat, VDError, this, &mContainer); 4410 ComAssertRCRet (vrc, E_FAIL); 4411 4412 /* Call queryInformation() anyway (even if it will block), because 4413 * it is the only way to get the UUID of the existing VDI and 4414 * initialize the vital mId property. */ 4415 Bstr errMsg; 4416 rc = queryInformation (&errMsg); 4417 if (SUCCEEDED (rc)) 4418 { 4419 /* We are constructing a new HVirtualDiskImage object. If there 4420 * is a fatal accessibility error (we cannot read image UUID), 4421 * we have to fail. We do so even on non-fatal errors as well, 4422 * because it's not worth to keep going with the inaccessible 4423 * image from the very beginning (when nothing else depends on 4424 * it yet). */ 4425 if (!errMsg.isNull()) 4426 rc = setErrorBstr (E_FAIL, errMsg); 4427 } 4428 } 4429 else 4430 { 4431 mRegistered = FALSE; 4432 mState = NotCreated; 4433 mId.create(); 4434 } 4435 } 4436 while (0); 4437 4438 if (FAILED (rc)) 4439 uninit(); 4440 4441 return rc; 4442 } 4443 4444 /** 4445 * Uninitializes the instance and sets the ready flag to FALSE. 4446 * Called either from FinalRelease(), by the parent when it gets destroyed, 4447 * or by a third party when it decides this object is no more valid. 4448 */ 4449 void HCustomHardDisk::uninit() 4450 { 4451 LogFlowThisFunc (("\n")); 4452 4453 AutoLock alock (this); 4454 if (!isReady()) 4455 return; 4456 4457 HardDisk::protectedUninit (alock); 4458 } 4459 4460 // IHardDisk properties 4461 //////////////////////////////////////////////////////////////////////////////// 4462 4463 STDMETHODIMP HCustomHardDisk::COMGETTER(Description) (BSTR *aDescription) 4464 { 4465 if (!aDescription) 4466 return E_POINTER; 4467 4468 AutoLock alock (this); 4469 CHECK_READY(); 4470 4471 mDescription.cloneTo (aDescription); 4472 return S_OK; 4473 } 4474 4475 STDMETHODIMP HCustomHardDisk::COMSETTER(Description) (INPTR BSTR aDescription) 4476 { 4477 AutoLock alock (this); 4478 CHECK_READY(); 4479 4480 CHECK_BUSY_AND_READERS(); 4481 4482 return E_NOTIMPL; 4483 } 4484 4485 STDMETHODIMP HCustomHardDisk::COMGETTER(Size) (ULONG64 *aSize) 4486 { 4487 if (!aSize) 4488 return E_POINTER; 4489 4490 AutoLock alock (this); 4491 CHECK_READY(); 4492 4493 *aSize = mSize; 4494 return S_OK; 4495 } 4496 4497 STDMETHODIMP HCustomHardDisk::COMGETTER(ActualSize) (ULONG64 *aActualSize) 4498 { 4499 if (!aActualSize) 4500 return E_POINTER; 4501 4502 AutoLock alock (this); 4503 CHECK_READY(); 4504 4505 *aActualSize = mActualSize; 4506 return S_OK; 4507 } 4508 4509 // IVirtualDiskImage properties 4510 //////////////////////////////////////////////////////////////////////////////// 4511 4512 STDMETHODIMP HCustomHardDisk::COMGETTER(Location) (BSTR *aLocation) 4513 { 4514 if (!aLocation) 4515 return E_POINTER; 4516 4517 AutoLock alock (this); 4518 CHECK_READY(); 4519 4520 mLocation.cloneTo (aLocation); 4521 return S_OK; 4522 } 4523 4524 STDMETHODIMP HCustomHardDisk::COMSETTER(Location) (INPTR BSTR aLocation) 4525 { 4526 AutoLock alock (this); 4527 CHECK_READY(); 4528 4529 if (mState != NotCreated) 4530 return setError (E_ACCESSDENIED, 4531 tr ("Cannot change the file path of the existing hard disk '%ls'"), 4532 toString().raw()); 4533 4534 CHECK_BUSY_AND_READERS(); 4535 4536 /* append the default path if only a name is given */ 4537 Bstr path = aLocation; 4538 if (aLocation && *aLocation) 4539 { 4540 Utf8Str fp = aLocation; 4541 if (!RTPathHavePath (fp)) 4542 { 4543 AutoReaderLock propsLock (mVirtualBox->systemProperties()); 4544 path = Utf8StrFmt ("%ls%c%s", 4545 mVirtualBox->systemProperties()->defaultVDIFolder().raw(), 4546 RTPATH_DELIMITER, 4547 fp.raw()); 4548 } 4549 } 4550 4551 return setLocation (path); 4552 } 4553 4554 STDMETHODIMP HCustomHardDisk::COMGETTER(Created) (BOOL *aCreated) 4555 { 4556 if (!aCreated) 4557 return E_POINTER; 4558 4559 AutoLock alock (this); 4560 CHECK_READY(); 4561 4562 *aCreated = mState >= Created; 4563 return S_OK; 4564 } 4565 4566 STDMETHODIMP HCustomHardDisk::COMGETTER(Format) (BSTR *aFormat) 4567 { 4568 if (!aFormat) 4569 return E_POINTER; 4570 4571 AutoLock alock (this); 4572 CHECK_READY(); 4573 4574 mFormat.cloneTo (aFormat); 4575 return S_OK; 4576 } 4577 4578 // ICustomHardDisk methods 4579 ///////////////////////////////////////////////////////////////////////////// 4580 4581 STDMETHODIMP HCustomHardDisk::CreateDynamicImage (ULONG64 aSize, IProgress **aProgress) 4582 { 4583 if (!aProgress) 4584 return E_POINTER; 4585 4586 AutoLock alock (this); 4587 CHECK_READY(); 4588 4589 return createImage (aSize, TRUE /* aDynamic */, aProgress); 4590 } 4591 4592 STDMETHODIMP HCustomHardDisk::CreateFixedImage (ULONG64 aSize, IProgress **aProgress) 4593 { 4594 if (!aProgress) 4595 return E_POINTER; 4596 4597 AutoLock alock (this); 4598 CHECK_READY(); 4599 4600 return createImage (aSize, FALSE /* aDynamic */, aProgress); 4601 } 4602 4603 STDMETHODIMP HCustomHardDisk::DeleteImage() 4604 { 4605 AutoLock alock (this); 4606 CHECK_READY(); 4607 CHECK_BUSY_AND_READERS(); 4608 4609 return E_NOTIMPL; 4610 4611 /// @todo later 4612 } 4613 4614 // public/protected methods for internal purposes only 4615 ///////////////////////////////////////////////////////////////////////////// 4616 4617 /** 4618 * Attempts to mark the hard disk as registered. 4619 * Only VirtualBox can call this method. 4620 */ 4621 HRESULT HCustomHardDisk::trySetRegistered (BOOL aRegistered) 4622 { 4623 AutoLock alock (this); 4624 CHECK_READY(); 4625 4626 if (aRegistered) 4627 { 4628 if (mState == NotCreated) 4629 return setError (E_FAIL, 4630 tr ("The storage location '%ls' is not yet created for this hard disk"), 4631 mLocation.raw()); 4632 } 4633 else 4634 { 4635 ComAssertRet (mState >= Created, E_FAIL); 4636 } 4637 4638 return HardDisk::trySetRegistered (aRegistered); 4639 } 4640 4641 /** 4642 * Checks accessibility of this hard disk image only (w/o parents). 4643 * 4644 * @param aAccessError on output, a null string indicates the hard disk is 4645 * accessible, otherwise contains a message describing 4646 * the reason of inaccessibility. 4647 */ 4648 HRESULT HCustomHardDisk::getAccessible (Bstr &aAccessError) 4649 { 4650 AutoLock alock (this); 4651 CHECK_READY(); 4652 4653 if (mStateCheckSem != NIL_RTSEMEVENTMULTI) 4654 { 4655 /* An accessibility check in progress on some other thread, 4656 * wait for it to finish. */ 4657 4658 ComAssertRet (mStateCheckWaiters != (ULONG) ~0, E_FAIL); 4659 ++ mStateCheckWaiters; 4660 alock.leave(); 4661 4662 int vrc = RTSemEventMultiWait (mStateCheckSem, RT_INDEFINITE_WAIT); 4663 4664 alock.enter(); 4665 AssertReturn (mStateCheckWaiters != 0, E_FAIL); 4666 -- mStateCheckWaiters; 4667 if (mStateCheckWaiters == 0) 4668 { 4669 RTSemEventMultiDestroy (mStateCheckSem); 4670 mStateCheckSem = NIL_RTSEMEVENTMULTI; 4671 } 4672 4673 AssertRCReturn (vrc, E_FAIL); 4674 4675 /* don't touch aAccessError, it has been already set */ 4676 return S_OK; 4677 } 4678 4679 /* check the basic accessibility */ 4680 HRESULT rc = getBaseAccessible (aAccessError, true /* aCheckBusy */); 4681 if (FAILED (rc) || !aAccessError.isNull()) 4682 return rc; 4683 4684 if (mState >= Created) 4685 { 4686 return queryInformation (&aAccessError); 4687 } 4688 4689 aAccessError = Utf8StrFmt ("Hard disk location '%ls' is not yet created", 4690 mLocation.raw()); 4691 return S_OK; 4692 } 4693 4694 /** 4695 * Saves hard disk settings to the specified storage node and saves 4696 * all children to the specified hard disk node 4697 * 4698 * @param aHDNode <HardDisk> or <DiffHardDisk> node 4699 * @param aStorageNode <VirtualDiskImage> node 4700 */ 4701 HRESULT HCustomHardDisk::saveSettings (CFGNODE aHDNode, CFGNODE aStorageNode) 4702 { 4703 AssertReturn (aHDNode && aStorageNode, E_FAIL); 4704 4705 AutoLock alock (this); 4706 CHECK_READY(); 4707 4708 /* location (required) */ 4709 CFGLDRSetBSTR (aStorageNode, "location", mLocation); 4710 4711 CFGLDRSetBSTR (aStorageNode, "format", mFormat); 4712 /* save basic settings and children */ 4713 return HardDisk::saveSettings (aHDNode); 4714 } 4715 4716 /** 4717 * Returns the string representation of this hard disk. 4718 * When \a aShort is false, returns the full image file path. 4719 * Otherwise, returns the image file name only. 4720 * 4721 * @param aShort if true, a short representation is returned 4722 */ 4723 Bstr HCustomHardDisk::toString (bool aShort /* = false */) 4724 { 4725 AutoLock alock (this); 4726 4727 if (!aShort) 4728 return mLocation; 4729 else 4730 { 4731 Utf8Str fname = mLocation; 4732 return fname.mutableRaw(); 4733 } 4734 } 4735 4736 /** 4737 * Creates a clone of this hard disk by storing hard disk data in the given 4738 * VDI file. 4739 * 4740 * If the operation fails, @a aDeleteTarget will be set to @c true unless the 4741 * failure happened because the target file already existed. 4742 * 4743 * @param aId UUID to assign to the created image. 4744 * @param aTargetPath VDI file where the cloned image is to be to stored. 4745 * @param aProgress progress object to run during operation. 4746 * @param aDeleteTarget Whether it is recommended to delete target on 4747 * failure or not. 4748 */ 4749 HRESULT 4750 HCustomHardDisk::cloneToImage (const Guid &aId, const Utf8Str &aTargetPath, 4751 Progress *aProgress, bool &aDeleteTarget) 4752 { 4753 ComAssertMsgFailed (("Not implemented")); 4754 return E_NOTIMPL; 4755 } 4756 4757 /** 4758 * Creates a new differencing image for this hard disk with the given 4759 * VDI file name. 4760 * 4761 * @param aId UUID to assign to the created image 4762 * @param aTargetPath VDI file where to store the created differencing image 4763 * @param aProgress progress object to run during operation 4764 * (can be NULL) 4765 */ 4766 HRESULT 4767 HCustomHardDisk::createDiffImage (const Guid &aId, const Utf8Str &aTargetPath, 4768 Progress *aProgress) 4769 { 4770 ComAssertMsgFailed (("Not implemented")); 4771 return E_NOTIMPL; 4772 } 4773 4774 // private methods 4775 ///////////////////////////////////////////////////////////////////////////// 4776 4777 /** 4778 * Helper to set a new location. 4779 * 4780 * @note 4781 * Must be called from under the object's lock! 4782 */ 4783 HRESULT HCustomHardDisk::setLocation (const BSTR aLocation) 4784 { 4785 if (aLocation && *aLocation) 4786 { 4787 mLocation = aLocation; 4788 } 4789 else 4790 { 4791 mLocation.setNull(); 4792 } 4793 4794 return S_OK; 4795 } 4796 4797 /** 4798 * Helper to query information about the custom hard disk. 4799 * 4800 * @param aAccessError not used when NULL, otherwise see #getAccessible() 4801 * 4802 * @note Must be called from under the object's lock, only after 4803 * CHECK_BUSY_AND_READERS() succeeds. 4804 */ 4805 HRESULT HCustomHardDisk::queryInformation (Bstr *aAccessError) 4806 { 4807 AssertReturn (isLockedOnCurrentThread(), E_FAIL); 4808 4809 /* create a lock object to completely release it later */ 4810 AutoLock alock (this); 4811 4812 AssertReturn (mStateCheckWaiters == 0, E_FAIL); 4813 4814 ComAssertRet (mState >= Created, E_FAIL); 4815 4816 HRESULT rc = S_OK; 4817 int vrc = VINF_SUCCESS; 4818 4819 /* lazily create a semaphore */ 4820 vrc = RTSemEventMultiCreate (&mStateCheckSem); 4821 ComAssertRCRet (vrc, E_FAIL); 4822 4823 /* Reference the disk to prevent any concurrent modifications 4824 * after releasing the lock below (to unblock getters before 4825 * a lengthy operation). */ 4826 addReader(); 4827 4828 alock.leave(); 4829 4830 /* VBoxVHDD management interface needs to be optimized: we're opening a 4831 * file three times in a raw to get three bits of information. */ 4832 4833 Utf8Str location = mLocation; 4834 Bstr errMsg; 4835 4836 /* reset any previous error report from VDError() */ 4837 mLastVDError.setNull(); 4838 4839 do 4840 { 4841 Guid id, parentId; 4842 4843 /// @todo changed from VD_OPEN_FLAGS_READONLY to VD_OPEN_FLAGS_NORMAL, 4844 /// because otherwise registering a virtual harddisk which so far has no UUID will 4845 /// yield a null UUID. It cannot be added to a custom harddisk opened readonly, 4846 /// obviously. This of course changes locking behavior, but for now 4847 /// this is acceptable. A better solution needs to be found later. 4848 vrc = VDOpen (mContainer, location, VD_OPEN_FLAGS_NORMAL); 4849 if (VBOX_FAILURE (vrc)) 4850 break; 4851 4852 vrc = VDGetUuid (mContainer, 0, id.ptr()); 4853 if (VBOX_FAILURE (vrc)) 4854 break; 4855 vrc = VDGetParentUuid (mContainer, 0, parentId.ptr()); 4856 if (VBOX_FAILURE (vrc)) 4857 break; 4858 4859 if (!mId.isEmpty()) 4860 { 4861 /* check that the actual UUID of the image matches the stored UUID */ 4862 if (mId != id) 4863 { 4864 errMsg = Utf8StrFmt ( 4865 tr ("Actual UUID {%Vuuid} of the hard disk image '%s' doesn't " 4866 "match UUID {%Vuuid} stored in the registry"), 4867 id.ptr(), location.raw(), mId.ptr()); 4868 break; 4869 } 4870 } 4871 else 4872 { 4873 /* assgn an UUID read from the image file */ 4874 mId = id; 4875 } 4876 4877 if (mParent) 4878 { 4879 /* check parent UUID */ 4880 AutoLock parentLock (mParent); 4881 if (mParent->id() != parentId) 4882 { 4883 errMsg = Utf8StrFmt ( 4884 tr ("UUID {%Vuuid} of the parent image '%ls' stored in " 4885 "the hard disk image file '%s' doesn't match " 4886 "UUID {%Vuuid} stored in the registry"), 4887 parentId.raw(), mParent->toString().raw(), 4888 location.raw(), mParent->id().raw()); 4889 break; 4890 } 4891 } 4892 else if (!parentId.isEmpty()) 4893 { 4894 errMsg = Utf8StrFmt ( 4895 tr ("Hard disk image '%s' is a differencing image that is linked " 4896 "to a hard disk with UUID {%Vuuid} and cannot be used " 4897 "directly as a base hard disk"), 4898 location.raw(), parentId.raw()); 4899 break; 4900 } 4901 4902 /* get actual file size */ 4903 /// @todo is there a direct method in RT? 4904 { 4905 RTFILE file = NIL_RTFILE; 4906 vrc = RTFileOpen (&file, location, RTFILE_O_READ); 4907 if (VBOX_SUCCESS (vrc)) 4908 { 4909 uint64_t size = 0; 4910 vrc = RTFileGetSize (file, &size); 4911 if (VBOX_SUCCESS (vrc)) 4912 mActualSize = size; 4913 RTFileClose (file); 4914 } 4915 if (VBOX_FAILURE (vrc)) 4916 break; 4917 } 4918 4919 /* query logical size only for non-differencing images */ 4920 if (!mParent) 4921 { 4922 uint64_t size = VDGetSize (mContainer); 4923 /* convert to MBytes */ 4924 mSize = size / 1024 / 1024; 4925 } 4926 } 4927 while (0); 4928 4929 VDCloseAll (mContainer); 4930 4931 /* enter the lock again */ 4932 alock.enter(); 4933 4934 /* remove the reference */ 4935 releaseReader(); 4936 4937 if (FAILED (rc) || VBOX_FAILURE (vrc) || !errMsg.isNull()) 4938 { 4939 LogWarningFunc (("'%ls' is not accessible " 4940 "(rc=%08X, vrc=%Vrc, errMsg='%ls')\n", 4941 mLocation.raw(), rc, vrc, errMsg.raw())); 4942 4943 if (aAccessError) 4944 { 4945 if (!errMsg.isNull()) 4946 *aAccessError = errMsg; 4947 else if (!mLastVDError.isNull()) 4948 *aAccessError = mLastVDError; 4949 else if (VBOX_FAILURE (vrc)) 4950 *aAccessError = Utf8StrFmt ( 4951 tr ("Could not access hard disk image '%ls' (%Vrc)"), 4952 mLocation.raw(), vrc); 4953 } 4954 4955 /* downgrade to not accessible */ 4956 mState = Created; 4957 } 4958 else 4959 { 4960 if (aAccessError) 4961 aAccessError->setNull(); 4962 4963 mState = Accessible; 4964 } 4965 4966 /* inform waiters if there are any */ 4967 if (mStateCheckWaiters > 0) 4968 { 4969 RTSemEventMultiSignal (mStateCheckSem); 4970 } 4971 else 4972 { 4973 /* delete the semaphore ourselves */ 4974 RTSemEventMultiDestroy (mStateCheckSem); 4975 mStateCheckSem = NIL_RTSEMEVENTMULTI; 4976 } 4977 4978 /* cleanup the last error report from VDError() */ 4979 mLastVDError.setNull(); 4980 4981 return rc; 4982 } 4983 4984 /** 4985 * Helper to create hard disk images. 4986 * 4987 * @param aSize size in MB 4988 * @param aDynamic dynamic or fixed image 4989 * @param aProgress address of IProgress pointer to return 4990 */ 4991 HRESULT HCustomHardDisk::createImage (ULONG64 aSize, BOOL aDynamic, 4992 IProgress **aProgress) 4993 { 4994 ComAssertMsgFailed (("Not implemented")); 4995 return E_NOTIMPL; 4996 } 4997 4998 /* static */ 4999 DECLCALLBACK(int) HCustomHardDisk::VDITaskThread (RTTHREAD thread, void *pvUser) 5000 { 5001 AssertMsgFailed (("Not implemented")); 5002 return VERR_GENERAL_FAILURE; 5003 } 5004 5005 /* static */ 5006 DECLCALLBACK(void) HCustomHardDisk::VDError (void *pvUser, int rc, RT_SRC_POS_DECL, 5007 const char *pszFormat, va_list va) 5008 { 5009 HCustomHardDisk *that = static_cast <HCustomHardDisk *> (pvUser); 5010 AssertReturnVoid (that != NULL); 5011 5012 /// @todo pass the error message to the operation initiator 5013 Utf8Str err = Utf8StrFmt (pszFormat, va); 5014 if (VBOX_FAILURE (rc)) 5015 err = Utf8StrFmt ("%s (%Vrc)", err.raw(), rc); 5016 5017 if (that->mLastVDError.isNull()) 5018 that->mLastVDError = err; 5019 else 5020 that->mLastVDError = Utf8StrFmt 5021 ("%s.\n%s", that->mLastVDError.raw(), err.raw()); 5022 } 5023 -
trunk/src/VBox/Main/VirtualBoxImpl.cpp
r5061 r5101 946 946 rc = vmdk->init (this, NULL, NULL); 947 947 hardDisk = vmdk; 948 break; 949 } 950 case HardDiskStorageType_CustomHardDisk: 951 { 952 ComObjPtr <HCustomHardDisk> custom; 953 custom.createObject(); 954 rc = custom->init (this, NULL, NULL); 955 hardDisk = custom; 948 956 break; 949 957 } … … 3599 3607 break; 3600 3608 } 3609 CFGLDRGetChildNode (hdNode, "CustomHardDisk", 0, &storageNode); 3610 if (storageNode) 3611 { 3612 ComObjPtr <HCustomHardDisk> custom; 3613 custom.createObject(); 3614 rc = custom->init (this, NULL, hdNode, storageNode); 3615 break; 3616 } 3601 3617 3602 3618 /// @todo (dmik) later … … 3855 3871 { 3856 3872 CFGLDRAppendChildNode (hdNode, "VMDKImage", &storageNode); 3873 ComAssertBreak (storageNode, rc = E_FAIL); 3874 rc = hd->saveSettings (hdNode, storageNode); 3875 break; 3876 } 3877 case HardDiskStorageType_CustomHardDisk: 3878 { 3879 CFGLDRAppendChildNode (hdNode, "CustomHardDisk", &storageNode); 3857 3880 ComAssertBreak (storageNode, rc = E_FAIL); 3858 3881 rc = hd->saveSettings (hdNode, storageNode); -
trunk/src/VBox/Main/idl/VirtualBox.xidl
r5060 r5101 4928 4928 </desc> 4929 4929 </const> 4930 <const name="CustomHardDisk" value="3"> 4931 <desc> 4932 Disk formats supported through plugins (the access type ( 4933 wether regular file in the host OS or something else) is up to 4934 the plugin, see <link to="ICustomHardDisk"/>) 4935 </desc> 4936 </const> 4930 4937 </enum> 4931 4938 … … 5038 5045 <i>VMware VMDK Image</i>, a regular file in the file system of the 5039 5046 host OS (represented by the <link to="IVMDKImage"/> interface). 5047 </li> 5048 5049 <li> 5050 <i>Custom HardDisk</i>, a disk accessed via a plugin which is loaded 5051 when the disk is accessed (represented by the 5052 <link to="ICustomHardDisk"/> interface). 5040 5053 </li> 5041 5054 … … 5866 5879 folder</link> will be used as a path to the image file. 5867 5880 </note> 5881 5882 </desc> 5883 </attribute> 5884 5885 <attribute name="created" type="boolean" readonly="yes"> 5886 <desc> 5887 5888 Whether the virual disk image is created or not. For newly created 5889 hard disk objects or after a successful invocation of 5890 <link to="#deleteImage()"/>, this value is <tt>false</tt> until 5891 <link to="#createFixedImage()"/> or <link to="#createDynamicImage()"/> 5892 is called. 5893 5894 </desc> 5895 </attribute> 5896 5897 <method name="createDynamicImage"> 5898 5899 <desc> 5900 5901 Starts creating a dymically expanding hard disk image in the 5902 background. The previous image associated with this object, if 5903 any, must be deleted using <link to="#deleteImage"/>, otherwise 5904 the operation will fail. 5905 5906 <note> 5907 After the returned progress object reports that the 5908 operation is complete, this hard disk object can be 5909 <link to="IVirtualBox::registerHardDisk()">registered</link> within 5910 this VirtualBox installation. 5911 </note> 5912 5913 </desc> 5914 5915 <param name="size" type="unsigned long long" dir="in"> 5916 <desc>Maximum logical size of the hard disk in megabytes.</desc> 5917 </param> 5918 <param name="progress" type="IProgress" dir="return"> 5919 <desc>Progress object to track the operation completion.</desc> 5920 </param> 5921 5922 </method> 5923 5924 <method name="createFixedImage"> 5925 <desc> 5926 5927 Starts creating a fixed-size hard disk image in the background. The 5928 previous image, if any, must be deleted using 5929 <link to="#deleteImage"/>, otherwise the operation will fail. 5930 5931 <note> 5932 After the returned progress object reports that the 5933 operation is complete, this hard disk object can be 5934 <link to="IVirtualBox::registerHardDisk()">registered</link> within 5935 this VirtualBox installation. 5936 </note> 5937 5938 </desc> 5939 5940 <param name="size" type="unsigned long long" dir="in"> 5941 <desc>Logical size of the hard disk in megabytes.</desc> 5942 </param> 5943 <param name="progress" type="IProgress" dir="return"> 5944 <desc>Progress object to track the operation completion.</desc> 5945 </param> 5946 5947 </method> 5948 5949 <method name="deleteImage"> 5950 <desc> 5951 5952 Deletes the existing hard disk image. The hard disk must not be 5953 registered within this VirtualBox installation, otherwise the 5954 operation will fail. 5955 5956 <note> 5957 After this operation succeeds, it will be impossible to register the 5958 hard disk until the image file is created again. 5959 </note> 5960 5961 <note> 5962 This operation is valid only for non-differencing hard disks, after 5963 they are unregistered using 5964 <link to="IVirtualBox::unregisterHardDisk()"/>. 5965 </note> 5966 5967 </desc> 5968 </method> 5969 5970 </interface> 5971 5972 <!-- 5973 // ICustomHardDisk 5974 ///////////////////////////////////////////////////////////////////////// 5975 --> 5976 5977 <interface 5978 name="ICustomHardDisk" extends="$unknown" 5979 uuid="a7b0236d-3ff4-47c0-a4aa-ddc4ddc1141a" 5980 wsmap="managed" 5981 > 5982 <desc> 5983 5984 The ICustomHardDisk interface represents <link to="IHardDisk">virtual hard 5985 disks</link> that are supported through third-parite plugins. 5986 5987 Hard disks using custom images can be opened using 5988 <link to="IVirtualBox::openHardDisk()"/>. 5989 5990 Objects that support this interface also support the 5991 <link to="IHardDisk"/> interface. 5992 5993 When a new hard disk object is created from scratch, an image file for it 5994 is not automatically created. To do it, you need to specify a 5995 valid <link to="#filePath">file path</link>, and call 5996 <link to="#createFixedImage()"/> or <link to="#createDynamicImage()"/>. 5997 When it is done, the hard disk object can be registered by calling 5998 <link to="IVirtualBox::registerHardDisk()"/> and then 5999 <link to="IMachine::attachHardDisk()">attached</link> to 6000 virtual machines. 6001 6002 The <link to="IHardDisk::description">description</link> 6003 of the hard disk is stored in the VirtualBox 6004 configuration file, so it can be changed (at appropriate 6005 times) even when 6006 <link to="IHardDisk::accessible">accessible</link> returns 6007 <tt>false</tt>. However, the hard disk must not be 6008 attached to a running virtual machine. 6009 6010 </desc> 6011 6012 <attribute name="location" type="wstring"> 6013 <desc> 6014 6015 Full location path of the custom image of this hard disk. 6016 6017 When assigning a new path, it must be absolute (full path). 6018 When reading this propery, a full path is always returned. 6019 6020 <note> 6021 This property cannot be changed when <link to="#created"/> 6022 returns <tt>true</tt>. In this case, the specified file name can be 6023 absolute (full path) or relative to 6024 the <link to="IVirtualBox::homeFolder"> VirtualBox home 6025 directory</link>. If only a file name without any path is given, 6026 the <link to="ISystemProperties::defaultVDIFolder"> default VDI 6027 folder</link> will be used as a path to the image file. 6028 </note> 6029 6030 </desc> 6031 </attribute> 6032 6033 <attribute name="format" type="wstring" readonly="yes"> 6034 <desc> 6035 6036 The plugin name of the image file. 5868 6037 5869 6038 </desc> -
trunk/src/VBox/Main/include/HardDiskImpl.h
r4071 r5101 538 538 }; 539 539 540 //////////////////////////////////////////////////////////////////////////////// 541 542 class ATL_NO_VTABLE HCustomHardDisk : 543 public HardDisk, 544 public VirtualBoxSupportTranslation <HCustomHardDisk>, 545 public ICustomHardDisk 546 { 547 548 public: 549 550 VIRTUALBOXSUPPORTTRANSLATION_OVERRIDE(HCustomHardDisk) 551 552 DECLARE_NOT_AGGREGATABLE(HCustomHardDisk) 553 554 DECLARE_PROTECT_FINAL_CONSTRUCT() 555 556 BEGIN_COM_MAP(HCustomHardDisk) 557 COM_INTERFACE_ENTRY(ISupportErrorInfo) 558 COM_INTERFACE_ENTRY(IHardDisk) 559 COM_INTERFACE_ENTRY(ICustomHardDisk) 560 END_COM_MAP() 561 562 NS_DECL_ISUPPORTS 563 564 HRESULT FinalConstruct(); 565 void FinalRelease(); 566 567 // public initializer/uninitializer for internal purposes only 568 569 HRESULT init (VirtualBox *aVirtualBox, HardDisk *aParent, 570 CFGNODE aHDNode, CFGNODE aCustomNode); 571 HRESULT init (VirtualBox *aVirtualBox, HardDisk *aParent, 572 INPTR BSTR aLocation, BOOL aRegistered = FALSE); 573 void uninit(); 574 575 // IHardDisk properties 576 STDMETHOD(COMGETTER(Description)) (BSTR *aDescription); 577 STDMETHOD(COMSETTER(Description)) (INPTR BSTR aDescription); 578 STDMETHOD(COMGETTER(Size)) (ULONG64 *aSize); 579 STDMETHOD(COMGETTER(ActualSize)) (ULONG64 *aActualSize); 580 581 // IVirtualDiskImage properties 582 STDMETHOD(COMGETTER(Location)) (BSTR *aLocation); 583 STDMETHOD(COMSETTER(Location)) (INPTR BSTR aLocation); 584 STDMETHOD(COMGETTER(Format)) (BSTR *aFormat); 585 STDMETHOD(COMGETTER(Created)) (BOOL *aCreated); 586 587 // IVirtualDiskImage methods 588 STDMETHOD(CreateDynamicImage) (ULONG64 aSize, IProgress **aProgress); 589 STDMETHOD(CreateFixedImage) (ULONG64 aSize, IProgress **aProgress); 590 STDMETHOD(DeleteImage)(); 591 592 // public methods for internal purposes only 593 594 const Bstr &Location() const { return mLocation; } 595 596 HRESULT trySetRegistered (BOOL aRegistered); 597 HRESULT getAccessible (Bstr &aAccessError); 598 599 HRESULT saveSettings (CFGNODE aHDNode, CFGNODE aStorageNode); 600 601 Bstr toString (bool aShort = false); 602 603 HRESULT cloneToImage (const Guid &aId, const Utf8Str &aTargetPath, 604 Progress *aProgress, bool &aDeleteTarget); 605 HRESULT createDiffImage (const Guid &aId, const Utf8Str &aTargetPath, 606 Progress *aProgress); 607 608 // for VirtualBoxSupportErrorInfoImpl 609 static const wchar_t *getComponentName() { return L"CustomHardDisk"; } 610 611 private: 612 613 HRESULT setLocation (const BSTR aLocation); 614 HRESULT queryInformation (Bstr *aAccessError); 615 HRESULT createImage (ULONG64 aSize, BOOL aDynamic, IProgress **aProgress); 616 617 /** VDI asynchronous operation thread function */ 618 static DECLCALLBACK(int) VDITaskThread (RTTHREAD thread, void *pvUser); 619 620 static DECLCALLBACK(void) VDError (void *pvUser, int rc, RT_SRC_POS_DECL, 621 const char *pszFormat, va_list va); 622 623 enum State 624 { 625 NotCreated, 626 Created, 627 /* the following must be greater than Created */ 628 Accessible, 629 }; 630 631 State mState; 632 633 RTSEMEVENTMULTI mStateCheckSem; 634 ULONG mStateCheckWaiters; 635 636 Bstr mDescription; 637 638 ULONG64 mSize; 639 ULONG64 mActualSize; 640 641 Bstr mLocation; 642 Bstr mFormat; 643 644 PVBOXHDD mContainer; 645 646 Utf8Str mLastVDError; 647 648 friend class HardDisk; 649 }; 650 540 651 541 652 COM_DECL_READONLY_ENUM_AND_COLLECTION (HardDisk) -
trunk/src/VBox/Main/linux/server.cpp
r4100 r5101 120 120 NS_DECL_CLASSINFO(HVMDKImage) 121 121 NS_IMPL_THREADSAFE_ISUPPORTS2_CI(HVMDKImage, IHardDisk, IVMDKImage) 122 NS_DECL_CLASSINFO(HCustomHardDisk) 123 NS_IMPL_THREADSAFE_ISUPPORTS2_CI(HCustomHardDisk, IHardDisk, ICustomHardDisk) 122 124 NS_DECL_CLASSINFO(HardDiskAttachment) 123 125 NS_IMPL_THREADSAFE_ISUPPORTS1_CI(HardDiskAttachment, IHardDiskAttachment) -
trunk/src/VBox/Main/xml/VirtualBox-settings-common.xsd
r5074 r5101 254 254 </xsd:complexType> 255 255 </xsd:element> 256 <xsd:element name="CustomHardDisk"> 257 <xsd:complexType> 258 <xsd:attribute name="location" type="TLocalFile" use="required"/> 259 <xsd:attribute name="format" type="xsd:string" use="required"/> 260 </xsd:complexType> 261 </xsd:element> 256 262 </xsd:choice> 257 263 <xsd:element name="DiffHardDisk" type="TDiffHardDisk" minOccurs="0" maxOccurs="unbounded"/>
Note:
See TracChangeset
for help on using the changeset viewer.