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