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