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