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