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