1 /* 2 * Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <boot/partitions.h> 8 9 #include <errno.h> 10 #include <unistd.h> 11 #include <string.h> 12 13 #include <boot/FileMapDisk.h> 14 #include <boot/platform.h> 15 #include <boot/stage2.h> 16 #include <boot/stdio.h> 17 #include <boot/vfs.h> 18 #include <ddm_modules.h> 19 20 #include "RootFileSystem.h" 21 22 23 using namespace boot; 24 25 #define TRACE_PARTITIONS 26 #ifdef TRACE_PARTITIONS 27 # define TRACE(x) dprintf x 28 #else 29 # define TRACE(x) ; 30 #endif 31 32 33 /* supported partition modules */ 34 35 static const partition_module_info *sPartitionModules[] = { 36 #ifdef BOOT_SUPPORT_PARTITION_AMIGA 37 &gAmigaPartitionModule, 38 #endif 39 #ifdef BOOT_SUPPORT_PARTITION_EFI 40 &gEFIPartitionModule, 41 #endif 42 #ifdef BOOT_SUPPORT_PARTITION_INTEL 43 &gIntelPartitionMapModule, 44 &gIntelExtendedPartitionModule, 45 #endif 46 #ifdef BOOT_SUPPORT_PARTITION_APPLE 47 &gApplePartitionModule, 48 #endif 49 }; 50 static const int32 sNumPartitionModules = sizeof(sPartitionModules) 51 / sizeof(partition_module_info *); 52 53 /* supported file system modules */ 54 55 static file_system_module_info *sFileSystemModules[] = { 56 #ifdef BOOT_SUPPORT_FILE_SYSTEM_BFS 57 &gBFSFileSystemModule, 58 #endif 59 #ifdef BOOT_SUPPORT_FILE_SYSTEM_AMIGA_FFS 60 &gAmigaFFSFileSystemModule, 61 #endif 62 #ifdef BOOT_SUPPORT_FILE_SYSTEM_FAT 63 &gFATFileSystemModule, 64 #endif 65 #ifdef BOOT_SUPPORT_FILE_SYSTEM_HFS_PLUS 66 &gHFSPlusFileSystemModule, 67 #endif 68 #ifdef BOOT_SUPPORT_FILE_SYSTEM_TARFS 69 &gTarFileSystemModule, 70 #endif 71 }; 72 static const int32 sNumFileSystemModules = sizeof(sFileSystemModules) 73 / sizeof(file_system_module_info *); 74 75 extern NodeList gPartitions; 76 77 78 namespace boot { 79 80 /*! A convenience class to automatically close a 81 file descriptor upon deconstruction. 82 */ 83 class NodeOpener { 84 public: 85 NodeOpener(Node *node, int mode) 86 { 87 fFD = open_node(node, mode); 88 } 89 90 ~NodeOpener() 91 { 92 close(fFD); 93 } 94 95 int Descriptor() const { return fFD; } 96 97 private: 98 int fFD; 99 }; 100 101 102 static int32 sIdCounter = 0; 103 104 105 // #pragma mark - 106 107 108 Partition::Partition(int fd) 109 : 110 fParent(NULL), 111 fIsFileSystem(false), 112 fIsPartitioningSystem(false) 113 { 114 TRACE(("%p Partition::Partition\n", this)); 115 116 memset((partition_data *)this, 0, sizeof(partition_data)); 117 118 id = atomic_add(&sIdCounter, 1); 119 120 // it's safe to close the file 121 fFD = dup(fd); 122 } 123 124 125 Partition::~Partition() 126 { 127 TRACE(("%p Partition::~Partition\n", this)); 128 129 // Tell the children that their parent is gone 130 131 NodeIterator iterator = gPartitions.GetIterator(); 132 Partition *child; 133 134 while ((child = (Partition *)iterator.Next()) != NULL) { 135 if (child->Parent() == this) 136 child->SetParent(NULL); 137 } 138 139 close(fFD); 140 } 141 142 143 Partition * 144 Partition::Lookup(partition_id id, NodeList *list) 145 { 146 Partition *p; 147 148 if (list == NULL) 149 list = &gPartitions; 150 151 NodeIterator iterator = list->GetIterator(); 152 153 while ((p = (Partition *)iterator.Next()) != NULL) { 154 if (p->id == id) 155 return p; 156 if (!p->fChildren.IsEmpty()) { 157 Partition *c = Lookup(id, &p->fChildren); 158 if (c) 159 return c; 160 } 161 } 162 return NULL; 163 } 164 165 166 void 167 Partition::SetParent(Partition *parent) 168 { 169 TRACE(("%p Partition::SetParent %p\n", this, parent)); 170 fParent = parent; 171 } 172 173 174 Partition * 175 Partition::Parent() const 176 { 177 //TRACE(("%p Partition::Parent is %p\n", this, fParent)); 178 return fParent; 179 } 180 181 182 ssize_t 183 Partition::ReadAt(void *cookie, off_t position, void *buffer, size_t bufferSize) 184 { 185 if (position > this->size) 186 return 0; 187 if (position < 0) 188 return B_BAD_VALUE; 189 190 if (position + (off_t)bufferSize > this->size) 191 bufferSize = this->size - position; 192 193 ssize_t result = read_pos(fFD, this->offset + position, buffer, bufferSize); 194 return result < 0 ? errno : result; 195 } 196 197 198 ssize_t 199 Partition::WriteAt(void *cookie, off_t position, const void *buffer, 200 size_t bufferSize) 201 { 202 if (position > this->size) 203 return 0; 204 if (position < 0) 205 return B_BAD_VALUE; 206 207 if (position + (off_t)bufferSize > this->size) 208 bufferSize = this->size - position; 209 210 ssize_t result = write_pos(fFD, this->offset + position, buffer, 211 bufferSize); 212 return result < 0 ? errno : result; 213 } 214 215 216 off_t 217 Partition::Size() const 218 { 219 struct stat stat; 220 if (fstat(fFD, &stat) == B_OK) 221 return stat.st_size; 222 223 return Node::Size(); 224 } 225 226 227 int32 228 Partition::Type() const 229 { 230 struct stat stat; 231 if (fstat(fFD, &stat) == B_OK) 232 return stat.st_mode; 233 234 return Node::Type(); 235 } 236 237 238 Partition * 239 Partition::AddChild() 240 { 241 Partition *child = new(nothrow) Partition(fFD); 242 TRACE(("%p Partition::AddChild %p\n", this, child)); 243 if (child == NULL) 244 return NULL; 245 246 child->SetParent(this); 247 child_count++; 248 fChildren.Add(child); 249 250 return child; 251 } 252 253 254 status_t 255 Partition::_Mount(file_system_module_info *module, Directory **_fileSystem) 256 { 257 TRACE(("%p Partition::_Mount check for file_system: %s\n", 258 this, module->pretty_name)); 259 260 Directory *fileSystem; 261 if (module->get_file_system(this, &fileSystem) == B_OK) { 262 gRoot->AddVolume(fileSystem, this); 263 if (_fileSystem) 264 *_fileSystem = fileSystem; 265 266 // remember the module that mounted us 267 fModuleName = module->module_name; 268 this->content_type = module->pretty_name; 269 270 fIsFileSystem = true; 271 272 #ifdef BOOT_SUPPORT_FILE_MAP_DISK 273 static int fileMapDiskDepth = 0; 274 // if we aren't already mounting an image 275 if (!fileMapDiskDepth++) { 276 // see if it contains an image file we could mount in turn 277 FileMapDisk *disk = FileMapDisk::FindAnyFileMapDisk(fileSystem); 278 if (disk) { 279 TRACE(("%p Partition::_Mount: found FileMapDisk\n", this)); 280 disk->RegisterFileMapBootItem(); 281 add_partitions_for(disk, true, false); 282 } 283 } 284 fileMapDiskDepth--; 285 #endif 286 287 return B_OK; 288 } 289 290 return B_BAD_VALUE; 291 } 292 293 294 status_t 295 Partition::Mount(Directory **_fileSystem, bool isBootDevice) 296 { 297 if (isBootDevice && gBootVolume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, 298 false)) { 299 return _Mount(&gTarFileSystemModule, _fileSystem); 300 } 301 302 for (int32 i = 0; i < sNumFileSystemModules; i++) { 303 status_t status = _Mount(sFileSystemModules[i], _fileSystem); 304 if (status == B_OK) 305 return B_OK; 306 } 307 308 return B_ENTRY_NOT_FOUND; 309 } 310 311 312 status_t 313 Partition::Scan(bool mountFileSystems, bool isBootDevice) 314 { 315 // scan for partitions first (recursively all eventual children as well) 316 317 TRACE(("%p Partition::Scan()\n", this)); 318 319 // if we were not booted from the real boot device, we won't scan 320 // the device we were booted from (which is likely to be a slow 321 // floppy or CD) 322 if (isBootDevice && gBootVolume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, 323 false)) { 324 return B_ENTRY_NOT_FOUND; 325 } 326 327 const partition_module_info *bestModule = NULL; 328 void *bestCookie = NULL; 329 float bestPriority = -1; 330 331 for (int32 i = 0; i < sNumPartitionModules; i++) { 332 const partition_module_info *module = sPartitionModules[i]; 333 void *cookie = NULL; 334 NodeOpener opener(this, O_RDONLY); 335 336 TRACE(("check for partitioning_system: %s\n", module->pretty_name)); 337 338 float priority 339 = module->identify_partition(opener.Descriptor(), this, &cookie); 340 if (priority < 0.0) 341 continue; 342 343 TRACE((" priority: %" B_PRId32 "\n", (int32)(priority * 1000))); 344 if (priority <= bestPriority) { 345 // the disk system recognized the partition worse than the currently 346 // best one 347 module->free_identify_partition_cookie(this, cookie); 348 continue; 349 } 350 351 // a new winner, replace the previous one 352 if (bestModule) 353 bestModule->free_identify_partition_cookie(this, bestCookie); 354 bestModule = module; 355 bestCookie = cookie; 356 bestPriority = priority; 357 } 358 359 // find the best FS module 360 const file_system_module_info *bestFSModule = NULL; 361 float bestFSPriority = -1; 362 for (int32 i = 0; i < sNumFileSystemModules; i++) { 363 if (sFileSystemModules[i]->identify_file_system == NULL) 364 continue; 365 366 float priority = sFileSystemModules[i]->identify_file_system(this); 367 if (priority <= 0) 368 continue; 369 370 if (priority > bestFSPriority) { 371 bestFSModule = sFileSystemModules[i]; 372 bestFSPriority = priority; 373 } 374 } 375 376 // now let the best matching disk system scan the partition 377 if (bestModule && bestPriority >= bestFSPriority) { 378 NodeOpener opener(this, O_RDONLY); 379 status_t status = bestModule->scan_partition(opener.Descriptor(), this, 380 bestCookie); 381 bestModule->free_identify_partition_cookie(this, bestCookie); 382 383 if (status != B_OK) { 384 dprintf("Partitioning module `%s' recognized the partition, but " 385 "failed to scan it\n", bestModule->pretty_name); 386 return status; 387 } 388 389 fIsPartitioningSystem = true; 390 391 content_type = bestModule->pretty_name; 392 flags |= B_PARTITION_PARTITIONING_SYSTEM; 393 394 // now that we've found something, check our children 395 // out as well! 396 397 NodeIterator iterator = fChildren.GetIterator(); 398 Partition *child = NULL; 399 400 while ((child = (Partition *)iterator.Next()) != NULL) { 401 TRACE(("%p Partition::Scan(): scan child %p (start = %" B_PRIdOFF 402 ", size = %" B_PRIdOFF ", parent = %p)!\n", this, child, 403 child->offset, child->size, child->Parent())); 404 405 child->Scan(mountFileSystems); 406 407 if (!mountFileSystems || child->IsFileSystem()) { 408 // move the partitions containing file systems to the partition 409 // list 410 fChildren.Remove(child); 411 gPartitions.Add(child); 412 } 413 } 414 415 // remove all unused children (we keep only file systems) 416 417 while ((child = (Partition *)fChildren.Head()) != NULL) { 418 fChildren.Remove(child); 419 delete child; 420 } 421 422 // remember the name of the module that identified us 423 fModuleName = bestModule->module.name; 424 425 return B_OK; 426 } 427 428 // scan for file systems 429 430 if (mountFileSystems) { 431 // TODO: Use the FS module we've got, if any. Requires to implement the 432 // identify_file_system() hook in every FS. 433 return Mount(); 434 } 435 436 return B_ENTRY_NOT_FOUND; 437 } 438 439 } // namespace boot 440 441 442 // #pragma mark - 443 444 445 /*! Scans the device passed in for partitioning systems. If none are found, 446 a partition containing the whole device is created. 447 All created partitions are added to the gPartitions list. 448 */ 449 status_t 450 add_partitions_for(int fd, bool mountFileSystems, bool isBootDevice) 451 { 452 TRACE(("add_partitions_for(fd = %d, mountFS = %s)\n", fd, 453 mountFileSystems ? "yes" : "no")); 454 455 Partition *partition = new(nothrow) Partition(fd); 456 457 // set some magic/default values 458 partition->block_size = 512; 459 partition->size = partition->Size(); 460 461 // add this partition to the list of partitions 462 // temporarily for Lookup() to work 463 gPartitions.Add(partition); 464 465 // keep it, if it contains or might contain a file system 466 if ((partition->Scan(mountFileSystems, isBootDevice) == B_OK 467 && partition->IsFileSystem()) 468 || (!partition->IsPartitioningSystem() && !mountFileSystems)) { 469 return B_OK; 470 } 471 472 // if not, we no longer need the partition 473 gPartitions.Remove(partition); 474 delete partition; 475 return B_OK; 476 } 477 478 479 status_t 480 add_partitions_for(Node *device, bool mountFileSystems, bool isBootDevice) 481 { 482 TRACE(("add_partitions_for(%p, mountFS = %s)\n", device, 483 mountFileSystems ? "yes" : "no")); 484 485 int fd = open_node(device, O_RDONLY); 486 if (fd < B_OK) 487 return fd; 488 489 status_t status = add_partitions_for(fd, mountFileSystems, isBootDevice); 490 if (status < B_OK) 491 dprintf("add_partitions_for(%d) failed: %" B_PRIx32 "\n", fd, status); 492 493 close(fd); 494 return B_OK; 495 } 496 497 498 partition_data * 499 create_child_partition(partition_id id, int32 index, off_t offset, off_t size, 500 partition_id childID) 501 { 502 Partition *partition = Partition::Lookup(id); 503 if (partition == NULL) { 504 dprintf("creating partition failed: could not find partition.\n"); 505 return NULL; 506 } 507 508 Partition *child = partition->AddChild(); 509 if (child == NULL) { 510 dprintf("creating partition failed: no memory\n"); 511 return NULL; 512 } 513 514 child->offset = offset; 515 child->size = size; 516 517 // we cannot do anything with the child here, because it was not 518 // yet initialized by the partition module. 519 TRACE(("new child partition!\n")); 520 521 return child; 522 } 523 524 525 partition_data * 526 get_child_partition(partition_id id, int32 index) 527 { 528 // TODO: do we really have to implement this? 529 // The intel partition module doesn't really need this for our mission... 530 TRACE(("get_child_partition(id = %" B_PRId32 ", index = %" B_PRId32 ")\n", 531 id, index)); 532 533 return NULL; 534 } 535 536 537 partition_data * 538 get_parent_partition(partition_id id) 539 { 540 Partition *partition = Partition::Lookup(id); 541 if (partition == NULL) { 542 dprintf("could not find parent partition.\n"); 543 return NULL; 544 } 545 return partition->Parent(); 546 } 547 548