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 * SECTOR_SIZE; 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->SetBlockSize(SECTOR_SIZE); 144 partition->SetContentSize(partition->Size() / SECTOR_SIZE * SECTOR_SIZE); 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 201 child->SetChildCookie(primary); 202 } 203 204 // The extended partition (if any) is initialized by 205 // ExtendedPartitionHandle::Init(). 206 207 return B_OK; 208 } 209 210 211 // SupportedOperations 212 uint32 213 PartitionMapHandle::SupportedOperations(uint32 mask) 214 { 215 BMutablePartition* partition = Partition(); 216 217 uint32 flags = B_DISK_SYSTEM_SUPPORTS_RESIZING 218 | B_DISK_SYSTEM_SUPPORTS_MOVING 219 | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS 220 | B_DISK_SYSTEM_SUPPORTS_INITIALIZING; 221 222 // creating child 223 if (mask & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) { 224 BPartitioningInfo info; 225 if (partition->CountChildren() < 4 226 && GetPartitioningInfo(&info) == B_OK 227 && info.CountPartitionableSpaces() > 1) { 228 flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD; 229 } 230 } 231 232 return flags; 233 } 234 235 236 // SupportedChildOperations 237 uint32 238 PartitionMapHandle::SupportedChildOperations(const BMutablePartition* child, 239 uint32 mask) 240 { 241 return B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD 242 | B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD 243 | B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE 244 | B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD; 245 } 246 247 248 // GetNextSupportedType 249 status_t 250 PartitionMapHandle::GetNextSupportedType(const BMutablePartition* child, 251 int32* cookie, BString* type) 252 { 253 // TODO: What are we supposed to do with the child? 254 255 // we support creating two types, primary and extended 256 if (*cookie < 0 || *cookie > 1) 257 return B_ENTRY_NOT_FOUND; 258 259 // check if there are any spaces at all 260 // TODO: check if the spaces have enough size at all 261 BPartitioningInfo info; 262 status_t ret = GetPartitioningInfo(&info); 263 if (ret < B_OK) 264 return ret; 265 266 if (info.CountPartitionableSpaces() == 0) 267 return B_ENTRY_NOT_FOUND; 268 269 // adjust the cookie here already so that we don't have 270 // to worry about it when returning early below 271 *cookie = *cookie + 1; 272 273 if (*cookie == 1) { 274 // On first iteration, check if we can create more primary 275 // partitions. If this is not possible, we cannot create 276 // any extended partitions either. 277 for (int32 i = 0; i < 4; i++) { 278 PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(i); 279 if (primary->IsEmpty()) { 280 *type = kPartitionTypeIntelPrimary; 281 return B_OK; 282 } 283 } 284 } else if (*cookie == 2) { 285 // On second iteration, check if we can create more primary 286 // partitions. Also check if there already is an extended 287 // partition, only if there is at least one empty and no 288 // extended partition, we can create an extended partition. 289 bool foundExtended = false; 290 bool foundEmpty = false; 291 for (int32 i = 0; i < 4; i++) { 292 PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(i); 293 if (primary->IsEmpty()) 294 foundEmpty = true; 295 else if (primary->IsExtended()) 296 foundExtended = true; 297 } 298 if (foundEmpty && !foundExtended) { 299 *type = kPartitionTypeIntelExtended; 300 return B_OK; 301 } 302 } 303 304 return B_ENTRY_NOT_FOUND; 305 } 306 307 308 // GetPartitioningInfo 309 status_t 310 PartitionMapHandle::GetPartitioningInfo(BPartitioningInfo* info) 311 { 312 // init to the full size (minus the first sector) 313 off_t size = Partition()->ContentSize(); 314 status_t error = info->SetTo(SECTOR_SIZE, size - SECTOR_SIZE); 315 if (error != B_OK) 316 return error; 317 318 // exclude the space of the existing partitions 319 for (int32 i = 0; i < 4; i++) { 320 PrimaryPartition* primary = fPartitionMap.PrimaryPartitionAt(i); 321 if (!primary->IsEmpty()) { 322 error = info->ExcludeOccupiedSpace(primary->Offset(), 323 primary->Size()); 324 if (error != B_OK) 325 return error; 326 } 327 } 328 329 return B_OK; 330 } 331 332 333 // GetChildCreationParameterEditor 334 status_t 335 PartitionMapHandle::GetChildCreationParameterEditor(const char* type, 336 BDiskDeviceParameterEditor** editor) 337 { 338 // TODO: We actually need an editor here. 339 *editor = NULL; 340 return B_OK; 341 } 342 343 344 // ValidateCreateChild 345 status_t 346 PartitionMapHandle::ValidateCreateChild(off_t* _offset, off_t* _size, 347 const char* typeString, BString* name, const char* parameters) 348 { 349 // check type 350 PartitionType type; 351 if (!type.SetType(typeString) || type.IsEmpty()) 352 return B_BAD_VALUE; 353 if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0) 354 return B_BAD_VALUE; 355 356 // check name 357 if (name) 358 name->Truncate(0); 359 360 // check parameters 361 // TODO:... 362 363 // do we have a spare primary partition? 364 if (fPartitionMap.CountNonEmptyPrimaryPartitions() == 4) 365 return B_BAD_VALUE; 366 367 // check the free space situation 368 BPartitioningInfo info; 369 status_t error = GetPartitioningInfo(&info); 370 if (error != B_OK) 371 return error; 372 373 // any space in the partition at all? 374 int32 spacesCount = info.CountPartitionableSpaces(); 375 if (spacesCount == 0) 376 return B_BAD_VALUE; 377 378 // check offset and size 379 off_t offset = sector_align(*_offset); 380 off_t size = sector_align(*_size); 381 // TODO: Rather round size up? 382 off_t end = offset + size; 383 384 // get the first partitionable space the requested interval intersects with 385 int32 spaceIndex = -1; 386 int32 closestSpaceIndex = -1; 387 off_t closestSpaceDistance = 0; 388 for (int32 i = 0; i < spacesCount; i++) { 389 off_t spaceOffset, spaceSize; 390 info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize); 391 off_t spaceEnd = spaceOffset + spaceSize; 392 393 if (spaceOffset >= offset && spaceOffset < end 394 || offset >= spaceOffset && offset < spaceEnd) { 395 spaceIndex = i; 396 break; 397 } 398 399 off_t distance; 400 if (offset < spaceOffset) 401 distance = spaceOffset - end; 402 else 403 distance = spaceEnd - offset; 404 405 if (closestSpaceIndex == -1 || distance < closestSpaceDistance) { 406 closestSpaceIndex = i; 407 closestSpaceDistance = distance; 408 } 409 } 410 411 // get the space we found 412 off_t spaceOffset, spaceSize; 413 info.GetPartitionableSpaceAt( 414 spaceIndex >= 0 ? spaceIndex : closestSpaceIndex, &spaceOffset, 415 &spaceSize); 416 off_t spaceEnd = spaceOffset + spaceSize; 417 418 // If the requested intervald doesn't intersect with any space yet, move 419 // it, so that it does. 420 if (spaceIndex < 0) { 421 spaceIndex = closestSpaceIndex; 422 if (offset < spaceOffset) { 423 offset = spaceOffset; 424 end = offset + size; 425 } else { 426 end = spaceEnd; 427 offset = end - size; 428 } 429 } 430 431 // move/shrink the interval, so that it fully lies within the space 432 if (offset < spaceOffset) { 433 offset = spaceOffset; 434 end = offset + size; 435 if (end > spaceEnd) { 436 end = spaceEnd; 437 size = end - offset; 438 } 439 } else if (end > spaceEnd) { 440 end = spaceEnd; 441 offset = end - size; 442 if (offset < spaceOffset) { 443 offset = spaceOffset; 444 size = end - offset; 445 } 446 } 447 448 *_offset = offset; 449 *_size = size; 450 451 return B_OK; 452 } 453 454 455 // CreateChild 456 status_t 457 PartitionMapHandle::CreateChild(off_t offset, off_t size, 458 const char* typeString, const char* name, const char* parameters, 459 BMutablePartition** _child) 460 { 461 // check type 462 PartitionType type; 463 if (!type.SetType(typeString) || type.IsEmpty()) 464 return B_BAD_VALUE; 465 if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0) 466 return B_BAD_VALUE; 467 468 // check name 469 if (name && strlen(name) > 0) 470 return B_BAD_VALUE; 471 472 // check parameters 473 // TODO:... 474 475 // get a spare primary partition 476 PrimaryPartition* primary = NULL; 477 for (int32 i = 0; i < 4; i++) { 478 if (fPartitionMap.PrimaryPartitionAt(i)->IsEmpty()) { 479 primary = fPartitionMap.PrimaryPartitionAt(i); 480 break; 481 } 482 } 483 if (!primary) 484 return B_BAD_VALUE; 485 486 // offset properly aligned? 487 if (offset != sector_align(offset) || size != sector_align(size)) 488 return B_BAD_VALUE; 489 490 // check the free space situation 491 BPartitioningInfo info; 492 status_t error = GetPartitioningInfo(&info); 493 if (error != B_OK) 494 return error; 495 496 bool foundSpace = false; 497 off_t end = offset + size; 498 int32 spacesCount = info.CountPartitionableSpaces(); 499 for (int32 i = 0; i < spacesCount; i++) { 500 off_t spaceOffset, spaceSize; 501 info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize); 502 off_t spaceEnd = spaceOffset + spaceSize; 503 504 if (offset >= spaceOffset && end <= spaceEnd) { 505 foundSpace = true; 506 break; 507 } 508 } 509 510 if (!foundSpace) 511 return B_BAD_VALUE; 512 513 // everything looks good, do it 514 515 // create the child 516 // (Note: the primary partition index is indeed the child index, since 517 // we picked the first empty primary partition.) 518 BMutablePartition* partition = Partition(); 519 BMutablePartition* child; 520 error = partition->CreateChild(primary->Index(), typeString, NULL, 521 parameters, &child); 522 if (error != B_OK) 523 return error; 524 525 // init the child 526 child->SetOffset(offset); 527 child->SetSize(size); 528 child->SetBlockSize(SECTOR_SIZE); 529 //child->SetFlags(0); 530 child->SetChildCookie(primary); 531 532 // init the primary partition 533 bool active = false; 534 // TODO: Get from parameters! 535 primary->SetTo(offset, size, type.Type(), active); 536 537 // TODO: If the child is an extended partition, we should trigger its 538 // initialization. 539 540 *_child = child; 541 return B_OK; 542 } 543 544