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