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