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 TRACE(("%p Partition::_Mount check for file_system: %s\n", 226 this, module->pretty_name)); 227 228 Directory *fileSystem; 229 if (module->get_file_system(this, &fileSystem) == B_OK) { 230 gRoot->AddVolume(fileSystem, this); 231 if (_fileSystem) 232 *_fileSystem = fileSystem; 233 234 // remember the module name that mounted us 235 fModuleName = module->module_name; 236 237 fIsFileSystem = true; 238 239 #ifdef BOOT_SUPPORT_FILE_MAP_DISK 240 static int fileMapDiskDepth = 0; 241 // if we aren't already mounting an image 242 if (!fileMapDiskDepth++) { 243 // see if it contains an image file we could mount in turn 244 FileMapDisk *disk = FileMapDisk::FindAnyFileMapDisk(fileSystem); 245 if (disk) { 246 TRACE(("%p Partition::_Mount: found FileMapDisk\n", this)); 247 disk->RegisterFileMapBootItem(); 248 add_partitions_for(disk, true, false); 249 } 250 } 251 fileMapDiskDepth--; 252 #endif 253 254 return B_OK; 255 } 256 257 return B_BAD_VALUE; 258 } 259 260 261 status_t 262 Partition::Mount(Directory **_fileSystem, bool isBootDevice) 263 { 264 if (isBootDevice && gKernelArgs.boot_volume.GetBool( 265 BOOT_VOLUME_BOOTED_FROM_IMAGE, false)) { 266 return _Mount(&gTarFileSystemModule, _fileSystem); 267 } 268 269 for (int32 i = 0; i < sNumFileSystemModules; i++) { 270 status_t status = _Mount(sFileSystemModules[i], _fileSystem); 271 if (status == B_OK) 272 return B_OK; 273 } 274 275 return B_ENTRY_NOT_FOUND; 276 } 277 278 279 status_t 280 Partition::Scan(bool mountFileSystems, bool isBootDevice) 281 { 282 // scan for partitions first (recursively all eventual children as well) 283 284 TRACE(("%p Partition::Scan()\n", this)); 285 286 // if we were not booted from the real boot device, we won't scan 287 // the device we were booted from (which is likely to be a slow 288 // floppy or CD) 289 if (isBootDevice && gKernelArgs.boot_volume.GetBool( 290 BOOT_VOLUME_BOOTED_FROM_IMAGE, false)) { 291 return B_ENTRY_NOT_FOUND; 292 } 293 294 const partition_module_info *bestModule = NULL; 295 void *bestCookie = NULL; 296 float bestPriority = -1; 297 298 for (int32 i = 0; i < sNumPartitionModules; i++) { 299 const partition_module_info *module = sPartitionModules[i]; 300 void *cookie = NULL; 301 NodeOpener opener(this, O_RDONLY); 302 303 TRACE(("check for partitioning_system: %s\n", module->pretty_name)); 304 305 float priority 306 = module->identify_partition(opener.Descriptor(), this, &cookie); 307 if (priority < 0.0) 308 continue; 309 310 TRACE((" priority: %ld\n", (int32)(priority * 1000))); 311 if (priority <= bestPriority) { 312 // the disk system recognized the partition worse than the currently 313 // best one 314 module->free_identify_partition_cookie(this, cookie); 315 continue; 316 } 317 318 // a new winner, replace the previous one 319 if (bestModule) 320 bestModule->free_identify_partition_cookie(this, bestCookie); 321 bestModule = module; 322 bestCookie = cookie; 323 bestPriority = priority; 324 } 325 326 // find the best FS module 327 const file_system_module_info *bestFSModule = NULL; 328 float bestFSPriority = -1; 329 for (int32 i = 0; i < sNumFileSystemModules; i++) { 330 if (sFileSystemModules[i]->identify_file_system == NULL) 331 continue; 332 333 float priority = sFileSystemModules[i]->identify_file_system(this); 334 if (priority <= 0) 335 continue; 336 337 if (priority > bestFSPriority) { 338 bestFSModule = sFileSystemModules[i]; 339 bestFSPriority = priority; 340 } 341 } 342 343 // now let the best matching disk system scan the partition 344 if (bestModule && bestPriority >= bestFSPriority) { 345 NodeOpener opener(this, O_RDONLY); 346 status_t status = bestModule->scan_partition(opener.Descriptor(), this, 347 bestCookie); 348 bestModule->free_identify_partition_cookie(this, bestCookie); 349 350 if (status != B_OK) { 351 dprintf("Partitioning module `%s' recognized the partition, but " 352 "failed to scan it\n", bestModule->pretty_name); 353 return status; 354 } 355 356 fIsPartitioningSystem = true; 357 358 content_type = bestModule->pretty_name; 359 flags |= B_PARTITION_PARTITIONING_SYSTEM; 360 361 // now that we've found something, check our children 362 // out as well! 363 364 NodeIterator iterator = fChildren.GetIterator(); 365 Partition *child = NULL; 366 367 while ((child = (Partition *)iterator.Next()) != NULL) { 368 TRACE(("%p Partition::Scan(): scan child %p (start = %Ld, size " 369 "= %Ld, parent = %p)!\n", this, child, child->offset, 370 child->size, child->Parent())); 371 372 child->Scan(mountFileSystems); 373 374 if (!mountFileSystems || child->IsFileSystem()) { 375 // move the partitions containing file systems to the partition 376 // list 377 fChildren.Remove(child); 378 gPartitions.Add(child); 379 } 380 } 381 382 // remove all unused children (we keep only file systems) 383 384 while ((child = (Partition *)fChildren.Head()) != NULL) { 385 fChildren.Remove(child); 386 delete child; 387 } 388 389 // remember the module name that identified us 390 fModuleName = bestModule->module.name; 391 392 return B_OK; 393 } 394 395 // scan for file systems 396 397 if (mountFileSystems) { 398 // TODO: Use the FS module we've got, if any. Requires to implement the 399 // identify_file_system() hook in every FS. 400 return Mount(); 401 } 402 403 return B_ENTRY_NOT_FOUND; 404 } 405 406 } // namespace boot 407 408 409 // #pragma mark - 410 411 412 /*! Scans the device passed in for partitioning systems. If none are found, 413 a partition containing the whole device is created. 414 All created partitions are added to the gPartitions list. 415 */ 416 status_t 417 add_partitions_for(int fd, bool mountFileSystems, bool isBootDevice) 418 { 419 TRACE(("add_partitions_for(fd = %d, mountFS = %s)\n", fd, 420 mountFileSystems ? "yes" : "no")); 421 422 Partition *partition = new Partition(fd); 423 424 // set some magic/default values 425 partition->block_size = 512; 426 partition->size = partition->Size(); 427 428 // add this partition to the list of partitions, if it contains 429 // or might contain a file system 430 if ((partition->Scan(mountFileSystems, isBootDevice) == B_OK 431 && partition->IsFileSystem()) 432 || (!partition->IsPartitioningSystem() && !mountFileSystems)) { 433 gPartitions.Add(partition); 434 return B_OK; 435 } 436 437 // if not, we no longer need the partition 438 delete partition; 439 return B_OK; 440 } 441 442 443 status_t 444 add_partitions_for(Node *device, bool mountFileSystems, bool isBootDevice) 445 { 446 TRACE(("add_partitions_for(%p, mountFS = %s)\n", device, 447 mountFileSystems ? "yes" : "no")); 448 449 int fd = open_node(device, O_RDONLY); 450 if (fd < B_OK) 451 return fd; 452 453 status_t status = add_partitions_for(fd, mountFileSystems, isBootDevice); 454 if (status < B_OK) 455 dprintf("add_partitions_for(%d) failed: %ld\n", fd, status); 456 457 close(fd); 458 return B_OK; 459 } 460 461 462 partition_data * 463 create_child_partition(partition_id id, int32 index, off_t offset, off_t size, 464 partition_id childID) 465 { 466 Partition &partition = *(Partition *)id; 467 Partition *child = partition.AddChild(); 468 if (child == NULL) { 469 dprintf("creating partition failed: no memory\n"); 470 return NULL; 471 } 472 473 child->offset = offset; 474 child->size = size; 475 476 // we cannot do anything with the child here, because it was not 477 // yet initialized by the partition module. 478 TRACE(("new child partition!\n")); 479 480 return child; 481 } 482 483 484 partition_data * 485 get_child_partition(partition_id id, int32 index) 486 { 487 //Partition &partition = *(Partition *)id; 488 489 // TODO: do we really have to implement this? 490 // The intel partition module doesn't really need this for our mission... 491 TRACE(("get_child_partition(id = %lu, index = %ld)\n", id, index)); 492 493 return NULL; 494 } 495 496 497 partition_data * 498 get_parent_partition(partition_id id) 499 { 500 Partition &partition = *(Partition *)id; 501 502 return partition.Parent(); 503 } 504 505