Changeset 25887 in vbox for trunk/src/VBox
- Timestamp:
- Jan 18, 2010 11:58:58 AM (15 years ago)
- Location:
- trunk/src/VBox/Main
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/MediumImpl.cpp
r25886 r25887 5145 5145 5146 5146 /** 5147 * Implementation code called from Medium::taskThread for the "merge" task. 5148 * @param task 5149 * @param pvdOperationIfaces 5150 * @return 5151 */ 5152 HRESULT Medium::taskThreadMerge(Task &task, void *pvdOperationIfaces, bool fIsAsync) 5153 { 5154 HRESULT rc = S_OK; 5155 5156 PVDINTERFACE vdOperationIfaces = (PVDINTERFACE)pvdOperationIfaces; 5157 5158 /* The lock is also used as a signal from the task initiator (which 5159 * releases it only after RTThreadCreate()) that we can start the 5160 * job. We don't actually need the lock for anything else since the 5161 * object is protected by MediumState_Deleting and we don't modify 5162 * its sensitive fields below */ 5163 { 5164 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS); 5165 } 5166 5167 MergeChain *chain = task.d.chain.get(); 5168 5169 try 5170 { 5171 PVBOXHDD hdd; 5172 int vrc = VDCreate(m->vdDiskIfaces, &hdd); 5173 ComAssertRCThrow(vrc, E_FAIL); 5174 5175 try 5176 { 5177 /* Open all hard disks in the chain (they are in the 5178 * {parent,child} order in there. Note that we don't lock 5179 * objects in this chain since they must be in states 5180 * (Deleting and LockedWrite) that prevent from changing 5181 * their format and location fields from outside. */ 5182 5183 for (MergeChain::const_iterator it = chain->begin(); 5184 it != chain->end(); ++ it) 5185 { 5186 /* complex sanity (sane complexity) */ 5187 Assert((chain->isForward() && 5188 ((*it != chain->back() && 5189 (*it)->m->state == MediumState_Deleting) || 5190 (*it == chain->back() && 5191 (*it)->m->state == MediumState_LockedWrite))) || 5192 (!chain->isForward() && 5193 ((*it != chain->front() && 5194 (*it)->m->state == MediumState_Deleting) || 5195 (*it == chain->front() && 5196 (*it)->m->state == MediumState_LockedWrite)))); 5197 5198 Assert(*it == chain->target() || 5199 (*it)->m->backRefs.size() == 0); 5200 5201 /* open the first image with VDOPEN_FLAGS_INFO because 5202 * it's not necessarily the base one */ 5203 vrc = VDOpen(hdd, (*it)->m->strFormat.c_str(), 5204 (*it)->m->strLocationFull.c_str(), 5205 it == chain->begin() ? 5206 VD_OPEN_FLAGS_INFO : 0, 5207 (*it)->m->vdDiskIfaces); 5208 if (RT_FAILURE(vrc)) 5209 throw vrc; 5210 } 5211 5212 unsigned start = chain->isForward() ? 5213 0 : (unsigned)chain->size() - 1; 5214 unsigned end = chain->isForward() ? 5215 (unsigned)chain->size() - 1 : 0; 5216 5217 vrc = VDMerge(hdd, start, end, vdOperationIfaces); 5218 if (RT_FAILURE(vrc)) 5219 throw vrc; 5220 5221 /* update parent UUIDs */ 5222 /// @todo VDMerge should be taught to do so, including the 5223 /// multiple children case 5224 if (chain->isForward()) 5225 { 5226 /* target's UUID needs to be updated (note that target 5227 * is the only image in the container on success) */ 5228 vrc = VDSetParentUuid(hdd, 0, chain->parent()->m->id); 5229 if (RT_FAILURE(vrc)) 5230 throw vrc; 5231 } 5232 else 5233 { 5234 /* we need to update UUIDs of all source's children 5235 * which cannot be part of the container at once so 5236 * add each one in there individually */ 5237 if (chain->children().size() > 0) 5238 { 5239 for (MediaList::const_iterator it = chain->children().begin(); 5240 it != chain->children().end(); 5241 ++it) 5242 { 5243 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */ 5244 vrc = VDOpen(hdd, (*it)->m->strFormat.c_str(), 5245 (*it)->m->strLocationFull.c_str(), 5246 VD_OPEN_FLAGS_INFO, 5247 (*it)->m->vdDiskIfaces); 5248 if (RT_FAILURE(vrc)) 5249 throw vrc; 5250 5251 vrc = VDSetParentUuid(hdd, 1, 5252 chain->target()->m->id); 5253 if (RT_FAILURE(vrc)) 5254 throw vrc; 5255 5256 vrc = VDClose(hdd, false /* fDelete */); 5257 if (RT_FAILURE(vrc)) 5258 throw vrc; 5259 } 5260 } 5261 } 5262 } 5263 catch (HRESULT aRC) { rc = aRC; } 5264 catch (int aVRC) 5265 { 5266 throw setError(E_FAIL, 5267 tr("Could not merge the hard disk '%s' to '%s'%s"), 5268 chain->source()->m->strLocationFull.raw(), 5269 chain->target()->m->strLocationFull.raw(), 5270 vdError(aVRC).raw()); 5271 } 5272 5273 VDDestroy(hdd); 5274 } 5275 catch (HRESULT aRC) { rc = aRC; } 5276 5277 HRESULT rc2; 5278 5279 bool saveSettingsFailed = false; 5280 5281 if (SUCCEEDED(rc)) 5282 { 5283 /* all hard disks but the target were successfully deleted by 5284 * VDMerge; reparent the last one and uninitialize deleted */ 5285 5286 /* we set mParent & children() (note that thatLock is released 5287 * here), but lock VirtualBox first to follow the rule */ 5288 AutoWriteLock alock1(m->pVirtualBox COMMA_LOCKVAL_SRC_POS); 5289 AutoWriteLock alock2(m->pVirtualBox->hardDiskTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 5290 5291 Medium *pSource = chain->source(); 5292 Medium *pTarget = chain->target(); 5293 5294 if (chain->isForward()) 5295 { 5296 /* first, unregister the target since it may become a base 5297 * hard disk which needs re-registration */ 5298 rc2 = pTarget->m->pVirtualBox->unregisterHardDisk(pTarget, false /* aSaveSettings */); 5299 AssertComRC(rc2); 5300 5301 /* then, reparent it and disconnect the deleted branch at 5302 * both ends (chain->parent() is source's parent) */ 5303 pTarget->deparent(); 5304 pTarget->m->pParent = chain->parent(); 5305 if (pTarget->m->pParent) 5306 { 5307 pTarget->m->pParent->m->llChildren.push_back(pTarget); 5308 pSource->deparent(); 5309 } 5310 5311 /* then, register again */ 5312 rc2 = pTarget->m->pVirtualBox->registerHardDisk(pTarget, false /* aSaveSettings */); 5313 AssertComRC(rc2); 5314 } 5315 else 5316 { 5317 Assert(pTarget->getChildren().size() == 1); 5318 Medium *targetChild = pTarget->getChildren().front(); 5319 5320 /* disconnect the deleted branch at the elder end */ 5321 targetChild->deparent(); 5322 5323 const MediaList &children = chain->children(); 5324 5325 /* reparent source's chidren and disconnect the deleted 5326 * branch at the younger end m*/ 5327 if (children.size() > 0) 5328 { 5329 /* obey {parent,child} lock order */ 5330 AutoWriteLock sourceLock(pSource COMMA_LOCKVAL_SRC_POS); 5331 5332 for (MediaList::const_iterator it = children.begin(); 5333 it != children.end(); 5334 ++it) 5335 { 5336 AutoWriteLock childLock(*it COMMA_LOCKVAL_SRC_POS); 5337 5338 Medium *p = *it; 5339 p->deparent(); // removes p from source 5340 pTarget->m->llChildren.push_back(p); 5341 p->m->pParent = pTarget; 5342 } 5343 } 5344 } 5345 5346 /* try to save the hard disk registry */ 5347 rc = m->pVirtualBox->saveSettings(); 5348 5349 if (SUCCEEDED(rc)) 5350 { 5351 /* unregister and uninitialize all hard disks in the chain 5352 * but the target */ 5353 5354 for (MergeChain::iterator it = chain->begin(); 5355 it != chain->end();) 5356 { 5357 if (*it == chain->target()) 5358 { 5359 ++ it; 5360 continue; 5361 } 5362 5363 rc2 = (*it)->m->pVirtualBox-> 5364 unregisterHardDisk(*it, false /* aSaveSettings */); 5365 AssertComRC(rc2); 5366 5367 /* now, uninitialize the deleted hard disk (note that 5368 * due to the Deleting state, uninit() will not touch 5369 * the parent-child relationship so we need to 5370 * uninitialize each disk individually) */ 5371 5372 /* note that the operation initiator hard disk (which is 5373 * normally also the source hard disk) is a special case 5374 * -- there is one more caller added by Task to it which 5375 * we must release. Also, if we are in sync mode, the 5376 * caller may still hold an AutoCaller instance for it 5377 * and therefore we cannot uninit() it (it's therefore 5378 * the caller's responsibility) */ 5379 if (*it == this) 5380 task.m_autoCaller.release(); 5381 5382 /* release the caller added by MergeChain before 5383 * uninit() */ 5384 (*it)->releaseCaller(); 5385 5386 if (fIsAsync || *it != this) 5387 (*it)->uninit(); 5388 5389 /* delete (to prevent uninitialization in MergeChain 5390 * dtor) and advance to the next item */ 5391 it = chain->erase(it); 5392 } 5393 5394 /* Note that states of all other hard disks (target, parent, 5395 * children) will be restored by the MergeChain dtor */ 5396 } 5397 else 5398 { 5399 /* too bad if we fail, but we'll need to rollback everything 5400 * we did above to at least keep the HD tree in sync with 5401 * the current registry on disk */ 5402 5403 saveSettingsFailed = true; 5404 5405 /// @todo NEWMEDIA implement a proper undo 5406 5407 AssertFailed(); 5408 } 5409 } 5410 5411 if (FAILED(rc)) 5412 { 5413 /* Here we come if either VDMerge() failed (in which case we 5414 * assume that it tried to do everything to make a further 5415 * retry possible -- e.g. not deleted intermediate hard disks 5416 * and so on) or VirtualBox::saveSettings() failed (where we 5417 * should have the original tree but with intermediate storage 5418 * units deleted by VDMerge()). We have to only restore states 5419 * (through the MergeChain dtor) unless we are run synchronously 5420 * in which case it's the responsibility of the caller as stated 5421 * in the mergeTo() docs. The latter also implies that we 5422 * don't own the merge chain, so release it in this case. */ 5423 5424 if (!fIsAsync) 5425 task.d.chain.release(); 5426 5427 NOREF(saveSettingsFailed); 5428 } 5429 5430 return rc; 5431 } 5432 5433 /** 5434 * Implementation code called from Medium::taskThread for the "clone" task. 5435 * @param task 5436 * @param pvdOperationIfaces 5437 * @return 5438 */ 5439 HRESULT Medium::taskThreadClone(Task &task, void *pvdOperationIfaces) 5440 { 5441 HRESULT rc = S_OK; 5442 5443 PVDINTERFACE vdOperationIfaces = (PVDINTERFACE)pvdOperationIfaces; 5444 5445 ComObjPtr<Medium> &pTarget = task.d.target; 5446 ComObjPtr<Medium> &pParent = task.d.parentDisk; 5447 5448 /* Lock all in {parent,child} order. The lock is also used as a 5449 * signal from the task initiator (which releases it only after 5450 * RTThreadCreate()) that we can start the job. */ 5451 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS); 5452 5453 ImageChain *srcChain = task.d.source.get(); 5454 ImageChain *parentChain = task.d.parent.get(); 5455 5456 uint64_t size = 0, logicalSize = 0; 5457 5458 /* The object may request a specific UUID (through a special form of 5459 * the setLocation() argument). Otherwise we have to generate it */ 5460 Guid targetId = pTarget->m->id; 5461 bool generateUuid = targetId.isEmpty(); 5462 if (generateUuid) 5463 { 5464 targetId.create(); 5465 /* VirtualBox::registerHardDisk() will need UUID */ 5466 unconst(pTarget->m->id) = targetId; 5467 } 5468 5469 try 5470 { 5471 PVBOXHDD hdd; 5472 int vrc = VDCreate(m->vdDiskIfaces, &hdd); 5473 ComAssertRCThrow(vrc, E_FAIL); 5474 5475 try 5476 { 5477 /* Open all hard disk images in the source chain. */ 5478 for (MediaList::const_iterator it = srcChain->begin(); 5479 it != srcChain->end(); 5480 ++it) 5481 { 5482 /* sanity check */ 5483 Assert((*it)->m->state == MediumState_LockedRead); 5484 5485 /** Open all images in read-only mode. */ 5486 vrc = VDOpen(hdd, 5487 (*it)->m->strFormat.c_str(), 5488 (*it)->m->strLocationFull.c_str(), 5489 VD_OPEN_FLAGS_READONLY, 5490 (*it)->m->vdDiskIfaces); 5491 if (RT_FAILURE(vrc)) 5492 throw setError(E_FAIL, 5493 tr("Could not open the hard disk storage unit '%s'%s"), 5494 (*it)->m->strLocationFull.raw(), 5495 vdError(vrc).raw()); 5496 } 5497 5498 Utf8Str targetFormat(pTarget->m->strFormat); 5499 Utf8Str targetLocation(pTarget->m->strLocationFull); 5500 5501 Assert( pTarget->m->state == MediumState_Creating 5502 || pTarget->m->state == MediumState_LockedWrite); 5503 Assert(m->state == MediumState_LockedRead); 5504 Assert(pParent.isNull() || pParent->m->state == MediumState_LockedRead); 5505 5506 /* unlock before the potentially lengthy operation */ 5507 thisLock.leave(); 5508 5509 /* ensure the target directory exists */ 5510 rc = VirtualBox::ensureFilePathExists(targetLocation); 5511 if (FAILED(rc)) throw rc; 5512 5513 PVBOXHDD targetHdd; 5514 vrc = VDCreate(m->vdDiskIfaces, &targetHdd); 5515 ComAssertRCThrow(vrc, E_FAIL); 5516 5517 try 5518 { 5519 /* Open all hard disk images in the parent chain. */ 5520 for (MediaList::const_iterator it = parentChain->begin(); 5521 it != parentChain->end(); 5522 ++it) 5523 { 5524 /** @todo r=klaus (*it) is not locked, lots of 5525 * race opportunities below */ 5526 /* sanity check */ 5527 Assert( (*it)->m->state == MediumState_LockedRead 5528 || (*it)->m->state == MediumState_LockedWrite); 5529 5530 /* Open all images in appropriate mode. */ 5531 vrc = VDOpen(targetHdd, 5532 (*it)->m->strFormat.c_str(), 5533 (*it)->m->strLocationFull.c_str(), 5534 ((*it)->m->state == MediumState_LockedWrite) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY, 5535 (*it)->m->vdDiskIfaces); 5536 if (RT_FAILURE(vrc)) 5537 throw setError(E_FAIL, 5538 tr("Could not open the hard disk storage unit '%s'%s"), 5539 (*it)->m->strLocationFull.raw(), 5540 vdError(vrc).raw()); 5541 } 5542 5543 /** @todo r=klaus target isn't locked, race getting the state */ 5544 vrc = VDCopy(hdd, 5545 VD_LAST_IMAGE, 5546 targetHdd, 5547 targetFormat.c_str(), 5548 pTarget->m->state == MediumState_Creating ? targetLocation.raw() : (char *)NULL, 5549 false, 5550 0, 5551 task.d.variant, 5552 targetId.raw(), 5553 NULL, 5554 pTarget->m->vdDiskIfaces, 5555 vdOperationIfaces); 5556 if (RT_FAILURE(vrc)) 5557 throw setError(E_FAIL, 5558 tr("Could not create the clone hard disk '%s'%s"), 5559 targetLocation.raw(), vdError(vrc).raw()); 5560 5561 size = VDGetFileSize(targetHdd, 0); 5562 logicalSize = VDGetSize(targetHdd, 0) / _1M; 5563 } 5564 catch (HRESULT aRC) { rc = aRC; } 5565 5566 VDDestroy(targetHdd); 5567 } 5568 catch (HRESULT aRC) { rc = aRC; } 5569 5570 VDDestroy(hdd); 5571 } 5572 catch (HRESULT aRC) { rc = aRC; } 5573 5574 /* Only do the parent changes for newly created images. */ 5575 if (pTarget->m->state == MediumState_Creating) 5576 { 5577 if (SUCCEEDED(rc)) 5578 { 5579 /* we set mParent & children() (note that thatLock is released 5580 * here), but lock VirtualBox first to follow the rule */ 5581 AutoWriteLock alock1(m->pVirtualBox COMMA_LOCKVAL_SRC_POS); 5582 AutoWriteLock alock2(m->pVirtualBox->hardDiskTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 5583 5584 Assert(pTarget->m->pParent.isNull()); 5585 5586 if (pParent) 5587 { 5588 /* associate the clone with the parent and deassociate 5589 * from VirtualBox */ 5590 pTarget->m->pParent = pParent; 5591 pParent->m->llChildren.push_back(pTarget); 5592 5593 /* register with mVirtualBox as the last step and move to 5594 * Created state only on success (leaving an orphan file is 5595 * better than breaking media registry consistency) */ 5596 rc = pParent->m->pVirtualBox->registerHardDisk(pTarget); 5597 5598 if (FAILED(rc)) 5599 /* break parent association on failure to register */ 5600 pTarget->deparent(); // removes target from parent 5601 } 5602 else 5603 { 5604 /* just register */ 5605 rc = m->pVirtualBox->registerHardDisk(pTarget); 5606 } 5607 } 5608 } 5609 5610 thisLock.maybeEnter(); 5611 5612 if (pTarget->m->state == MediumState_Creating) 5613 { 5614 if (SUCCEEDED(rc)) 5615 { 5616 pTarget->m->state = MediumState_Created; 5617 5618 pTarget->m->size = size; 5619 pTarget->m->logicalSize = logicalSize; 5620 } 5621 else 5622 { 5623 /* back to NotCreated on failure */ 5624 pTarget->m->state = MediumState_NotCreated; 5625 5626 /* reset UUID to prevent it from being reused next time */ 5627 if (generateUuid) 5628 unconst(pTarget->m->id).clear(); 5629 } 5630 } 5631 5632 /* Everything is explicitly unlocked when the task exits, 5633 * as the task destruction also destroys the source chain. */ 5634 5635 /* Make sure the source chain is released early. It could happen 5636 * that we get a deadlock in Appliance::Import when Medium::Close 5637 * is called & the source chain is released at the same time. */ 5638 task.d.source.reset(); 5639 5640 return rc; 5641 } 5642 5643 5644 /** 5147 5645 * Thread function for time-consuming tasks. 5148 5646 * … … 5200 5698 break; 5201 5699 5202 ////////////////////////////////////////////////////////////////////////5203 5204 5700 case Task::Merge: 5205 { 5206 /* The lock is also used as a signal from the task initiator (which 5207 * releases it only after RTThreadCreate()) that we can start the 5208 * job. We don't actually need the lock for anything else since the 5209 * object is protected by MediumState_Deleting and we don't modify 5210 * its sensitive fields below */ 5211 { 5212 AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS); 5213 } 5214 5215 MergeChain *chain = task->d.chain.get(); 5216 5217 #if 0 5218 LogFlow(("*** MERGE forward = %RTbool\n", chain->isForward())); 5219 #endif 5220 5221 try 5222 { 5223 PVBOXHDD hdd; 5224 int vrc = VDCreate(that->m->vdDiskIfaces, &hdd); 5225 ComAssertRCThrow(vrc, E_FAIL); 5226 5227 try 5228 { 5229 /* Open all hard disks in the chain (they are in the 5230 * {parent,child} order in there. Note that we don't lock 5231 * objects in this chain since they must be in states 5232 * (Deleting and LockedWrite) that prevent from changing 5233 * their format and location fields from outside. */ 5234 5235 for (MergeChain::const_iterator it = chain->begin(); 5236 it != chain->end(); ++ it) 5237 { 5238 /* complex sanity (sane complexity) */ 5239 Assert((chain->isForward() && 5240 ((*it != chain->back() && 5241 (*it)->m->state == MediumState_Deleting) || 5242 (*it == chain->back() && 5243 (*it)->m->state == MediumState_LockedWrite))) || 5244 (!chain->isForward() && 5245 ((*it != chain->front() && 5246 (*it)->m->state == MediumState_Deleting) || 5247 (*it == chain->front() && 5248 (*it)->m->state == MediumState_LockedWrite)))); 5249 5250 Assert(*it == chain->target() || 5251 (*it)->m->backRefs.size() == 0); 5252 5253 /* open the first image with VDOPEN_FLAGS_INFO because 5254 * it's not necessarily the base one */ 5255 vrc = VDOpen(hdd, (*it)->m->strFormat.c_str(), 5256 (*it)->m->strLocationFull.c_str(), 5257 it == chain->begin() ? 5258 VD_OPEN_FLAGS_INFO : 0, 5259 (*it)->m->vdDiskIfaces); 5260 if (RT_FAILURE(vrc)) 5261 throw vrc; 5262 #if 0 5263 LogFlow(("*** MERGE disk = %s\n", (*it)->m->strLocationFull.raw())); 5264 #endif 5265 } 5266 5267 unsigned start = chain->isForward() ? 5268 0 : (unsigned)chain->size() - 1; 5269 unsigned end = chain->isForward() ? 5270 (unsigned)chain->size() - 1 : 0; 5271 #if 0 5272 LogFlow(("*** MERGE from %d to %d\n", start, end)); 5273 #endif 5274 vrc = VDMerge(hdd, start, end, vdOperationIfaces); 5275 if (RT_FAILURE(vrc)) 5276 throw vrc; 5277 5278 /* update parent UUIDs */ 5279 /// @todo VDMerge should be taught to do so, including the 5280 /// multiple children case 5281 if (chain->isForward()) 5282 { 5283 /* target's UUID needs to be updated (note that target 5284 * is the only image in the container on success) */ 5285 vrc = VDSetParentUuid(hdd, 0, chain->parent()->m->id); 5286 if (RT_FAILURE(vrc)) 5287 throw vrc; 5288 } 5289 else 5290 { 5291 /* we need to update UUIDs of all source's children 5292 * which cannot be part of the container at once so 5293 * add each one in there individually */ 5294 if (chain->children().size() > 0) 5295 { 5296 for (MediaList::const_iterator it = chain->children().begin(); 5297 it != chain->children().end(); 5298 ++it) 5299 { 5300 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */ 5301 vrc = VDOpen(hdd, (*it)->m->strFormat.c_str(), 5302 (*it)->m->strLocationFull.c_str(), 5303 VD_OPEN_FLAGS_INFO, 5304 (*it)->m->vdDiskIfaces); 5305 if (RT_FAILURE(vrc)) 5306 throw vrc; 5307 5308 vrc = VDSetParentUuid(hdd, 1, 5309 chain->target()->m->id); 5310 if (RT_FAILURE(vrc)) 5311 throw vrc; 5312 5313 vrc = VDClose(hdd, false /* fDelete */); 5314 if (RT_FAILURE(vrc)) 5315 throw vrc; 5316 } 5317 } 5318 } 5319 } 5320 catch (HRESULT aRC) { rc = aRC; } 5321 catch (int aVRC) 5322 { 5323 throw setError(E_FAIL, 5324 tr("Could not merge the hard disk '%s' to '%s'%s"), 5325 chain->source()->m->strLocationFull.raw(), 5326 chain->target()->m->strLocationFull.raw(), 5327 that->vdError(aVRC).raw()); 5328 } 5329 5330 VDDestroy(hdd); 5331 } 5332 catch (HRESULT aRC) { rc = aRC; } 5333 5334 HRESULT rc2; 5335 5336 bool saveSettingsFailed = false; 5337 5338 if (SUCCEEDED(rc)) 5339 { 5340 /* all hard disks but the target were successfully deleted by 5341 * VDMerge; reparent the last one and uninitialize deleted */ 5342 5343 /* we set mParent & children() (note that thatLock is released 5344 * here), but lock VirtualBox first to follow the rule */ 5345 AutoWriteLock alock1(that->m->pVirtualBox COMMA_LOCKVAL_SRC_POS); 5346 AutoWriteLock alock2(that->m->pVirtualBox->hardDiskTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 5347 5348 Medium *pSource = chain->source(); 5349 Medium *pTarget = chain->target(); 5350 5351 if (chain->isForward()) 5352 { 5353 /* first, unregister the target since it may become a base 5354 * hard disk which needs re-registration */ 5355 rc2 = pTarget->m->pVirtualBox->unregisterHardDisk(pTarget, false /* aSaveSettings */); 5356 AssertComRC(rc2); 5357 5358 /* then, reparent it and disconnect the deleted branch at 5359 * both ends (chain->parent() is source's parent) */ 5360 pTarget->deparent(); 5361 pTarget->m->pParent = chain->parent(); 5362 if (pTarget->m->pParent) 5363 { 5364 pTarget->m->pParent->m->llChildren.push_back(pTarget); 5365 pSource->deparent(); 5366 } 5367 5368 /* then, register again */ 5369 rc2 = pTarget->m->pVirtualBox->registerHardDisk(pTarget, false /* aSaveSettings */); 5370 AssertComRC(rc2); 5371 } 5372 else 5373 { 5374 Assert(pTarget->getChildren().size() == 1); 5375 Medium *targetChild = pTarget->getChildren().front(); 5376 5377 /* disconnect the deleted branch at the elder end */ 5378 targetChild->deparent(); 5379 5380 const MediaList &children = chain->children(); 5381 5382 /* reparent source's chidren and disconnect the deleted 5383 * branch at the younger end m*/ 5384 if (children.size() > 0) 5385 { 5386 /* obey {parent,child} lock order */ 5387 AutoWriteLock sourceLock(pSource COMMA_LOCKVAL_SRC_POS); 5388 5389 for (MediaList::const_iterator it = children.begin(); 5390 it != children.end(); 5391 ++it) 5392 { 5393 AutoWriteLock childLock(*it COMMA_LOCKVAL_SRC_POS); 5394 5395 Medium *p = *it; 5396 p->deparent(); // removes p from source 5397 pTarget->m->llChildren.push_back(p); 5398 p->m->pParent = pTarget; 5399 } 5400 } 5401 } 5402 5403 /* try to save the hard disk registry */ 5404 rc = that->m->pVirtualBox->saveSettings(); 5405 5406 if (SUCCEEDED(rc)) 5407 { 5408 /* unregister and uninitialize all hard disks in the chain 5409 * but the target */ 5410 5411 for (MergeChain::iterator it = chain->begin(); 5412 it != chain->end();) 5413 { 5414 if (*it == chain->target()) 5415 { 5416 ++ it; 5417 continue; 5418 } 5419 5420 rc2 = (*it)->m->pVirtualBox-> 5421 unregisterHardDisk(*it, false /* aSaveSettings */); 5422 AssertComRC(rc2); 5423 5424 /* now, uninitialize the deleted hard disk (note that 5425 * due to the Deleting state, uninit() will not touch 5426 * the parent-child relationship so we need to 5427 * uninitialize each disk individually) */ 5428 5429 /* note that the operation initiator hard disk (which is 5430 * normally also the source hard disk) is a special case 5431 * -- there is one more caller added by Task to it which 5432 * we must release. Also, if we are in sync mode, the 5433 * caller may still hold an AutoCaller instance for it 5434 * and therefore we cannot uninit() it (it's therefore 5435 * the caller's responsibility) */ 5436 if (*it == that) 5437 task->m_autoCaller.release(); 5438 5439 /* release the caller added by MergeChain before 5440 * uninit() */ 5441 (*it)->releaseCaller(); 5442 5443 if (fIsAsync || *it != that) 5444 (*it)->uninit(); 5445 5446 /* delete (to prevent uninitialization in MergeChain 5447 * dtor) and advance to the next item */ 5448 it = chain->erase(it); 5449 } 5450 5451 /* Note that states of all other hard disks (target, parent, 5452 * children) will be restored by the MergeChain dtor */ 5453 } 5454 else 5455 { 5456 /* too bad if we fail, but we'll need to rollback everything 5457 * we did above to at least keep the HD tree in sync with 5458 * the current registry on disk */ 5459 5460 saveSettingsFailed = true; 5461 5462 /// @todo NEWMEDIA implement a proper undo 5463 5464 AssertFailed(); 5465 } 5466 } 5467 5468 if (FAILED(rc)) 5469 { 5470 /* Here we come if either VDMerge() failed (in which case we 5471 * assume that it tried to do everything to make a further 5472 * retry possible -- e.g. not deleted intermediate hard disks 5473 * and so on) or VirtualBox::saveSettings() failed (where we 5474 * should have the original tree but with intermediate storage 5475 * units deleted by VDMerge()). We have to only restore states 5476 * (through the MergeChain dtor) unless we are run synchronously 5477 * in which case it's the responsibility of the caller as stated 5478 * in the mergeTo() docs. The latter also implies that we 5479 * don't own the merge chain, so release it in this case. */ 5480 5481 if (!fIsAsync) 5482 task->d.chain.release(); 5483 5484 NOREF(saveSettingsFailed); 5485 } 5486 5487 break; 5488 } 5489 5490 //////////////////////////////////////////////////////////////////////// 5701 rc = that->taskThreadMerge(*task, (void*)vdOperationIfaces, fIsAsync); 5702 break; 5491 5703 5492 5704 case Task::Clone: 5493 { 5494 ComObjPtr<Medium> &pTarget = task->d.target; 5495 ComObjPtr<Medium> &pParent = task->d.parentDisk; 5496 5497 /* Lock all in {parent,child} order. The lock is also used as a 5498 * signal from the task initiator (which releases it only after 5499 * RTThreadCreate()) that we can start the job. */ 5500 AutoMultiWriteLock3 thatLock(that, pTarget, pParent COMMA_LOCKVAL_SRC_POS); 5501 5502 ImageChain *srcChain = task->d.source.get(); 5503 ImageChain *parentChain = task->d.parent.get(); 5504 5505 uint64_t size = 0, logicalSize = 0; 5506 5507 /* The object may request a specific UUID (through a special form of 5508 * the setLocation() argument). Otherwise we have to generate it */ 5509 Guid targetId = pTarget->m->id; 5510 bool generateUuid = targetId.isEmpty(); 5511 if (generateUuid) 5512 { 5513 targetId.create(); 5514 /* VirtualBox::registerHardDisk() will need UUID */ 5515 unconst(pTarget->m->id) = targetId; 5516 } 5517 5518 try 5519 { 5520 PVBOXHDD hdd; 5521 int vrc = VDCreate(that->m->vdDiskIfaces, &hdd); 5522 ComAssertRCThrow(vrc, E_FAIL); 5523 5524 try 5525 { 5526 /* Open all hard disk images in the source chain. */ 5527 for (MediaList::const_iterator it = srcChain->begin(); 5528 it != srcChain->end(); 5529 ++it) 5530 { 5531 /* sanity check */ 5532 Assert((*it)->m->state == MediumState_LockedRead); 5533 5534 /** Open all images in read-only mode. */ 5535 vrc = VDOpen(hdd, (*it)->m->strFormat.c_str(), 5536 (*it)->m->strLocationFull.c_str(), 5537 VD_OPEN_FLAGS_READONLY, 5538 (*it)->m->vdDiskIfaces); 5539 if (RT_FAILURE(vrc)) 5540 { 5541 throw setError(E_FAIL, 5542 tr("Could not open the hard disk storage unit '%s'%s"), 5543 (*it)->m->strLocationFull.raw(), 5544 that->vdError(vrc).raw()); 5545 } 5546 } 5547 5548 Utf8Str targetFormat(pTarget->m->strFormat); 5549 Utf8Str targetLocation(pTarget->m->strLocationFull); 5550 5551 Assert( pTarget->m->state == MediumState_Creating 5552 || pTarget->m->state == MediumState_LockedWrite); 5553 Assert(that->m->state == MediumState_LockedRead); 5554 Assert(pParent.isNull() || pParent->m->state == MediumState_LockedRead); 5555 5556 /* unlock before the potentially lengthy operation */ 5557 thatLock.leave(); 5558 5559 /* ensure the target directory exists */ 5560 rc = VirtualBox::ensureFilePathExists(targetLocation); 5561 if (FAILED(rc)) throw rc; 5562 5563 PVBOXHDD targetHdd; 5564 vrc = VDCreate(that->m->vdDiskIfaces, &targetHdd); 5565 ComAssertRCThrow(vrc, E_FAIL); 5566 5567 try 5568 { 5569 /* Open all hard disk images in the parent chain. */ 5570 for (MediaList::const_iterator it = parentChain->begin(); 5571 it != parentChain->end(); 5572 ++it) 5573 { 5574 /** @todo r=klaus (*it) is not locked, lots of 5575 * race opportunities below */ 5576 /* sanity check */ 5577 Assert( (*it)->m->state == MediumState_LockedRead 5578 || (*it)->m->state == MediumState_LockedWrite); 5579 5580 /* Open all images in appropriate mode. */ 5581 vrc = VDOpen(targetHdd, (*it)->m->strFormat.c_str(), 5582 (*it)->m->strLocationFull.c_str(), 5583 ((*it)->m->state == MediumState_LockedWrite) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY, 5584 (*it)->m->vdDiskIfaces); 5585 if (RT_FAILURE(vrc)) 5586 { 5587 throw setError(E_FAIL, 5588 tr("Could not open the hard disk storage unit '%s'%s"), 5589 (*it)->m->strLocationFull.raw(), 5590 that->vdError(vrc).raw()); 5591 } 5592 } 5593 5594 /** @todo r=klaus target isn't locked, race getting the state */ 5595 vrc = VDCopy(hdd, VD_LAST_IMAGE, targetHdd, 5596 targetFormat.c_str(), 5597 pTarget->m->state == MediumState_Creating ? targetLocation.raw() : (char *)NULL, 5598 false, 0, 5599 task->d.variant, targetId.raw(), NULL, 5600 pTarget->m->vdDiskIfaces, 5601 vdOperationIfaces); 5602 if (RT_FAILURE(vrc)) 5603 { 5604 throw setError(E_FAIL, 5605 tr("Could not create the clone hard disk '%s'%s"), 5606 targetLocation.raw(), that->vdError(vrc).raw()); 5607 } 5608 size = VDGetFileSize(targetHdd, 0); 5609 logicalSize = VDGetSize(targetHdd, 0) / _1M; 5610 } 5611 catch (HRESULT aRC) { rc = aRC; } 5612 5613 VDDestroy(targetHdd); 5614 } 5615 catch (HRESULT aRC) { rc = aRC; } 5616 5617 VDDestroy(hdd); 5618 } 5619 catch (HRESULT aRC) { rc = aRC; } 5620 5621 /* Only do the parent changes for newly created images. */ 5622 if (pTarget->m->state == MediumState_Creating) 5623 { 5624 if (SUCCEEDED(rc)) 5625 { 5626 /* we set mParent & children() (note that thatLock is released 5627 * here), but lock VirtualBox first to follow the rule */ 5628 AutoWriteLock alock1(that->m->pVirtualBox COMMA_LOCKVAL_SRC_POS); 5629 AutoWriteLock alock2(that->m->pVirtualBox->hardDiskTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 5630 5631 Assert(pTarget->m->pParent.isNull()); 5632 5633 if (pParent) 5634 { 5635 /* associate the clone with the parent and deassociate 5636 * from VirtualBox */ 5637 pTarget->m->pParent = pParent; 5638 pParent->m->llChildren.push_back(pTarget); 5639 5640 /* register with mVirtualBox as the last step and move to 5641 * Created state only on success (leaving an orphan file is 5642 * better than breaking media registry consistency) */ 5643 rc = pParent->m->pVirtualBox->registerHardDisk(pTarget); 5644 5645 if (FAILED(rc)) 5646 /* break parent association on failure to register */ 5647 pTarget->deparent(); // removes target from parent 5648 } 5649 else 5650 { 5651 /* just register */ 5652 rc = that->m->pVirtualBox->registerHardDisk(pTarget); 5653 } 5654 } 5655 } 5656 5657 thatLock.maybeEnter(); 5658 5659 if (pTarget->m->state == MediumState_Creating) 5660 { 5661 if (SUCCEEDED(rc)) 5662 { 5663 pTarget->m->state = MediumState_Created; 5664 5665 pTarget->m->size = size; 5666 pTarget->m->logicalSize = logicalSize; 5667 } 5668 else 5669 { 5670 /* back to NotCreated on failure */ 5671 pTarget->m->state = MediumState_NotCreated; 5672 5673 /* reset UUID to prevent it from being reused next time */ 5674 if (generateUuid) 5675 unconst(pTarget->m->id).clear(); 5676 } 5677 } 5678 5679 /* Everything is explicitly unlocked when the task exits, 5680 * as the task destruction also destroys the source chain. */ 5681 5682 /* Make sure the source chain is released early. It could happen 5683 * that we get a deadlock in Appliance::Import when Medium::Close 5684 * is called & the source chain is released at the same time. */ 5685 task->d.source.reset(); 5686 break; 5687 } 5688 5689 //////////////////////////////////////////////////////////////////////// 5705 rc = that->taskThreadClone(*task, (void*)vdOperationIfaces); 5706 break; 5690 5707 5691 5708 case Task::Delete: -
trunk/src/VBox/Main/include/MediumImpl.h
r25886 r25887 311 311 HRESULT taskThreadCreateBase(Task &task, void *pvdOperationIfaces); 312 312 HRESULT taskThreadCreateDiff(Task &task, void *pvdOperationIfaces, bool fIsAsync); 313 HRESULT taskThreadMerge(Task &task, void *pvdOperationIfaces, bool fIsAsync); 314 HRESULT taskThreadClone(Task &task, void *pvdOperationIfaces); 313 315 314 316 static DECLCALLBACK(int) taskThread(RTTHREAD thread, void *pvUser);
Note:
See TracChangeset
for help on using the changeset viewer.