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