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