1 /* 2 * Copyright 2007, Ingo Weinhold, bonefish@users.sf.net. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "PartitionMapAddOn.h" 8 9 #include <new> 10 #include <stdio.h> 11 12 #include <DiskDeviceTypes.h> 13 #include <MutablePartition.h> 14 #include <PartitioningInfo.h> 15 16 #include <AutoDeleter.h> 17 18 #include "IntelDiskSystem.h" 19 #include "PrimaryParameterEditor.h" 20 21 22 //#define TRACE_PARTITION_MAP_ADD_ON 23 #undef TRACE 24 #ifdef TRACE_PARTITION_MAP_ADD_ON 25 # define TRACE(x...) printf(x) 26 #else 27 # define TRACE(x...) do {} while (false) 28 #endif 29 30 31 using std::nothrow; 32 33 34 static const uint32 kDiskSystemFlags = 35 0 36 // | B_DISK_SYSTEM_SUPPORTS_CHECKING 37 // | B_DISK_SYSTEM_SUPPORTS_REPAIRING 38 | B_DISK_SYSTEM_SUPPORTS_RESIZING 39 | B_DISK_SYSTEM_SUPPORTS_MOVING 40 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME 41 | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS 42 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING 43 // | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME 44 45 | B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD 46 | B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD 47 // | B_DISK_SYSTEM_SUPPORTS_SETTING_NAME 48 | B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE 49 // | B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS 50 | B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD 51 | B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD 52 // | B_DISK_SYSTEM_SUPPORTS_NAME 53 ; 54 55 56 // #pragma mark - PartitionMapAddOn 57 58 59 PartitionMapAddOn::PartitionMapAddOn() 60 : 61 BDiskSystemAddOn(kPartitionTypeIntel, kDiskSystemFlags) 62 { 63 } 64 65 66 PartitionMapAddOn::~PartitionMapAddOn() 67 { 68 } 69 70 71 status_t 72 PartitionMapAddOn::CreatePartitionHandle(BMutablePartition* partition, 73 BPartitionHandle** _handle) 74 { 75 PartitionMapHandle* handle = new(nothrow) PartitionMapHandle(partition); 76 if (!handle) 77 return B_NO_MEMORY; 78 79 status_t error = handle->Init(); 80 if (error != B_OK) { 81 delete handle; 82 return error; 83 } 84 85 *_handle = handle; 86 return B_OK; 87 } 88 89 90 bool 91 PartitionMapAddOn::CanInitialize(const BMutablePartition* partition) 92 { 93 // If it's big enough, but not too big (ie. larger than 2^32 blocks) we can 94 // initialize it. 95 return partition->Size() >= 2 * partition->BlockSize() 96 && partition->Size() / partition->BlockSize() < UINT32_MAX; 97 } 98 99 100 status_t 101 PartitionMapAddOn::ValidateInitialize(const BMutablePartition* partition, 102 BString* name, const char* parameters) 103 { 104 if (!CanInitialize(partition) 105 || (parameters != NULL && parameters[0] != '\0')) { 106 return B_BAD_VALUE; 107 } 108 109 // we don't support a content name 110 if (name != NULL) 111 name->Truncate(0); 112 113 return B_OK; 114 } 115 116 117 status_t 118 PartitionMapAddOn::Initialize(BMutablePartition* partition, const char* name, 119 const char* parameters, BPartitionHandle** _handle) 120 { 121 if (!CanInitialize(partition) 122 || (name != NULL && name[0] != '\0') 123 || (parameters != NULL && parameters[0] != '\0')) { 124 return B_BAD_VALUE; 125 } 126 127 // create the handle 128 PartitionMapHandle* handle = new(nothrow) PartitionMapHandle(partition); 129 if (!handle) 130 return B_NO_MEMORY; 131 ObjectDeleter<PartitionMapHandle> handleDeleter(handle); 132 133 // init the partition 134 status_t error = partition->SetContentType(Name()); 135 if (error != B_OK) 136 return error; 137 // TODO: The content type could as well be set by the caller. 138 139 partition->SetContentName(NULL); 140 partition->SetContentParameters(NULL); 141 partition->SetContentSize( 142 sector_align(partition->Size(), partition->BlockSize())); 143 partition->Changed(B_PARTITION_CHANGED_INITIALIZATION); 144 145 *_handle = handleDeleter.Detach(); 146 147 return B_OK; 148 } 149 150 151 // #pragma mark - PartitionMapHandle 152 153 154 PartitionMapHandle::PartitionMapHandle(BMutablePartition* partition) 155 : 156 BPartitionHandle(partition) 157 { 158 } 159 160 161 PartitionMapHandle::~PartitionMapHandle() 162 { 163 } 164 165 166 status_t 167 PartitionMapHandle::Init() 168 { 169 // initialize the partition map from the mutable partition 170 171 BMutablePartition* partition = Partition(); 172 173 int32 count = partition->CountChildren(); 174 if (count > 4) 175 return B_BAD_VALUE; 176 177 int32 extendedCount = 0; 178 179 for (int32 i = 0; i < count; i++) { 180 BMutablePartition* child = partition->ChildAt(i); 181 PartitionType type; 182 if (!type.SetType(child->Type())) 183 return B_BAD_VALUE; 184 185 // only one extended partition is allowed 186 if (type.IsExtended()) { 187 if (++extendedCount > 1) 188 return B_BAD_VALUE; 189 } 190 191 // TODO: Get these from the parameters. 192 int32 index = i; 193 bool active = false; 194 195 PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(index); 196 primary->SetTo(child->Offset(), child->Size(), type.Type(), active, 197 partition->BlockSize()); 198 199 child->SetChildCookie(primary); 200 } 201 202 // The extended partition (if any) is initialized by 203 // ExtendedPartitionHandle::Init(). 204 205 return B_OK; 206 } 207 208 209 uint32 210 PartitionMapHandle::SupportedOperations(uint32 mask) 211 { 212 BMutablePartition* partition = Partition(); 213 214 uint32 flags = B_DISK_SYSTEM_SUPPORTS_RESIZING 215 | B_DISK_SYSTEM_SUPPORTS_MOVING 216 | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS 217 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING; 218 219 // creating child 220 if ((mask & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) != 0) { 221 BPartitioningInfo info; 222 if (partition->CountChildren() < 4 223 && GetPartitioningInfo(&info) == B_OK 224 && info.CountPartitionableSpaces() > 1) { 225 flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD; 226 } 227 } 228 229 return flags; 230 } 231 232 233 uint32 234 PartitionMapHandle::SupportedChildOperations(const BMutablePartition* child, 235 uint32 mask) 236 { 237 return B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD 238 | B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD 239 | B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS 240 | B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD; 241 } 242 243 244 status_t 245 PartitionMapHandle::GetNextSupportedType(const BMutablePartition* child, 246 int32* cookie, BString* type) 247 { 248 TRACE("%p->PartitionMapHandle::GetNextSupportedType(child: %p, " 249 "cookie: %ld)\n", this, child, *cookie); 250 251 int32 index = *cookie; 252 const partition_type* nextType; 253 while (true) { 254 nextType = fPartitionMap.GetNextSupportedPartitionType(index); 255 if (nextType == NULL) 256 return B_ENTRY_NOT_FOUND; 257 index++; 258 if (nextType->used) 259 break; 260 } 261 262 if (!nextType) 263 return B_ENTRY_NOT_FOUND; 264 265 type->SetTo(nextType->name); 266 *cookie = index; 267 268 return B_OK; 269 } 270 271 272 status_t 273 PartitionMapHandle::GetPartitioningInfo(BPartitioningInfo* info) 274 { 275 // init to the full size (minus the first sector) 276 off_t size = Partition()->ContentSize(); 277 status_t error = info->SetTo(Partition()->BlockSize(), 278 size - Partition()->BlockSize()); 279 if (error != B_OK) 280 return error; 281 282 // exclude the space of the existing partitions 283 for (int32 i = 0; i < 4; i++) { 284 PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(i); 285 if (!primary->IsEmpty()) { 286 error = info->ExcludeOccupiedSpace(primary->Offset(), 287 primary->Size()); 288 if (error != B_OK) 289 return error; 290 } 291 } 292 293 return B_OK; 294 } 295 296 297 status_t 298 PartitionMapHandle::ValidateSetParameters(const BMutablePartition* child, 299 const char* parameters) 300 { 301 if (child == NULL || parameters == NULL) 302 return B_NO_INIT; 303 304 void* handle = parse_driver_settings_string(parameters); 305 if (handle == NULL) 306 return B_BAD_DATA; 307 308 unload_driver_settings(handle); 309 310 return B_OK; 311 } 312 313 314 status_t 315 PartitionMapHandle::SetParameters(BMutablePartition* child, 316 const char* parameters) 317 { 318 void* handle = parse_driver_settings_string(parameters); 319 if (handle == NULL) 320 return B_BAD_DATA; 321 322 bool active = get_driver_boolean_parameter(handle, "active", false, true); 323 unload_driver_settings(handle); 324 325 // Update our local state 326 PrimaryPartition* partition = (PrimaryPartition*)child->ChildCookie(); 327 partition->SetActive(active); 328 329 // Forward the request to the BMutablePartition so it can be committed to 330 // disk 331 return child->SetParameters(parameters); 332 } 333 334 335 status_t 336 PartitionMapHandle::GetParameterEditor(B_PARAMETER_EDITOR_TYPE type, 337 BPartitionParameterEditor** editor) 338 { 339 *editor = NULL; 340 if (type == B_CREATE_PARAMETER_EDITOR 341 || type == B_PROPERTIES_PARAMETER_EDITOR) { 342 try { 343 *editor = new PrimaryPartitionEditor(); 344 } catch (std::bad_alloc&) { 345 return B_NO_MEMORY; 346 } 347 return B_OK; 348 } 349 return B_NOT_SUPPORTED; 350 } 351 352 353 status_t 354 PartitionMapHandle::ValidateCreateChild(off_t* _offset, off_t* _size, 355 const char* typeString, BString* name, const char* parameters) 356 { 357 // check type 358 PartitionType type; 359 if (!type.SetType(typeString) || type.IsEmpty()) 360 return B_BAD_TYPE; 361 362 if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0) { 363 // There can only be a single extended partition 364 return B_NAME_IN_USE; 365 } 366 367 // check name 368 if (name) 369 name->Truncate(0); 370 371 // check parameters 372 void* handle = parse_driver_settings_string(parameters); 373 if (handle == NULL) 374 return B_ERROR; 375 get_driver_boolean_parameter(handle, "active", false, true); 376 unload_driver_settings(handle); 377 378 // do we have a spare primary partition? 379 if (fPartitionMap.CountNonEmptyPrimaryPartitions() == 4) 380 return B_BAD_INDEX; 381 382 // check the free space situation 383 BPartitioningInfo info; 384 status_t error = GetPartitioningInfo(&info); 385 if (error != B_OK) 386 return error; 387 388 // any space in the partition at all? 389 int32 spacesCount = info.CountPartitionableSpaces(); 390 if (spacesCount == 0) 391 return B_DEVICE_FULL; 392 393 // check offset and size 394 off_t offset = sector_align(*_offset, Partition()->BlockSize()); 395 off_t size = sector_align(*_size, Partition()->BlockSize()); 396 // TODO: Rather round size up? 397 off_t end = offset + size; 398 399 // get the first partitionable space the requested interval intersects with 400 int32 spaceIndex = -1; 401 int32 closestSpaceIndex = -1; 402 off_t closestSpaceDistance = 0; 403 for (int32 i = 0; i < spacesCount; i++) { 404 off_t spaceOffset, spaceSize; 405 info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize); 406 off_t spaceEnd = spaceOffset + spaceSize; 407 408 if ((spaceOffset >= offset && spaceOffset < end) 409 || (offset >= spaceOffset && offset < spaceEnd)) { 410 spaceIndex = i; 411 break; 412 } 413 414 off_t distance; 415 if (offset < spaceOffset) 416 distance = spaceOffset - end; 417 else 418 distance = spaceEnd - offset; 419 420 if (closestSpaceIndex == -1 || distance < closestSpaceDistance) { 421 closestSpaceIndex = i; 422 closestSpaceDistance = distance; 423 } 424 } 425 426 // get the space we found 427 off_t spaceOffset, spaceSize; 428 info.GetPartitionableSpaceAt( 429 spaceIndex >= 0 ? spaceIndex : closestSpaceIndex, &spaceOffset, 430 &spaceSize); 431 off_t spaceEnd = spaceOffset + spaceSize; 432 433 // If the requested intervald doesn't intersect with any space yet, move 434 // it, so that it does. 435 if (spaceIndex < 0) { 436 spaceIndex = closestSpaceIndex; 437 if (offset < spaceOffset) { 438 offset = spaceOffset; 439 end = offset + size; 440 } else { 441 end = spaceEnd; 442 offset = end - size; 443 } 444 } 445 446 // move/shrink the interval, so that it fully lies within the space 447 if (offset < spaceOffset) { 448 offset = spaceOffset; 449 end = offset + size; 450 if (end > spaceEnd) { 451 end = spaceEnd; 452 size = end - offset; 453 } 454 } else if (end > spaceEnd) { 455 end = spaceEnd; 456 offset = end - size; 457 if (offset < spaceOffset) { 458 offset = spaceOffset; 459 size = end - offset; 460 } 461 } 462 463 *_offset = offset; 464 *_size = size; 465 466 return B_OK; 467 } 468 469 470 status_t 471 PartitionMapHandle::CreateChild(off_t offset, off_t size, 472 const char* typeString, const char* name, const char* parameters, 473 BMutablePartition** _child) 474 { 475 // check type 476 PartitionType type; 477 if (!type.SetType(typeString) || type.IsEmpty()) 478 return B_BAD_VALUE; 479 if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0) 480 return B_BAD_VALUE; 481 482 // check name 483 if (name && *name != '\0') 484 return B_BAD_VALUE; 485 486 // check parameters 487 void* handle = parse_driver_settings_string(parameters); 488 if (handle == NULL) 489 return B_ERROR; 490 491 bool active = get_driver_boolean_parameter(handle, "active", false, true); 492 unload_driver_settings(handle); 493 494 // get a spare primary partition 495 PrimaryPartition* primary = NULL; 496 for (int32 i = 0; i < 4; i++) { 497 if (fPartitionMap.PrimaryPartitionAt(i)->IsEmpty()) { 498 primary = fPartitionMap.PrimaryPartitionAt(i); 499 break; 500 } 501 } 502 if (!primary) 503 return B_BAD_VALUE; 504 505 // offset properly aligned? 506 if (offset != sector_align(offset, Partition()->BlockSize()) 507 || size != sector_align(size, Partition()->BlockSize())) 508 return B_BAD_VALUE; 509 510 // check the free space situation 511 BPartitioningInfo info; 512 status_t error = GetPartitioningInfo(&info); 513 if (error != B_OK) 514 return error; 515 516 bool foundSpace = false; 517 off_t end = offset + size; 518 int32 spacesCount = info.CountPartitionableSpaces(); 519 for (int32 i = 0; i < spacesCount; i++) { 520 off_t spaceOffset, spaceSize; 521 info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize); 522 off_t spaceEnd = spaceOffset + spaceSize; 523 524 if (offset >= spaceOffset && end <= spaceEnd) { 525 foundSpace = true; 526 break; 527 } 528 } 529 530 if (!foundSpace) 531 return B_BAD_VALUE; 532 533 // create the child 534 // (Note: the primary partition index is indeed the child index, since 535 // we picked the first empty primary partition.) 536 BMutablePartition* partition = Partition(); 537 BMutablePartition* child; 538 error = partition->CreateChild(primary->Index(), typeString, name, 539 parameters, &child); 540 if (error != B_OK) 541 return error; 542 543 // init the child 544 child->SetOffset(offset); 545 child->SetSize(size); 546 child->SetBlockSize(partition->BlockSize()); 547 //child->SetFlags(0); 548 child->SetChildCookie(primary); 549 550 // init the primary partition 551 primary->SetTo(offset, size, type.Type(), active, partition->BlockSize()); 552 553 *_child = child; 554 return B_OK; 555 } 556 557 558 status_t 559 PartitionMapHandle::DeleteChild(BMutablePartition* child) 560 { 561 BMutablePartition* parent = child->Parent(); 562 status_t error = parent->DeleteChild(child); 563 564 return error; 565 } 566