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