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