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