1 //---------------------------------------------------------------------- 2 // This software is part of the Haiku distribution and is covered 3 // by the Haiku license. 4 //--------------------------------------------------------------------- 5 /*! 6 \file intel.cpp 7 \brief disk_scanner partition module for "intel" style partitions. 8 */ 9 10 // TODO: The implementation is very strict right now. It rejects a partition 11 // completely, if it finds an error in its partition tables. We should see, 12 // what error can be handled gracefully, e.g. by ignoring the partition 13 // descriptor or the whole partition table sector. 14 15 #include <errno.h> 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <string.h> 19 20 #include <util/kernel_cpp.h> 21 #include <AutoDeleter.h> 22 #include <ddm_modules.h> 23 #ifndef _BOOT_MODE 24 # include <DiskDeviceTypes.h> 25 #else 26 # include <boot/partitions.h> 27 //# include "DiskDeviceUtils.h" 28 #endif 29 #include <KernelExport.h> 30 31 #include "PartitionMap.h" 32 #include "PartitionMapParser.h" 33 34 #define TRACE(x) ; 35 //#define TRACE(x) dprintf x 36 37 // module names 38 #define INTEL_PARTITION_MODULE_NAME "partitioning_systems/intel/map/v1" 39 #define INTEL_EXTENDED_PARTITION_MODULE_NAME "partitioning_systems/intel/extended/v1" 40 41 // no atomic_add() in the boot loader 42 #ifdef _BOOT_MODE 43 44 inline int32 45 atomic_add(int32 *a, int32 num) 46 { 47 int32 oldA = *a; 48 *a += num; 49 return oldA; 50 } 51 52 #endif 53 54 55 // A PartitionMap with reference count. 56 struct PartitionMapCookie : PartitionMap { 57 int32 ref_count; 58 }; 59 60 61 // intel partition map module 62 63 // module 64 static status_t pm_std_ops(int32 op, ...); 65 66 // scanning 67 static float pm_identify_partition(int fd, partition_data *partition, 68 void **cookie); 69 static status_t pm_scan_partition(int fd, partition_data *partition, 70 void *cookie); 71 static void pm_free_identify_partition_cookie(partition_data *partition, 72 void *cookie); 73 static void pm_free_partition_cookie(partition_data *partition); 74 static void pm_free_partition_content_cookie(partition_data *partition); 75 76 #ifndef _BOOT_MODE 77 // querying 78 static bool pm_supports_resizing_child(partition_data *partition, 79 partition_data *child); 80 81 static bool pm_validate_resize_child(partition_data *partition, 82 partition_data *child, off_t *size); 83 84 // writing 85 static status_t pm_resize_child(int fd, partition_id partition, off_t size, 86 disk_job_id job); 87 #endif 88 89 #ifdef _BOOT_MODE 90 partition_module_info gIntelPartitionMapModule = 91 #else 92 static partition_module_info intel_partition_map_module = 93 #endif 94 { 95 { 96 INTEL_PARTITION_MODULE_NAME, 97 0, 98 pm_std_ops 99 }, 100 INTEL_PARTITION_NAME, // pretty_name 101 0, // flags 102 103 // scanning 104 pm_identify_partition, // identify_partition 105 pm_scan_partition, // scan_partition 106 pm_free_identify_partition_cookie, // free_identify_partition_cookie 107 pm_free_partition_cookie, // free_partition_cookie 108 pm_free_partition_content_cookie, // free_partition_content_cookie 109 110 #ifndef _BOOT_MODE 111 // querying 112 NULL, // supports_repairing 113 NULL, // supports_resizing 114 pm_supports_resizing_child, // supports_resizing_child 115 NULL, // supports_moving 116 NULL, // supports_moving_child 117 NULL, // supports_setting_name 118 NULL, // supports_setting_content_name 119 NULL, // supports_setting_type 120 NULL, // supports_setting_parameters 121 NULL, // supports_setting_content_parameters 122 NULL, // supports_initializing 123 NULL, // supports_initializing_child 124 NULL, // supports_creating_child 125 NULL, // supports_deleting_child 126 NULL, // is_sub_system_for 127 128 NULL, // validate_resize 129 pm_validate_resize_child, // validate_resize_child 130 NULL, // validate_move 131 NULL, // validate_move_child 132 NULL, // validate_set_name 133 NULL, // validate_set_content_name 134 NULL, // validate_set_type 135 NULL, // validate_set_parameters 136 NULL, // validate_set_content_parameters 137 NULL, // validate_initialize 138 NULL, // validate_create_child 139 NULL, // get_partitionable_spaces 140 NULL, // get_next_supported_type 141 NULL, // get_type_for_content_type 142 143 // shadow partition modification 144 NULL, // shadow_changed 145 146 // writing 147 NULL, // repair 148 NULL, // resize 149 pm_resize_child, // resize_child 150 NULL, // move 151 NULL, // move_child 152 NULL, // set_name 153 NULL, // set_content_name 154 NULL, // set_type 155 NULL, // set_parameters 156 NULL, // set_content_parameters 157 NULL, // initialize 158 NULL, // create_child 159 NULL, // delete_child 160 #else 161 NULL 162 #endif // _BOOT_MODE 163 }; 164 165 166 // intel extended partition module 167 168 // module 169 static status_t ep_std_ops(int32 op, ...); 170 171 // scanning 172 static float ep_identify_partition(int fd, partition_data *partition, 173 void **cookie); 174 static status_t ep_scan_partition(int fd, partition_data *partition, 175 void *cookie); 176 static void ep_free_identify_partition_cookie(partition_data *partition, 177 void *cookie); 178 static void ep_free_partition_cookie(partition_data *partition); 179 static void ep_free_partition_content_cookie(partition_data *partition); 180 181 #ifdef _BOOT_MODE 182 partition_module_info gIntelExtendedPartitionModule = 183 #else 184 static partition_module_info intel_extended_partition_module = 185 #endif 186 { 187 { 188 INTEL_EXTENDED_PARTITION_MODULE_NAME, 189 0, 190 ep_std_ops 191 }, 192 INTEL_EXTENDED_PARTITION_NAME, // pretty_name 193 0, // flags 194 195 // scanning 196 ep_identify_partition, // identify_partition 197 ep_scan_partition, // scan_partition 198 ep_free_identify_partition_cookie, // free_identify_partition_cookie 199 ep_free_partition_cookie, // free_partition_cookie 200 ep_free_partition_content_cookie, // free_partition_content_cookie 201 202 #ifndef _BOOT_MODE 203 // querying 204 NULL, // supports_repairing 205 NULL, // supports_resizing 206 NULL, // supports_resizing_child 207 NULL, // supports_moving 208 NULL, // supports_moving_child 209 NULL, // supports_setting_name 210 NULL, // supports_setting_content_name 211 NULL, // supports_setting_type 212 NULL, // supports_setting_parameters 213 NULL, // supports_setting_content_parameters 214 NULL, // supports_initializing 215 NULL, // supports_initializing_child 216 NULL, // supports_creating_child 217 NULL, // supports_deleting_child 218 NULL, // is_sub_system_for 219 220 NULL, // validate_resize 221 NULL, // validate_resize_child 222 NULL, // validate_move 223 NULL, // validate_move_child 224 NULL, // validate_set_name 225 NULL, // validate_set_content_name 226 NULL, // validate_set_type 227 NULL, // validate_set_parameters 228 NULL, // validate_set_content_parameters 229 NULL, // validate_initialize 230 NULL, // validate_create_child 231 NULL, // get_partitionable_spaces 232 NULL, // get_next_supported_type 233 NULL, // get_type_for_content_type 234 235 // shadow partition modification 236 NULL, // shadow_changed 237 238 // writing 239 NULL, // repair 240 NULL, // resize 241 NULL, // resize_child 242 NULL, // move 243 NULL, // move_child 244 NULL, // set_name 245 NULL, // set_content_name 246 NULL, // set_type 247 NULL, // set_parameters 248 NULL, // set_content_parameters 249 NULL, // initialize 250 NULL, // create_child 251 NULL, // delete_child 252 #else 253 NULL 254 #endif // _BOOT_MODE 255 }; 256 257 258 #ifndef _BOOT_MODE 259 extern "C" partition_module_info *modules[]; 260 _EXPORT partition_module_info *modules[] = 261 { 262 &intel_partition_map_module, 263 &intel_extended_partition_module, 264 NULL 265 }; 266 #endif 267 268 269 // intel partition map module 270 271 // pm_std_ops 272 static 273 status_t 274 pm_std_ops(int32 op, ...) 275 { 276 TRACE(("intel: pm_std_ops(0x%lx)\n", op)); 277 switch(op) { 278 case B_MODULE_INIT: 279 case B_MODULE_UNINIT: 280 return B_OK; 281 } 282 return B_ERROR; 283 } 284 285 // pm_identify_partition 286 static 287 float 288 pm_identify_partition(int fd, partition_data *partition, void **cookie) 289 { 290 // check parameters 291 if (fd < 0 || !partition || !cookie) 292 return -1; 293 294 TRACE(("intel: pm_identify_partition(%d, %ld: %lld, %lld, %ld)\n", fd, 295 partition->id, partition->offset, partition->size, 296 partition->block_size)); 297 298 // reject extended partitions 299 if (partition->type 300 && !strcmp(partition->type, kPartitionTypeIntelExtended)) { 301 return -1; 302 } 303 304 // check block size 305 uint32 blockSize = partition->block_size; 306 if (blockSize < sizeof(partition_table_sector)) { 307 TRACE(("intel: read_partition_map: bad block size: %ld, should be " 308 ">= %ld\n", blockSize, sizeof(partition_table_sector))); 309 return -1; 310 } 311 312 // allocate a PartitionMap 313 PartitionMapCookie *map = new(nothrow) PartitionMapCookie; 314 if (!map) 315 return -1; 316 map->ref_count = 1; 317 318 // read the partition structure 319 PartitionMapParser parser(fd, 0, partition->size, blockSize); 320 status_t error = parser.Parse(NULL, map); 321 if (error == B_OK) { 322 *cookie = map; 323 return 0.5; 324 } 325 326 // cleanup, if not detected 327 delete map; 328 return -1; 329 } 330 331 // pm_scan_partition 332 static 333 status_t 334 pm_scan_partition(int fd, partition_data *partition, void *cookie) 335 { 336 // check parameters 337 if (fd < 0 || !partition || !cookie) 338 return B_ERROR; 339 340 TRACE(("intel: pm_scan_partition(%d, %ld: %lld, %lld, %ld)\n", fd, 341 partition->id, partition->offset, partition->size, 342 partition->block_size)); 343 344 PartitionMapCookie *map = (PartitionMapCookie*)cookie; 345 // fill in the partition_data structure 346 partition->status = B_PARTITION_VALID; 347 partition->flags |= B_PARTITION_PARTITIONING_SYSTEM 348 | B_PARTITION_READ_ONLY 349 | B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD; 350 // TODO: Update when write functionality is implemented. 351 partition->content_size = partition->size; 352 // (no content_name and content_parameters) 353 // (content_type is set by the system) 354 partition->content_cookie = map; 355 // children 356 status_t error = B_OK; 357 int32 index = 0; 358 for (int32 i = 0; i < 4; i++) { 359 PrimaryPartition *primary = map->PrimaryPartitionAt(i); 360 if (!primary->IsEmpty()) { 361 partition_data *child = create_child_partition(partition->id, 362 index, -1); 363 index++; 364 if (!child) { 365 // something went wrong 366 error = B_ERROR; 367 break; 368 } 369 child->offset = partition->offset + primary->Offset(); 370 child->size = primary->Size(); 371 child->block_size = partition->block_size; 372 // (no name) 373 char type[B_FILE_NAME_LENGTH]; 374 primary->GetTypeString(type); 375 child->type = strdup(type); 376 // parameters 377 char buffer[128]; 378 sprintf(buffer, "type = %u ; active = %d", primary->Type(), 379 primary->Active()); 380 child->parameters = strdup(buffer); 381 child->cookie = primary; 382 // check for allocation problems 383 if (!child->type || !child->parameters) { 384 error = B_NO_MEMORY; 385 break; 386 } 387 } 388 } 389 390 // keep map on success or cleanup on error 391 if (error == B_OK) { 392 atomic_add(&map->ref_count, 1); 393 } else { 394 partition->content_cookie = NULL; 395 for (int32 i = 0; i < partition->child_count; i++) { 396 if (partition_data *child = get_child_partition(partition->id, i)) 397 child->cookie = NULL; 398 } 399 } 400 return error; 401 } 402 403 // pm_free_identify_partition_cookie 404 static 405 void 406 pm_free_identify_partition_cookie(partition_data */*partition*/, void *cookie) 407 { 408 if (cookie) { 409 PartitionMapCookie *map = (PartitionMapCookie*)cookie; 410 if (atomic_add(&map->ref_count, -1) == 1) 411 delete map; 412 } 413 } 414 415 // pm_free_partition_cookie 416 static 417 void 418 pm_free_partition_cookie(partition_data *partition) 419 { 420 // called for the primary partitions: the PrimaryPartition is allocated 421 // by the partition containing the partition map 422 if (partition) 423 partition->cookie = NULL; 424 } 425 426 // pm_free_partition_content_cookie 427 static 428 void 429 pm_free_partition_content_cookie(partition_data *partition) 430 { 431 if (partition && partition->content_cookie) { 432 pm_free_identify_partition_cookie(partition, partition->content_cookie); 433 partition->content_cookie = NULL; 434 } 435 } 436 437 #ifndef _BOOT_MODE 438 // pm_supports_resizing_child 439 static 440 bool 441 pm_supports_resizing_child(partition_data *partition, partition_data *child) 442 { 443 return (partition && child && partition->content_type 444 && !strcmp(partition->content_type, kPartitionTypeIntel)); 445 } 446 447 // pm_validate_resize_child 448 static 449 bool 450 pm_validate_resize_child(partition_data *partition, partition_data *child, 451 off_t *size) 452 { 453 if (!partition || !child || !partition->content_type 454 || strcmp(partition->content_type, kPartitionTypeIntel) 455 || !size) { 456 return false; 457 } 458 // size remains the same? 459 if (*size == child->size) 460 return true; 461 // shrink partition? 462 if (*size < child->size) { 463 if (*size < 0) 464 *size = 0; 465 // make the size a multiple of the block size 466 *size = *size / partition->block_size * partition->block_size; 467 return true; 468 } 469 // grow partition 470 // child must completely lie within the parent partition 471 if (child->offset + *size > partition->offset + partition->size) 472 *size = partition->offset + partition->size - child->offset; 473 // child must not intersect with sibling partitions 474 for (int32 i = 0; i < partition->child_count; i++) { 475 partition_data *sibling = get_child_partition(partition->id, i); 476 if (sibling && sibling != child && sibling->offset > child->offset) { 477 if (child->offset + *size > sibling->offset) 478 *size = sibling->offset - child->offset; 479 } 480 } 481 // make the size a multiple of the block size 482 *size = *size / partition->block_size * partition->block_size; 483 return true; 484 } 485 486 // pm_resize_child 487 static 488 status_t 489 pm_resize_child(int fd, partition_id partitionID, off_t size, disk_job_id job) 490 { 491 /* 492 disk_device_data *device = read_lock_disk_device(partitionID); 493 if (!device) 494 return B_BAD_VALUE; 495 DeviceStructWriteLocker locker(device, true); 496 // get partition and child and out partition map structure 497 partition_data *partition = get_parent_partition(partitionID); 498 partition_data *child = get_partition(partitionID); 499 if (!partition || !child) 500 return B_BAD_VALUE; 501 PartitionMap *map = (PartitionMap*)partition->content_cookie; 502 PrimaryPartition *primary = child->cookie; 503 if (!map || !primary) 504 return B_BAD_VALUE; 505 // validate the new size 506 off_t validatedSize = size; 507 if (!pm_validate_resize_child(partition, child, validatedSize) 508 return B_BAD_VALUE; 509 if (child->size == validatedSize) 510 return B_OK; 511 // write the changes to disk 512 primary->SetSize(validatedSize); 513 // TODO: Write... 514 // TODO: PartitionMapParser into separate file, 515 // implement PartitionMapWriter 516 */ 517 set_disk_device_job_error_message(job, "Resize not implemented yet."); 518 return B_ERROR; 519 } 520 #endif // _BOOT_MODE 521 522 523 // intel extended partition module 524 525 // ep_std_ops 526 static 527 status_t 528 ep_std_ops(int32 op, ...) 529 { 530 TRACE(("intel: ep_std_ops(0x%lx)\n", op)); 531 switch(op) { 532 case B_MODULE_INIT: 533 case B_MODULE_UNINIT: 534 return B_OK; 535 } 536 return B_ERROR; 537 } 538 539 // ep_identify_partition 540 static 541 float 542 ep_identify_partition(int fd, partition_data *partition, void **cookie) 543 { 544 // check parameters 545 if (fd < 0 || !partition || !cookie || !partition->cookie) 546 return -1; 547 548 TRACE(("intel: ep_identify_partition(%d, %lld, %lld, %ld)\n", fd, 549 partition->offset, partition->size, partition->block_size)); 550 551 // our parent must be a intel partition map partition and we must have 552 // extended partition type 553 if (!partition->type 554 || strcmp(partition->type, kPartitionTypeIntelExtended)) { 555 return -1; 556 } 557 partition_data *parent = get_parent_partition(partition->id); 558 if (!parent || !parent->content_type 559 || strcmp(parent->content_type, kPartitionTypeIntel)) { 560 return -1; 561 } 562 563 // things seem to be in order 564 return 0.5; 565 } 566 567 // ep_scan_partition 568 static 569 status_t 570 ep_scan_partition(int fd, partition_data *partition, void *cookie) 571 { 572 // check parameters 573 if (fd < 0 || !partition || !partition->cookie) 574 return B_ERROR; 575 576 TRACE(("intel: ep_scan_partition(%d, %lld, %lld, %ld)\n", fd, 577 partition->offset, partition->size, partition->block_size)); 578 579 partition_data *parent = get_parent_partition(partition->id); 580 if (!parent) 581 return B_ERROR; 582 PrimaryPartition *primary = (PrimaryPartition*)partition->cookie; 583 // fill in the partition_data structure 584 partition->status = B_PARTITION_VALID; 585 partition->flags |= B_PARTITION_PARTITIONING_SYSTEM 586 | B_PARTITION_READ_ONLY; 587 // TODO: Update when write functionality is implemented. 588 partition->content_size = partition->size; 589 // (no content_name and content_parameters) 590 // (content_type is set by the system) 591 partition->content_cookie = primary; 592 // children 593 status_t error = B_OK; 594 int32 index = 0; 595 for (int32 i = 0; i < primary->CountLogicalPartitions(); i++) { 596 LogicalPartition *logical = primary->LogicalPartitionAt(i); 597 partition_data *child = create_child_partition(partition->id, 598 index, -1); 599 index++; 600 if (!child) { 601 // something went wrong 602 TRACE(("intel: ep_scan_partition(): failed to create child " 603 "partition\n")); 604 error = B_ERROR; 605 break; 606 } 607 child->offset = parent->offset + logical->Offset(); 608 child->size = logical->Size(); 609 child->block_size = partition->block_size; 610 // (no name) 611 char type[B_FILE_NAME_LENGTH]; 612 logical->GetTypeString(type); 613 child->type = strdup(type); 614 615 // parameters 616 char buffer[128]; 617 sprintf(buffer, "type = %u ; active = %d", logical->Type(), 618 logical->Active()); 619 child->parameters = strdup(buffer); 620 child->cookie = logical; 621 // check for allocation problems 622 if (!child->type || !child->parameters) { 623 TRACE(("intel: ep_scan_partition(): failed to allocation type " 624 "or parameters\n")); 625 error = B_NO_MEMORY; 626 break; 627 } 628 } 629 // cleanup on error 630 if (error != B_OK) { 631 partition->content_cookie = NULL; 632 for (int32 i = 0; i < partition->child_count; i++) { 633 if (partition_data *child = get_child_partition(partition->id, i)) 634 child->cookie = NULL; 635 } 636 } 637 return error; 638 } 639 640 // ep_free_identify_partition_cookie 641 static 642 void 643 ep_free_identify_partition_cookie(partition_data *partition, void *cookie) 644 { 645 // nothing to do 646 } 647 648 // ep_free_partition_cookie 649 static 650 void 651 ep_free_partition_cookie(partition_data *partition) 652 { 653 // the logical partition's cookie belongs to the partition map partition 654 if (partition) 655 partition->cookie = NULL; 656 } 657 658 // ep_free_partition_content_cookie 659 static 660 void 661 ep_free_partition_content_cookie(partition_data *partition) 662 { 663 // the extended partition's cookie belongs to the partition map partition 664 if (partition) 665 partition->content_cookie = NULL; 666 } 667 668