1 /* 2 * Copyright 2007, Ingo Weinhold, bonefish@users.sf.net. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "ExtendedPartitionAddOn.h" 7 8 #include <new> 9 #include <stdio.h> 10 11 #include <DiskDeviceTypes.h> 12 #include <MutablePartition.h> 13 #include <PartitioningInfo.h> 14 15 #include <AutoDeleter.h> 16 17 #include "IntelDiskSystem.h" 18 19 //#define TRACE_EXTENDED_PARTITION_ADD_ON 20 #undef TRACE 21 #ifdef TRACE_EXTENDED_PARTITION_ADD_ON 22 # define TRACE(x...) printf(x) 23 #else 24 # define TRACE(x...) do {} while (false) 25 #endif 26 27 #define PTS_OFFSET (63 * Partition()->BlockSize()) 28 29 using std::nothrow; 30 31 32 static const uint32 kDiskSystemFlags = 33 0 34 // | B_DISK_SYSTEM_SUPPORTS_CHECKING 35 // | B_DISK_SYSTEM_SUPPORTS_REPAIRING 36 // | B_DISK_SYSTEM_SUPPORTS_RESIZING 37 // | B_DISK_SYSTEM_SUPPORTS_MOVING 38 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_NAME 39 // | B_DISK_SYSTEM_SUPPORTS_SETTING_CONTENT_PARAMETERS 40 // | B_DISK_SYSTEM_SUPPORTS_INITIALIZING 41 // | B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME 42 43 // | B_DISK_SYSTEM_SUPPORTS_RESIZING_CHILD 44 // | B_DISK_SYSTEM_SUPPORTS_MOVING_CHILD 45 // | B_DISK_SYSTEM_SUPPORTS_SETTING_NAME 46 | B_DISK_SYSTEM_SUPPORTS_SETTING_TYPE 47 // | B_DISK_SYSTEM_SUPPORTS_SETTING_PARAMETERS 48 | B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD 49 | B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD 50 // | B_DISK_SYSTEM_SUPPORTS_NAME 51 ; 52 53 54 // #pragma mark - ExtendedPartitionAddOn 55 56 57 // constructor 58 ExtendedPartitionAddOn::ExtendedPartitionAddOn() 59 : BDiskSystemAddOn(kPartitionTypeIntelExtended, kDiskSystemFlags) 60 { 61 } 62 63 64 // destructor 65 ExtendedPartitionAddOn::~ExtendedPartitionAddOn() 66 { 67 } 68 69 70 // CreatePartitionHandle 71 status_t 72 ExtendedPartitionAddOn::CreatePartitionHandle(BMutablePartition* partition, 73 BPartitionHandle** _handle) 74 { 75 ExtendedPartitionHandle* handle 76 = new(nothrow) ExtendedPartitionHandle(partition); 77 if (!handle) 78 return B_NO_MEMORY; 79 80 status_t error = handle->Init(); 81 if (error != B_OK) { 82 delete handle; 83 return error; 84 } 85 86 *_handle = handle; 87 return B_OK; 88 } 89 90 91 // CanInitialize 92 bool 93 ExtendedPartitionAddOn::CanInitialize(const BMutablePartition* partition) 94 { 95 // If it's big enough, we can initialize it. 96 return false; 97 } 98 99 100 // GetInitializationParameterEditor 101 status_t 102 ExtendedPartitionAddOn::GetInitializationParameterEditor( 103 const BMutablePartition* partition, BPartitionParameterEditor** editor) 104 { 105 // Nothing to edit, really. 106 *editor = NULL; 107 return B_OK; 108 } 109 110 111 // ValidateInitialize 112 status_t 113 ExtendedPartitionAddOn::ValidateInitialize(const BMutablePartition* partition, 114 BString* name, const char* parameters) 115 { 116 if (!CanInitialize(partition) 117 || parameters != NULL && strlen(parameters) > 0) { 118 return B_BAD_VALUE; 119 } 120 121 // we don't support a content name 122 if (name != NULL) 123 name->Truncate(0); 124 125 return B_OK; 126 } 127 128 129 // Initialize 130 status_t 131 ExtendedPartitionAddOn::Initialize(BMutablePartition* partition, 132 const char* name, const char* parameters, BPartitionHandle** _handle) 133 { 134 if (!CanInitialize(partition) 135 || name != NULL && strlen(name) > 0 136 || parameters != NULL && strlen(parameters) > 0) { 137 return B_BAD_VALUE; 138 } 139 140 // create the handle 141 ExtendedPartitionHandle* handle 142 = new(nothrow) ExtendedPartitionHandle(partition); 143 if (!handle) 144 return B_NO_MEMORY; 145 ObjectDeleter<ExtendedPartitionHandle> handleDeleter(handle); 146 147 // init the partition 148 status_t error = partition->SetContentType(Name()); 149 if (error != B_OK) 150 return error; 151 // TODO: The content type could as well be set by the caller. 152 153 partition->SetContentName(NULL); 154 partition->SetContentParameters(NULL); 155 partition->SetContentSize( 156 sector_align(partition->Size(), partition->BlockSize())); 157 158 *_handle = handleDeleter.Detach(); 159 160 return B_OK; 161 } 162 163 164 // #pragma mark - ExtendedPartitionHandle 165 166 167 // constructor 168 ExtendedPartitionHandle::ExtendedPartitionHandle(BMutablePartition* partition) 169 : BPartitionHandle(partition) 170 { 171 } 172 173 174 // destructor 175 ExtendedPartitionHandle::~ExtendedPartitionHandle() 176 { 177 } 178 179 180 // Init 181 status_t 182 ExtendedPartitionHandle::Init() 183 { 184 // initialize the extended partition from the mutable partition 185 186 BMutablePartition* partition = Partition(); 187 188 // our parent has already set the child cookie to the primary partition. 189 fPrimaryPartition = (PrimaryPartition*)partition->ChildCookie(); 190 if (!fPrimaryPartition) 191 return B_BAD_VALUE; 192 193 if (!fPrimaryPartition->IsExtended()) 194 return B_BAD_VALUE; 195 196 // init the child partitions 197 int32 count = partition->CountChildren(); 198 for (int32 i = 0; i < count; i++) { 199 BMutablePartition* child = partition->ChildAt(i); 200 201 PartitionType type; 202 if (!type.SetType(child->Type())) 203 return B_BAD_VALUE; 204 205 void* handle = parse_driver_settings_string(child->Parameters()); 206 if (handle == NULL) 207 return B_ERROR; 208 209 bool active = get_driver_boolean_parameter( 210 handle, "active", false, true); 211 212 off_t ptsOffset = 0; 213 const char* buffer = get_driver_parameter(handle, 214 "partition_table_offset", NULL, NULL); 215 if (buffer != NULL) 216 ptsOffset = strtoull(buffer, NULL, 10); 217 else { 218 delete_driver_settings(handle); 219 return B_BAD_VALUE; 220 } 221 delete_driver_settings(handle); 222 223 LogicalPartition* logical = new(nothrow) LogicalPartition; 224 if (!logical) 225 return B_NO_MEMORY; 226 227 logical->SetTo(child->Offset(), child->Size(), type.Type(), active, 228 ptsOffset, fPrimaryPartition); 229 230 child->SetChildCookie(logical); 231 } 232 233 return B_OK; 234 } 235 236 237 // SupportedOperations 238 uint32 239 ExtendedPartitionHandle::SupportedOperations(uint32 mask) 240 { 241 uint32 flags = 0; 242 243 // creating child 244 if (mask & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) { 245 BPartitioningInfo info; 246 if (GetPartitioningInfo(&info) == B_OK 247 && info.CountPartitionableSpaces() > 1) { 248 flags |= B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD; 249 } 250 } 251 252 return flags; 253 } 254 255 256 // SupportedChildOperations 257 uint32 258 ExtendedPartitionHandle::SupportedChildOperations( 259 const BMutablePartition* child, uint32 mask) 260 { 261 return B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD; 262 } 263 264 265 // GetNextSupportedType 266 status_t 267 ExtendedPartitionHandle::GetNextSupportedType(const BMutablePartition* child, 268 int32* cookie, BString* type) 269 { 270 int32 index = *cookie; 271 const partition_type* nextType; 272 PartitionMap partitionMap; 273 while (true) { 274 nextType = partitionMap.GetNextSupportedPartitionType(index); 275 if (nextType == NULL) 276 return B_ENTRY_NOT_FOUND; 277 index++; 278 if (nextType->used 279 && strcmp(nextType->name, kPartitionTypeIntelExtended) != 0) 280 break; 281 } 282 283 if (!nextType) 284 return B_ENTRY_NOT_FOUND; 285 286 type->SetTo(nextType->name); 287 *cookie = index; 288 289 return B_OK; 290 } 291 292 293 // GetPartitioningInfo 294 status_t 295 ExtendedPartitionHandle::GetPartitioningInfo(BPartitioningInfo* info) 296 { 297 // init to the full size (minus the first PTS_OFFSET) 298 BMutablePartition* partition = Partition(); 299 off_t offset = partition->Offset() + PTS_OFFSET; 300 off_t size = partition->Size() - PTS_OFFSET; 301 status_t error = info->SetTo(offset, size); 302 if (error != B_OK) 303 return error; 304 305 // exclude the space of the existing logical partitions 306 int32 count = partition->CountChildren(); 307 for (int32 i = 0; i < count; i++) { 308 BMutablePartition* child = partition->ChildAt(i); 309 error = info->ExcludeOccupiedSpace(child->Offset(), 310 child->Size() + PTS_OFFSET + Partition()->BlockSize()); 311 if (error != B_OK) 312 return error; 313 314 LogicalPartition* logical = (LogicalPartition*)child->ChildCookie(); 315 if (logical == NULL) 316 return B_BAD_VALUE; 317 error = info->ExcludeOccupiedSpace( 318 logical->PartitionTableOffset(), 319 PTS_OFFSET + Partition()->BlockSize()); 320 if (error != B_OK) 321 return error; 322 } 323 324 return B_OK; 325 } 326 327 328 // GetChildCreationParameterEditor 329 status_t 330 ExtendedPartitionHandle::GetChildCreationParameterEditor(const char* type, 331 BPartitionParameterEditor** editor) 332 { 333 *editor = NULL; 334 return B_OK; 335 } 336 337 338 // ValidateCreateChild 339 status_t 340 ExtendedPartitionHandle::ValidateCreateChild(off_t* _offset, off_t* _size, 341 const char* typeString, BString* name, const char* parameters) 342 { 343 // check type 344 if (!typeString) 345 return B_BAD_VALUE; 346 347 // check name 348 if (name) 349 name->Truncate(0); 350 351 // check the free space situation 352 BPartitioningInfo info; 353 status_t error = GetPartitioningInfo(&info); 354 if (error != B_OK) 355 return error; 356 357 // any space in the partition at all? 358 int32 spacesCount = info.CountPartitionableSpaces(); 359 if (spacesCount == 0) 360 return B_BAD_VALUE; 361 362 // check offset and size 363 off_t offset = sector_align(*_offset, Partition()->BlockSize()); 364 off_t size = sector_align(*_size, Partition()->BlockSize()); 365 // TODO: Rather round size up? 366 off_t end = offset + size; 367 368 // get the first partitionable space the requested interval intersects with 369 int32 spaceIndex = -1; 370 int32 closestSpaceIndex = -1; 371 off_t closestSpaceDistance = 0; 372 for (int32 i = 0; i < spacesCount; i++) { 373 off_t spaceOffset, spaceSize; 374 info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize); 375 off_t spaceEnd = spaceOffset + spaceSize; 376 377 if (spaceOffset >= offset && spaceOffset < end 378 || offset >= spaceOffset && offset < spaceEnd) { 379 spaceIndex = i; 380 break; 381 } 382 383 off_t distance; 384 if (offset < spaceOffset) 385 distance = spaceOffset - end; 386 else 387 distance = spaceEnd - offset; 388 389 if (closestSpaceIndex == -1 || distance < closestSpaceDistance) { 390 closestSpaceIndex = i; 391 closestSpaceDistance = distance; 392 } 393 } 394 395 // get the space we found 396 off_t spaceOffset, spaceSize; 397 info.GetPartitionableSpaceAt( 398 spaceIndex >= 0 ? spaceIndex : closestSpaceIndex, &spaceOffset, 399 &spaceSize); 400 off_t spaceEnd = spaceOffset + spaceSize; 401 402 // If the requested intervald doesn't intersect with any space yet, move 403 // it, so that it does. 404 if (spaceIndex < 0) { 405 spaceIndex = closestSpaceIndex; 406 if (offset < spaceOffset) { 407 offset = spaceOffset; 408 end = offset + size; 409 } else { 410 end = spaceEnd; 411 offset = end - size; 412 } 413 } 414 415 // move/shrink the interval, so that it fully lies within the space 416 if (offset < spaceOffset) { 417 offset = spaceOffset; 418 end = offset + size; 419 if (end > spaceEnd) { 420 end = spaceEnd; 421 size = end - offset; 422 } 423 } else if (end > spaceEnd) { 424 end = spaceEnd; 425 offset = end - size; 426 if (offset < spaceOffset) { 427 offset = spaceOffset; 428 size = end - offset; 429 } 430 } 431 432 *_offset = offset; 433 *_size = size; 434 435 return B_OK; 436 } 437 438 439 // CreateChild 440 status_t 441 ExtendedPartitionHandle::CreateChild(off_t offset, off_t size, 442 const char* typeString, const char* name, const char* _parameters, 443 BMutablePartition** _child) 444 { 445 // check type 446 PartitionType type; 447 if (!type.SetType(typeString) || type.IsEmpty()) 448 return B_BAD_VALUE; 449 450 // check name 451 if (name && strlen(name) > 0) 452 return B_BAD_VALUE; 453 454 // offset properly aligned? 455 if (offset != sector_align(offset, Partition()->BlockSize()) 456 || size != sector_align(size, Partition()->BlockSize())) 457 return B_BAD_VALUE; 458 459 // check the free space situation 460 BPartitioningInfo info; 461 status_t error = GetPartitioningInfo(&info); 462 if (error != B_OK) 463 return error; 464 465 bool foundSpace = false; 466 off_t end = offset + size; 467 int32 spacesCount = info.CountPartitionableSpaces(); 468 for (int32 i = 0; i < spacesCount; i++) { 469 off_t spaceOffset, spaceSize; 470 info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize); 471 off_t spaceEnd = spaceOffset + spaceSize; 472 473 if (offset >= spaceOffset && end <= spaceEnd) { 474 foundSpace = true; 475 break; 476 } 477 } 478 479 if (!foundSpace) 480 return B_BAD_VALUE; 481 482 BString parameters(_parameters); 483 parameters << "partition_table_offset " << offset - PTS_OFFSET << " ;\n"; 484 // everything looks good, create the child 485 BMutablePartition* child; 486 error = Partition()->CreateChild(-1, typeString, 487 NULL, parameters.String(), &child); 488 if (error != B_OK) 489 return error; 490 491 // init the child 492 child->SetOffset(offset); 493 child->SetSize(size); 494 child->SetBlockSize(Partition()->BlockSize()); 495 //child->SetFlags(0); 496 child->SetChildCookie(Partition()); 497 498 *_child = child; 499 return B_OK; 500 } 501 502 503 // DeleteChild 504 status_t 505 ExtendedPartitionHandle::DeleteChild(BMutablePartition* child) 506 { 507 BMutablePartition* parent = child->Parent(); 508 status_t error = parent->DeleteChild(child); 509 510 return error; 511 } 512 513