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