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