1 /* 2 * Copyright 2007, Ingo Weinhold, bonefish@users.sf.net. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "PartitionMapAddOn.h" 7 8 #include <new> 9 #include <stdio.h> 10 11 #include <DiskDeviceTypes.h> 12 #include <MutablePartition.h> 13 #include <PartitioningInfo.h> 14 15 #include <AutoDeleter.h> 16 17 #include "IntelDiskSystem.h" 18 19 20 //#define TRACE_PARTITION_MAP_ADD_ON 21 #undef TRACE 22 #ifdef TRACE_PARTITION_MAP_ADD_ON 23 # define TRACE(x...) printf(x) 24 #else 25 # define TRACE(x...) do {} while (false) 26 #endif 27 28 29 using std::nothrow; 30 31 32 static const uint32 kDiskSystemFlags = 33 0 34 // | B_DISK_SYSTEM_SUPPORTS_CHECKING 35 // | B_DISK_SYSTEM_SUPPORTS_REPAIRING 36 | B_DISK_SYSTEM_SUPPORTS_RESIZING 37 | B_DISK_SYSTEM_SUPPORTS_MOVING 38 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME 39 | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS 40 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING 41 // | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME 42 43 | B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD 44 | B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD 45 // | B_DISK_SYSTEM_SUPPORTS_SETTING_NAME 46 | B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE 47 // | B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS 48 | B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD 49 | B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD 50 // | B_DISK_SYSTEM_SUPPORTS_NAME 51 ; 52 53 54 // #pragma mark - PartitionMapAddOn 55 56 57 // constructor 58 PartitionMapAddOn::PartitionMapAddOn() 59 : BDiskSystemAddOn(kPartitionTypeIntel, kDiskSystemFlags) 60 { 61 } 62 63 64 // destructor 65 PartitionMapAddOn::~PartitionMapAddOn() 66 { 67 } 68 69 70 // CreatePartitionHandle 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 // CanInitialize 91 bool 92 PartitionMapAddOn::CanInitialize(const BMutablePartition* partition) 93 { 94 // If it's big enough, we can initialize it. 95 return partition->Size() >= 2 * partition->BlockSize(); 96 } 97 98 99 // GetInitializationParameterEditor 100 status_t 101 PartitionMapAddOn::GetInitializationParameterEditor( 102 const BMutablePartition* partition, BDiskDeviceParameterEditor** editor) 103 { 104 // Nothing to edit, really. 105 *editor = NULL; 106 return B_OK; 107 } 108 109 110 // ValidateInitialize 111 status_t 112 PartitionMapAddOn::ValidateInitialize(const BMutablePartition* partition, 113 BString* name, const char* parameters) 114 { 115 if (!CanInitialize(partition) 116 || parameters != NULL && strlen(parameters) > 0) { 117 return B_BAD_VALUE; 118 } 119 120 // we don't support a content name 121 if (name != NULL) 122 name->Truncate(0); 123 124 return B_OK; 125 } 126 127 128 // Initialize 129 status_t 130 PartitionMapAddOn::Initialize(BMutablePartition* partition, const char* name, 131 const char* parameters, BPartitionHandle** _handle) 132 { 133 if (!CanInitialize(partition) 134 || name != NULL && strlen(name) > 0 135 || parameters != NULL && strlen(parameters) > 0) { 136 return B_BAD_VALUE; 137 } 138 139 // create the handle 140 PartitionMapHandle* handle = new(nothrow) PartitionMapHandle(partition); 141 if (!handle) 142 return B_NO_MEMORY; 143 ObjectDeleter<PartitionMapHandle> handleDeleter(handle); 144 145 // init the partition 146 status_t error = partition->SetContentType(Name()); 147 if (error != B_OK) 148 return error; 149 // TODO: The content type could as well be set by the caller. 150 151 partition->SetContentName(NULL); 152 partition->SetContentParameters(NULL); 153 partition->SetContentSize( 154 sector_align(partition->Size(), partition->BlockSize())); 155 156 *_handle = handleDeleter.Detach(); 157 158 return B_OK; 159 } 160 161 162 // #pragma mark - PartitionMapHandle 163 164 165 // constructor 166 PartitionMapHandle::PartitionMapHandle(BMutablePartition* partition) 167 : BPartitionHandle(partition) 168 { 169 } 170 171 172 // destructor 173 PartitionMapHandle::~PartitionMapHandle() 174 { 175 } 176 177 178 // Init 179 status_t 180 PartitionMapHandle::Init() 181 { 182 // initialize the partition map from the mutable partition 183 184 BMutablePartition* partition = Partition(); 185 186 int32 count = partition->CountChildren(); 187 if (count > 4) 188 return B_BAD_VALUE; 189 190 int32 extendedCount = 0; 191 192 for (int32 i = 0; i < count; i++) { 193 BMutablePartition* child = partition->ChildAt(i); 194 PartitionType type; 195 if (!type.SetType(child->Type())) 196 return B_BAD_VALUE; 197 198 // only one extended partition is allowed 199 if (type.IsExtended()) { 200 if (++extendedCount > 1) 201 return B_BAD_VALUE; 202 } 203 204 // TODO: Get these from the parameters. 205 int32 index = i; 206 bool active = false; 207 208 PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(index); 209 primary->SetTo(child->Offset(), child->Size(), type.Type(), active, 210 partition->BlockSize()); 211 212 child->SetChildCookie(primary); 213 } 214 215 // The extended partition (if any) is initialized by 216 // ExtendedPartitionHandle::Init(). 217 218 return B_OK; 219 } 220 221 222 // SupportedOperations 223 uint32 224 PartitionMapHandle::SupportedOperations(uint32 mask) 225 { 226 BMutablePartition* partition = Partition(); 227 228 uint32 flags = B_DISK_SYSTEM_SUPPORTS_RESIZING 229 | B_DISK_SYSTEM_SUPPORTS_MOVING 230 | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS 231 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING; 232 233 // creating child 234 if (mask & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) { 235 BPartitioningInfo info; 236 if (partition->CountChildren() < 4 237 && GetPartitioningInfo(&info) == B_OK 238 && info.CountPartitionableSpaces() > 1) { 239 flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD; 240 } 241 } 242 243 return flags; 244 } 245 246 247 // SupportedChildOperations 248 uint32 249 PartitionMapHandle::SupportedChildOperations(const BMutablePartition* child, 250 uint32 mask) 251 { 252 return B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD 253 | B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD 254 | B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE 255 | B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD; 256 } 257 258 259 // GetNextSupportedType 260 status_t 261 PartitionMapHandle::GetNextSupportedType(const BMutablePartition* child, 262 int32* cookie, BString* type) 263 { 264 TRACE("%p->PartitionMapHandle::GetNextSupportedType(child: %p, " 265 "cookie: %ld)\n", this, child, *cookie); 266 // TODO: What are we supposed to do with the child? 267 268 // we support creating two types, primary and extended 269 if (*cookie < 0 || *cookie > 1) 270 return B_ENTRY_NOT_FOUND; 271 272 // check if there are any spaces at all 273 // TODO: check if the spaces have enough size at all 274 BPartitioningInfo info; 275 status_t ret = GetPartitioningInfo(&info); 276 if (ret < B_OK) 277 return ret; 278 279 if (info.CountPartitionableSpaces() == 0) 280 return B_ENTRY_NOT_FOUND; 281 282 // adjust the cookie here already so that we don't have 283 // to worry about it when returning early below 284 *cookie = *cookie + 1; 285 286 if (*cookie == 1) { 287 // On first iteration, check if we can create more primary 288 // partitions. If this is not possible, we cannot create 289 // any extended partitions either. 290 for (int32 i = 0; i < 4; i++) { 291 PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(i); 292 if (primary->IsEmpty()) { 293 *type = kPartitionTypeIntelPrimary; 294 return B_OK; 295 } 296 } 297 } else if (*cookie == 2) { 298 // On second iteration, check if we can create more primary 299 // partitions. Also check if there already is an extended 300 // partition, only if there is at least one empty and no 301 // extended partition, we can create an extended partition. 302 bool foundExtended = false; 303 bool foundEmpty = false; 304 for (int32 i = 0; i < 4; i++) { 305 PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(i); 306 if (primary->IsEmpty()) 307 foundEmpty = true; 308 else if (primary->IsExtended()) 309 foundExtended = true; 310 } 311 if (foundEmpty && !foundExtended) { 312 *type = kPartitionTypeIntelExtended; 313 return B_OK; 314 } 315 } 316 317 return B_ENTRY_NOT_FOUND; 318 } 319 320 321 // GetPartitioningInfo 322 status_t 323 PartitionMapHandle::GetPartitioningInfo(BPartitioningInfo* info) 324 { 325 // init to the full size (minus the first sector) 326 off_t size = Partition()->ContentSize(); 327 status_t error = info->SetTo(Partition()->BlockSize(), 328 size - Partition()->BlockSize()); 329 if (error != B_OK) 330 return error; 331 332 // exclude the space of the existing partitions 333 for (int32 i = 0; i < 4; i++) { 334 PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(i); 335 if (!primary->IsEmpty()) { 336 error = info->ExcludeOccupiedSpace(primary->Offset(), 337 primary->Size()); 338 if (error != B_OK) 339 return error; 340 } 341 } 342 343 return B_OK; 344 } 345 346 347 // GetChildCreationParameterEditor 348 status_t 349 PartitionMapHandle::GetChildCreationParameterEditor(const char* type, 350 BDiskDeviceParameterEditor** editor) 351 { 352 // TODO: We actually need an editor here. 353 *editor = NULL; 354 return B_OK; 355 } 356 357 358 // ValidateCreateChild 359 status_t 360 PartitionMapHandle::ValidateCreateChild(off_t* _offset, off_t* _size, 361 const char* typeString, BString* name, const char* parameters) 362 { 363 // check type 364 PartitionType type; 365 if (!type.SetType(typeString) || type.IsEmpty()) 366 return B_BAD_VALUE; 367 368 if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0) 369 return B_BAD_VALUE; 370 371 // check name 372 if (name) 373 name->Truncate(0); 374 375 // check parameters 376 // TODO:... 377 378 // do we have a spare primary partition? 379 if (fPartitionMap.CountNonEmptyPrimaryPartitions() == 4) 380 return B_BAD_VALUE; 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_BAD_VALUE; 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 // CreateChild 471 status_t 472 PartitionMapHandle::CreateChild(off_t offset, off_t size, 473 const char* typeString, const char* name, const char* parameters, 474 BMutablePartition** _child) 475 { 476 // check type 477 PartitionType type; 478 if (!type.SetType(typeString) || type.IsEmpty()) 479 return B_BAD_VALUE; 480 if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0) 481 return B_BAD_VALUE; 482 483 // check name 484 if (name && strlen(name) > 0) 485 return B_BAD_VALUE; 486 487 // check parameters 488 // TODO:... 489 490 // get a spare primary partition 491 PrimaryPartition* primary = NULL; 492 for (int32 i = 0; i < 4; i++) { 493 if (fPartitionMap.PrimaryPartitionAt(i)->IsEmpty()) { 494 primary = fPartitionMap.PrimaryPartitionAt(i); 495 break; 496 } 497 } 498 if (!primary) 499 return B_BAD_VALUE; 500 501 // offset properly aligned? 502 if (offset != sector_align(offset, Partition()->BlockSize()) 503 || size != sector_align(size, Partition()->BlockSize())) 504 return B_BAD_VALUE; 505 506 // check the free space situation 507 BPartitioningInfo info; 508 status_t error = GetPartitioningInfo(&info); 509 if (error != B_OK) 510 return error; 511 512 bool foundSpace = false; 513 off_t end = offset + size; 514 int32 spacesCount = info.CountPartitionableSpaces(); 515 for (int32 i = 0; i < spacesCount; i++) { 516 off_t spaceOffset, spaceSize; 517 info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize); 518 off_t spaceEnd = spaceOffset + spaceSize; 519 520 if (offset >= spaceOffset && end <= spaceEnd) { 521 foundSpace = true; 522 break; 523 } 524 } 525 526 if (!foundSpace) 527 return B_BAD_VALUE; 528 529 // everything looks good, do it 530 531 // create the child 532 // (Note: the primary partition index is indeed the child index, since 533 // we picked the first empty primary partition.) 534 BMutablePartition* partition = Partition(); 535 BMutablePartition* child; 536 error = partition->CreateChild(primary->Index(), typeString, NULL, 537 parameters, &child); 538 if (error != B_OK) 539 return error; 540 541 // init the child 542 child->SetOffset(offset); 543 child->SetSize(size); 544 child->SetBlockSize(partition->BlockSize()); 545 //child->SetFlags(0); 546 child->SetChildCookie(primary); 547 548 // init the primary partition 549 bool active = false; 550 // TODO: Get from parameters! 551 primary->SetTo(offset, size, type.Type(), active, partition->BlockSize()); 552 553 // TODO: If the child is an extended partition, we should trigger its 554 // initialization. 555 556 *_child = child; 557 return B_OK; 558 } 559 560 561 // DeleteChild 562 status_t 563 PartitionMapHandle::DeleteChild(BMutablePartition* child) 564 { 565 BMutablePartition* parent = child->Parent(); 566 status_t error = parent->DeleteChild(child); 567 568 return error; 569 } 570