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::GetChildCreationParameterEditor(const char* type, 306 BPartitionParameterEditor** editor) 307 { 308 *editor = NULL; 309 310 try { 311 *editor = new PrimaryPartitionEditor(); 312 } catch (std::bad_alloc) { 313 return B_NO_MEMORY; 314 } 315 return B_OK; 316 } 317 318 319 status_t 320 PartitionMapHandle::ValidateCreateChild(off_t* _offset, off_t* _size, 321 const char* typeString, BString* name, const char* parameters) 322 { 323 // check type 324 PartitionType type; 325 if (!type.SetType(typeString) || type.IsEmpty()) 326 return B_BAD_VALUE; 327 328 if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0) 329 return B_BAD_VALUE; 330 331 // check name 332 if (name) 333 name->Truncate(0); 334 335 // check parameters 336 void* handle = parse_driver_settings_string(parameters); 337 if (handle == NULL) 338 return B_ERROR; 339 get_driver_boolean_parameter(handle, "active", false, true); 340 delete_driver_settings(handle); 341 342 // do we have a spare primary partition? 343 if (fPartitionMap.CountNonEmptyPrimaryPartitions() == 4) 344 return B_BAD_VALUE; 345 346 // check the free space situation 347 BPartitioningInfo info; 348 status_t error = GetPartitioningInfo(&info); 349 if (error != B_OK) 350 return error; 351 352 // any space in the partition at all? 353 int32 spacesCount = info.CountPartitionableSpaces(); 354 if (spacesCount == 0) 355 return B_BAD_VALUE; 356 357 // check offset and size 358 off_t offset = sector_align(*_offset, Partition()->BlockSize()); 359 off_t size = sector_align(*_size, Partition()->BlockSize()); 360 // TODO: Rather round size up? 361 off_t end = offset + size; 362 363 // get the first partitionable space the requested interval intersects with 364 int32 spaceIndex = -1; 365 int32 closestSpaceIndex = -1; 366 off_t closestSpaceDistance = 0; 367 for (int32 i = 0; i < spacesCount; i++) { 368 off_t spaceOffset, spaceSize; 369 info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize); 370 off_t spaceEnd = spaceOffset + spaceSize; 371 372 if ((spaceOffset >= offset && spaceOffset < end) 373 || (offset >= spaceOffset && offset < spaceEnd)) { 374 spaceIndex = i; 375 break; 376 } 377 378 off_t distance; 379 if (offset < spaceOffset) 380 distance = spaceOffset - end; 381 else 382 distance = spaceEnd - offset; 383 384 if (closestSpaceIndex == -1 || distance < closestSpaceDistance) { 385 closestSpaceIndex = i; 386 closestSpaceDistance = distance; 387 } 388 } 389 390 // get the space we found 391 off_t spaceOffset, spaceSize; 392 info.GetPartitionableSpaceAt( 393 spaceIndex >= 0 ? spaceIndex : closestSpaceIndex, &spaceOffset, 394 &spaceSize); 395 off_t spaceEnd = spaceOffset + spaceSize; 396 397 // If the requested intervald doesn't intersect with any space yet, move 398 // it, so that it does. 399 if (spaceIndex < 0) { 400 spaceIndex = closestSpaceIndex; 401 if (offset < spaceOffset) { 402 offset = spaceOffset; 403 end = offset + size; 404 } else { 405 end = spaceEnd; 406 offset = end - size; 407 } 408 } 409 410 // move/shrink the interval, so that it fully lies within the space 411 if (offset < spaceOffset) { 412 offset = spaceOffset; 413 end = offset + size; 414 if (end > spaceEnd) { 415 end = spaceEnd; 416 size = end - offset; 417 } 418 } else if (end > spaceEnd) { 419 end = spaceEnd; 420 offset = end - size; 421 if (offset < spaceOffset) { 422 offset = spaceOffset; 423 size = end - offset; 424 } 425 } 426 427 *_offset = offset; 428 *_size = size; 429 430 return B_OK; 431 } 432 433 434 status_t 435 PartitionMapHandle::CreateChild(off_t offset, off_t size, 436 const char* typeString, const char* name, const char* parameters, 437 BMutablePartition** _child) 438 { 439 // check type 440 PartitionType type; 441 if (!type.SetType(typeString) || type.IsEmpty()) 442 return B_BAD_VALUE; 443 if (type.IsExtended() && fPartitionMap.ExtendedPartitionIndex() >= 0) 444 return B_BAD_VALUE; 445 446 // check name 447 if (name && *name != '\0') 448 return B_BAD_VALUE; 449 450 // check parameters 451 void* handle = parse_driver_settings_string(parameters); 452 if (handle == NULL) 453 return B_ERROR; 454 455 bool active = get_driver_boolean_parameter(handle, "active", false, true); 456 delete_driver_settings(handle); 457 458 // get a spare primary partition 459 PrimaryPartition* primary = NULL; 460 for (int32 i = 0; i < 4; i++) { 461 if (fPartitionMap.PrimaryPartitionAt(i)->IsEmpty()) { 462 primary = fPartitionMap.PrimaryPartitionAt(i); 463 break; 464 } 465 } 466 if (!primary) 467 return B_BAD_VALUE; 468 469 // offset properly aligned? 470 if (offset != sector_align(offset, Partition()->BlockSize()) 471 || size != sector_align(size, Partition()->BlockSize())) 472 return B_BAD_VALUE; 473 474 // check the free space situation 475 BPartitioningInfo info; 476 status_t error = GetPartitioningInfo(&info); 477 if (error != B_OK) 478 return error; 479 480 bool foundSpace = false; 481 off_t end = offset + size; 482 int32 spacesCount = info.CountPartitionableSpaces(); 483 for (int32 i = 0; i < spacesCount; i++) { 484 off_t spaceOffset, spaceSize; 485 info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize); 486 off_t spaceEnd = spaceOffset + spaceSize; 487 488 if (offset >= spaceOffset && end <= spaceEnd) { 489 foundSpace = true; 490 break; 491 } 492 } 493 494 if (!foundSpace) 495 return B_BAD_VALUE; 496 497 // create the child 498 // (Note: the primary partition index is indeed the child index, since 499 // we picked the first empty primary partition.) 500 BMutablePartition* partition = Partition(); 501 BMutablePartition* child; 502 error = partition->CreateChild(primary->Index(), typeString, name, 503 parameters, &child); 504 if (error != B_OK) 505 return error; 506 507 // init the child 508 child->SetOffset(offset); 509 child->SetSize(size); 510 child->SetBlockSize(partition->BlockSize()); 511 //child->SetFlags(0); 512 child->SetChildCookie(primary); 513 514 // init the primary partition 515 primary->SetTo(offset, size, type.Type(), active, partition->BlockSize()); 516 517 *_child = child; 518 return B_OK; 519 } 520 521 522 status_t 523 PartitionMapHandle::DeleteChild(BMutablePartition* child) 524 { 525 BMutablePartition* parent = child->Parent(); 526 status_t error = parent->DeleteChild(child); 527 528 return error; 529 } 530