1 /* 2 * Copyright 2003-2007, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "DiskDeviceJobGenerator.h" 7 8 #include <new> 9 10 #include <string.h> 11 12 #include <DiskDevice.h> 13 #include <MutablePartition.h> 14 15 #include <disk_device_manager/ddm_userland_interface.h> 16 17 #include "DiskDeviceJob.h" 18 #include "DiskDeviceJobQueue.h" 19 #include "PartitionDelegate.h" 20 #include "PartitionReference.h" 21 22 #include "InitializeJob.h" 23 24 25 using std::nothrow; 26 27 28 // compare_string 29 /*! \brief \c NULL aware strcmp(). 30 31 \c NULL is considered the least of all strings. \c NULL equals \c NULL. 32 33 \param str1 First string. 34 \param str2 Second string. 35 \return A value less than 0, if \a str1 is less than \a str2, 36 0, if they are equal, or a value greater than 0, if 37 \a str1 is greater \a str2. 38 */ 39 static inline int 40 compare_string(const char* str1, const char* str2) 41 { 42 if (str1 == NULL) { 43 if (str2 == NULL) 44 return 0; 45 return 1; 46 } else if (str2 == NULL) 47 return -1; 48 49 return strcmp(str1, str2); 50 } 51 52 53 // MoveInfo 54 struct DiskDeviceJobGenerator::MoveInfo { 55 BPartition* partition; 56 off_t position; 57 off_t target_position; 58 off_t size; 59 }; 60 61 62 // PartitionRefInfo 63 struct DiskDeviceJobGenerator::PartitionRefInfo { 64 PartitionRefInfo() 65 : partition(NULL), 66 reference(NULL) 67 { 68 } 69 70 ~PartitionRefInfo() 71 { 72 if (reference) 73 reference->RemoveReference(); 74 } 75 76 BPartition* partition; 77 PartitionReference* reference; 78 }; 79 80 81 // constructor 82 DiskDeviceJobGenerator::DiskDeviceJobGenerator(BDiskDevice* device, 83 DiskDeviceJobQueue* jobQueue) 84 : fDevice(device), 85 fJobQueue(jobQueue), 86 fMoveInfos(NULL), 87 fPartitionRefs(NULL) 88 { 89 fPartitionCount = fDevice->CountDescendants(); 90 91 fMoveInfos = new(nothrow) MoveInfo[fPartitionCount]; 92 // fPartitionIDs = new(nothrow) partition_id[fPartitionCount]; 93 fPartitionRefs = new(nothrow) PartitionRefInfo[fPartitionCount]; 94 } 95 96 97 // destructor 98 DiskDeviceJobGenerator::~DiskDeviceJobGenerator() 99 { 100 delete[] fMoveInfos; 101 // delete[] fPartitionIDs; 102 delete[] fPartitionRefs; 103 } 104 105 106 // GenerateJobs 107 status_t 108 DiskDeviceJobGenerator::GenerateJobs() 109 { 110 // check parameters 111 if (!fDevice || !fJobQueue) 112 return B_BAD_VALUE; 113 114 if (!fMoveInfos /*|| !fPartitionIDs*/ || !fPartitionRefs) 115 return B_NO_MEMORY; 116 117 // 1) Generate jobs for all physical partitions that don't have an 118 // associated shadow partition, i.e. those that shall be deleted. 119 // 2) Generate uninitialize jobs for all partition whose initialization 120 // changes, also those that shall be initialized with a disk system. 121 // This simplifies moving and resizing. 122 status_t error = _GenerateCleanupJobs(fDevice); 123 if (error != B_OK) 124 return error; 125 126 // Generate jobs that move and resize the remaining physical partitions 127 // to their final position/size. 128 error = _GeneratePlacementJobs(fDevice); 129 if (error != B_OK) 130 return error; 131 132 // Generate the remaining jobs in one run: initialization, creation of 133 // partitions, and changing of name, content name, type, parameters, and 134 // content parameters. 135 error = _GenerateRemainingJobs(NULL, fDevice); 136 if (error != B_OK) 137 return error; 138 139 return B_OK; 140 } 141 142 143 // _AddJob 144 status_t 145 DiskDeviceJobGenerator::_AddJob(DiskDeviceJob* job) 146 { 147 if (!job) 148 return B_NO_MEMORY; 149 150 status_t error = fJobQueue->AddJob(job); 151 if (error != B_OK) 152 delete job; 153 154 return error; 155 } 156 157 158 // _GenerateCleanupJobs 159 status_t 160 DiskDeviceJobGenerator::_GenerateCleanupJobs(BPartition* partition) 161 { 162 // TODO: Depending on how this shall be handled, we might want to unmount 163 // all descendants of a partition to be uninitialized or removed. 164 if (BMutablePartition* shadow = _GetMutablePartition(partition)) { 165 if (shadow->ChangeFlags() & B_PARTITION_CHANGED_INITIALIZATION) { 166 // partition changes initialization 167 status_t error = _GenerateUninitializeJob(partition); 168 if (error != B_OK) 169 return error; 170 } else { 171 // recurse 172 for (int32 i = 0; BPartition* child = partition->_ChildAt(i); i++) { 173 status_t error = _GenerateCleanupJobs(child); 174 if (error != B_OK) 175 return error; 176 } 177 } 178 } else if (BPartition* parent = partition->Parent()) { 179 // create job and add it to the queue 180 status_t error = _GenerateDeleteChildJob(parent, partition); 181 if (error != B_OK) 182 return error; 183 } 184 return B_OK; 185 } 186 187 188 // _GeneratePlacementJobs 189 status_t 190 DiskDeviceJobGenerator::_GeneratePlacementJobs(BPartition* partition) 191 { 192 if (BMutablePartition* shadow = _GetMutablePartition(partition)) { 193 // Don't resize/move partitions that have an unrecognized contents. 194 // They must have been uninitialized before. 195 if (shadow->Status() == B_PARTITION_UNRECOGNIZED 196 && (shadow->Size() != partition->Size() 197 || shadow->Offset() != partition->Offset())) { 198 return B_ERROR; 199 } 200 201 if (shadow->Size() > partition->Size()) { 202 // size grows: resize first 203 status_t error = _GenerateResizeJob(partition); 204 if (error != B_OK) 205 return error; 206 } 207 208 // place the children 209 status_t error = _GenerateChildPlacementJobs(partition); 210 if (error != B_OK) 211 return error; 212 213 if (shadow->Size() < partition->Size()) { 214 // size shrinks: resize now 215 status_t error = _GenerateResizeJob(partition); 216 if (error != B_OK) 217 return error; 218 } 219 } 220 221 return B_OK; 222 } 223 224 225 // _GenerateChildPlacementJobs 226 status_t 227 DiskDeviceJobGenerator::_GenerateChildPlacementJobs(BPartition* partition) 228 { 229 BMutablePartition* shadow = _GetMutablePartition(partition); 230 231 // nothing to do, if the partition contains no partitioning system or 232 // shall be re-initialized 233 if (!shadow->ContentType() 234 || (shadow->ChangeFlags() & B_PARTITION_CHANGED_INITIALIZATION)) { 235 return B_OK; 236 } 237 238 // first resize all children that shall shrink and place their descendants 239 int32 childCount = 0; 240 int32 moveForth = 0; 241 int32 moveBack = 0; 242 243 for (int32 i = 0; BPartition* child = partition->_ChildAt(i); i++) { 244 if (BMutablePartition* childShadow = _GetMutablePartition(child)) { 245 // add a MoveInfo for the child 246 MoveInfo& info = fMoveInfos[childCount]; 247 childCount++; 248 info.partition = child; 249 info.position = child->Offset(); 250 info.target_position = childShadow->Offset(); 251 info.size = child->Size(); 252 253 if (info.position < info.target_position) 254 moveForth++; 255 else if (info.position > info.target_position) 256 moveBack++; 257 258 // resize the child, if it shall shrink 259 if (childShadow->Size() < child->Size()) { 260 status_t error = _GeneratePlacementJobs(child); 261 if (error != B_OK) 262 return error; 263 info.size = childShadow->Size(); 264 } 265 } 266 } 267 268 // sort the move infos 269 if (childCount > 0 && moveForth + moveBack > 0) { 270 qsort(fMoveInfos, childCount, sizeof(MoveInfo), 271 _CompareMoveInfoPosition); 272 } 273 274 // move the children to their final positions 275 while (moveForth + moveBack > 0) { 276 int32 moved = 0; 277 if (moveForth < moveBack) { 278 // move children back 279 for (int32 i = 0; i < childCount; i++) { 280 MoveInfo &info = fMoveInfos[i]; 281 if (info.position > info.target_position) { 282 if (i == 0 283 || info.target_position >= fMoveInfos[i - 1].position 284 + fMoveInfos[i - 1].size) { 285 // check OK -- the partition wouldn't be moved before 286 // the end of the preceding one 287 status_t error = _GenerateMoveJob(info.partition); 288 if (error != B_OK) 289 return error; 290 info.position = info.target_position; 291 moved++; 292 moveBack--; 293 } 294 } 295 } 296 } else { 297 // move children forth 298 for (int32 i = childCount - 1; i >= 0; i--) { 299 MoveInfo &info = fMoveInfos[i]; 300 if (info.position > info.target_position) { 301 if (i == childCount - 1 302 || info.target_position + info.size 303 <= fMoveInfos[i - 1].position) { 304 // check OK -- the partition wouldn't be moved before 305 // the end of the preceding one 306 status_t error = _GenerateMoveJob(info.partition); 307 if (error != B_OK) 308 return error; 309 info.position = info.target_position; 310 moved++; 311 moveForth--; 312 } 313 } 314 } 315 } 316 317 // terminate, if no partition could be moved 318 if (moved == 0) 319 return B_ERROR; 320 } 321 322 // now resize all children that shall grow/keep their size and place 323 // their descendants 324 for (int32 i = 0; BPartition* child = partition->_ChildAt(i); i++) { 325 if (BMutablePartition* childShadow = _GetMutablePartition(child)) { 326 if (childShadow->Size() >= child->Size()) { 327 status_t error = _GeneratePlacementJobs(child); 328 if (error != B_OK) 329 return error; 330 } 331 } 332 } 333 334 return B_OK; 335 } 336 337 338 // _GenerateRemainingJobs 339 status_t 340 DiskDeviceJobGenerator::_GenerateRemainingJobs(BPartition* parent, 341 BPartition* partition) 342 { 343 user_partition_data* partitionData = partition->fPartitionData; 344 345 uint32 changeFlags 346 = partition->fDelegate->MutablePartition()->ChangeFlags(); 347 348 // create the partition, if not existing yet 349 if (!partitionData) { 350 if (!parent) 351 return B_BAD_VALUE; 352 353 status_t error = _GenerateCreateChildJob(parent, partition); 354 if (error != B_OK) 355 return error; 356 } else { 357 // partition already exists: set non-content properties 358 359 360 // name 361 if ((changeFlags & B_PARTITION_CHANGED_NAME) 362 || compare_string(partition->Name(), partitionData->name)) { 363 if (!parent) 364 return B_BAD_VALUE; 365 366 status_t error = _GenerateSetNameJob(parent, partition); 367 if (error != B_OK) 368 return error; 369 } 370 371 // type 372 if ((changeFlags & B_PARTITION_CHANGED_TYPE) 373 || compare_string(partition->Type(), partitionData->type)) { 374 if (!parent) 375 return B_BAD_VALUE; 376 377 status_t error = _GenerateSetTypeJob(parent, partition); 378 if (error != B_OK) 379 return error; 380 } 381 382 // parameters 383 if ((changeFlags & B_PARTITION_CHANGED_PARAMETERS) 384 || compare_string(partition->Parameters(), 385 partitionData->parameters)) { 386 if (!parent) 387 return B_BAD_VALUE; 388 389 status_t error = _GenerateSetParametersJob(parent, partition); 390 if (error != B_OK) 391 return error; 392 } 393 } 394 395 if (partition->ContentType()) { 396 // initialize the partition, if required 397 if (changeFlags & B_PARTITION_CHANGED_INITIALIZATION) { 398 status_t error = _GenerateInitializeJob(partition); 399 if (error != B_OK) 400 return error; 401 } else { 402 // partition not (re-)initialized, set content properties 403 404 // content name 405 if ((changeFlags & B_PARTITION_CHANGED_NAME) 406 || compare_string(partition->ContentName(), 407 partitionData->content_name)) { 408 status_t error = _GenerateSetContentNameJob(partition); 409 if (error != B_OK) 410 return error; 411 } 412 413 // content parameters 414 if ((changeFlags & B_PARTITION_CHANGED_PARAMETERS) 415 || compare_string(partition->ContentParameters(), 416 partitionData->content_parameters)) { 417 status_t error = _GenerateSetContentParametersJob(partition); 418 if (error != B_OK) 419 return error; 420 } 421 422 // defragment 423 if (changeFlags & B_PARTITION_CHANGED_DEFRAGMENTATION) { 424 status_t error = _GenerateDefragmentJob(partition); 425 if (error != B_OK) 426 return error; 427 } 428 429 // check / repair 430 bool repair = (changeFlags & B_PARTITION_CHANGED_REPAIR); 431 if ((changeFlags & B_PARTITION_CHANGED_CHECK) 432 || repair) { 433 status_t error = _GenerateRepairJob(partition, repair); 434 if (error != B_OK) 435 return error; 436 } 437 } 438 } 439 440 // recurse 441 for (int32 i = 0; BPartition* child = partition->ChildAt(i); i++) { 442 status_t error = _GenerateRemainingJobs(partition, child); 443 if (error != B_OK) 444 return error; 445 } 446 447 return B_OK; 448 } 449 450 451 // _GetMutablePartition 452 BMutablePartition* 453 DiskDeviceJobGenerator::_GetMutablePartition(BPartition* partition) 454 { 455 if (!partition) 456 return NULL; 457 458 return partition->fDelegate 459 ? partition->fDelegate->MutablePartition() : NULL; 460 } 461 462 463 // _GenerateInitializeJob 464 status_t 465 DiskDeviceJobGenerator::_GenerateInitializeJob(BPartition* partition) 466 { 467 PartitionReference* reference; 468 status_t error = _GetPartitionReference(partition, reference); 469 if (error != B_OK) 470 return error; 471 472 InitializeJob* job = new(nothrow) InitializeJob(reference); 473 if (!job) 474 return B_NO_MEMORY; 475 476 error = job->Init(partition->ContentType(), 477 partition->ContentName(), partition->ContentParameters()); 478 if (error != B_OK) { 479 delete job; 480 return error; 481 } 482 483 return _AddJob(job); 484 } 485 486 487 // _GenerateUninitializeJob 488 status_t 489 DiskDeviceJobGenerator::_GenerateUninitializeJob(BPartition* partition) 490 { 491 // TODO: Implement! 492 return B_BAD_VALUE; 493 } 494 495 496 // _GenerateSetContentNameJob 497 status_t 498 DiskDeviceJobGenerator::_GenerateSetContentNameJob(BPartition* partition) 499 { 500 // TODO: Implement! 501 return B_BAD_VALUE; 502 } 503 504 505 // _GenerateSetContentParametersJob 506 status_t 507 DiskDeviceJobGenerator::_GenerateSetContentParametersJob(BPartition* partition) 508 { 509 // TODO: Implement! 510 return B_BAD_VALUE; 511 } 512 513 514 // _GenerateDefragmentJob 515 status_t 516 DiskDeviceJobGenerator::_GenerateDefragmentJob(BPartition* partition) 517 { 518 // TODO: Implement! 519 return B_BAD_VALUE; 520 } 521 522 523 // _GenerateRepairJob 524 status_t 525 DiskDeviceJobGenerator::_GenerateRepairJob(BPartition* partition, bool repair) 526 { 527 // TODO: Implement! 528 return B_BAD_VALUE; 529 } 530 531 532 // _GenerateCreateChildJob 533 status_t 534 DiskDeviceJobGenerator::_GenerateCreateChildJob(BPartition* parent, 535 BPartition* partition) 536 { 537 // TODO: Implement! 538 return B_BAD_VALUE; 539 } 540 541 542 // _GenerateDeleteChildJob 543 status_t 544 DiskDeviceJobGenerator::_GenerateDeleteChildJob(BPartition* parent, 545 BPartition* child) 546 { 547 // TODO: Implement! 548 return B_BAD_VALUE; 549 } 550 551 552 // _GenerateResizeJob 553 status_t 554 DiskDeviceJobGenerator::_GenerateResizeJob(BPartition* partition) 555 { 556 // TODO: Implement! 557 return B_BAD_VALUE; 558 } 559 560 561 // _GenerateMoveJob 562 status_t 563 DiskDeviceJobGenerator::_GenerateMoveJob(BPartition* partition) 564 { 565 // TODO: Implement! 566 return B_BAD_VALUE; 567 } 568 569 570 // _GenerateSetNameJob 571 status_t 572 DiskDeviceJobGenerator::_GenerateSetNameJob(BPartition* parent, 573 BPartition* partition) 574 { 575 // TODO: Implement! 576 return B_BAD_VALUE; 577 } 578 579 580 // _GenerateSetTypeJob 581 status_t 582 DiskDeviceJobGenerator::_GenerateSetTypeJob(BPartition* parent, 583 BPartition* partition) 584 { 585 // TODO: Implement! 586 return B_BAD_VALUE; 587 } 588 589 590 // _GenerateSetParametersJob 591 status_t 592 DiskDeviceJobGenerator::_GenerateSetParametersJob(BPartition* parent, 593 BPartition* partition) 594 { 595 // TODO: Implement! 596 return B_BAD_VALUE; 597 } 598 599 600 // _CollectContentsToMove 601 status_t 602 DiskDeviceJobGenerator::_CollectContentsToMove(BPartition* partition) 603 { 604 // TODO: Implement! 605 return B_BAD_VALUE; 606 } 607 608 609 // _GetPartitionReference 610 status_t 611 DiskDeviceJobGenerator::_GetPartitionReference(BPartition* partition, 612 PartitionReference*& reference) 613 { 614 if (!partition) 615 return B_BAD_VALUE; 616 617 for (int32 i = 0; i < fPartitionCount; i++) { 618 PartitionRefInfo& info = fPartitionRefs[i]; 619 620 if (info.partition == partition) { 621 reference = info.reference; 622 return B_OK; 623 } 624 625 if (info.partition == NULL) { 626 // create partition reference 627 info.reference = new(nothrow) PartitionReference(); 628 if (!info.reference) 629 return B_NO_MEMORY; 630 631 // set partition ID and change counter 632 user_partition_data* partitionData = partition->fPartitionData; 633 if (partitionData) { 634 info.reference->SetPartitionID(partitionData->id); 635 info.reference->SetChangeCounter(partitionData->change_counter); 636 } 637 638 info.partition = partition; 639 reference = info.reference; 640 return B_OK; 641 } 642 } 643 644 // Out of slots -- that can't happen. 645 return B_ERROR; 646 } 647 648 649 // _CompareMoveInfoOffset 650 int 651 DiskDeviceJobGenerator::_CompareMoveInfoPosition(const void* _a, const void* _b) 652 { 653 const MoveInfo* a = static_cast<const MoveInfo*>(_a); 654 const MoveInfo* b = static_cast<const MoveInfo*>(_b); 655 if (a->position < b->position) 656 return -1; 657 if (a->position > b->position) 658 return 1; 659 return 0; 660 } 661