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