/* ** Copyright 2003-2004, Ingo Weinhold, bonefish@cs.tu-berlin.de. All rights reserved. ** Distributed under the terms of the Haiku License. */ #include #include #include #include #include #include #include #include #include #include #include #include "KDiskDevice.h" #include "KDiskDeviceJob.h" #include "KDiskDeviceJobFactory.h" #include "KDiskDeviceJobQueue.h" #include "KDiskDeviceManager.h" #include "KDiskDeviceUtils.h" #include "KDiskSystem.h" #include "KFileDiskDevice.h" #include "KFileSystem.h" #include "KPartition.h" #include "KPartitioningSystem.h" #include "KPartitionVisitor.h" #include "KPath.h" #include "KShadowPartition.h" // debugging //#define DBG(x) #define DBG(x) x #define OUT dprintf // directories for partitioning and file system modules static const char *kPartitioningSystemPrefix = "partitioning_systems"; static const char *kFileSystemPrefix = "file_systems"; // singleton instance KDiskDeviceManager *KDiskDeviceManager::sDefaultManager = NULL; // GetPartitionID struct GetPartitionID { inline partition_id operator()(const KPartition *partition) const { return partition->ID(); } }; // GetDiskSystemID struct GetDiskSystemID { inline disk_system_id operator()(const KDiskSystem *system) const { return system->ID(); } }; // GetJobID struct GetJobID { inline disk_job_id operator()(const KDiskDeviceJob *job) const { return job->ID(); } }; // PartitionMap struct KDiskDeviceManager::PartitionMap : VectorMap > { }; // DeviceMap struct KDiskDeviceManager::DeviceMap : VectorMap > { }; // DiskSystemMap struct KDiskDeviceManager::DiskSystemMap : VectorMap > { }; // PartitionSet struct KDiskDeviceManager::PartitionSet : VectorSet { }; // JobMap struct KDiskDeviceManager::JobMap : VectorMap > { }; // JobQueueVector struct KDiskDeviceManager::JobQueueVector : Vector {}; static bool is_active_job_status(uint32 status) { return (status == B_DISK_DEVICE_JOB_SCHEDULED || status == B_DISK_DEVICE_JOB_IN_PROGRESS); } // #pragma mark - KDiskDeviceManager::KDiskDeviceManager() : fLock("disk device manager"), fDevices(new(nothrow) DeviceMap), fPartitions(new(nothrow) PartitionMap), fDiskSystems(new(nothrow) DiskSystemMap), fObsoletePartitions(new(nothrow) PartitionSet), fJobs(new(nothrow) JobMap), fJobQueues(new(nothrow) JobQueueVector), fJobFactory(new(nothrow) KDiskDeviceJobFactory) { if (InitCheck() != B_OK) return; // We are in early boot mode, so open_module_list() won't find what // we're looking for; we need to use get_next_loaded_module_name() // instead. uint32 cookie = 0; size_t partitioningPrefixLength = strlen(kPartitioningSystemPrefix); size_t filePrefixLength = strlen(kFileSystemPrefix); while (true) { KPath name; if (name.InitCheck() != B_OK) break; size_t nameLength = name.BufferSize(); if (get_next_loaded_module_name(&cookie, name.LockBuffer(), &nameLength) != B_OK) { break; } name.UnlockBuffer(); if (!strncmp(name.Path(), kPartitioningSystemPrefix, partitioningPrefixLength)) { DBG(OUT("partitioning system: %s\n", name.Path())); _AddPartitioningSystem(name.Path()); } else if (!strncmp(name.Path(), kFileSystemPrefix, filePrefixLength)) { DBG(OUT("file system: %s\n", name.Path())); _AddFileSystem(name.Path()); } } DBG(OUT("number of disk systems: %ld\n", CountDiskSystems())); // TODO: Watch the disk systems and the relevant directories. } KDiskDeviceManager::~KDiskDeviceManager() { // TODO: terminate and remove all jobs // remove all devices for (int32 cookie = 0; KDiskDevice *device = NextDevice(&cookie);) { PartitionRegistrar _(device); _RemoveDevice(device); } // some sanity checks if (fPartitions->Count() > 0) { DBG(OUT("WARNING: There are still %ld unremoved partitions!\n", fPartitions->Count())); for (PartitionMap::Iterator it = fPartitions->Begin(); it != fPartitions->End(); ++it) { DBG(OUT(" partition: %ld\n", it->Value()->ID())); } } if (fObsoletePartitions->Count() > 0) { DBG(OUT("WARNING: There are still %ld obsolete partitions!\n", fObsoletePartitions->Count())); for (PartitionSet::Iterator it = fObsoletePartitions->Begin(); it != fObsoletePartitions->End(); ++it) { DBG(OUT(" partition: %ld\n", (*it)->ID())); } } // remove all disk systems for (int32 cookie = 0; KDiskSystem *diskSystem = NextDiskSystem(&cookie); ) { fDiskSystems->Remove(diskSystem->ID()); if (diskSystem->IsLoaded()) { DBG(OUT("WARNING: Disk system `%s' (%ld) is still loaded!\n", diskSystem->Name(), diskSystem->ID())); } else delete diskSystem; } // delete the containers delete fPartitions; delete fDevices; delete fDiskSystems; delete fObsoletePartitions; delete fJobs; delete fJobQueues; delete fJobFactory; } // InitCheck status_t KDiskDeviceManager::InitCheck() const { if (!fPartitions || !fDevices || !fDiskSystems || !fObsoletePartitions || !fJobs || !fJobQueues || !fJobFactory) { return B_NO_MEMORY; } return (fLock.Sem() >= 0 ? B_OK : fLock.Sem()); } /** This creates the system's default DiskDeviceManager. * The creation is not thread-safe, and shouldn't be done * more than once. */ status_t KDiskDeviceManager::CreateDefault() { if (sDefaultManager != NULL) return B_OK; sDefaultManager = new(nothrow) KDiskDeviceManager; if (sDefaultManager == NULL) return B_NO_MEMORY; return sDefaultManager->InitCheck(); } /** This deletes the default DiskDeviceManager. The * deletion is not thread-safe either, you should * make sure that it's called only once. */ void KDiskDeviceManager::DeleteDefault() { delete sDefaultManager; sDefaultManager = NULL; } // Default KDiskDeviceManager * KDiskDeviceManager::Default() { return sDefaultManager; } // Lock bool KDiskDeviceManager::Lock() { return fLock.Lock(); } // Unlock void KDiskDeviceManager::Unlock() { fLock.Unlock(); } // FindDevice KDiskDevice * KDiskDeviceManager::FindDevice(const char *path) { for (int32 cookie = 0; KDiskDevice *device = NextDevice(&cookie); ) { if (device->Path() && !strcmp(path, device->Path())) return device; } return NULL; } // FindDevice KDiskDevice * KDiskDeviceManager::FindDevice(partition_id id, bool deviceOnly) { if (KPartition *partition = FindPartition(id)) { KDiskDevice *device = partition->Device(); if (!deviceOnly || id == device->ID()) return device; } return NULL; } // FindPartition KPartition * KDiskDeviceManager::FindPartition(const char *path, bool noShadow) { // TODO: Optimize! KPath partitionPath; if (partitionPath.InitCheck() != B_OK) return NULL; for (PartitionMap::Iterator it = fPartitions->Begin(); it != fPartitions->End(); ++it) { KPartition *partition = it->Value(); if (partition->GetPath(&partitionPath) == B_OK && partitionPath == path) { if (noShadow && partition->IsShadowPartition()) return partition->PhysicalPartition(); return partition; } } return NULL; } // FindPartition KPartition * KDiskDeviceManager::FindPartition(partition_id id, bool noShadow) { PartitionMap::Iterator it = fPartitions->Find(id); if (it != fPartitions->End()) { if (noShadow && it->Value()->IsShadowPartition()) return it->Value()->PhysicalPartition(); return it->Value(); } return NULL; } // FindFileDevice KFileDiskDevice * KDiskDeviceManager::FindFileDevice(const char *filePath) { for (int32 cookie = 0; KDiskDevice *device = NextDevice(&cookie); ) { KFileDiskDevice *fileDevice = dynamic_cast(device); if (fileDevice && fileDevice->FilePath() && !strcmp(filePath, fileDevice->FilePath())) { return fileDevice; } } return NULL; } // RegisterDevice KDiskDevice * KDiskDeviceManager::RegisterDevice(const char *path) { if (ManagerLocker locker = this) { if (KDiskDevice *device = FindDevice(path)) { device->Register(); return device; } } return NULL; } // RegisterDevice KDiskDevice * KDiskDeviceManager::RegisterDevice(partition_id id, bool deviceOnly) { if (ManagerLocker locker = this) { if (KDiskDevice *device = FindDevice(id, deviceOnly)) { device->Register(); return device; } } return NULL; } // RegisterNextDevice KDiskDevice * KDiskDeviceManager::RegisterNextDevice(int32 *cookie) { if (!cookie) return NULL; if (ManagerLocker locker = this) { if (KDiskDevice *device = NextDevice(cookie)) { device->Register(); return device; } } return NULL; } // RegisterPartition KPartition * KDiskDeviceManager::RegisterPartition(const char *path, bool noShadow) { if (ManagerLocker locker = this) { if (KPartition *partition = FindPartition(path, noShadow)) { partition->Register(); return partition; } } return NULL; } // RegisterPartition KPartition * KDiskDeviceManager::RegisterPartition(partition_id id, bool noShadow) { if (ManagerLocker locker = this) { if (KPartition *partition = FindPartition(id, noShadow)) { partition->Register(); return partition; } } return NULL; } // RegisterFileDevice KFileDiskDevice * KDiskDeviceManager::RegisterFileDevice(const char *filePath) { if (ManagerLocker locker = this) { if (KFileDiskDevice *device = FindFileDevice(filePath)) { device->Register(); return device; } } return NULL; } // ReadLockDevice KDiskDevice * KDiskDeviceManager::ReadLockDevice(partition_id id, bool deviceOnly) { // register device KDiskDevice *device = RegisterDevice(id, deviceOnly); if (!device) return NULL; // lock device if (device->ReadLock()) return device; device->Unregister(); return NULL; } // WriteLockDevice KDiskDevice * KDiskDeviceManager::WriteLockDevice(partition_id id, bool deviceOnly) { // register device KDiskDevice *device = RegisterDevice(id, deviceOnly); if (!device) return NULL; // lock device if (device->WriteLock()) return device; device->Unregister(); return NULL; } // ReadLockPartition KPartition * KDiskDeviceManager::ReadLockPartition(partition_id id) { // register partition KPartition *partition = RegisterPartition(id); if (!partition) return NULL; // get and register the device KDiskDevice *device = NULL; if (ManagerLocker locker = this) { device = partition->Device(); if (device) device->Register(); } // lock the device if (device->ReadLock()) { // final check, if the partition still belongs to the device if (partition->Device() == device) return partition; device->ReadUnlock(); } // cleanup on failure if (device) device->Unregister(); partition->Unregister(); return NULL; } // WriteLockPartition KPartition * KDiskDeviceManager::WriteLockPartition(partition_id id) { // register partition KPartition *partition = RegisterPartition(id); if (!partition) return NULL; // get and register the device KDiskDevice *device = NULL; if (ManagerLocker locker = this) { device = partition->Device(); if (device) device->Register(); } // lock the device if (device->WriteLock()) { // final check, if the partition still belongs to the device if (partition->Device() == device) return partition; device->WriteUnlock(); } // cleanup on failure if (device) device->Unregister(); partition->Unregister(); return NULL; } // CreateFileDevice partition_id KDiskDeviceManager::CreateFileDevice(const char *filePath, bool *newlyCreated) { if (!filePath) return B_BAD_VALUE; // normalize the file path KPath normalizedFilePath; status_t error = normalizedFilePath.SetTo(filePath, true); if (error != B_OK) return error; filePath = normalizedFilePath.Path(); KFileDiskDevice *device = NULL; if (ManagerLocker locker = this) { // check, if the device does already exist if ((device = FindFileDevice(filePath))) { if (newlyCreated) *newlyCreated = false; return device->ID(); } // allocate a KFileDiskDevice device = new(nothrow) KFileDiskDevice; if (!device) return B_NO_MEMORY; // initialize and add the device error = device->SetTo(filePath); // Note: Here we are allowed to lock a device although already having // the manager locked, since it is not yet added to the manager. DeviceWriteLocker deviceLocker(device); if (error == B_OK && !deviceLocker.IsLocked()) error = B_ERROR; if (error == B_OK && !_AddDevice(device)) error = B_NO_MEMORY; // scan device if (error == B_OK) { _ScanPartition(device); if (newlyCreated) *newlyCreated = true; return device->ID(); } // cleanup on failure delete device; } else error = B_ERROR; return error; } // DeleteFileDevice status_t KDiskDeviceManager::DeleteFileDevice(const char *filePath) { if (KFileDiskDevice *device = RegisterFileDevice(filePath)) { PartitionRegistrar _(device, true); if (DeviceWriteLocker locker = device) { if (_RemoveDevice(device)) return B_OK; } } return B_ERROR; } // DeleteFileDevice status_t KDiskDeviceManager::DeleteFileDevice(partition_id id) { if (KDiskDevice *device = RegisterDevice(id)) { PartitionRegistrar _(device, true); if (!dynamic_cast(device) || id != device->ID()) return B_ENTRY_NOT_FOUND; if (DeviceWriteLocker locker = device) { if (_RemoveDevice(device)) return B_OK; } } return B_ERROR; } // CountDevices int32 KDiskDeviceManager::CountDevices() { return fDevices->Count(); } // NextDevice KDiskDevice * KDiskDeviceManager::NextDevice(int32 *cookie) { if (!cookie) return NULL; DeviceMap::Iterator it = fDevices->FindClose(*cookie, false); if (it != fDevices->End()) { KDiskDevice *device = it->Value(); *cookie = device->ID() + 1; return device; } return NULL; } // PartitionAdded bool KDiskDeviceManager::PartitionAdded(KPartition *partition) { return (partition && fPartitions->Put(partition->ID(), partition) == B_OK); } // PartitionRemoved bool KDiskDeviceManager::PartitionRemoved(KPartition *partition) { if (partition && partition->PrepareForRemoval() && fPartitions->Remove(partition->ID())) { // If adding the partition to the obsolete list fails (due to lack // of memory), we can't do anything about it. We will leak memory then. fObsoletePartitions->Insert(partition); partition->MarkObsolete(); return true; } return false; } // DeletePartition bool KDiskDeviceManager::DeletePartition(KPartition *partition) { if (partition && partition->IsObsolete() && partition->CountReferences() == 0 && partition->PrepareForDeletion() && fObsoletePartitions->Remove(partition)) { delete partition; return true; } return false; } // FindJob KDiskDeviceJob * KDiskDeviceManager::FindJob(disk_job_id id) { JobMap::Iterator it = fJobs->Find(id); if (it == fJobs->End()) return NULL; return it->Value(); } // CountJobs int32 KDiskDeviceManager::CountJobs() { return fJobs->Count(); } // NextJob KDiskDeviceJob * KDiskDeviceManager::NextJob(int32 *cookie) { if (!cookie) return NULL; JobMap::Iterator it = fJobs->FindClose(*cookie, false); if (it != fJobs->End()) { KDiskDeviceJob *job = it->Value(); *cookie = job->ID() + 1; return job; } return NULL; } // AddJobQueue /*! The device must be write locked, the manager must be locked. */ status_t KDiskDeviceManager::AddJobQueue(KDiskDeviceJobQueue *jobQueue) { // check the parameter if (!jobQueue) return B_BAD_VALUE; if (jobQueue->InitCheck() != B_OK) return jobQueue->InitCheck(); // add the queue status_t error = fJobQueues->PushBack(jobQueue); if (error != B_OK) return error; // add the queue's jobs int32 count = jobQueue->CountJobs(); for (int32 i = 0; i < count; i++) { KDiskDeviceJob *job = jobQueue->JobAt(i); error = fJobs->Put(job->ID(), job); if (error != B_OK) { _RemoveJobQueue(jobQueue); return error; } } // mark the jobs scheduled for (int32 i = 0; KDiskDeviceJob *job = jobQueue->JobAt(i); i++) _UpdateJobStatus(job, B_DISK_DEVICE_JOB_SCHEDULED, false); _UpdateBusyPartitions(jobQueue->Device()); // start the execution of the queue error = jobQueue->Execute(); if (error != B_OK) { // resuming the execution failed -- mark all jobs failed and // remove the queue for (int32 i = 0; KDiskDeviceJob *job = jobQueue->JobAt(i); i++) _UpdateJobStatus(job, B_DISK_DEVICE_JOB_FAILED, false); _RemoveJobQueue(jobQueue); _UpdateBusyPartitions(jobQueue->Device()); } return error; } // RemoveJobQueue status_t KDiskDeviceManager::RemoveJobQueue(KDiskDeviceJobQueue *jobQueue) { if (!jobQueue) return B_BAD_VALUE; if (jobQueue->InitCheck() != B_OK) return jobQueue->InitCheck(); if (jobQueue->IsExecuting()) return B_BAD_VALUE; return (_RemoveJobQueue(jobQueue) ? B_OK : B_ENTRY_NOT_FOUND); } // DeleteJobQueue status_t KDiskDeviceManager::DeleteJobQueue(KDiskDeviceJobQueue *jobQueue) { status_t error = RemoveJobQueue(jobQueue); if (error == B_OK) delete jobQueue; return error; } // CountJobQueues int32 KDiskDeviceManager::CountJobQueues() { return fJobQueues->Count(); } // NextJobQueue KDiskDeviceJobQueue * KDiskDeviceManager::NextJobQueue(int32 *cookie) { if (!cookie || *cookie < 0 || *cookie >= CountJobQueues()) return NULL; return fJobQueues->ElementAt((*cookie)++); } // JobFactory KDiskDeviceJobFactory * KDiskDeviceManager::JobFactory() const { return fJobFactory; } // UpdateBusyPartitions status_t KDiskDeviceManager::UpdateBusyPartitions(KDiskDevice *device) { if (!device) return B_BAD_VALUE; if (DeviceWriteLocker deviceLocker = device) { if (ManagerLocker locker = this) return _UpdateBusyPartitions(device); } return B_ERROR; } // UpdateJobStatus status_t KDiskDeviceManager::UpdateJobStatus(KDiskDeviceJob *job, uint32 status, bool updateBusyPartitions) { // check parameters if (!job) return B_BAD_VALUE; KDiskDeviceJobQueue *jobQueue = job->JobQueue(); KDiskDevice *device = (jobQueue ? jobQueue->Device() : NULL); if (!device) return B_BAD_VALUE; // lock device and manager if (DeviceWriteLocker deviceLocker = device) { if (ManagerLocker locker = this) return _UpdateJobStatus(job, status, updateBusyPartitions); } return B_ERROR; } // FindDiskSystem KDiskSystem * KDiskDeviceManager::FindDiskSystem(const char *name) { for (int32 cookie = 0; KDiskSystem *diskSystem = NextDiskSystem(&cookie); ) { if (!strcmp(name, diskSystem->Name())) return diskSystem; } return NULL; } // FindDiskSystem KDiskSystem * KDiskDeviceManager::FindDiskSystem(disk_system_id id) { DiskSystemMap::Iterator it = fDiskSystems->Find(id); if (it != fDiskSystems->End()) return it->Value(); return NULL; } // CountDiskSystems int32 KDiskDeviceManager::CountDiskSystems() { return fDiskSystems->Count(); } // NextDiskSystem KDiskSystem * KDiskDeviceManager::NextDiskSystem(int32 *cookie) { if (!cookie) return NULL; DiskSystemMap::Iterator it = fDiskSystems->FindClose(*cookie, false); if (it != fDiskSystems->End()) { KDiskSystem *diskSystem = it->Value(); *cookie = diskSystem->ID() + 1; return diskSystem; } return NULL; } // LoadDiskSystem KDiskSystem * KDiskDeviceManager::LoadDiskSystem(const char *name) { KDiskSystem *diskSystem = NULL; if (ManagerLocker locker = this) { diskSystem = FindDiskSystem(name); if (diskSystem && diskSystem->Load() != B_OK) diskSystem = NULL; } return diskSystem; } // LoadDiskSystem KDiskSystem * KDiskDeviceManager::LoadDiskSystem(disk_system_id id) { KDiskSystem *diskSystem = NULL; if (ManagerLocker locker = this) { diskSystem = FindDiskSystem(id); if (diskSystem && diskSystem->Load() != B_OK) diskSystem = NULL; } return diskSystem; } // LoadNextDiskSystem KDiskSystem * KDiskDeviceManager::LoadNextDiskSystem(int32 *cookie) { if (!cookie) return NULL; if (ManagerLocker locker = this) { if (KDiskSystem *diskSystem = NextDiskSystem(cookie)) { if (diskSystem->Load() == B_OK) { *cookie = diskSystem->ID() + 1; return diskSystem; } } } return NULL; } // InitialDeviceScan status_t KDiskDeviceManager::InitialDeviceScan() { status_t error = B_ERROR; // scan for devices if (ManagerLocker locker = this) { error = _Scan("/dev/disk"); if (error != B_OK) return error; } // scan the devices for partitions int32 cookie = 0; while (KDiskDevice *device = RegisterNextDevice(&cookie)) { PartitionRegistrar _(device, true); if (DeviceWriteLocker deviceLocker = device) { if (ManagerLocker locker = this) { error = _ScanPartition(device); if (error != B_OK) break; } else return B_ERROR; } else return B_ERROR; } return error; } // _AddPartitioningSystem status_t KDiskDeviceManager::_AddPartitioningSystem(const char *name) { if (!name) return B_BAD_VALUE; KDiskSystem *diskSystem = new(nothrow) KPartitioningSystem(name); if (!diskSystem) return B_NO_MEMORY; return _AddDiskSystem(diskSystem); } // _AddFileSystem status_t KDiskDeviceManager::_AddFileSystem(const char *name) { if (!name) return B_BAD_VALUE; KDiskSystem *diskSystem = new(nothrow) KFileSystem(name); if (!diskSystem) return B_NO_MEMORY; return _AddDiskSystem(diskSystem); } // _AddDiskSystem status_t KDiskDeviceManager::_AddDiskSystem(KDiskSystem *diskSystem) { if (!diskSystem) return B_BAD_VALUE; DBG(OUT("KDiskDeviceManager::_AddDiskSystem(%s)\n", diskSystem->Name())); status_t error = diskSystem->Init(); if (error != B_OK) DBG(OUT(" initialization failed: %s\n", strerror(error))); if (error == B_OK) error = fDiskSystems->Put(diskSystem->ID(), diskSystem); if (error != B_OK) delete diskSystem; DBG(OUT("KDiskDeviceManager::_AddDiskSystem() done: %s\n", strerror(error))); return error; } // _AddDevice bool KDiskDeviceManager::_AddDevice(KDiskDevice *device) { if (!device || !PartitionAdded(device)) return false; if (fDevices->Put(device->ID(), device) == B_OK) return true; PartitionRemoved(device); return false; } // _RemoveDevice bool KDiskDeviceManager::_RemoveDevice(KDiskDevice *device) { return (device && fDevices->Remove(device->ID()) && PartitionRemoved(device)); } // _RemoveJobQueue bool KDiskDeviceManager::_RemoveJobQueue(KDiskDeviceJobQueue *jobQueue) { if (!jobQueue) return false; // find the job queue JobQueueVector::Iterator it = fJobQueues->Find(jobQueue); if (it == fJobQueues->End()) return false; // remove the queue's jobs int32 count = jobQueue->CountJobs(); for (int32 i = 0; i < count; i++) { KDiskDeviceJob *job = jobQueue->JobAt(i); fJobs->Remove(job->ID()); } fJobQueues->Erase(it); return true; } // _UpdateBusyPartitions /*! The device must be write locked, the manager must be locked. */ status_t KDiskDeviceManager::_UpdateBusyPartitions(KDiskDevice *device) { if (!device) return B_BAD_VALUE; // mark all partitions un-busy struct UnmarkBusyVisitor : KPartitionVisitor { virtual bool VisitPre(KPartition *partition) { partition->ClearFlags(B_PARTITION_BUSY | B_PARTITION_DESCENDANT_BUSY); return false; } } visitor; device->VisitEachDescendant(&visitor); // Iterate through all job queues and all jobs scheduled or in // progress and mark their scope busy. for (int32 cookie = 0; KDiskDeviceJobQueue *jobQueue = NextJobQueue(&cookie); ) { if (jobQueue->Device() != device) continue; for (int32 i = jobQueue->ActiveJobIndex(); KDiskDeviceJob *job = jobQueue->JobAt(i); i++) { if (job->Status() != B_DISK_DEVICE_JOB_IN_PROGRESS && job->Status() != B_DISK_DEVICE_JOB_SCHEDULED) { continue; } KPartition *partition = FindPartition(job->ScopeID()); if (!partition || partition->Device() != device) continue; partition->AddFlags(B_PARTITION_BUSY); } } // mark all anscestors of busy partitions descendant busy and all // descendants busy struct MarkBusyVisitor : KPartitionVisitor { virtual bool VisitPre(KPartition *partition) { // parent busy => child busy if (partition->Parent() && partition->Parent()->IsBusy()) partition->AddFlags(B_PARTITION_BUSY); return false; } virtual bool VisitPost(KPartition *partition) { // child [descendant] busy => parent descendant busy if ((partition->IsBusy() || partition->IsDescendantBusy()) && partition->Parent()) { partition->Parent()->AddFlags(B_PARTITION_DESCENDANT_BUSY); } return false; } } visitor2; device->VisitEachDescendant(&visitor2); return B_OK; } // _UpdateJobStatus status_t KDiskDeviceManager::_UpdateJobStatus(KDiskDeviceJob *job, uint32 status, bool updateBusyPartitions) { // check parameters if (!job) return B_BAD_VALUE; KDiskDeviceJobQueue *jobQueue = job->JobQueue(); KDiskDevice *device = (jobQueue ? jobQueue->Device() : NULL); if (!device) return B_BAD_VALUE; if (job->Status() == status) return B_OK; // check migration of a schedule/in progress to a terminal state // or vice versa updateBusyPartitions &= (is_active_job_status(job->Status()) != is_active_job_status(status)); // set new state and update the partitions' busy flags job->SetStatus(status); if (updateBusyPartitions) return _UpdateBusyPartitions(device); // TODO: notifications return B_OK; } // _Scan status_t KDiskDeviceManager::_Scan(const char *path) { DBG(OUT("KDiskDeviceManager::_Scan(%s)\n", path)); status_t error = B_OK; struct stat st; if (lstat(path, &st) < 0) { return errno; } if (S_ISDIR(st.st_mode)) { // a directory: iterate through its contents DIR *dir = opendir(path); if (!dir) return errno; while (dirent *entry = readdir(dir)) { // skip "." and ".." if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) continue; KPath entryPath; if (entryPath.SetPath(path) != B_OK || entryPath.Append(entry->d_name) != B_OK) { continue; } _Scan(entryPath.Path()); } closedir(dir); } else { // not a directory // check, if it is named "raw" int32 len = strlen(path); int32 leafLen = strlen("/raw"); if (len <= leafLen || strcmp(path + len - leafLen, "/raw")) return B_ERROR; DBG(OUT(" found device: %s\n", path)); // create a KDiskDevice for it KDiskDevice *device = new(nothrow) KDiskDevice; if (!device) return B_NO_MEMORY; // init the KDiskDevice status_t error = device->SetTo(path); // add the device if (error == B_OK && !_AddDevice(device)) error = B_NO_MEMORY; // cleanup on error if (error != B_OK) delete device; } return error; } // _ScanPartition /*! The device must be write locked, the manager must be locked. */ status_t KDiskDeviceManager::_ScanPartition(KPartition *partition) { if (!partition) return B_BAD_VALUE; // create a new job queue for the device KDiskDeviceJobQueue *jobQueue = new(nothrow) KDiskDeviceJobQueue; if (!jobQueue) return B_NO_MEMORY; jobQueue->SetDevice(partition->Device()); // create a job for scanning the device and add it to the job queue KDiskDeviceJob *job = fJobFactory->CreateScanPartitionJob(partition->ID()); if (!job) { delete jobQueue; return B_NO_MEMORY; } if (!jobQueue->AddJob(job)) { delete jobQueue; delete job; return B_NO_MEMORY; } // add the job queue status_t error = AddJobQueue(jobQueue); if (error != B_OK) delete jobQueue; return error; }