1 /* 2 * Copyright 2004-2008, Haiku, Inc. All rights reserved. 3 * Copyright 2003-2004, Ingo Weinhold, bonefish@cs.tu-berlin.de. All rights reserved. 4 * 5 * Distributed under the terms of the MIT License. 6 */ 7 8 9 #include "KDiskDevice.h" 10 #include "KDiskDeviceManager.h" 11 #include "KDiskDeviceUtils.h" 12 #include "KDiskSystem.h" 13 #include "KFileDiskDevice.h" 14 #include "KFileSystem.h" 15 #include "KPartition.h" 16 #include "KPartitioningSystem.h" 17 #include "KPartitionVisitor.h" 18 #include "KPath.h" 19 20 #include <VectorMap.h> 21 #include <VectorSet.h> 22 23 #include <KernelExport.h> 24 #include <NodeMonitor.h> 25 26 #include <boot_device.h> 27 #include <kmodule.h> 28 #include <node_monitor.h> 29 #include <Notifications.h> 30 #include <util/kernel_cpp.h> 31 #include <vfs.h> 32 33 #include <dirent.h> 34 #include <errno.h> 35 #include <module.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <sys/stat.h> 40 41 // debugging 42 //#define DBG(x) 43 #define DBG(x) x 44 #define OUT dprintf 45 46 47 // directories for partitioning and file system modules 48 static const char* kPartitioningSystemPrefix = "partitioning_systems"; 49 static const char* kFileSystemPrefix = "file_systems"; 50 51 52 // singleton instance 53 KDiskDeviceManager* KDiskDeviceManager::sDefaultManager = NULL; 54 55 56 struct GetPartitionID { 57 inline partition_id operator()(const KPartition* partition) const 58 { 59 return partition->ID(); 60 } 61 }; 62 63 64 struct GetDiskSystemID { 65 inline disk_system_id operator()(const KDiskSystem* system) const 66 { 67 return system->ID(); 68 } 69 }; 70 71 72 struct KDiskDeviceManager::PartitionMap : VectorMap<partition_id, KPartition*, 73 VectorMapEntryStrategy::ImplicitKey<partition_id, KPartition*, 74 GetPartitionID> > { 75 }; 76 77 78 struct KDiskDeviceManager::DeviceMap : VectorMap<partition_id, KDiskDevice*, 79 VectorMapEntryStrategy::ImplicitKey<partition_id, KDiskDevice*, 80 GetPartitionID> > { 81 }; 82 83 84 struct KDiskDeviceManager::DiskSystemMap : VectorMap<disk_system_id, 85 KDiskSystem*, 86 VectorMapEntryStrategy::ImplicitKey<disk_system_id, KDiskSystem*, 87 GetDiskSystemID> > { 88 }; 89 90 91 struct KDiskDeviceManager::PartitionSet : VectorSet<KPartition*> { 92 }; 93 94 95 class KDiskDeviceManager::DiskSystemWatcher : public NotificationListener { 96 public: 97 DiskSystemWatcher(KDiskDeviceManager* manager) 98 : 99 fManager(manager) 100 { 101 } 102 103 virtual ~DiskSystemWatcher() 104 { 105 } 106 107 virtual void EventOccured(NotificationService& service, 108 const KMessage* event) 109 { 110 if (event->GetInt32("opcode", -1) != B_ENTRY_REMOVED) 111 fManager->RescanDiskSystems(); 112 } 113 114 private: 115 KDiskDeviceManager* fManager; 116 }; 117 118 119 class KDiskDeviceManager::DeviceWatcher : public NotificationListener { 120 public: 121 DeviceWatcher(KDiskDeviceManager* manager) 122 : 123 fManager(manager) 124 { 125 } 126 127 virtual ~DeviceWatcher() 128 { 129 } 130 131 virtual void EventOccured(NotificationService& service, 132 const KMessage* event) 133 { 134 int32 opCode = event->GetInt32("opcode", -1); 135 switch (opCode) { 136 case B_ENTRY_CREATED: 137 case B_ENTRY_REMOVED: 138 { 139 const char* name = event->GetString("name", ""); 140 dev_t device = event->GetInt32("device", -1); 141 ino_t directory = event->GetInt64("directory", -1); 142 ino_t node = event->GetInt64("node", -1); 143 144 struct stat st; 145 if (vfs_stat_node_ref(device, node, &st) != 0) 146 break; 147 148 if (S_ISDIR(st.st_mode)) { 149 if (opCode == B_ENTRY_CREATED) 150 add_node_listener(device, node, B_WATCH_DIRECTORY, 151 *this); 152 else 153 remove_node_listener(device, node, *this); 154 } else { 155 if (strcmp(name, "raw") == 0) { 156 // a new raw device was added/removed 157 KPath path(B_PATH_NAME_LENGTH + 1); 158 if (path.InitCheck() != B_OK 159 || vfs_entry_ref_to_path(device, directory, 160 name, path.LockBuffer(), 161 path.BufferSize()) != B_OK) { 162 break; 163 } 164 path.UnlockBuffer(); 165 166 if (opCode == B_ENTRY_CREATED) 167 fManager->CreateDevice(path.Path()); 168 else 169 fManager->DeleteDevice(path.Path()); 170 } 171 } 172 173 break; 174 } 175 176 default: 177 break; 178 } 179 } 180 181 private: 182 KDiskDeviceManager* fManager; 183 }; 184 185 186 // #pragma mark - 187 188 189 KDiskDeviceManager::KDiskDeviceManager() 190 : fLock("disk device manager"), 191 fDevices(new(nothrow) DeviceMap), 192 fPartitions(new(nothrow) PartitionMap), 193 fDiskSystems(new(nothrow) DiskSystemMap), 194 fObsoletePartitions(new(nothrow) PartitionSet), 195 fMediaChecker(-1), 196 fTerminating(false), 197 fDiskSystemWatcher(NULL), 198 fDeviceWatcher(new(nothrow) DeviceWatcher(this)) 199 { 200 if (InitCheck() != B_OK) 201 return; 202 203 RescanDiskSystems(); 204 205 fMediaChecker = spawn_kernel_thread(_CheckMediaStatusDaemon, 206 "media checker", B_NORMAL_PRIORITY, this); 207 if (fMediaChecker >= 0) 208 resume_thread(fMediaChecker); 209 210 DBG(OUT("number of disk systems: %ld\n", CountDiskSystems())); 211 // TODO: Watch the disk systems and the relevant directories. 212 } 213 214 215 KDiskDeviceManager::~KDiskDeviceManager() 216 { 217 fTerminating = true; 218 219 status_t result; 220 wait_for_thread(fMediaChecker, &result); 221 222 // stop all node monitoring 223 _AddRemoveMonitoring("/dev/disk", false); 224 delete fDeviceWatcher; 225 226 // remove all devices 227 for (int32 cookie = 0; KDiskDevice* device = NextDevice(&cookie);) { 228 PartitionRegistrar _(device); 229 _RemoveDevice(device); 230 } 231 232 // some sanity checks 233 if (fPartitions->Count() > 0) { 234 DBG(OUT("WARNING: There are still %ld unremoved partitions!\n", 235 fPartitions->Count())); 236 for (PartitionMap::Iterator it = fPartitions->Begin(); 237 it != fPartitions->End(); ++it) { 238 DBG(OUT(" partition: %ld\n", it->Value()->ID())); 239 } 240 } 241 if (fObsoletePartitions->Count() > 0) { 242 DBG(OUT("WARNING: There are still %ld obsolete partitions!\n", 243 fObsoletePartitions->Count())); 244 for (PartitionSet::Iterator it = fObsoletePartitions->Begin(); 245 it != fObsoletePartitions->End(); ++it) { 246 DBG(OUT(" partition: %ld\n", (*it)->ID())); 247 } 248 } 249 // remove all disk systems 250 for (int32 cookie = 0; KDiskSystem* diskSystem = NextDiskSystem(&cookie);) { 251 fDiskSystems->Remove(diskSystem->ID()); 252 if (diskSystem->IsLoaded()) { 253 DBG(OUT("WARNING: Disk system `%s' (%ld) is still loaded!\n", 254 diskSystem->Name(), diskSystem->ID())); 255 } else 256 delete diskSystem; 257 } 258 259 // delete the containers 260 delete fPartitions; 261 delete fDevices; 262 delete fDiskSystems; 263 delete fObsoletePartitions; 264 } 265 266 267 status_t 268 KDiskDeviceManager::InitCheck() const 269 { 270 if (!fPartitions || !fDevices || !fDiskSystems || !fObsoletePartitions) 271 return B_NO_MEMORY; 272 273 return fLock.Sem() >= 0 ? B_OK : fLock.Sem(); 274 } 275 276 277 /*! This creates the system's default DiskDeviceManager. 278 The creation is not thread-safe, and shouldn't be done more than once. 279 */ 280 status_t 281 KDiskDeviceManager::CreateDefault() 282 { 283 if (sDefaultManager != NULL) 284 return B_OK; 285 286 sDefaultManager = new(nothrow) KDiskDeviceManager; 287 if (sDefaultManager == NULL) 288 return B_NO_MEMORY; 289 290 return sDefaultManager->InitCheck(); 291 } 292 293 294 /*! This deletes the default DiskDeviceManager. The deletion is not 295 thread-safe either, you should make sure that it's called only once. 296 */ 297 void 298 KDiskDeviceManager::DeleteDefault() 299 { 300 delete sDefaultManager; 301 sDefaultManager = NULL; 302 } 303 304 305 KDiskDeviceManager* 306 KDiskDeviceManager::Default() 307 { 308 return sDefaultManager; 309 } 310 311 312 bool 313 KDiskDeviceManager::Lock() 314 { 315 return fLock.Lock(); 316 } 317 318 319 void 320 KDiskDeviceManager::Unlock() 321 { 322 fLock.Unlock(); 323 } 324 325 326 KDiskDevice* 327 KDiskDeviceManager::FindDevice(const char* path) 328 { 329 for (int32 cookie = 0; KDiskDevice* device = NextDevice(&cookie); ) { 330 if (device->Path() && !strcmp(path, device->Path())) 331 return device; 332 } 333 return NULL; 334 } 335 336 337 KDiskDevice* 338 KDiskDeviceManager::FindDevice(partition_id id, bool deviceOnly) 339 { 340 if (KPartition* partition = FindPartition(id)) { 341 KDiskDevice* device = partition->Device(); 342 if (!deviceOnly || id == device->ID()) 343 return device; 344 } 345 return NULL; 346 } 347 348 349 KPartition* 350 KDiskDeviceManager::FindPartition(const char* path) 351 { 352 // TODO: Optimize! 353 KPath partitionPath; 354 if (partitionPath.InitCheck() != B_OK) 355 return NULL; 356 357 for (PartitionMap::Iterator iterator = fPartitions->Begin(); 358 iterator != fPartitions->End(); ++iterator) { 359 KPartition* partition = iterator->Value(); 360 if (partition->GetPath(&partitionPath) == B_OK 361 && partitionPath == path) { 362 return partition; 363 } 364 } 365 366 return NULL; 367 } 368 369 370 KPartition* 371 KDiskDeviceManager::FindPartition(partition_id id) 372 { 373 PartitionMap::Iterator iterator = fPartitions->Find(id); 374 if (iterator != fPartitions->End()) 375 return iterator->Value(); 376 377 return NULL; 378 } 379 380 381 KFileDiskDevice* 382 KDiskDeviceManager::FindFileDevice(const char* filePath) 383 { 384 for (int32 cookie = 0; KDiskDevice* device = NextDevice(&cookie); ) { 385 KFileDiskDevice* fileDevice = dynamic_cast<KFileDiskDevice*>(device); 386 if (fileDevice && fileDevice->FilePath() 387 && !strcmp(filePath, fileDevice->FilePath())) { 388 return fileDevice; 389 } 390 } 391 return NULL; 392 } 393 394 395 KDiskDevice* 396 KDiskDeviceManager::RegisterDevice(const char* path) 397 { 398 if (ManagerLocker locker = this) { 399 for (int32 i = 0; i < 2; i++) { 400 if (KDiskDevice* device = FindDevice(path)) { 401 device->Register(); 402 return device; 403 } 404 405 // if the device is not known yet, create it and try again 406 const char* leaf = strrchr(path, '/'); 407 if (i == 0 && !strncmp(path, "/dev/disk", 9) 408 && !strcmp(leaf + 1, "raw") && CreateDevice(path) < B_OK) 409 break; 410 } 411 } 412 return NULL; 413 } 414 415 416 KDiskDevice* 417 KDiskDeviceManager::RegisterDevice(partition_id id, bool deviceOnly) 418 { 419 if (ManagerLocker locker = this) { 420 if (KDiskDevice* device = FindDevice(id, deviceOnly)) { 421 device->Register(); 422 return device; 423 } 424 } 425 return NULL; 426 } 427 428 429 KDiskDevice* 430 KDiskDeviceManager::RegisterNextDevice(int32* cookie) 431 { 432 if (!cookie) 433 return NULL; 434 435 if (ManagerLocker locker = this) { 436 if (KDiskDevice* device = NextDevice(cookie)) { 437 device->Register(); 438 return device; 439 } 440 } 441 return NULL; 442 } 443 444 445 KPartition* 446 KDiskDeviceManager::RegisterPartition(const char* path) 447 { 448 if (ManagerLocker locker = this) { 449 for (int32 i = 0; i < 2; i++) { 450 if (KPartition* partition = FindPartition(path)) { 451 partition->Register(); 452 return partition; 453 } 454 455 // if the device is not known yet, create it and try again 456 const char* leaf = strrchr(path, '/'); 457 if (i == 0 && !strncmp(path, "/dev/disk", 9) 458 && !strcmp(leaf + 1, "raw") && CreateDevice(path) < B_OK) 459 break; 460 } 461 } 462 return NULL; 463 } 464 465 466 KPartition* 467 KDiskDeviceManager::RegisterPartition(partition_id id) 468 { 469 if (ManagerLocker locker = this) { 470 if (KPartition* partition = FindPartition(id)) { 471 partition->Register(); 472 return partition; 473 } 474 } 475 return NULL; 476 } 477 478 479 KFileDiskDevice* 480 KDiskDeviceManager::RegisterFileDevice(const char* filePath) 481 { 482 if (ManagerLocker locker = this) { 483 if (KFileDiskDevice* device = FindFileDevice(filePath)) { 484 device->Register(); 485 return device; 486 } 487 } 488 return NULL; 489 } 490 491 492 KDiskDevice* 493 KDiskDeviceManager::ReadLockDevice(partition_id id, bool deviceOnly) 494 { 495 // register device 496 KDiskDevice* device = RegisterDevice(id, deviceOnly); 497 if (!device) 498 return NULL; 499 // lock device 500 if (device->ReadLock()) 501 return device; 502 device->Unregister(); 503 return NULL; 504 } 505 506 507 KDiskDevice* 508 KDiskDeviceManager::WriteLockDevice(partition_id id, bool deviceOnly) 509 { 510 // register device 511 KDiskDevice* device = RegisterDevice(id, deviceOnly); 512 if (!device) 513 return NULL; 514 // lock device 515 if (device->WriteLock()) 516 return device; 517 device->Unregister(); 518 return NULL; 519 } 520 521 522 KPartition* 523 KDiskDeviceManager::ReadLockPartition(partition_id id) 524 { 525 // register partition 526 KPartition* partition = RegisterPartition(id); 527 if (!partition) 528 return NULL; 529 // get and register the device 530 KDiskDevice* device = NULL; 531 if (ManagerLocker locker = this) { 532 device = partition->Device(); 533 if (device) 534 device->Register(); 535 } 536 // lock the device 537 if (device && device->ReadLock()) { 538 // final check, if the partition still belongs to the device 539 if (partition->Device() == device) 540 return partition; 541 device->ReadUnlock(); 542 } 543 // cleanup on failure 544 if (device) 545 device->Unregister(); 546 partition->Unregister(); 547 return NULL; 548 } 549 550 551 KPartition* 552 KDiskDeviceManager::WriteLockPartition(partition_id id) 553 { 554 // register partition 555 KPartition* partition = RegisterPartition(id); 556 if (!partition) 557 return NULL; 558 // get and register the device 559 KDiskDevice* device = NULL; 560 if (ManagerLocker locker = this) { 561 device = partition->Device(); 562 if (device) 563 device->Register(); 564 } 565 // lock the device 566 if (device && device->WriteLock()) { 567 // final check, if the partition still belongs to the device 568 if (partition->Device() == device) 569 return partition; 570 device->WriteUnlock(); 571 } 572 // cleanup on failure 573 if (device) 574 device->Unregister(); 575 partition->Unregister(); 576 return NULL; 577 } 578 579 580 status_t 581 KDiskDeviceManager::ScanPartition(KPartition* partition) 582 { 583 // TODO: This won't do. Locking the DDM while scanning the partition is not a 584 // good idea. Even locking the device doesn't feel right. Marking the partition 585 // busy and passing the disk system a temporary clone of the partition_data 586 // should work as well. 587 if (DeviceWriteLocker deviceLocker = partition->Device()) { 588 if (ManagerLocker locker = this) 589 return _ScanPartition(partition, false); 590 } 591 592 return B_ERROR; 593 } 594 595 596 partition_id 597 KDiskDeviceManager::CreateDevice(const char* path, bool* newlyCreated) 598 { 599 if (!path) 600 return B_BAD_VALUE; 601 602 status_t error = B_ERROR; 603 if (ManagerLocker locker = this) { 604 KDiskDevice* device = FindDevice(path); 605 if (device != NULL) { 606 // we already know this device 607 if (newlyCreated) 608 *newlyCreated = false; 609 610 return device->ID(); 611 } 612 613 // create a KDiskDevice for it 614 device = new(nothrow) KDiskDevice; 615 if (!device) 616 return B_NO_MEMORY; 617 618 // initialize and add the device 619 error = device->SetTo(path); 620 621 // Note: Here we are allowed to lock a device although already having 622 // the manager locked, since it is not yet added to the manager. 623 DeviceWriteLocker deviceLocker(device); 624 if (error == B_OK && !deviceLocker.IsLocked()) 625 error = B_ERROR; 626 if (error == B_OK && !_AddDevice(device)) 627 error = B_NO_MEMORY; 628 629 // cleanup on error 630 if (error != B_OK) { 631 delete device; 632 return error; 633 } 634 635 if (error == B_OK) { 636 // scan for partitions 637 _ScanPartition(device, false); 638 device->UnmarkBusy(true); 639 640 if (newlyCreated) 641 *newlyCreated = true; 642 643 return device->ID(); 644 } 645 } 646 647 return error; 648 } 649 650 651 status_t 652 KDiskDeviceManager::DeleteDevice(const char* path) 653 { 654 KDiskDevice* device = FindDevice(path); 655 if (device == NULL) 656 return B_ENTRY_NOT_FOUND; 657 658 PartitionRegistrar _(device, false); 659 if (DeviceWriteLocker locker = device) { 660 if (_RemoveDevice(device)) 661 return B_OK; 662 } 663 664 return B_ERROR; 665 } 666 667 668 partition_id 669 KDiskDeviceManager::CreateFileDevice(const char* filePath, bool* newlyCreated) 670 { 671 if (!filePath) 672 return B_BAD_VALUE; 673 674 // normalize the file path 675 KPath normalizedFilePath; 676 status_t error = normalizedFilePath.SetTo(filePath, true); 677 if (error != B_OK) 678 return error; 679 filePath = normalizedFilePath.Path(); 680 681 KFileDiskDevice* device = NULL; 682 if (ManagerLocker locker = this) { 683 // check, if the device does already exist 684 if ((device = FindFileDevice(filePath))) { 685 if (newlyCreated) 686 *newlyCreated = false; 687 688 return device->ID(); 689 } 690 691 // allocate a KFileDiskDevice 692 device = new(nothrow) KFileDiskDevice; 693 if (!device) 694 return B_NO_MEMORY; 695 696 // initialize and add the device 697 error = device->SetTo(filePath); 698 699 // Note: Here we are allowed to lock a device although already having 700 // the manager locked, since it is not yet added to the manager. 701 DeviceWriteLocker deviceLocker(device); 702 if (error == B_OK && !deviceLocker.IsLocked()) 703 error = B_ERROR; 704 if (error == B_OK && !_AddDevice(device)) 705 error = B_NO_MEMORY; 706 707 // scan device 708 if (error == B_OK) { 709 _ScanPartition(device, false); 710 device->UnmarkBusy(true); 711 712 if (newlyCreated) 713 *newlyCreated = true; 714 715 return device->ID(); 716 } 717 718 // cleanup on failure 719 delete device; 720 } else 721 error = B_ERROR; 722 return error; 723 } 724 725 726 status_t 727 KDiskDeviceManager::DeleteFileDevice(const char* filePath) 728 { 729 if (KFileDiskDevice* device = RegisterFileDevice(filePath)) { 730 PartitionRegistrar _(device, true); 731 if (DeviceWriteLocker locker = device) { 732 if (_RemoveDevice(device)) 733 return B_OK; 734 } 735 } 736 return B_ERROR; 737 } 738 739 740 status_t 741 KDiskDeviceManager::DeleteFileDevice(partition_id id) 742 { 743 if (KDiskDevice* device = RegisterDevice(id)) { 744 PartitionRegistrar _(device, true); 745 if (!dynamic_cast<KFileDiskDevice*>(device) || id != device->ID()) 746 return B_ENTRY_NOT_FOUND; 747 if (DeviceWriteLocker locker = device) { 748 if (_RemoveDevice(device)) 749 return B_OK; 750 } 751 } 752 return B_ERROR; 753 } 754 755 756 int32 757 KDiskDeviceManager::CountDevices() 758 { 759 return fDevices->Count(); 760 } 761 762 763 KDiskDevice* 764 KDiskDeviceManager::NextDevice(int32* cookie) 765 { 766 if (!cookie) 767 return NULL; 768 DeviceMap::Iterator it = fDevices->FindClose(*cookie, false); 769 if (it != fDevices->End()) { 770 KDiskDevice* device = it->Value(); 771 *cookie = device->ID() + 1; 772 return device; 773 } 774 return NULL; 775 } 776 777 778 bool 779 KDiskDeviceManager::PartitionAdded(KPartition* partition) 780 { 781 return partition && fPartitions->Put(partition->ID(), partition) == B_OK; 782 } 783 784 785 bool 786 KDiskDeviceManager::PartitionRemoved(KPartition* partition) 787 { 788 if (partition && partition->PrepareForRemoval() 789 && fPartitions->Remove(partition->ID())) { 790 // If adding the partition to the obsolete list fails (due to lack 791 // of memory), we can't do anything about it. We will leak memory then. 792 fObsoletePartitions->Insert(partition); 793 partition->MarkObsolete(); 794 return true; 795 } 796 return false; 797 } 798 799 800 bool 801 KDiskDeviceManager::DeletePartition(KPartition* partition) 802 { 803 if (partition && partition->IsObsolete() 804 && partition->CountReferences() == 0 805 && partition->PrepareForDeletion() 806 && fObsoletePartitions->Remove(partition)) { 807 delete partition; 808 return true; 809 } 810 return false; 811 } 812 813 814 KDiskSystem* 815 KDiskDeviceManager::FindDiskSystem(const char* name, bool byPrettyName) 816 { 817 for (int32 cookie = 0; KDiskSystem* diskSystem = NextDiskSystem(&cookie);) { 818 if (byPrettyName) { 819 if (strcmp(name, diskSystem->PrettyName()) == 0) 820 return diskSystem; 821 } else { 822 if (strcmp(name, diskSystem->Name()) == 0) 823 return diskSystem; 824 } 825 } 826 return NULL; 827 } 828 829 830 KDiskSystem* 831 KDiskDeviceManager::FindDiskSystem(disk_system_id id) 832 { 833 DiskSystemMap::Iterator it = fDiskSystems->Find(id); 834 if (it != fDiskSystems->End()) 835 return it->Value(); 836 return NULL; 837 } 838 839 840 int32 841 KDiskDeviceManager::CountDiskSystems() 842 { 843 return fDiskSystems->Count(); 844 } 845 846 847 KDiskSystem* 848 KDiskDeviceManager::NextDiskSystem(int32* cookie) 849 { 850 if (!cookie) 851 return NULL; 852 853 DiskSystemMap::Iterator it = fDiskSystems->FindClose(*cookie, false); 854 if (it != fDiskSystems->End()) { 855 KDiskSystem* diskSystem = it->Value(); 856 *cookie = diskSystem->ID() + 1; 857 return diskSystem; 858 } 859 return NULL; 860 } 861 862 863 KDiskSystem* 864 KDiskDeviceManager::LoadDiskSystem(const char* name, bool byPrettyName) 865 { 866 KDiskSystem* diskSystem = NULL; 867 if (ManagerLocker locker = this) { 868 diskSystem = FindDiskSystem(name, byPrettyName); 869 if (diskSystem && diskSystem->Load() != B_OK) 870 diskSystem = NULL; 871 } 872 return diskSystem; 873 } 874 875 876 KDiskSystem* 877 KDiskDeviceManager::LoadDiskSystem(disk_system_id id) 878 { 879 KDiskSystem* diskSystem = NULL; 880 if (ManagerLocker locker = this) { 881 diskSystem = FindDiskSystem(id); 882 if (diskSystem && diskSystem->Load() != B_OK) 883 diskSystem = NULL; 884 } 885 return diskSystem; 886 } 887 888 889 KDiskSystem* 890 KDiskDeviceManager::LoadNextDiskSystem(int32* cookie) 891 { 892 if (!cookie) 893 return NULL; 894 895 if (ManagerLocker locker = this) { 896 if (KDiskSystem* diskSystem = NextDiskSystem(cookie)) { 897 if (diskSystem->Load() == B_OK) { 898 *cookie = diskSystem->ID() + 1; 899 return diskSystem; 900 } 901 } 902 } 903 return NULL; 904 } 905 906 907 status_t 908 KDiskDeviceManager::InitialDeviceScan() 909 { 910 status_t error = B_ERROR; 911 912 // scan for devices 913 if (ManagerLocker locker = this) { 914 error = _Scan("/dev/disk"); 915 if (error != B_OK) 916 return error; 917 } 918 919 // scan the devices for partitions 920 int32 cookie = 0; 921 while (KDiskDevice* device = RegisterNextDevice(&cookie)) { 922 PartitionRegistrar _(device, true); 923 if (DeviceWriteLocker deviceLocker = device) { 924 if (ManagerLocker locker = this) { 925 error = _ScanPartition(device, false); 926 device->UnmarkBusy(true); 927 if (error != B_OK) 928 break; 929 } else 930 return B_ERROR; 931 } else 932 return B_ERROR; 933 } 934 return error; 935 } 936 937 938 status_t 939 KDiskDeviceManager::StartMonitoring() 940 { 941 // do another scan, this will populate the devfs directories 942 InitialDeviceScan(); 943 944 // start monitoring the disk systems 945 fDiskSystemWatcher = new(std::nothrow) DiskSystemWatcher(this); 946 if (fDiskSystemWatcher != NULL) { 947 start_watching_modules(kFileSystemPrefix, *fDiskSystemWatcher); 948 start_watching_modules(kPartitioningSystemPrefix, 949 *fDiskSystemWatcher); 950 } 951 952 // start monitoring all dirs under /dev/disk 953 return _AddRemoveMonitoring("/dev/disk", true); 954 } 955 956 957 status_t 958 KDiskDeviceManager::_RescanDiskSystems(DiskSystemMap& addedSystems, 959 bool fileSystems) 960 { 961 void* cookie = open_module_list(fileSystems 962 ? kFileSystemPrefix : kPartitioningSystemPrefix); 963 if (cookie == NULL) 964 return B_NO_MEMORY; 965 966 while (true) { 967 KPath name; 968 if (name.InitCheck() != B_OK) 969 break; 970 size_t nameLength = name.BufferSize(); 971 if (read_next_module_name(cookie, name.LockBuffer(), 972 &nameLength) != B_OK) { 973 break; 974 } 975 name.UnlockBuffer(); 976 977 if (FindDiskSystem(name.Path())) 978 continue; 979 980 if (fileSystems) { 981 DBG(OUT("file system: %s\n", name.Path())); 982 _AddFileSystem(name.Path()); 983 } else { 984 DBG(OUT("partitioning system: %s\n", name.Path())); 985 _AddPartitioningSystem(name.Path()); 986 } 987 988 if (KDiskSystem* system = FindDiskSystem(name.Path())) 989 addedSystems.Put(system->ID(), system); 990 } 991 992 close_module_list(cookie); 993 return B_OK; 994 } 995 996 997 /*! Rescan the existing disk systems. This is called after the boot device 998 has become available. 999 */ 1000 status_t 1001 KDiskDeviceManager::RescanDiskSystems() 1002 { 1003 DiskSystemMap addedSystems; 1004 1005 Lock(); 1006 1007 // rescan for partitioning and file systems 1008 _RescanDiskSystems(addedSystems, false); 1009 _RescanDiskSystems(addedSystems, true); 1010 1011 Unlock(); 1012 1013 // rescan existing devices with the new disk systems 1014 int32 cookie = 0; 1015 while (KDiskDevice* device = RegisterNextDevice(&cookie)) { 1016 PartitionRegistrar _(device, true); 1017 if (DeviceWriteLocker deviceLocker = device) { 1018 if (ManagerLocker locker = this) { 1019 status_t status = _ScanPartition(device, false, &addedSystems); 1020 device->UnmarkBusy(true); 1021 if (status != B_OK) 1022 break; 1023 } else 1024 return B_ERROR; 1025 } else 1026 return B_ERROR; 1027 } 1028 1029 return B_OK; 1030 } 1031 1032 1033 status_t 1034 KDiskDeviceManager::_AddPartitioningSystem(const char* name) 1035 { 1036 if (!name) 1037 return B_BAD_VALUE; 1038 1039 KDiskSystem* diskSystem = new(nothrow) KPartitioningSystem(name); 1040 if (!diskSystem) 1041 return B_NO_MEMORY; 1042 return _AddDiskSystem(diskSystem); 1043 } 1044 1045 1046 status_t 1047 KDiskDeviceManager::_AddFileSystem(const char* name) 1048 { 1049 if (!name) 1050 return B_BAD_VALUE; 1051 1052 KDiskSystem* diskSystem = new(nothrow) KFileSystem(name); 1053 if (!diskSystem) 1054 return B_NO_MEMORY; 1055 1056 return _AddDiskSystem(diskSystem); 1057 } 1058 1059 1060 status_t 1061 KDiskDeviceManager::_AddDiskSystem(KDiskSystem* diskSystem) 1062 { 1063 if (!diskSystem) 1064 return B_BAD_VALUE; 1065 DBG(OUT("KDiskDeviceManager::_AddDiskSystem(%s)\n", diskSystem->Name())); 1066 status_t error = diskSystem->Init(); 1067 DBG(if (error != B_OK) 1068 OUT(" initialization failed: %s\n", strerror(error))); 1069 if (error == B_OK) 1070 error = fDiskSystems->Put(diskSystem->ID(), diskSystem); 1071 if (error != B_OK) 1072 delete diskSystem; 1073 DBG(OUT("KDiskDeviceManager::_AddDiskSystem() done: %s\n", 1074 strerror(error))); 1075 return error; 1076 } 1077 1078 1079 bool 1080 KDiskDeviceManager::_AddDevice(KDiskDevice* device) 1081 { 1082 if (!device || !PartitionAdded(device)) 1083 return false; 1084 if (fDevices->Put(device->ID(), device) == B_OK) 1085 return true; 1086 PartitionRemoved(device); 1087 return false; 1088 } 1089 1090 1091 bool 1092 KDiskDeviceManager::_RemoveDevice(KDiskDevice* device) 1093 { 1094 return (device && fDevices->Remove(device->ID()) 1095 && PartitionRemoved(device)); 1096 } 1097 1098 1099 #if 0 1100 /*! 1101 The device must be write locked, the manager must be locked. 1102 */ 1103 status_t 1104 KDiskDeviceManager::_UpdateBusyPartitions(KDiskDevice *device) 1105 { 1106 if (!device) 1107 return B_BAD_VALUE; 1108 // mark all partitions un-busy 1109 struct UnmarkBusyVisitor : KPartitionVisitor { 1110 virtual bool VisitPre(KPartition *partition) 1111 { 1112 partition->ClearFlags(B_PARTITION_BUSY 1113 | B_PARTITION_DESCENDANT_BUSY); 1114 return false; 1115 } 1116 } visitor; 1117 device->VisitEachDescendant(&visitor); 1118 // Iterate through all job queues and all jobs scheduled or in 1119 // progress and mark their scope busy. 1120 for (int32 cookie = 0; 1121 KDiskDeviceJobQueue *jobQueue = NextJobQueue(&cookie); ) { 1122 if (jobQueue->Device() != device) 1123 continue; 1124 for (int32 i = jobQueue->ActiveJobIndex(); 1125 KDiskDeviceJob *job = jobQueue->JobAt(i); i++) { 1126 if (job->Status() != B_DISK_DEVICE_JOB_IN_PROGRESS 1127 && job->Status() != B_DISK_DEVICE_JOB_SCHEDULED) { 1128 continue; 1129 } 1130 KPartition *partition = FindPartition(job->ScopeID()); 1131 if (!partition || partition->Device() != device) 1132 continue; 1133 partition->AddFlags(B_PARTITION_BUSY); 1134 } 1135 } 1136 // mark all anscestors of busy partitions descendant busy and all 1137 // descendants busy 1138 struct MarkBusyVisitor : KPartitionVisitor { 1139 virtual bool VisitPre(KPartition *partition) 1140 { 1141 // parent busy => child busy 1142 if (partition->Parent() && partition->Parent()->IsBusy()) 1143 partition->AddFlags(B_PARTITION_BUSY); 1144 return false; 1145 } 1146 1147 virtual bool VisitPost(KPartition *partition) 1148 { 1149 // child [descendant] busy => parent descendant busy 1150 if ((partition->IsBusy() || partition->IsDescendantBusy()) 1151 && partition->Parent()) { 1152 partition->Parent()->AddFlags(B_PARTITION_DESCENDANT_BUSY); 1153 } 1154 return false; 1155 } 1156 } visitor2; 1157 device->VisitEachDescendant(&visitor2); 1158 return B_OK; 1159 } 1160 #endif 1161 1162 1163 status_t 1164 KDiskDeviceManager::_Scan(const char* path) 1165 { 1166 DBG(OUT("KDiskDeviceManager::_Scan(%s)\n", path)); 1167 status_t error = B_ENTRY_NOT_FOUND; 1168 struct stat st; 1169 if (lstat(path, &st) < 0) { 1170 return errno; 1171 } 1172 if (S_ISDIR(st.st_mode)) { 1173 // a directory: iterate through its contents 1174 DIR* dir = opendir(path); 1175 if (!dir) 1176 return errno; 1177 while (dirent* entry = readdir(dir)) { 1178 // skip "." and ".." 1179 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) 1180 continue; 1181 KPath entryPath; 1182 if (entryPath.SetPath(path) != B_OK 1183 || entryPath.Append(entry->d_name) != B_OK) { 1184 continue; 1185 } 1186 if (_Scan(entryPath.Path()) == B_OK) 1187 error = B_OK; 1188 } 1189 closedir(dir); 1190 } else { 1191 // not a directory 1192 // check, if it is named "raw" 1193 int32 len = strlen(path); 1194 int32 leafLen = strlen("/raw"); 1195 if (len <= leafLen || strcmp(path + len - leafLen, "/raw")) 1196 return B_ERROR; 1197 if (FindDevice(path) != NULL) { 1198 // we already know this device 1199 return B_OK; 1200 } 1201 1202 DBG(OUT(" found device: %s\n", path)); 1203 // create a KDiskDevice for it 1204 KDiskDevice* device = new(nothrow) KDiskDevice; 1205 if (!device) 1206 return B_NO_MEMORY; 1207 1208 // init the KDiskDevice 1209 error = device->SetTo(path); 1210 // add the device 1211 if (error == B_OK && !_AddDevice(device)) 1212 error = B_NO_MEMORY; 1213 // cleanup on error 1214 if (error != B_OK) 1215 delete device; 1216 } 1217 return error; 1218 } 1219 1220 1221 /*! 1222 The device must be write locked, the manager must be locked. 1223 */ 1224 status_t 1225 KDiskDeviceManager::_ScanPartition(KPartition* partition, bool async, 1226 DiskSystemMap* restrictScan) 1227 { 1228 // TODO: There's no reason why the manager needs to be locked anymore. 1229 if (!partition) 1230 return B_BAD_VALUE; 1231 1232 // TODO: Reimplement asynchronous scanning, if we really need it. 1233 #if 0 1234 if (async) { 1235 // create a new job queue for the device 1236 KDiskDeviceJobQueue *jobQueue = new(nothrow) KDiskDeviceJobQueue; 1237 if (!jobQueue) 1238 return B_NO_MEMORY; 1239 jobQueue->SetDevice(partition->Device()); 1240 1241 // create a job for scanning the device and add it to the job queue 1242 KDiskDeviceJob *job = fJobFactory->CreateScanPartitionJob(partition->ID()); 1243 if (!job) { 1244 delete jobQueue; 1245 return B_NO_MEMORY; 1246 } 1247 1248 if (!jobQueue->AddJob(job)) { 1249 delete jobQueue; 1250 delete job; 1251 return B_NO_MEMORY; 1252 } 1253 1254 // add the job queue 1255 status_t error = AddJobQueue(jobQueue); 1256 if (error != B_OK) 1257 delete jobQueue; 1258 1259 return error; 1260 } 1261 #endif 1262 1263 // scan synchronously 1264 1265 return _ScanPartition(partition, restrictScan); 1266 } 1267 1268 1269 status_t 1270 KDiskDeviceManager::_ScanPartition(KPartition* partition, 1271 DiskSystemMap* restrictScan) 1272 { 1273 // the partition's device must be write-locked 1274 if (partition == NULL) 1275 return B_BAD_VALUE; 1276 if (!partition->Device()->HasMedia() || partition->IsMounted()) 1277 return B_OK; 1278 1279 if (partition->CountChildren() > 0) { 1280 // Since this partition has already children, we don't scan it 1281 // again, but only its children. 1282 for (int32 i = 0; KPartition* child = partition->ChildAt(i); i++) { 1283 _ScanPartition(child, restrictScan); 1284 } 1285 return B_OK; 1286 } 1287 1288 DBG( 1289 KPath partitionPath; 1290 partition->GetPath(&partitionPath); 1291 OUT("KDiskDeviceManager::_ScanPartition(%s)\n", partitionPath.Path()); 1292 ) 1293 1294 // publish the partition 1295 status_t error = B_OK; 1296 if (!partition->IsPublished()) { 1297 error = partition->PublishDevice(); 1298 if (error != B_OK) 1299 return error; 1300 } 1301 1302 DiskSystemMap* diskSystems = restrictScan; 1303 if (diskSystems == NULL) 1304 diskSystems = fDiskSystems; 1305 1306 // find the disk system that returns the best priority for this partition 1307 float bestPriority = partition->DiskSystemPriority(); 1308 KDiskSystem* bestDiskSystem = NULL; 1309 void* bestCookie = NULL; 1310 for (DiskSystemMap::Iterator iterator = diskSystems->Begin(); 1311 iterator != diskSystems->End(); iterator++) { 1312 KDiskSystem* diskSystem = iterator->Value(); 1313 if (diskSystem->Load() != B_OK) 1314 continue; 1315 1316 DBG(OUT(" trying: %s\n", diskSystem->Name())); 1317 1318 void* cookie = NULL; 1319 float priority = diskSystem->Identify(partition, &cookie); 1320 1321 DBG(OUT(" returned: %g\n", priority)); 1322 1323 if (priority >= 0 && priority > bestPriority) { 1324 // new best disk system 1325 if (bestDiskSystem) { 1326 bestDiskSystem->FreeIdentifyCookie(partition, bestCookie); 1327 bestDiskSystem->Unload(); 1328 } 1329 bestPriority = priority; 1330 bestDiskSystem = diskSystem; 1331 bestCookie = cookie; 1332 } else { 1333 // disk system doesn't identify the partition or worse than our 1334 // current favorite 1335 if (priority >= 0) 1336 diskSystem->FreeIdentifyCookie(partition, cookie); 1337 diskSystem->Unload(); 1338 } 1339 } 1340 1341 // now, if we have found a disk system, let it scan the partition 1342 if (bestDiskSystem) { 1343 DBG(OUT(" scanning with: %s\n", bestDiskSystem->Name())); 1344 error = bestDiskSystem->Scan(partition, bestCookie); 1345 bestDiskSystem->FreeIdentifyCookie(partition, bestCookie); 1346 if (error == B_OK) { 1347 partition->SetDiskSystem(bestDiskSystem, bestPriority); 1348 for (int32 i = 0; KPartition* child = partition->ChildAt(i); i++) 1349 _ScanPartition(child, restrictScan); 1350 } else { 1351 // TODO: Handle the error. 1352 DBG(OUT(" scanning failed: %s\n", strerror(error))); 1353 } 1354 1355 // now we can safely unload the disk system -- it has been loaded by 1356 // the partition(s) and thus will not really be unloaded 1357 bestDiskSystem->Unload(); 1358 } else { 1359 // contents not recognized 1360 // nothing to be done -- partitions are created as unrecognized 1361 } 1362 1363 return error; 1364 } 1365 1366 1367 status_t 1368 KDiskDeviceManager::_AddRemoveMonitoring(const char* path, bool add) 1369 { 1370 struct stat st; 1371 if (lstat(path, &st) < 0) 1372 return errno; 1373 1374 status_t error = B_ENTRY_NOT_FOUND; 1375 if (S_ISDIR(st.st_mode)) { 1376 if (add) { 1377 error = add_node_listener(st.st_dev, st.st_ino, B_WATCH_DIRECTORY, 1378 *fDeviceWatcher); 1379 } else { 1380 error = remove_node_listener(st.st_dev, st.st_ino, 1381 *fDeviceWatcher); 1382 } 1383 if (error != B_OK) 1384 return error; 1385 1386 DIR* dir = opendir(path); 1387 if (!dir) 1388 return errno; 1389 1390 while (dirent* entry = readdir(dir)) { 1391 // skip "." and ".." 1392 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) 1393 continue; 1394 1395 KPath entryPath; 1396 if (entryPath.SetPath(path) != B_OK 1397 || entryPath.Append(entry->d_name) != B_OK) { 1398 continue; 1399 } 1400 1401 if (_AddRemoveMonitoring(entryPath.Path(), add) == B_OK) 1402 error = B_OK; 1403 } 1404 closedir(dir); 1405 } 1406 1407 return error; 1408 } 1409 1410 1411 status_t 1412 KDiskDeviceManager::_CheckMediaStatus() 1413 { 1414 while (!fTerminating) { 1415 int32 cookie = 0; 1416 while (KDiskDevice* device = RegisterNextDevice(&cookie)) { 1417 PartitionRegistrar _(device, true); 1418 DeviceWriteLocker locker(device); 1419 1420 if (device->IsBusy(true)) 1421 continue; 1422 1423 bool hadMedia = device->HasMedia(); 1424 device->UpdateMediaStatusIfNeeded(); 1425 1426 if (!device->MediaChanged() && (device->HasMedia() || !hadMedia)) 1427 continue; 1428 1429 device->MarkBusy(true); 1430 device->UninitializeMedia(); 1431 1432 if (device->MediaChanged()) { 1433 dprintf("Media changed from %s\n", device->Path()); 1434 device->UpdateGeometry(); 1435 _ScanPartition(device, false); 1436 } else if (!device->HasMedia() && hadMedia) { 1437 dprintf("Media removed from %s\n", device->Path()); 1438 } 1439 1440 device->UnmarkBusy(true); 1441 } 1442 1443 snooze(1000000); 1444 } 1445 1446 return 0; 1447 } 1448 1449 1450 status_t 1451 KDiskDeviceManager::_CheckMediaStatusDaemon(void* self) 1452 { 1453 return ((KDiskDeviceManager*)self)->_CheckMediaStatus(); 1454 } 1455