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 145 *_handle = handleDeleter.Detach(); 146 147 return B_OK; 148 } 149 150 151 // #pragma mark - ExtendedPartitionHandle 152 153 154 ExtendedPartitionHandle::ExtendedPartitionHandle(BMutablePartition* partition) 155 : 156 BPartitionHandle(partition) 157 { 158 } 159 160 161 ExtendedPartitionHandle::~ExtendedPartitionHandle() 162 { 163 } 164 165 166 status_t 167 ExtendedPartitionHandle::Init() 168 { 169 // initialize the extended partition from the mutable partition 170 171 BMutablePartition* partition = Partition(); 172 173 // our parent has already set the child cookie to the primary partition. 174 fPrimaryPartition = (PrimaryPartition*)partition->ChildCookie(); 175 if (!fPrimaryPartition) 176 return B_BAD_VALUE; 177 178 if (!fPrimaryPartition->IsExtended()) 179 return B_BAD_VALUE; 180 181 // init the child partitions 182 int32 count = partition->CountChildren(); 183 for (int32 i = 0; i < count; i++) { 184 BMutablePartition* child = partition->ChildAt(i); 185 186 PartitionType type; 187 if (!type.SetType(child->Type())) 188 return B_BAD_VALUE; 189 190 void* handle = parse_driver_settings_string(child->Parameters()); 191 if (handle == NULL) 192 return B_ERROR; 193 194 bool active = get_driver_boolean_parameter( 195 handle, "active", false, true); 196 197 off_t ptsOffset = 0; 198 const char* buffer = get_driver_parameter(handle, 199 "partition_table_offset", NULL, NULL); 200 if (buffer != NULL) 201 ptsOffset = strtoull(buffer, NULL, 10); 202 else { 203 delete_driver_settings(handle); 204 return B_BAD_VALUE; 205 } 206 delete_driver_settings(handle); 207 208 LogicalPartition* logical = new(nothrow) LogicalPartition; 209 if (!logical) 210 return B_NO_MEMORY; 211 212 logical->SetTo(child->Offset(), child->Size(), type.Type(), active, 213 ptsOffset, fPrimaryPartition); 214 215 child->SetChildCookie(logical); 216 } 217 218 return B_OK; 219 } 220 221 222 uint32 223 ExtendedPartitionHandle::SupportedOperations(uint32 mask) 224 { 225 uint32 flags = 0; 226 227 // creating child 228 if ((mask & B_DISK_SYSTEM_SUPPORTS_CREATING_CHILD) != 0) { 229 BPartitioningInfo info; 230 if (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 ExtendedPartitionHandle::SupportedChildOperations( 242 const BMutablePartition* child, uint32 mask) 243 { 244 return B_DISK_SYSTEM_SUPPORTS_DELETING_CHILD; 245 } 246 247 248 status_t 249 ExtendedPartitionHandle::GetNextSupportedType(const BMutablePartition* child, 250 int32* cookie, BString* type) 251 { 252 int32 index = *cookie; 253 const partition_type* nextType; 254 PartitionMap partitionMap; 255 while (true) { 256 nextType = partitionMap.GetNextSupportedPartitionType(index); 257 if (nextType == NULL) 258 return B_ENTRY_NOT_FOUND; 259 index++; 260 if (nextType->used 261 && strcmp(nextType->name, kPartitionTypeIntelExtended) != 0) 262 break; 263 } 264 265 if (!nextType) 266 return B_ENTRY_NOT_FOUND; 267 268 type->SetTo(nextType->name); 269 *cookie = index; 270 271 return B_OK; 272 } 273 274 275 status_t 276 ExtendedPartitionHandle::GetPartitioningInfo(BPartitioningInfo* info) 277 { 278 // init to the full size (minus the first PTS_OFFSET) 279 BMutablePartition* partition = Partition(); 280 off_t offset = partition->Offset() + PTS_OFFSET; 281 off_t size = partition->Size() - PTS_OFFSET; 282 status_t error = info->SetTo(offset, size); 283 if (error != B_OK) 284 return error; 285 286 // exclude the space of the existing logical partitions 287 int32 count = partition->CountChildren(); 288 for (int32 i = 0; i < count; i++) { 289 BMutablePartition* child = partition->ChildAt(i); 290 error = info->ExcludeOccupiedSpace(child->Offset(), 291 child->Size() + PTS_OFFSET + Partition()->BlockSize()); 292 if (error != B_OK) 293 return error; 294 295 LogicalPartition* logical = (LogicalPartition*)child->ChildCookie(); 296 if (logical == NULL) 297 return B_BAD_VALUE; 298 error = info->ExcludeOccupiedSpace( 299 logical->PartitionTableOffset(), 300 PTS_OFFSET + Partition()->BlockSize()); 301 if (error != B_OK) 302 return error; 303 } 304 305 return B_OK; 306 } 307 308 309 status_t 310 ExtendedPartitionHandle::GetParameterEditor(B_PARAMETER_EDITOR_TYPE type, 311 BPartitionParameterEditor** editor) 312 { 313 *editor = NULL; 314 return B_NOT_SUPPORTED; 315 } 316 317 318 status_t 319 ExtendedPartitionHandle::ValidateCreateChild(off_t* _offset, off_t* _size, 320 const char* typeString, BString* name, const char* parameters) 321 { 322 // check type 323 if (!typeString) 324 return B_BAD_VALUE; 325 326 // check name 327 if (name) 328 name->Truncate(0); 329 330 // check the free space situation 331 BPartitioningInfo info; 332 status_t error = GetPartitioningInfo(&info); 333 if (error != B_OK) 334 return error; 335 336 // any space in the partition at all? 337 int32 spacesCount = info.CountPartitionableSpaces(); 338 if (spacesCount == 0) 339 return B_BAD_VALUE; 340 341 // check offset and size 342 off_t offset = sector_align(*_offset, Partition()->BlockSize()); 343 off_t size = sector_align(*_size, Partition()->BlockSize()); 344 // TODO: Rather round size up? 345 off_t end = offset + size; 346 347 // get the first partitionable space the requested interval intersects with 348 int32 spaceIndex = -1; 349 int32 closestSpaceIndex = -1; 350 off_t closestSpaceDistance = 0; 351 for (int32 i = 0; i < spacesCount; i++) { 352 off_t spaceOffset, spaceSize; 353 info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize); 354 off_t spaceEnd = spaceOffset + spaceSize; 355 356 if ((spaceOffset >= offset && spaceOffset < end) 357 || (offset >= spaceOffset && offset < spaceEnd)) { 358 spaceIndex = i; 359 break; 360 } 361 362 off_t distance; 363 if (offset < spaceOffset) 364 distance = spaceOffset - end; 365 else 366 distance = spaceEnd - offset; 367 368 if (closestSpaceIndex == -1 || distance < closestSpaceDistance) { 369 closestSpaceIndex = i; 370 closestSpaceDistance = distance; 371 } 372 } 373 374 // get the space we found 375 off_t spaceOffset, spaceSize; 376 info.GetPartitionableSpaceAt( 377 spaceIndex >= 0 ? spaceIndex : closestSpaceIndex, &spaceOffset, 378 &spaceSize); 379 off_t spaceEnd = spaceOffset + spaceSize; 380 381 // If the requested intervald doesn't intersect with any space yet, move 382 // it, so that it does. 383 if (spaceIndex < 0) { 384 spaceIndex = closestSpaceIndex; 385 if (offset < spaceOffset) { 386 offset = spaceOffset; 387 end = offset + size; 388 } else { 389 end = spaceEnd; 390 offset = end - size; 391 } 392 } 393 394 // move/shrink the interval, so that it fully lies within the space 395 if (offset < spaceOffset) { 396 offset = spaceOffset; 397 end = offset + size; 398 if (end > spaceEnd) { 399 end = spaceEnd; 400 size = end - offset; 401 } 402 } else if (end > spaceEnd) { 403 end = spaceEnd; 404 offset = end - size; 405 if (offset < spaceOffset) { 406 offset = spaceOffset; 407 size = end - offset; 408 } 409 } 410 411 *_offset = offset; 412 *_size = size; 413 414 return B_OK; 415 } 416 417 418 status_t 419 ExtendedPartitionHandle::CreateChild(off_t offset, off_t size, 420 const char* typeString, const char* name, const char* _parameters, 421 BMutablePartition** _child) 422 { 423 // check type 424 PartitionType type; 425 if (!type.SetType(typeString) || type.IsEmpty()) 426 return B_BAD_VALUE; 427 428 // check name 429 if (name != NULL && name[0] != '\0') 430 return B_BAD_VALUE; 431 432 // offset properly aligned? 433 if (offset != sector_align(offset, Partition()->BlockSize()) 434 || size != sector_align(size, Partition()->BlockSize())) 435 return B_BAD_VALUE; 436 437 // check the free space situation 438 BPartitioningInfo info; 439 status_t error = GetPartitioningInfo(&info); 440 if (error != B_OK) 441 return error; 442 443 bool foundSpace = false; 444 off_t end = offset + size; 445 int32 spacesCount = info.CountPartitionableSpaces(); 446 for (int32 i = 0; i < spacesCount; i++) { 447 off_t spaceOffset, spaceSize; 448 info.GetPartitionableSpaceAt(i, &spaceOffset, &spaceSize); 449 off_t spaceEnd = spaceOffset + spaceSize; 450 451 if (offset >= spaceOffset && end <= spaceEnd) { 452 foundSpace = true; 453 break; 454 } 455 } 456 457 if (!foundSpace) 458 return B_BAD_VALUE; 459 460 BString parameters(_parameters); 461 parameters << "partition_table_offset " << offset - PTS_OFFSET << " ;\n"; 462 // everything looks good, create the child 463 BMutablePartition* child; 464 error = Partition()->CreateChild(-1, typeString, 465 NULL, parameters.String(), &child); 466 if (error != B_OK) 467 return error; 468 469 // init the child 470 child->SetOffset(offset); 471 child->SetSize(size); 472 child->SetBlockSize(Partition()->BlockSize()); 473 //child->SetFlags(0); 474 child->SetChildCookie(Partition()); 475 476 *_child = child; 477 return B_OK; 478 } 479 480 481 status_t 482 ExtendedPartitionHandle::DeleteChild(BMutablePartition* child) 483 { 484 BMutablePartition* parent = child->Parent(); 485 status_t error = parent->DeleteChild(child); 486 487 return error; 488 } 489