1 /* 2 ** Copyright 2003-2004, Ingo Weinhold, bonefish@cs.tu-berlin.de. All rights reserved. 3 ** Distributed under the terms of the Haiku License. 4 */ 5 6 7 #include <KernelExport.h> 8 #include <util/kernel_cpp.h> 9 10 #include <dirent.h> 11 #include <errno.h> 12 #include <module.h> 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 #include <sys/stat.h> 17 18 #include <VectorMap.h> 19 #include <VectorSet.h> 20 21 #include "KDiskDevice.h" 22 #include "KDiskDeviceJob.h" 23 #include "KDiskDeviceJobFactory.h" 24 #include "KDiskDeviceJobQueue.h" 25 #include "KDiskDeviceManager.h" 26 #include "KDiskDeviceUtils.h" 27 #include "KDiskSystem.h" 28 #include "KFileDiskDevice.h" 29 #include "KFileSystem.h" 30 #include "KPartition.h" 31 #include "KPartitioningSystem.h" 32 #include "KPartitionVisitor.h" 33 #include "KPath.h" 34 #include "KShadowPartition.h" 35 36 // debugging 37 //#define DBG(x) 38 #define DBG(x) x 39 #define OUT dprintf 40 41 42 // directories for partitioning and file system modules 43 static const char *kPartitioningSystemPrefix = "partitioning_systems"; 44 static const char *kFileSystemPrefix = "file_systems"; 45 46 47 // singleton instance 48 KDiskDeviceManager *KDiskDeviceManager::sDefaultManager = NULL; 49 50 51 // GetPartitionID 52 struct GetPartitionID { 53 inline partition_id operator()(const KPartition *partition) const 54 { 55 return partition->ID(); 56 } 57 }; 58 59 // GetDiskSystemID 60 struct GetDiskSystemID { 61 inline disk_system_id operator()(const KDiskSystem *system) const 62 { 63 return system->ID(); 64 } 65 }; 66 67 // GetJobID 68 struct GetJobID { 69 inline disk_job_id operator()(const KDiskDeviceJob *job) const 70 { 71 return job->ID(); 72 } 73 }; 74 75 // PartitionMap 76 struct KDiskDeviceManager::PartitionMap : VectorMap<partition_id, KPartition*, 77 VectorMapEntryStrategy::ImplicitKey<partition_id, KPartition*, 78 GetPartitionID> > { 79 }; 80 81 // DeviceMap 82 struct KDiskDeviceManager::DeviceMap : VectorMap<partition_id, KDiskDevice*, 83 VectorMapEntryStrategy::ImplicitKey<partition_id, KDiskDevice*, 84 GetPartitionID> > { 85 }; 86 87 // DiskSystemMap 88 struct KDiskDeviceManager::DiskSystemMap : VectorMap<disk_system_id, 89 KDiskSystem*, 90 VectorMapEntryStrategy::ImplicitKey<disk_system_id, KDiskSystem*, 91 GetDiskSystemID> > { 92 }; 93 94 // PartitionSet 95 struct KDiskDeviceManager::PartitionSet : VectorSet<KPartition*> { 96 }; 97 98 // JobMap 99 struct KDiskDeviceManager::JobMap : VectorMap<disk_job_id, KDiskDeviceJob*, 100 VectorMapEntryStrategy::ImplicitKey<disk_job_id, KDiskDeviceJob*, 101 GetJobID> > { 102 }; 103 104 // JobQueueVector 105 struct KDiskDeviceManager::JobQueueVector : Vector<KDiskDeviceJobQueue*> {}; 106 107 108 static bool 109 is_active_job_status(uint32 status) 110 { 111 return (status == B_DISK_DEVICE_JOB_SCHEDULED 112 || status == B_DISK_DEVICE_JOB_IN_PROGRESS); 113 } 114 115 116 // #pragma mark - 117 118 119 KDiskDeviceManager::KDiskDeviceManager() 120 : fLock("disk device manager"), 121 fDevices(new(nothrow) DeviceMap), 122 fPartitions(new(nothrow) PartitionMap), 123 fDiskSystems(new(nothrow) DiskSystemMap), 124 fObsoletePartitions(new(nothrow) PartitionSet), 125 fJobs(new(nothrow) JobMap), 126 fJobQueues(new(nothrow) JobQueueVector), 127 fJobFactory(new(nothrow) KDiskDeviceJobFactory) 128 { 129 if (InitCheck() != B_OK) 130 return; 131 132 // We are in early boot mode, so open_module_list() won't find what 133 // we're looking for; we need to use get_next_loaded_module_name() 134 // instead. 135 136 uint32 cookie = 0; 137 size_t partitioningPrefixLength = strlen(kPartitioningSystemPrefix); 138 size_t filePrefixLength = strlen(kFileSystemPrefix); 139 140 while (true) { 141 KPath name; 142 if (name.InitCheck() != B_OK) 143 break; 144 size_t nameLength = name.BufferSize(); 145 if (get_next_loaded_module_name(&cookie, name.LockBuffer(), 146 &nameLength) != B_OK) { 147 break; 148 } 149 name.UnlockBuffer(); 150 151 if (!strncmp(name.Path(), kPartitioningSystemPrefix, 152 partitioningPrefixLength)) { 153 DBG(OUT("partitioning system: %s\n", name.Path())); 154 _AddPartitioningSystem(name.Path()); 155 } else if (!strncmp(name.Path(), kFileSystemPrefix, filePrefixLength)) { 156 DBG(OUT("file system: %s\n", name.Path())); 157 _AddFileSystem(name.Path()); 158 } 159 } 160 161 DBG(OUT("number of disk systems: %ld\n", CountDiskSystems())); 162 // TODO: Watch the disk systems and the relevant directories. 163 } 164 165 166 KDiskDeviceManager::~KDiskDeviceManager() 167 { 168 // TODO: terminate and remove all jobs 169 // remove all devices 170 for (int32 cookie = 0; KDiskDevice *device = NextDevice(&cookie);) { 171 PartitionRegistrar _(device); 172 _RemoveDevice(device); 173 } 174 // some sanity checks 175 if (fPartitions->Count() > 0) { 176 DBG(OUT("WARNING: There are still %ld unremoved partitions!\n", 177 fPartitions->Count())); 178 for (PartitionMap::Iterator it = fPartitions->Begin(); 179 it != fPartitions->End(); ++it) { 180 DBG(OUT(" partition: %ld\n", it->Value()->ID())); 181 } 182 } 183 if (fObsoletePartitions->Count() > 0) { 184 DBG(OUT("WARNING: There are still %ld obsolete partitions!\n", 185 fObsoletePartitions->Count())); 186 for (PartitionSet::Iterator it = fObsoletePartitions->Begin(); 187 it != fObsoletePartitions->End(); ++it) { 188 DBG(OUT(" partition: %ld\n", (*it)->ID())); 189 } 190 } 191 // remove all disk systems 192 for (int32 cookie = 0; 193 KDiskSystem *diskSystem = NextDiskSystem(&cookie); ) { 194 fDiskSystems->Remove(diskSystem->ID()); 195 if (diskSystem->IsLoaded()) { 196 DBG(OUT("WARNING: Disk system `%s' (%ld) is still loaded!\n", 197 diskSystem->Name(), diskSystem->ID())); 198 } else 199 delete diskSystem; 200 } 201 202 // delete the containers 203 delete fPartitions; 204 delete fDevices; 205 delete fDiskSystems; 206 delete fObsoletePartitions; 207 delete fJobs; 208 delete fJobQueues; 209 delete fJobFactory; 210 } 211 212 // InitCheck 213 status_t 214 KDiskDeviceManager::InitCheck() const 215 { 216 if (!fPartitions || !fDevices || !fDiskSystems || !fObsoletePartitions 217 || !fJobs || !fJobQueues || !fJobFactory) { 218 return B_NO_MEMORY; 219 } 220 return (fLock.Sem() >= 0 ? B_OK : fLock.Sem()); 221 } 222 223 224 /** This creates the system's default DiskDeviceManager. 225 * The creation is not thread-safe, and shouldn't be done 226 * more than once. 227 */ 228 229 status_t 230 KDiskDeviceManager::CreateDefault() 231 { 232 if (sDefaultManager != NULL) 233 return B_OK; 234 235 sDefaultManager = new(nothrow) KDiskDeviceManager; 236 if (sDefaultManager == NULL) 237 return B_NO_MEMORY; 238 239 return sDefaultManager->InitCheck(); 240 } 241 242 243 /** This deletes the default DiskDeviceManager. The 244 * deletion is not thread-safe either, you should 245 * make sure that it's called only once. 246 */ 247 248 void 249 KDiskDeviceManager::DeleteDefault() 250 { 251 delete sDefaultManager; 252 sDefaultManager = NULL; 253 } 254 255 // Default 256 KDiskDeviceManager * 257 KDiskDeviceManager::Default() 258 { 259 return sDefaultManager; 260 } 261 262 // Lock 263 bool 264 KDiskDeviceManager::Lock() 265 { 266 return fLock.Lock(); 267 } 268 269 // Unlock 270 void 271 KDiskDeviceManager::Unlock() 272 { 273 fLock.Unlock(); 274 } 275 276 // FindDevice 277 KDiskDevice * 278 KDiskDeviceManager::FindDevice(const char *path) 279 { 280 for (int32 cookie = 0; KDiskDevice *device = NextDevice(&cookie); ) { 281 if (device->Path() && !strcmp(path, device->Path())) 282 return device; 283 } 284 return NULL; 285 } 286 287 // FindDevice 288 KDiskDevice * 289 KDiskDeviceManager::FindDevice(partition_id id, bool deviceOnly) 290 { 291 if (KPartition *partition = FindPartition(id)) { 292 KDiskDevice *device = partition->Device(); 293 if (!deviceOnly || id == device->ID()) 294 return device; 295 } 296 return NULL; 297 } 298 299 // FindPartition 300 KPartition * 301 KDiskDeviceManager::FindPartition(const char *path, bool noShadow) 302 { 303 // TODO: Optimize! 304 KPath partitionPath; 305 if (partitionPath.InitCheck() != B_OK) 306 return NULL; 307 for (PartitionMap::Iterator it = fPartitions->Begin(); 308 it != fPartitions->End(); 309 ++it) { 310 KPartition *partition = it->Value(); 311 if (partition->GetPath(&partitionPath) == B_OK 312 && partitionPath == path) { 313 if (noShadow && partition->IsShadowPartition()) 314 return partition->PhysicalPartition(); 315 return partition; 316 } 317 } 318 return NULL; 319 } 320 321 // FindPartition 322 KPartition * 323 KDiskDeviceManager::FindPartition(partition_id id, bool noShadow) 324 { 325 PartitionMap::Iterator it = fPartitions->Find(id); 326 if (it != fPartitions->End()) { 327 if (noShadow && it->Value()->IsShadowPartition()) 328 return it->Value()->PhysicalPartition(); 329 return it->Value(); 330 } 331 return NULL; 332 } 333 334 // FindFileDevice 335 KFileDiskDevice * 336 KDiskDeviceManager::FindFileDevice(const char *filePath) 337 { 338 for (int32 cookie = 0; KDiskDevice *device = NextDevice(&cookie); ) { 339 KFileDiskDevice *fileDevice = dynamic_cast<KFileDiskDevice*>(device); 340 if (fileDevice && fileDevice->FilePath() 341 && !strcmp(filePath, fileDevice->FilePath())) { 342 return fileDevice; 343 } 344 } 345 return NULL; 346 } 347 348 // RegisterDevice 349 KDiskDevice * 350 KDiskDeviceManager::RegisterDevice(const char *path) 351 { 352 if (ManagerLocker locker = this) { 353 if (KDiskDevice *device = FindDevice(path)) { 354 device->Register(); 355 return device; 356 } 357 } 358 return NULL; 359 } 360 361 // RegisterDevice 362 KDiskDevice * 363 KDiskDeviceManager::RegisterDevice(partition_id id, bool deviceOnly) 364 { 365 if (ManagerLocker locker = this) { 366 if (KDiskDevice *device = FindDevice(id, deviceOnly)) { 367 device->Register(); 368 return device; 369 } 370 } 371 return NULL; 372 } 373 374 // RegisterNextDevice 375 KDiskDevice * 376 KDiskDeviceManager::RegisterNextDevice(int32 *cookie) 377 { 378 if (!cookie) 379 return NULL; 380 if (ManagerLocker locker = this) { 381 if (KDiskDevice *device = NextDevice(cookie)) { 382 device->Register(); 383 return device; 384 } 385 } 386 return NULL; 387 } 388 389 // RegisterPartition 390 KPartition * 391 KDiskDeviceManager::RegisterPartition(const char *path, bool noShadow) 392 { 393 if (ManagerLocker locker = this) { 394 if (KPartition *partition = FindPartition(path, noShadow)) { 395 partition->Register(); 396 return partition; 397 } 398 } 399 return NULL; 400 } 401 402 // RegisterPartition 403 KPartition * 404 KDiskDeviceManager::RegisterPartition(partition_id id, bool noShadow) 405 { 406 if (ManagerLocker locker = this) { 407 if (KPartition *partition = FindPartition(id, noShadow)) { 408 partition->Register(); 409 return partition; 410 } 411 } 412 return NULL; 413 } 414 415 // RegisterFileDevice 416 KFileDiskDevice * 417 KDiskDeviceManager::RegisterFileDevice(const char *filePath) 418 { 419 if (ManagerLocker locker = this) { 420 if (KFileDiskDevice *device = FindFileDevice(filePath)) { 421 device->Register(); 422 return device; 423 } 424 } 425 return NULL; 426 } 427 428 // ReadLockDevice 429 KDiskDevice * 430 KDiskDeviceManager::ReadLockDevice(partition_id id, bool deviceOnly) 431 { 432 // register device 433 KDiskDevice *device = RegisterDevice(id, deviceOnly); 434 if (!device) 435 return NULL; 436 // lock device 437 if (device->ReadLock()) 438 return device; 439 device->Unregister(); 440 return NULL; 441 } 442 443 // WriteLockDevice 444 KDiskDevice * 445 KDiskDeviceManager::WriteLockDevice(partition_id id, bool deviceOnly) 446 { 447 // register device 448 KDiskDevice *device = RegisterDevice(id, deviceOnly); 449 if (!device) 450 return NULL; 451 // lock device 452 if (device->WriteLock()) 453 return device; 454 device->Unregister(); 455 return NULL; 456 } 457 458 // ReadLockPartition 459 KPartition * 460 KDiskDeviceManager::ReadLockPartition(partition_id id) 461 { 462 // register partition 463 KPartition *partition = RegisterPartition(id); 464 if (!partition) 465 return NULL; 466 // get and register the device 467 KDiskDevice *device = NULL; 468 if (ManagerLocker locker = this) { 469 device = partition->Device(); 470 if (device) 471 device->Register(); 472 } 473 // lock the device 474 if (device->ReadLock()) { 475 // final check, if the partition still belongs to the device 476 if (partition->Device() == device) 477 return partition; 478 device->ReadUnlock(); 479 } 480 // cleanup on failure 481 if (device) 482 device->Unregister(); 483 partition->Unregister(); 484 return NULL; 485 } 486 487 // WriteLockPartition 488 KPartition * 489 KDiskDeviceManager::WriteLockPartition(partition_id id) 490 { 491 // register partition 492 KPartition *partition = RegisterPartition(id); 493 if (!partition) 494 return NULL; 495 // get and register the device 496 KDiskDevice *device = NULL; 497 if (ManagerLocker locker = this) { 498 device = partition->Device(); 499 if (device) 500 device->Register(); 501 } 502 // lock the device 503 if (device->WriteLock()) { 504 // final check, if the partition still belongs to the device 505 if (partition->Device() == device) 506 return partition; 507 device->WriteUnlock(); 508 } 509 // cleanup on failure 510 if (device) 511 device->Unregister(); 512 partition->Unregister(); 513 return NULL; 514 } 515 516 // CreateFileDevice 517 partition_id 518 KDiskDeviceManager::CreateFileDevice(const char *filePath, bool *newlyCreated) 519 { 520 if (!filePath) 521 return B_BAD_VALUE; 522 523 // normalize the file path 524 KPath normalizedFilePath; 525 status_t error = normalizedFilePath.SetTo(filePath, true); 526 if (error != B_OK) 527 return error; 528 filePath = normalizedFilePath.Path(); 529 530 KFileDiskDevice *device = NULL; 531 if (ManagerLocker locker = this) { 532 // check, if the device does already exist 533 if ((device = FindFileDevice(filePath))) { 534 if (newlyCreated) 535 *newlyCreated = false; 536 return device->ID(); 537 } 538 539 // allocate a KFileDiskDevice 540 device = new(nothrow) KFileDiskDevice; 541 if (!device) 542 return B_NO_MEMORY; 543 544 // initialize and add the device 545 error = device->SetTo(filePath); 546 // Note: Here we are allowed to lock a device although already having 547 // the manager locked, since it is not yet added to the manager. 548 DeviceWriteLocker deviceLocker(device); 549 if (error == B_OK && !deviceLocker.IsLocked()) 550 error = B_ERROR; 551 if (error == B_OK && !_AddDevice(device)) 552 error = B_NO_MEMORY; 553 554 // scan device 555 if (error == B_OK) { 556 _ScanPartition(device); 557 558 if (newlyCreated) 559 *newlyCreated = true; 560 return device->ID(); 561 } 562 563 // cleanup on failure 564 delete device; 565 } else 566 error = B_ERROR; 567 return error; 568 } 569 570 // DeleteFileDevice 571 status_t 572 KDiskDeviceManager::DeleteFileDevice(const char *filePath) 573 { 574 if (KFileDiskDevice *device = RegisterFileDevice(filePath)) { 575 PartitionRegistrar _(device, true); 576 if (DeviceWriteLocker locker = device) { 577 if (_RemoveDevice(device)) 578 return B_OK; 579 } 580 } 581 return B_ERROR; 582 } 583 584 // DeleteFileDevice 585 status_t 586 KDiskDeviceManager::DeleteFileDevice(partition_id id) 587 { 588 if (KDiskDevice *device = RegisterDevice(id)) { 589 PartitionRegistrar _(device, true); 590 if (!dynamic_cast<KFileDiskDevice*>(device) || id != device->ID()) 591 return B_ENTRY_NOT_FOUND; 592 if (DeviceWriteLocker locker = device) { 593 if (_RemoveDevice(device)) 594 return B_OK; 595 } 596 } 597 return B_ERROR; 598 } 599 600 // CountDevices 601 int32 602 KDiskDeviceManager::CountDevices() 603 { 604 return fDevices->Count(); 605 } 606 607 // NextDevice 608 KDiskDevice * 609 KDiskDeviceManager::NextDevice(int32 *cookie) 610 { 611 if (!cookie) 612 return NULL; 613 DeviceMap::Iterator it = fDevices->FindClose(*cookie, false); 614 if (it != fDevices->End()) { 615 KDiskDevice *device = it->Value(); 616 *cookie = device->ID() + 1; 617 return device; 618 } 619 return NULL; 620 } 621 622 // PartitionAdded 623 bool 624 KDiskDeviceManager::PartitionAdded(KPartition *partition) 625 { 626 return (partition && fPartitions->Put(partition->ID(), partition) == B_OK); 627 } 628 629 // PartitionRemoved 630 bool 631 KDiskDeviceManager::PartitionRemoved(KPartition *partition) 632 { 633 if (partition && partition->PrepareForRemoval() 634 && fPartitions->Remove(partition->ID())) { 635 // If adding the partition to the obsolete list fails (due to lack 636 // of memory), we can't do anything about it. We will leak memory then. 637 fObsoletePartitions->Insert(partition); 638 partition->MarkObsolete(); 639 return true; 640 } 641 return false; 642 } 643 644 // DeletePartition 645 bool 646 KDiskDeviceManager::DeletePartition(KPartition *partition) 647 { 648 if (partition && partition->IsObsolete() 649 && partition->CountReferences() == 0 650 && partition->PrepareForDeletion() 651 && fObsoletePartitions->Remove(partition)) { 652 delete partition; 653 return true; 654 } 655 return false; 656 } 657 658 // FindJob 659 KDiskDeviceJob * 660 KDiskDeviceManager::FindJob(disk_job_id id) 661 { 662 JobMap::Iterator it = fJobs->Find(id); 663 if (it == fJobs->End()) 664 return NULL; 665 return it->Value(); 666 } 667 668 // CountJobs 669 int32 670 KDiskDeviceManager::CountJobs() 671 { 672 return fJobs->Count(); 673 } 674 675 // NextJob 676 KDiskDeviceJob * 677 KDiskDeviceManager::NextJob(int32 *cookie) 678 { 679 if (!cookie) 680 return NULL; 681 JobMap::Iterator it = fJobs->FindClose(*cookie, false); 682 if (it != fJobs->End()) { 683 KDiskDeviceJob *job = it->Value(); 684 *cookie = job->ID() + 1; 685 return job; 686 } 687 return NULL; 688 } 689 690 // AddJobQueue 691 /*! 692 The device must be write locked, the manager must be locked. 693 */ 694 status_t 695 KDiskDeviceManager::AddJobQueue(KDiskDeviceJobQueue *jobQueue) 696 { 697 // check the parameter 698 if (!jobQueue) 699 return B_BAD_VALUE; 700 if (jobQueue->InitCheck() != B_OK) 701 return jobQueue->InitCheck(); 702 // add the queue 703 status_t error = fJobQueues->PushBack(jobQueue); 704 if (error != B_OK) 705 return error; 706 // add the queue's jobs 707 int32 count = jobQueue->CountJobs(); 708 for (int32 i = 0; i < count; i++) { 709 KDiskDeviceJob *job = jobQueue->JobAt(i); 710 error = fJobs->Put(job->ID(), job); 711 if (error != B_OK) { 712 _RemoveJobQueue(jobQueue); 713 return error; 714 } 715 } 716 // mark the jobs scheduled 717 for (int32 i = 0; KDiskDeviceJob *job = jobQueue->JobAt(i); i++) 718 _UpdateJobStatus(job, B_DISK_DEVICE_JOB_SCHEDULED, false); 719 _UpdateBusyPartitions(jobQueue->Device()); 720 // start the execution of the queue 721 error = jobQueue->Execute(); 722 if (error != B_OK) { 723 // resuming the execution failed -- mark all jobs failed and 724 // remove the queue 725 for (int32 i = 0; KDiskDeviceJob *job = jobQueue->JobAt(i); i++) 726 _UpdateJobStatus(job, B_DISK_DEVICE_JOB_FAILED, false); 727 _RemoveJobQueue(jobQueue); 728 _UpdateBusyPartitions(jobQueue->Device()); 729 } 730 return error; 731 } 732 733 // RemoveJobQueue 734 status_t 735 KDiskDeviceManager::RemoveJobQueue(KDiskDeviceJobQueue *jobQueue) 736 { 737 if (!jobQueue) 738 return B_BAD_VALUE; 739 if (jobQueue->InitCheck() != B_OK) 740 return jobQueue->InitCheck(); 741 if (jobQueue->IsExecuting()) 742 return B_BAD_VALUE; 743 return (_RemoveJobQueue(jobQueue) ? B_OK : B_ENTRY_NOT_FOUND); 744 } 745 746 // DeleteJobQueue 747 status_t 748 KDiskDeviceManager::DeleteJobQueue(KDiskDeviceJobQueue *jobQueue) 749 { 750 status_t error = RemoveJobQueue(jobQueue); 751 if (error == B_OK) 752 delete jobQueue; 753 return error; 754 } 755 756 // CountJobQueues 757 int32 758 KDiskDeviceManager::CountJobQueues() 759 { 760 return fJobQueues->Count(); 761 } 762 763 // NextJobQueue 764 KDiskDeviceJobQueue * 765 KDiskDeviceManager::NextJobQueue(int32 *cookie) 766 { 767 if (!cookie || *cookie < 0 || *cookie >= CountJobQueues()) 768 return NULL; 769 return fJobQueues->ElementAt((*cookie)++); 770 } 771 772 // JobFactory 773 KDiskDeviceJobFactory * 774 KDiskDeviceManager::JobFactory() const 775 { 776 return fJobFactory; 777 } 778 779 // UpdateBusyPartitions 780 status_t 781 KDiskDeviceManager::UpdateBusyPartitions(KDiskDevice *device) 782 { 783 if (!device) 784 return B_BAD_VALUE; 785 if (DeviceWriteLocker deviceLocker = device) { 786 if (ManagerLocker locker = this) 787 return _UpdateBusyPartitions(device); 788 } 789 return B_ERROR; 790 } 791 792 // UpdateJobStatus 793 status_t 794 KDiskDeviceManager::UpdateJobStatus(KDiskDeviceJob *job, uint32 status, 795 bool updateBusyPartitions) 796 { 797 // check parameters 798 if (!job) 799 return B_BAD_VALUE; 800 KDiskDeviceJobQueue *jobQueue = job->JobQueue(); 801 KDiskDevice *device = (jobQueue ? jobQueue->Device() : NULL); 802 if (!device) 803 return B_BAD_VALUE; 804 // lock device and manager 805 if (DeviceWriteLocker deviceLocker = device) { 806 if (ManagerLocker locker = this) 807 return _UpdateJobStatus(job, status, updateBusyPartitions); 808 } 809 return B_ERROR; 810 } 811 812 // FindDiskSystem 813 KDiskSystem * 814 KDiskDeviceManager::FindDiskSystem(const char *name) 815 { 816 for (int32 cookie = 0; 817 KDiskSystem *diskSystem = NextDiskSystem(&cookie); ) { 818 if (!strcmp(name, diskSystem->Name())) 819 return diskSystem; 820 } 821 return NULL; 822 } 823 824 // FindDiskSystem 825 KDiskSystem * 826 KDiskDeviceManager::FindDiskSystem(disk_system_id id) 827 { 828 DiskSystemMap::Iterator it = fDiskSystems->Find(id); 829 if (it != fDiskSystems->End()) 830 return it->Value(); 831 return NULL; 832 } 833 834 // CountDiskSystems 835 int32 836 KDiskDeviceManager::CountDiskSystems() 837 { 838 return fDiskSystems->Count(); 839 } 840 841 // NextDiskSystem 842 KDiskSystem * 843 KDiskDeviceManager::NextDiskSystem(int32 *cookie) 844 { 845 if (!cookie) 846 return NULL; 847 DiskSystemMap::Iterator it = fDiskSystems->FindClose(*cookie, false); 848 if (it != fDiskSystems->End()) { 849 KDiskSystem *diskSystem = it->Value(); 850 *cookie = diskSystem->ID() + 1; 851 return diskSystem; 852 } 853 return NULL; 854 } 855 856 // LoadDiskSystem 857 KDiskSystem * 858 KDiskDeviceManager::LoadDiskSystem(const char *name) 859 { 860 KDiskSystem *diskSystem = NULL; 861 if (ManagerLocker locker = this) { 862 diskSystem = FindDiskSystem(name); 863 if (diskSystem && diskSystem->Load() != B_OK) 864 diskSystem = NULL; 865 } 866 return diskSystem; 867 } 868 869 // LoadDiskSystem 870 KDiskSystem * 871 KDiskDeviceManager::LoadDiskSystem(disk_system_id id) 872 { 873 KDiskSystem *diskSystem = NULL; 874 if (ManagerLocker locker = this) { 875 diskSystem = FindDiskSystem(id); 876 if (diskSystem && diskSystem->Load() != B_OK) 877 diskSystem = NULL; 878 } 879 return diskSystem; 880 } 881 882 // LoadNextDiskSystem 883 KDiskSystem * 884 KDiskDeviceManager::LoadNextDiskSystem(int32 *cookie) 885 { 886 if (!cookie) 887 return NULL; 888 if (ManagerLocker locker = this) { 889 if (KDiskSystem *diskSystem = NextDiskSystem(cookie)) { 890 if (diskSystem->Load() == B_OK) { 891 *cookie = diskSystem->ID() + 1; 892 return diskSystem; 893 } 894 } 895 } 896 return NULL; 897 } 898 899 // InitialDeviceScan 900 status_t 901 KDiskDeviceManager::InitialDeviceScan() 902 { 903 status_t error = B_ERROR; 904 // scan for devices 905 if (ManagerLocker locker = this) { 906 error = _Scan("/dev/disk"); 907 if (error != B_OK) 908 return error; 909 } 910 // scan the devices for partitions 911 int32 cookie = 0; 912 while (KDiskDevice *device = RegisterNextDevice(&cookie)) { 913 PartitionRegistrar _(device, true); 914 if (DeviceWriteLocker deviceLocker = device) { 915 if (ManagerLocker locker = this) { 916 error = _ScanPartition(device); 917 if (error != B_OK) 918 break; 919 } else 920 return B_ERROR; 921 } else 922 return B_ERROR; 923 } 924 return error; 925 } 926 927 // _AddPartitioningSystem 928 status_t 929 KDiskDeviceManager::_AddPartitioningSystem(const char *name) 930 { 931 if (!name) 932 return B_BAD_VALUE; 933 KDiskSystem *diskSystem = new(nothrow) KPartitioningSystem(name); 934 if (!diskSystem) 935 return B_NO_MEMORY; 936 return _AddDiskSystem(diskSystem); 937 } 938 939 // _AddFileSystem 940 status_t 941 KDiskDeviceManager::_AddFileSystem(const char *name) 942 { 943 if (!name) 944 return B_BAD_VALUE; 945 946 KDiskSystem *diskSystem = new(nothrow) KFileSystem(name); 947 if (!diskSystem) 948 return B_NO_MEMORY; 949 950 return _AddDiskSystem(diskSystem); 951 } 952 953 // _AddDiskSystem 954 status_t 955 KDiskDeviceManager::_AddDiskSystem(KDiskSystem *diskSystem) 956 { 957 if (!diskSystem) 958 return B_BAD_VALUE; 959 DBG(OUT("KDiskDeviceManager::_AddDiskSystem(%s)\n", diskSystem->Name())); 960 status_t error = diskSystem->Init(); 961 if (error != B_OK) 962 DBG(OUT(" initialization failed: %s\n", strerror(error))); 963 if (error == B_OK) 964 error = fDiskSystems->Put(diskSystem->ID(), diskSystem); 965 if (error != B_OK) 966 delete diskSystem; 967 DBG(OUT("KDiskDeviceManager::_AddDiskSystem() done: %s\n", strerror(error))); 968 return error; 969 } 970 971 // _AddDevice 972 bool 973 KDiskDeviceManager::_AddDevice(KDiskDevice *device) 974 { 975 if (!device || !PartitionAdded(device)) 976 return false; 977 if (fDevices->Put(device->ID(), device) == B_OK) 978 return true; 979 PartitionRemoved(device); 980 return false; 981 } 982 983 // _RemoveDevice 984 bool 985 KDiskDeviceManager::_RemoveDevice(KDiskDevice *device) 986 { 987 return (device && fDevices->Remove(device->ID()) 988 && PartitionRemoved(device)); 989 } 990 991 // _RemoveJobQueue 992 bool 993 KDiskDeviceManager::_RemoveJobQueue(KDiskDeviceJobQueue *jobQueue) 994 { 995 if (!jobQueue) 996 return false; 997 // find the job queue 998 JobQueueVector::Iterator it = fJobQueues->Find(jobQueue); 999 if (it == fJobQueues->End()) 1000 return false; 1001 // remove the queue's jobs 1002 int32 count = jobQueue->CountJobs(); 1003 for (int32 i = 0; i < count; i++) { 1004 KDiskDeviceJob *job = jobQueue->JobAt(i); 1005 fJobs->Remove(job->ID()); 1006 } 1007 fJobQueues->Erase(it); 1008 return true; 1009 } 1010 1011 // _UpdateBusyPartitions 1012 /*! 1013 The device must be write locked, the manager must be locked. 1014 */ 1015 status_t 1016 KDiskDeviceManager::_UpdateBusyPartitions(KDiskDevice *device) 1017 { 1018 if (!device) 1019 return B_BAD_VALUE; 1020 // mark all partitions un-busy 1021 struct UnmarkBusyVisitor : KPartitionVisitor { 1022 virtual bool VisitPre(KPartition *partition) 1023 { 1024 partition->ClearFlags(B_PARTITION_BUSY 1025 | B_PARTITION_DESCENDANT_BUSY); 1026 return false; 1027 } 1028 } visitor; 1029 device->VisitEachDescendant(&visitor); 1030 // Iterate through all job queues and all jobs scheduled or in 1031 // progress and mark their scope busy. 1032 for (int32 cookie = 0; 1033 KDiskDeviceJobQueue *jobQueue = NextJobQueue(&cookie); ) { 1034 if (jobQueue->Device() != device) 1035 continue; 1036 for (int32 i = jobQueue->ActiveJobIndex(); 1037 KDiskDeviceJob *job = jobQueue->JobAt(i); i++) { 1038 if (job->Status() != B_DISK_DEVICE_JOB_IN_PROGRESS 1039 && job->Status() != B_DISK_DEVICE_JOB_SCHEDULED) { 1040 continue; 1041 } 1042 KPartition *partition = FindPartition(job->ScopeID()); 1043 if (!partition || partition->Device() != device) 1044 continue; 1045 partition->AddFlags(B_PARTITION_BUSY); 1046 } 1047 } 1048 // mark all anscestors of busy partitions descendant busy and all 1049 // descendants busy 1050 struct MarkBusyVisitor : KPartitionVisitor { 1051 virtual bool VisitPre(KPartition *partition) 1052 { 1053 // parent busy => child busy 1054 if (partition->Parent() && partition->Parent()->IsBusy()) 1055 partition->AddFlags(B_PARTITION_BUSY); 1056 return false; 1057 } 1058 1059 virtual bool VisitPost(KPartition *partition) 1060 { 1061 // child [descendant] busy => parent descendant busy 1062 if ((partition->IsBusy() || partition->IsDescendantBusy()) 1063 && partition->Parent()) { 1064 partition->Parent()->AddFlags(B_PARTITION_DESCENDANT_BUSY); 1065 } 1066 return false; 1067 } 1068 } visitor2; 1069 device->VisitEachDescendant(&visitor2); 1070 return B_OK; 1071 } 1072 1073 // _UpdateJobStatus 1074 status_t 1075 KDiskDeviceManager::_UpdateJobStatus(KDiskDeviceJob *job, uint32 status, 1076 bool updateBusyPartitions) 1077 { 1078 // check parameters 1079 if (!job) 1080 return B_BAD_VALUE; 1081 KDiskDeviceJobQueue *jobQueue = job->JobQueue(); 1082 KDiskDevice *device = (jobQueue ? jobQueue->Device() : NULL); 1083 if (!device) 1084 return B_BAD_VALUE; 1085 if (job->Status() == status) 1086 return B_OK; 1087 // check migration of a schedule/in progress to a terminal state 1088 // or vice versa 1089 updateBusyPartitions &= (is_active_job_status(job->Status()) 1090 != is_active_job_status(status)); 1091 // set new state and update the partitions' busy flags 1092 job->SetStatus(status); 1093 if (updateBusyPartitions) 1094 return _UpdateBusyPartitions(device); 1095 // TODO: notifications 1096 return B_OK; 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_OK; 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 _Scan(entryPath.Path()); 1124 } 1125 closedir(dir); 1126 } else { 1127 // not a directory 1128 // check, if it is named "raw" 1129 int32 len = strlen(path); 1130 int32 leafLen = strlen("/raw"); 1131 if (len <= leafLen || strcmp(path + len - leafLen, "/raw")) 1132 return B_ERROR; 1133 DBG(OUT(" found device: %s\n", path)); 1134 // create a KDiskDevice for it 1135 KDiskDevice *device = new(nothrow) KDiskDevice; 1136 if (!device) 1137 return B_NO_MEMORY; 1138 // init the KDiskDevice 1139 status_t error = device->SetTo(path); 1140 // add the device 1141 if (error == B_OK && !_AddDevice(device)) 1142 error = B_NO_MEMORY; 1143 // cleanup on error 1144 if (error != B_OK) 1145 delete device; 1146 } 1147 return error; 1148 } 1149 1150 // _ScanPartition 1151 /*! 1152 The device must be write locked, the manager must be locked. 1153 */ 1154 status_t 1155 KDiskDeviceManager::_ScanPartition(KPartition *partition) 1156 { 1157 if (!partition) 1158 return B_BAD_VALUE; 1159 // create a new job queue for the device 1160 KDiskDeviceJobQueue *jobQueue = new(nothrow) KDiskDeviceJobQueue; 1161 if (!jobQueue) 1162 return B_NO_MEMORY; 1163 jobQueue->SetDevice(partition->Device()); 1164 // create a job for scanning the device and add it to the job queue 1165 KDiskDeviceJob *job = fJobFactory->CreateScanPartitionJob(partition->ID()); 1166 if (!job) { 1167 delete jobQueue; 1168 return B_NO_MEMORY; 1169 } 1170 if (!jobQueue->AddJob(job)) { 1171 delete jobQueue; 1172 delete job; 1173 return B_NO_MEMORY; 1174 } 1175 // add the job queue 1176 status_t error = AddJobQueue(jobQueue); 1177 if (error != B_OK) 1178 delete jobQueue; 1179 return error; 1180 } 1181 1182