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