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