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