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