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