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 // #pragma mark - 103 104 105 Partition::Partition(int fd) 106 : 107 fParent(NULL), 108 fIsFileSystem(false), 109 fIsPartitioningSystem(false) 110 { 111 TRACE(("%p Partition::Partition\n", this)); 112 113 memset((partition_data *)this, 0, sizeof(partition_data)); 114 id = (partition_id)this; 115 116 // it's safe to close the file 117 fFD = dup(fd); 118 } 119 120 121 Partition::~Partition() 122 { 123 TRACE(("%p Partition::~Partition\n", this)); 124 125 // Tell the children that their parent is gone 126 127 NodeIterator iterator = gPartitions.GetIterator(); 128 Partition *child; 129 130 while ((child = (Partition *)iterator.Next()) != NULL) { 131 if (child->Parent() == this) 132 child->SetParent(NULL); 133 } 134 135 close(fFD); 136 } 137 138 139 void 140 Partition::SetParent(Partition *parent) 141 { 142 TRACE(("%p Partition::SetParent %p\n", this, parent)); 143 fParent = parent; 144 } 145 146 147 Partition * 148 Partition::Parent() const 149 { 150 //TRACE(("%p Partition::Parent is %p\n", this, fParent)); 151 return fParent; 152 } 153 154 155 ssize_t 156 Partition::ReadAt(void *cookie, off_t position, void *buffer, size_t bufferSize) 157 { 158 if (position > this->size) 159 return 0; 160 if (position < 0) 161 return B_BAD_VALUE; 162 163 if (position + bufferSize > this->size) 164 bufferSize = this->size - position; 165 166 ssize_t result = read_pos(fFD, this->offset + position, buffer, bufferSize); 167 return result < 0 ? errno : result; 168 } 169 170 171 ssize_t 172 Partition::WriteAt(void *cookie, off_t position, const void *buffer, 173 size_t bufferSize) 174 { 175 if (position > this->size) 176 return 0; 177 if (position < 0) 178 return B_BAD_VALUE; 179 180 if (position + bufferSize > this->size) 181 bufferSize = this->size - position; 182 183 ssize_t result = write_pos(fFD, this->offset + position, buffer, 184 bufferSize); 185 return result < 0 ? errno : result; 186 } 187 188 189 off_t 190 Partition::Size() const 191 { 192 struct stat stat; 193 if (fstat(fFD, &stat) == B_OK) 194 return stat.st_size; 195 196 return Node::Size(); 197 } 198 199 200 int32 201 Partition::Type() const 202 { 203 struct stat stat; 204 if (fstat(fFD, &stat) == B_OK) 205 return stat.st_mode; 206 207 return Node::Type(); 208 } 209 210 211 Partition * 212 Partition::AddChild() 213 { 214 Partition *child = new(nothrow) Partition(fFD); 215 TRACE(("%p Partition::AddChild %p\n", this, child)); 216 if (child == NULL) 217 return NULL; 218 219 child->SetParent(this); 220 child_count++; 221 fChildren.Add(child); 222 223 return child; 224 } 225 226 227 status_t 228 Partition::_Mount(file_system_module_info *module, Directory **_fileSystem) 229 { 230 TRACE(("%p Partition::_Mount check for file_system: %s\n", 231 this, module->pretty_name)); 232 233 Directory *fileSystem; 234 if (module->get_file_system(this, &fileSystem) == B_OK) { 235 gRoot->AddVolume(fileSystem, this); 236 if (_fileSystem) 237 *_fileSystem = fileSystem; 238 239 // remember the module that mounted us 240 fModuleName = module->module_name; 241 this->content_type = module->pretty_name; 242 243 fIsFileSystem = true; 244 245 #ifdef BOOT_SUPPORT_FILE_MAP_DISK 246 static int fileMapDiskDepth = 0; 247 // if we aren't already mounting an image 248 if (!fileMapDiskDepth++) { 249 // see if it contains an image file we could mount in turn 250 FileMapDisk *disk = FileMapDisk::FindAnyFileMapDisk(fileSystem); 251 if (disk) { 252 TRACE(("%p Partition::_Mount: found FileMapDisk\n", this)); 253 disk->RegisterFileMapBootItem(); 254 add_partitions_for(disk, true, false); 255 } 256 } 257 fileMapDiskDepth--; 258 #endif 259 260 return B_OK; 261 } 262 263 return B_BAD_VALUE; 264 } 265 266 267 status_t 268 Partition::Mount(Directory **_fileSystem, bool isBootDevice) 269 { 270 if (isBootDevice && gBootVolume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, 271 false)) { 272 return _Mount(&gTarFileSystemModule, _fileSystem); 273 } 274 275 for (int32 i = 0; i < sNumFileSystemModules; i++) { 276 status_t status = _Mount(sFileSystemModules[i], _fileSystem); 277 if (status == B_OK) 278 return B_OK; 279 } 280 281 return B_ENTRY_NOT_FOUND; 282 } 283 284 285 status_t 286 Partition::Scan(bool mountFileSystems, bool isBootDevice) 287 { 288 // scan for partitions first (recursively all eventual children as well) 289 290 TRACE(("%p Partition::Scan()\n", this)); 291 292 // if we were not booted from the real boot device, we won't scan 293 // the device we were booted from (which is likely to be a slow 294 // floppy or CD) 295 if (isBootDevice && gBootVolume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, 296 false)) { 297 return B_ENTRY_NOT_FOUND; 298 } 299 300 const partition_module_info *bestModule = NULL; 301 void *bestCookie = NULL; 302 float bestPriority = -1; 303 304 for (int32 i = 0; i < sNumPartitionModules; i++) { 305 const partition_module_info *module = sPartitionModules[i]; 306 void *cookie = NULL; 307 NodeOpener opener(this, O_RDONLY); 308 309 TRACE(("check for partitioning_system: %s\n", module->pretty_name)); 310 311 float priority 312 = module->identify_partition(opener.Descriptor(), this, &cookie); 313 if (priority < 0.0) 314 continue; 315 316 TRACE((" priority: %ld\n", (int32)(priority * 1000))); 317 if (priority <= bestPriority) { 318 // the disk system recognized the partition worse than the currently 319 // best one 320 module->free_identify_partition_cookie(this, cookie); 321 continue; 322 } 323 324 // a new winner, replace the previous one 325 if (bestModule) 326 bestModule->free_identify_partition_cookie(this, bestCookie); 327 bestModule = module; 328 bestCookie = cookie; 329 bestPriority = priority; 330 } 331 332 // find the best FS module 333 const file_system_module_info *bestFSModule = NULL; 334 float bestFSPriority = -1; 335 for (int32 i = 0; i < sNumFileSystemModules; i++) { 336 if (sFileSystemModules[i]->identify_file_system == NULL) 337 continue; 338 339 float priority = sFileSystemModules[i]->identify_file_system(this); 340 if (priority <= 0) 341 continue; 342 343 if (priority > bestFSPriority) { 344 bestFSModule = sFileSystemModules[i]; 345 bestFSPriority = priority; 346 } 347 } 348 349 // now let the best matching disk system scan the partition 350 if (bestModule && bestPriority >= bestFSPriority) { 351 NodeOpener opener(this, O_RDONLY); 352 status_t status = bestModule->scan_partition(opener.Descriptor(), this, 353 bestCookie); 354 bestModule->free_identify_partition_cookie(this, bestCookie); 355 356 if (status != B_OK) { 357 dprintf("Partitioning module `%s' recognized the partition, but " 358 "failed to scan it\n", bestModule->pretty_name); 359 return status; 360 } 361 362 fIsPartitioningSystem = true; 363 364 content_type = bestModule->pretty_name; 365 flags |= B_PARTITION_PARTITIONING_SYSTEM; 366 367 // now that we've found something, check our children 368 // out as well! 369 370 NodeIterator iterator = fChildren.GetIterator(); 371 Partition *child = NULL; 372 373 while ((child = (Partition *)iterator.Next()) != NULL) { 374 TRACE(("%p Partition::Scan(): scan child %p (start = %Ld, size " 375 "= %Ld, parent = %p)!\n", this, child, child->offset, 376 child->size, child->Parent())); 377 378 child->Scan(mountFileSystems); 379 380 if (!mountFileSystems || child->IsFileSystem()) { 381 // move the partitions containing file systems to the partition 382 // list 383 fChildren.Remove(child); 384 gPartitions.Add(child); 385 } 386 } 387 388 // remove all unused children (we keep only file systems) 389 390 while ((child = (Partition *)fChildren.Head()) != NULL) { 391 fChildren.Remove(child); 392 delete child; 393 } 394 395 // remember the name of the module that identified us 396 fModuleName = bestModule->module.name; 397 398 return B_OK; 399 } 400 401 // scan for file systems 402 403 if (mountFileSystems) { 404 // TODO: Use the FS module we've got, if any. Requires to implement the 405 // identify_file_system() hook in every FS. 406 return Mount(); 407 } 408 409 return B_ENTRY_NOT_FOUND; 410 } 411 412 } // namespace boot 413 414 415 // #pragma mark - 416 417 418 /*! Scans the device passed in for partitioning systems. If none are found, 419 a partition containing the whole device is created. 420 All created partitions are added to the gPartitions list. 421 */ 422 status_t 423 add_partitions_for(int fd, bool mountFileSystems, bool isBootDevice) 424 { 425 TRACE(("add_partitions_for(fd = %d, mountFS = %s)\n", fd, 426 mountFileSystems ? "yes" : "no")); 427 428 Partition *partition = new(nothrow) Partition(fd); 429 430 // set some magic/default values 431 partition->block_size = 512; 432 partition->size = partition->Size(); 433 434 // add this partition to the list of partitions, if it contains 435 // or might contain a file system 436 if ((partition->Scan(mountFileSystems, isBootDevice) == B_OK 437 && partition->IsFileSystem()) 438 || (!partition->IsPartitioningSystem() && !mountFileSystems)) { 439 gPartitions.Add(partition); 440 return B_OK; 441 } 442 443 // if not, we no longer need the partition 444 delete partition; 445 return B_OK; 446 } 447 448 449 status_t 450 add_partitions_for(Node *device, bool mountFileSystems, bool isBootDevice) 451 { 452 TRACE(("add_partitions_for(%p, mountFS = %s)\n", device, 453 mountFileSystems ? "yes" : "no")); 454 455 int fd = open_node(device, O_RDONLY); 456 if (fd < B_OK) 457 return fd; 458 459 status_t status = add_partitions_for(fd, mountFileSystems, isBootDevice); 460 if (status < B_OK) 461 dprintf("add_partitions_for(%d) failed: %ld\n", fd, status); 462 463 close(fd); 464 return B_OK; 465 } 466 467 468 partition_data * 469 create_child_partition(partition_id id, int32 index, off_t offset, off_t size, 470 partition_id childID) 471 { 472 Partition &partition = *(Partition *)id; 473 Partition *child = partition.AddChild(); 474 if (child == NULL) { 475 dprintf("creating partition failed: no memory\n"); 476 return NULL; 477 } 478 479 child->offset = offset; 480 child->size = size; 481 482 // we cannot do anything with the child here, because it was not 483 // yet initialized by the partition module. 484 TRACE(("new child partition!\n")); 485 486 return child; 487 } 488 489 490 partition_data * 491 get_child_partition(partition_id id, int32 index) 492 { 493 //Partition &partition = *(Partition *)id; 494 495 // TODO: do we really have to implement this? 496 // The intel partition module doesn't really need this for our mission... 497 TRACE(("get_child_partition(id = %lu, index = %ld)\n", id, index)); 498 499 return NULL; 500 } 501 502 503 partition_data * 504 get_parent_partition(partition_id id) 505 { 506 Partition &partition = *(Partition *)id; 507 508 return partition.Parent(); 509 } 510 511