headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / kernel / disk_device_manager / KDiskDeviceManager.cpp
blob74b11d9223b33505cc1a71cddb1764d65936458f
1 /*
2 * Copyright 2004-2017, Haiku, Inc. All rights reserved.
3 * Copyright 2003-2004, Ingo Weinhold, bonefish@cs.tu-berlin.de. All rights reserved.
5 * Distributed under the terms of the MIT License.
6 */
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"
20 #include <VectorMap.h>
21 #include <VectorSet.h>
23 #include <DiskDeviceRoster.h>
24 #include <KernelExport.h>
25 #include <NodeMonitor.h>
27 #include <boot_device.h>
28 #include <kmodule.h>
29 #include <node_monitor.h>
30 #include <Notifications.h>
31 #include <util/kernel_cpp.h>
32 #include <vfs.h>
34 #include <dirent.h>
35 #include <errno.h>
36 #include <module.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/stat.h>
42 // debugging
43 //#define DBG(x)
44 #define DBG(x) x
45 #define OUT dprintf
48 // directories for partitioning and file system modules
49 static const char* kPartitioningSystemPrefix = "partitioning_systems";
50 static const char* kFileSystemPrefix = "file_systems";
53 // singleton instance
54 KDiskDeviceManager* KDiskDeviceManager::sDefaultManager = NULL;
57 struct device_event {
58 int32 opcode;
59 const char* name;
60 dev_t device;
61 ino_t directory;
62 ino_t node;
66 struct GetPartitionID {
67 inline partition_id operator()(const KPartition* partition) const
69 return partition->ID();
74 struct GetDiskSystemID {
75 inline disk_system_id operator()(const KDiskSystem* system) const
77 return system->ID();
82 struct KDiskDeviceManager::PartitionMap : VectorMap<partition_id, KPartition*,
83 VectorMapEntryStrategy::ImplicitKey<partition_id, KPartition*,
84 GetPartitionID> > {
88 struct KDiskDeviceManager::DeviceMap : VectorMap<partition_id, KDiskDevice*,
89 VectorMapEntryStrategy::ImplicitKey<partition_id, KDiskDevice*,
90 GetPartitionID> > {
94 struct KDiskDeviceManager::DiskSystemMap : VectorMap<disk_system_id,
95 KDiskSystem*,
96 VectorMapEntryStrategy::ImplicitKey<disk_system_id, KDiskSystem*,
97 GetDiskSystemID> > {
101 struct KDiskDeviceManager::PartitionSet : VectorSet<KPartition*> {
105 class KDiskDeviceManager::DiskSystemWatcher : public NotificationListener {
106 public:
107 DiskSystemWatcher(KDiskDeviceManager* manager)
109 fManager(manager)
113 virtual ~DiskSystemWatcher()
117 virtual void EventOccurred(NotificationService& service,
118 const KMessage* event)
120 if (event->GetInt32("opcode", -1) != B_ENTRY_REMOVED)
121 fManager->RescanDiskSystems();
124 private:
125 KDiskDeviceManager* fManager;
129 class KDiskDeviceManager::DeviceWatcher : public NotificationListener {
130 public:
131 DeviceWatcher()
135 virtual ~DeviceWatcher()
139 virtual void EventOccurred(NotificationService& service,
140 const KMessage* event)
142 int32 opcode = event->GetInt32("opcode", -1);
143 switch (opcode) {
144 case B_ENTRY_CREATED:
145 case B_ENTRY_REMOVED:
147 device_event* deviceEvent = new(std::nothrow) device_event;
148 if (deviceEvent == NULL)
149 break;
151 const char* name = event->GetString("name", NULL);
152 if (name != NULL)
153 deviceEvent->name = strdup(name);
154 else
155 deviceEvent->name = NULL;
157 deviceEvent->opcode = opcode;
158 deviceEvent->device = event->GetInt32("device", -1);
159 deviceEvent->directory = event->GetInt64("directory", -1);
160 deviceEvent->node = event->GetInt64("node", -1);
162 struct stat stat;
163 if (vfs_stat_node_ref(deviceEvent->device, deviceEvent->node,
164 &stat) != 0) {
165 delete deviceEvent;
166 break;
168 if (S_ISDIR(stat.st_mode)) {
169 if (opcode == B_ENTRY_CREATED) {
170 add_node_listener(deviceEvent->device,
171 deviceEvent->node, B_WATCH_DIRECTORY, *this);
172 } else {
173 remove_node_listener(deviceEvent->device,
174 deviceEvent->node, *this);
176 delete deviceEvent;
177 break;
180 // TODO: a real in-kernel DPC mechanism would be preferred...
181 thread_id thread = spawn_kernel_thread(_HandleDeviceEvent,
182 "device event", B_NORMAL_PRIORITY, deviceEvent);
183 if (thread < 0)
184 delete deviceEvent;
185 else
186 resume_thread(thread);
187 break;
190 default:
191 break;
195 static status_t _HandleDeviceEvent(void* _event)
197 device_event* event = (device_event*)_event;
199 if (strcmp(event->name, "raw") == 0) {
200 // a new raw device was added/removed
201 KPath path(B_PATH_NAME_LENGTH + 1);
202 if (path.InitCheck() != B_OK
203 || vfs_entry_ref_to_path(event->device, event->directory,
204 event->name, true, path.LockBuffer(),
205 path.BufferSize()) != B_OK) {
206 delete event;
207 return B_ERROR;
210 path.UnlockBuffer();
211 if (event->opcode == B_ENTRY_CREATED)
212 KDiskDeviceManager::Default()->CreateDevice(path.Path());
213 else
214 KDiskDeviceManager::Default()->DeleteDevice(path.Path());
217 delete event;
218 return B_OK;
223 class KDiskDeviceManager::DiskNotifications
224 : public DefaultUserNotificationService {
225 public:
226 DiskNotifications()
227 : DefaultUserNotificationService("disk devices")
231 virtual ~DiskNotifications()
237 // #pragma mark -
240 KDiskDeviceManager::KDiskDeviceManager()
242 fDevices(new(nothrow) DeviceMap),
243 fPartitions(new(nothrow) PartitionMap),
244 fDiskSystems(new(nothrow) DiskSystemMap),
245 fObsoletePartitions(new(nothrow) PartitionSet),
246 fMediaChecker(-1),
247 fTerminating(false),
248 fDiskSystemWatcher(NULL),
249 fDeviceWatcher(new(nothrow) DeviceWatcher()),
250 fNotifications(new(nothrow) DiskNotifications)
252 recursive_lock_init(&fLock, "disk device manager");
254 if (InitCheck() != B_OK)
255 return;
257 fNotifications->Register();
259 RescanDiskSystems();
261 fMediaChecker = spawn_kernel_thread(_CheckMediaStatusDaemon,
262 "media checker", B_NORMAL_PRIORITY, this);
263 if (fMediaChecker >= 0)
264 resume_thread(fMediaChecker);
266 DBG(OUT("number of disk systems: %" B_PRId32 "\n", CountDiskSystems()));
267 // TODO: Watch the disk systems and the relevant directories.
271 KDiskDeviceManager::~KDiskDeviceManager()
273 fTerminating = true;
275 status_t result;
276 wait_for_thread(fMediaChecker, &result);
278 // stop all node monitoring
279 _AddRemoveMonitoring("/dev/disk", false);
280 delete fDeviceWatcher;
282 // remove all devices
283 for (int32 cookie = 0; KDiskDevice* device = NextDevice(&cookie);) {
284 PartitionRegistrar _(device);
285 _RemoveDevice(device);
288 // some sanity checks
289 if (fPartitions->Count() > 0) {
290 DBG(OUT("WARNING: There are still %" B_PRId32 " unremoved partitions!\n",
291 fPartitions->Count()));
292 for (PartitionMap::Iterator it = fPartitions->Begin();
293 it != fPartitions->End(); ++it) {
294 DBG(OUT(" partition: %" B_PRId32 "\n", it->Value()->ID()));
297 if (fObsoletePartitions->Count() > 0) {
298 DBG(OUT("WARNING: There are still %" B_PRId32 " obsolete partitions!\n",
299 fObsoletePartitions->Count()));
300 for (PartitionSet::Iterator it = fObsoletePartitions->Begin();
301 it != fObsoletePartitions->End(); ++it) {
302 DBG(OUT(" partition: %" B_PRId32 "\n", (*it)->ID()));
305 // remove all disk systems
306 for (int32 cookie = 0; KDiskSystem* diskSystem = NextDiskSystem(&cookie);) {
307 fDiskSystems->Remove(diskSystem->ID());
308 if (diskSystem->IsLoaded()) {
309 DBG(OUT("WARNING: Disk system `%s' (%" B_PRId32 ") is still loaded!\n",
310 diskSystem->Name(), diskSystem->ID()));
311 } else
312 delete diskSystem;
315 fNotifications->Unregister();
317 // delete the containers
318 delete fPartitions;
319 delete fDevices;
320 delete fDiskSystems;
321 delete fObsoletePartitions;
325 status_t
326 KDiskDeviceManager::InitCheck() const
328 if (fPartitions == NULL || fDevices == NULL || fDiskSystems == NULL
329 || fObsoletePartitions == NULL || fNotifications == NULL)
330 return B_NO_MEMORY;
332 return B_OK;
336 /*! This creates the system's default DiskDeviceManager.
337 The creation is not thread-safe, and shouldn't be done more than once.
339 status_t
340 KDiskDeviceManager::CreateDefault()
342 if (sDefaultManager != NULL)
343 return B_OK;
345 sDefaultManager = new(nothrow) KDiskDeviceManager;
346 if (sDefaultManager == NULL)
347 return B_NO_MEMORY;
349 return sDefaultManager->InitCheck();
353 /*! This deletes the default DiskDeviceManager. The deletion is not
354 thread-safe either, you should make sure that it's called only once.
356 void
357 KDiskDeviceManager::DeleteDefault()
359 delete sDefaultManager;
360 sDefaultManager = NULL;
364 KDiskDeviceManager*
365 KDiskDeviceManager::Default()
367 return sDefaultManager;
371 bool
372 KDiskDeviceManager::Lock()
374 return recursive_lock_lock(&fLock) == B_OK;
378 void
379 KDiskDeviceManager::Unlock()
381 recursive_lock_unlock(&fLock);
385 DefaultUserNotificationService&
386 KDiskDeviceManager::Notifications()
388 return *fNotifications;
392 void
393 KDiskDeviceManager::Notify(const KMessage& event, uint32 eventMask)
395 fNotifications->Notify(event, eventMask);
399 KDiskDevice*
400 KDiskDeviceManager::FindDevice(const char* path)
402 for (int32 cookie = 0; KDiskDevice* device = NextDevice(&cookie); ) {
403 if (device->Path() && !strcmp(path, device->Path()))
404 return device;
406 return NULL;
410 KDiskDevice*
411 KDiskDeviceManager::FindDevice(partition_id id, bool deviceOnly)
413 if (KPartition* partition = FindPartition(id)) {
414 KDiskDevice* device = partition->Device();
415 if (!deviceOnly || id == device->ID())
416 return device;
418 return NULL;
422 KPartition*
423 KDiskDeviceManager::FindPartition(const char* path)
425 // TODO: Optimize!
426 KPath partitionPath;
427 if (partitionPath.InitCheck() != B_OK)
428 return NULL;
430 for (PartitionMap::Iterator iterator = fPartitions->Begin();
431 iterator != fPartitions->End(); ++iterator) {
432 KPartition* partition = iterator->Value();
433 if (partition->GetPath(&partitionPath) == B_OK
434 && partitionPath == path) {
435 return partition;
439 return NULL;
443 KPartition*
444 KDiskDeviceManager::FindPartition(partition_id id)
446 PartitionMap::Iterator iterator = fPartitions->Find(id);
447 if (iterator != fPartitions->End())
448 return iterator->Value();
450 return NULL;
454 KFileDiskDevice*
455 KDiskDeviceManager::FindFileDevice(const char* filePath)
457 for (int32 cookie = 0; KDiskDevice* device = NextDevice(&cookie); ) {
458 KFileDiskDevice* fileDevice = dynamic_cast<KFileDiskDevice*>(device);
459 if (fileDevice && fileDevice->FilePath()
460 && !strcmp(filePath, fileDevice->FilePath())) {
461 return fileDevice;
464 return NULL;
468 KDiskDevice*
469 KDiskDeviceManager::RegisterDevice(const char* path)
471 if (ManagerLocker locker = this) {
472 for (int32 i = 0; i < 2; i++) {
473 if (KDiskDevice* device = FindDevice(path)) {
474 device->Register();
475 return device;
478 // if the device is not known yet, create it and try again
479 const char* leaf = strrchr(path, '/');
480 if (i == 0 && !strncmp(path, "/dev/disk", 9)
481 && !strcmp(leaf + 1, "raw") && CreateDevice(path) < B_OK)
482 break;
485 return NULL;
489 KDiskDevice*
490 KDiskDeviceManager::RegisterDevice(partition_id id, bool deviceOnly)
492 if (ManagerLocker locker = this) {
493 if (KDiskDevice* device = FindDevice(id, deviceOnly)) {
494 device->Register();
495 return device;
498 return NULL;
502 KDiskDevice*
503 KDiskDeviceManager::RegisterNextDevice(int32* cookie)
505 if (!cookie)
506 return NULL;
508 if (ManagerLocker locker = this) {
509 if (KDiskDevice* device = NextDevice(cookie)) {
510 device->Register();
511 return device;
514 return NULL;
518 KPartition*
519 KDiskDeviceManager::RegisterPartition(const char* path)
521 if (ManagerLocker locker = this) {
522 for (int32 i = 0; i < 2; i++) {
523 if (KPartition* partition = FindPartition(path)) {
524 partition->Register();
525 return partition;
528 // if the device is not known yet, create it and try again
529 const char* leaf = strrchr(path, '/');
530 if (i == 0 && !strncmp(path, "/dev/disk", 9)
531 && !strcmp(leaf + 1, "raw") && CreateDevice(path) < B_OK)
532 break;
535 return NULL;
539 KPartition*
540 KDiskDeviceManager::RegisterPartition(partition_id id)
542 if (ManagerLocker locker = this) {
543 if (KPartition* partition = FindPartition(id)) {
544 partition->Register();
545 return partition;
548 return NULL;
552 KFileDiskDevice*
553 KDiskDeviceManager::RegisterFileDevice(const char* filePath)
555 if (ManagerLocker locker = this) {
556 if (KFileDiskDevice* device = FindFileDevice(filePath)) {
557 device->Register();
558 return device;
561 return NULL;
565 KDiskDevice*
566 KDiskDeviceManager::ReadLockDevice(partition_id id, bool deviceOnly)
568 // register device
569 KDiskDevice* device = RegisterDevice(id, deviceOnly);
570 if (!device)
571 return NULL;
572 // lock device
573 if (device->ReadLock())
574 return device;
575 device->Unregister();
576 return NULL;
580 KDiskDevice*
581 KDiskDeviceManager::WriteLockDevice(partition_id id, bool deviceOnly)
583 // register device
584 KDiskDevice* device = RegisterDevice(id, deviceOnly);
585 if (!device)
586 return NULL;
587 // lock device
588 if (device->WriteLock())
589 return device;
590 device->Unregister();
591 return NULL;
595 KPartition*
596 KDiskDeviceManager::ReadLockPartition(partition_id id)
598 // register partition
599 KPartition* partition = RegisterPartition(id);
600 if (!partition)
601 return NULL;
602 // get and register the device
603 KDiskDevice* device = NULL;
604 if (ManagerLocker locker = this) {
605 device = partition->Device();
606 if (device)
607 device->Register();
609 // lock the device
610 if (device && device->ReadLock()) {
611 // final check, if the partition still belongs to the device
612 if (partition->Device() == device)
613 return partition;
614 device->ReadUnlock();
616 // cleanup on failure
617 if (device)
618 device->Unregister();
619 partition->Unregister();
620 return NULL;
624 KPartition*
625 KDiskDeviceManager::WriteLockPartition(partition_id id)
627 // register partition
628 KPartition* partition = RegisterPartition(id);
629 if (!partition)
630 return NULL;
631 // get and register the device
632 KDiskDevice* device = NULL;
633 if (ManagerLocker locker = this) {
634 device = partition->Device();
635 if (device)
636 device->Register();
638 // lock the device
639 if (device && device->WriteLock()) {
640 // final check, if the partition still belongs to the device
641 if (partition->Device() == device)
642 return partition;
643 device->WriteUnlock();
645 // cleanup on failure
646 if (device)
647 device->Unregister();
648 partition->Unregister();
649 return NULL;
653 status_t
654 KDiskDeviceManager::ScanPartition(KPartition* partition)
656 // TODO: This won't do. Locking the DDM while scanning the partition is not a
657 // good idea. Even locking the device doesn't feel right. Marking the partition
658 // busy and passing the disk system a temporary clone of the partition_data
659 // should work as well.
660 if (DeviceWriteLocker deviceLocker = partition->Device()) {
661 if (ManagerLocker locker = this)
662 return _ScanPartition(partition, false);
665 return B_ERROR;
669 partition_id
670 KDiskDeviceManager::CreateDevice(const char* path, bool* newlyCreated)
672 if (!path)
673 return B_BAD_VALUE;
675 status_t error = B_ERROR;
676 if (ManagerLocker locker = this) {
677 KDiskDevice* device = FindDevice(path);
678 if (device != NULL) {
679 // we already know this device
680 if (newlyCreated)
681 *newlyCreated = false;
683 return device->ID();
686 // create a KDiskDevice for it
687 device = new(nothrow) KDiskDevice;
688 if (!device)
689 return B_NO_MEMORY;
691 // initialize and add the device
692 error = device->SetTo(path);
694 // Note: Here we are allowed to lock a device although already having
695 // the manager locked, since it is not yet added to the manager.
696 DeviceWriteLocker deviceLocker(device);
697 if (error == B_OK && !deviceLocker.IsLocked())
698 error = B_ERROR;
699 if (error == B_OK && !_AddDevice(device))
700 error = B_NO_MEMORY;
702 // cleanup on error
703 if (error != B_OK) {
704 deviceLocker.Unlock();
705 delete device;
706 return error;
709 if (error == B_OK) {
710 // scan for partitions
711 _ScanPartition(device, false);
712 device->UnmarkBusy(true);
714 _NotifyDeviceEvent(device, B_DEVICE_ADDED,
715 B_DEVICE_REQUEST_DEVICE_LIST);
717 if (newlyCreated)
718 *newlyCreated = true;
720 return device->ID();
724 return error;
728 status_t
729 KDiskDeviceManager::DeleteDevice(const char* path)
731 KDiskDevice* device = FindDevice(path);
732 if (device == NULL)
733 return B_ENTRY_NOT_FOUND;
735 PartitionRegistrar _(device, false);
736 if (DeviceWriteLocker locker = device) {
737 if (_RemoveDevice(device))
738 return B_OK;
741 return B_ERROR;
745 partition_id
746 KDiskDeviceManager::CreateFileDevice(const char* filePath, bool* newlyCreated)
748 if (!filePath)
749 return B_BAD_VALUE;
751 // normalize the file path
752 KPath normalizedFilePath;
753 status_t error = normalizedFilePath.SetTo(filePath, KPath::NORMALIZE);
754 if (error != B_OK)
755 return error;
756 filePath = normalizedFilePath.Path();
758 KFileDiskDevice* device = NULL;
759 if (ManagerLocker locker = this) {
760 // check, if the device does already exist
761 if ((device = FindFileDevice(filePath))) {
762 if (newlyCreated)
763 *newlyCreated = false;
765 return device->ID();
768 // allocate a KFileDiskDevice
769 device = new(nothrow) KFileDiskDevice;
770 if (!device)
771 return B_NO_MEMORY;
773 // initialize and add the device
774 error = device->SetTo(filePath);
776 // Note: Here we are allowed to lock a device although already having
777 // the manager locked, since it is not yet added to the manager.
778 DeviceWriteLocker deviceLocker(device);
779 if (error == B_OK && !deviceLocker.IsLocked())
780 error = B_ERROR;
781 if (error == B_OK && !_AddDevice(device))
782 error = B_NO_MEMORY;
784 // scan device
785 if (error == B_OK) {
786 _ScanPartition(device, false);
787 device->UnmarkBusy(true);
789 _NotifyDeviceEvent(device, B_DEVICE_ADDED,
790 B_DEVICE_REQUEST_DEVICE_LIST);
792 if (newlyCreated)
793 *newlyCreated = true;
795 return device->ID();
798 // cleanup on failure
799 deviceLocker.Unlock();
800 delete device;
801 } else
802 error = B_ERROR;
803 return error;
807 status_t
808 KDiskDeviceManager::DeleteFileDevice(const char* filePath)
810 if (KFileDiskDevice* device = RegisterFileDevice(filePath)) {
811 PartitionRegistrar _(device, true);
812 if (DeviceWriteLocker locker = device) {
813 if (_RemoveDevice(device))
814 return B_OK;
817 return B_ERROR;
821 status_t
822 KDiskDeviceManager::DeleteFileDevice(partition_id id)
824 if (KDiskDevice* device = RegisterDevice(id)) {
825 PartitionRegistrar _(device, true);
826 if (!dynamic_cast<KFileDiskDevice*>(device) || id != device->ID())
827 return B_ENTRY_NOT_FOUND;
828 if (DeviceWriteLocker locker = device) {
829 if (_RemoveDevice(device))
830 return B_OK;
833 return B_ERROR;
837 int32
838 KDiskDeviceManager::CountDevices()
840 return fDevices->Count();
844 KDiskDevice*
845 KDiskDeviceManager::NextDevice(int32* cookie)
847 if (!cookie)
848 return NULL;
850 DeviceMap::Iterator it = fDevices->FindClose(*cookie, false);
851 if (it != fDevices->End()) {
852 KDiskDevice* device = it->Value();
853 *cookie = device->ID() + 1;
854 return device;
856 return NULL;
860 bool
861 KDiskDeviceManager::PartitionAdded(KPartition* partition)
863 return partition && fPartitions->Put(partition->ID(), partition) == B_OK;
867 bool
868 KDiskDeviceManager::PartitionRemoved(KPartition* partition)
870 if (partition && partition->PrepareForRemoval()
871 && fPartitions->Remove(partition->ID())) {
872 // TODO: If adding the partition to the obsolete list fails (due to lack
873 // of memory), we can't do anything about it. We will leak memory then.
874 fObsoletePartitions->Insert(partition);
875 partition->MarkObsolete();
876 return true;
878 return false;
882 bool
883 KDiskDeviceManager::DeletePartition(KPartition* partition)
885 if (partition && partition->IsObsolete()
886 && partition->CountReferences() == 0
887 && partition->PrepareForDeletion()
888 && fObsoletePartitions->Remove(partition)) {
889 delete partition;
890 return true;
892 return false;
896 KDiskSystem*
897 KDiskDeviceManager::FindDiskSystem(const char* name, bool byPrettyName)
899 for (int32 cookie = 0; KDiskSystem* diskSystem = NextDiskSystem(&cookie);) {
900 if (byPrettyName) {
901 if (strcmp(name, diskSystem->PrettyName()) == 0)
902 return diskSystem;
903 } else {
904 if (strcmp(name, diskSystem->Name()) == 0)
905 return diskSystem;
908 return NULL;
912 KDiskSystem*
913 KDiskDeviceManager::FindDiskSystem(disk_system_id id)
915 DiskSystemMap::Iterator it = fDiskSystems->Find(id);
916 if (it != fDiskSystems->End())
917 return it->Value();
918 return NULL;
922 int32
923 KDiskDeviceManager::CountDiskSystems()
925 return fDiskSystems->Count();
929 KDiskSystem*
930 KDiskDeviceManager::NextDiskSystem(int32* cookie)
932 if (!cookie)
933 return NULL;
935 DiskSystemMap::Iterator it = fDiskSystems->FindClose(*cookie, false);
936 if (it != fDiskSystems->End()) {
937 KDiskSystem* diskSystem = it->Value();
938 *cookie = diskSystem->ID() + 1;
939 return diskSystem;
941 return NULL;
945 KDiskSystem*
946 KDiskDeviceManager::LoadDiskSystem(const char* name, bool byPrettyName)
948 KDiskSystem* diskSystem = NULL;
949 if (ManagerLocker locker = this) {
950 diskSystem = FindDiskSystem(name, byPrettyName);
951 if (diskSystem && diskSystem->Load() != B_OK)
952 diskSystem = NULL;
954 return diskSystem;
958 KDiskSystem*
959 KDiskDeviceManager::LoadDiskSystem(disk_system_id id)
961 KDiskSystem* diskSystem = NULL;
962 if (ManagerLocker locker = this) {
963 diskSystem = FindDiskSystem(id);
964 if (diskSystem && diskSystem->Load() != B_OK)
965 diskSystem = NULL;
967 return diskSystem;
971 KDiskSystem*
972 KDiskDeviceManager::LoadNextDiskSystem(int32* cookie)
974 if (!cookie)
975 return NULL;
977 if (ManagerLocker locker = this) {
978 if (KDiskSystem* diskSystem = NextDiskSystem(cookie)) {
979 if (diskSystem->Load() == B_OK) {
980 *cookie = diskSystem->ID() + 1;
981 return diskSystem;
985 return NULL;
989 status_t
990 KDiskDeviceManager::InitialDeviceScan()
992 status_t error = B_ERROR;
994 // scan for devices
995 if (ManagerLocker locker = this) {
996 error = _Scan("/dev/disk");
997 if (error != B_OK)
998 return error;
1001 // scan the devices for partitions
1002 int32 cookie = 0;
1003 while (KDiskDevice* device = RegisterNextDevice(&cookie)) {
1004 PartitionRegistrar _(device, true);
1005 if (DeviceWriteLocker deviceLocker = device) {
1006 if (ManagerLocker locker = this) {
1007 error = _ScanPartition(device, false);
1008 device->UnmarkBusy(true);
1009 if (error != B_OK)
1010 break;
1011 } else
1012 return B_ERROR;
1013 } else
1014 return B_ERROR;
1016 return error;
1020 status_t
1021 KDiskDeviceManager::StartMonitoring()
1023 // do another scan, this will populate the devfs directories
1024 InitialDeviceScan();
1026 // start monitoring the disk systems
1027 fDiskSystemWatcher = new(std::nothrow) DiskSystemWatcher(this);
1028 if (fDiskSystemWatcher != NULL) {
1029 start_watching_modules(kFileSystemPrefix, *fDiskSystemWatcher);
1030 start_watching_modules(kPartitioningSystemPrefix,
1031 *fDiskSystemWatcher);
1034 // start monitoring all dirs under /dev/disk
1035 return _AddRemoveMonitoring("/dev/disk", true);
1039 status_t
1040 KDiskDeviceManager::_RescanDiskSystems(DiskSystemMap& addedSystems,
1041 bool fileSystems)
1043 void* cookie = open_module_list(fileSystems
1044 ? kFileSystemPrefix : kPartitioningSystemPrefix);
1045 if (cookie == NULL)
1046 return B_NO_MEMORY;
1048 while (true) {
1049 KPath name;
1050 if (name.InitCheck() != B_OK)
1051 break;
1052 size_t nameLength = name.BufferSize();
1053 if (read_next_module_name(cookie, name.LockBuffer(),
1054 &nameLength) != B_OK) {
1055 break;
1057 name.UnlockBuffer();
1059 if (FindDiskSystem(name.Path()))
1060 continue;
1062 if (fileSystems) {
1063 DBG(OUT("file system: %s\n", name.Path()));
1064 _AddFileSystem(name.Path());
1065 } else {
1066 DBG(OUT("partitioning system: %s\n", name.Path()));
1067 _AddPartitioningSystem(name.Path());
1070 if (KDiskSystem* system = FindDiskSystem(name.Path()))
1071 addedSystems.Put(system->ID(), system);
1074 close_module_list(cookie);
1075 return B_OK;
1079 /*! Rescan the existing disk systems. This is called after the boot device
1080 has become available.
1082 status_t
1083 KDiskDeviceManager::RescanDiskSystems()
1085 DiskSystemMap addedSystems;
1087 Lock();
1089 // rescan for partitioning and file systems
1090 _RescanDiskSystems(addedSystems, false);
1091 _RescanDiskSystems(addedSystems, true);
1093 Unlock();
1095 // rescan existing devices with the new disk systems
1096 int32 cookie = 0;
1097 while (KDiskDevice* device = RegisterNextDevice(&cookie)) {
1098 PartitionRegistrar _(device, true);
1099 if (DeviceWriteLocker deviceLocker = device) {
1100 if (ManagerLocker locker = this) {
1101 status_t status = _ScanPartition(device, false, &addedSystems);
1102 device->UnmarkBusy(true);
1103 if (status != B_OK)
1104 break;
1105 } else
1106 return B_ERROR;
1107 } else
1108 return B_ERROR;
1111 return B_OK;
1115 status_t
1116 KDiskDeviceManager::_AddPartitioningSystem(const char* name)
1118 if (!name)
1119 return B_BAD_VALUE;
1121 KDiskSystem* diskSystem = new(nothrow) KPartitioningSystem(name);
1122 if (!diskSystem)
1123 return B_NO_MEMORY;
1124 return _AddDiskSystem(diskSystem);
1128 status_t
1129 KDiskDeviceManager::_AddFileSystem(const char* name)
1131 if (!name)
1132 return B_BAD_VALUE;
1134 KDiskSystem* diskSystem = new(nothrow) KFileSystem(name);
1135 if (!diskSystem)
1136 return B_NO_MEMORY;
1138 return _AddDiskSystem(diskSystem);
1142 status_t
1143 KDiskDeviceManager::_AddDiskSystem(KDiskSystem* diskSystem)
1145 if (!diskSystem)
1146 return B_BAD_VALUE;
1147 DBG(OUT("KDiskDeviceManager::_AddDiskSystem(%s)\n", diskSystem->Name()));
1148 status_t error = diskSystem->Init();
1149 DBG(if (error != B_OK)
1150 OUT(" initialization failed: %s\n", strerror(error)));
1151 if (error == B_OK)
1152 error = fDiskSystems->Put(diskSystem->ID(), diskSystem);
1153 if (error != B_OK)
1154 delete diskSystem;
1155 DBG(OUT("KDiskDeviceManager::_AddDiskSystem() done: %s\n",
1156 strerror(error)));
1157 return error;
1161 bool
1162 KDiskDeviceManager::_AddDevice(KDiskDevice* device)
1164 if (!device || !PartitionAdded(device))
1165 return false;
1166 if (fDevices->Put(device->ID(), device) == B_OK)
1167 return true;
1168 PartitionRemoved(device);
1169 return false;
1173 bool
1174 KDiskDeviceManager::_RemoveDevice(KDiskDevice* device)
1176 if (device != NULL && fDevices->Remove(device->ID())
1177 && PartitionRemoved(device)) {
1178 _NotifyDeviceEvent(device, B_DEVICE_REMOVED,
1179 B_DEVICE_REQUEST_DEVICE_LIST);
1180 return true;
1183 return false;
1187 #if 0
1189 The device must be write locked, the manager must be locked.
1191 status_t
1192 KDiskDeviceManager::_UpdateBusyPartitions(KDiskDevice *device)
1194 if (!device)
1195 return B_BAD_VALUE;
1196 // mark all partitions un-busy
1197 struct UnmarkBusyVisitor : KPartitionVisitor {
1198 virtual bool VisitPre(KPartition *partition)
1200 partition->ClearFlags(B_PARTITION_BUSY
1201 | B_PARTITION_DESCENDANT_BUSY);
1202 return false;
1204 } visitor;
1205 device->VisitEachDescendant(&visitor);
1206 // Iterate through all job queues and all jobs scheduled or in
1207 // progress and mark their scope busy.
1208 for (int32 cookie = 0;
1209 KDiskDeviceJobQueue *jobQueue = NextJobQueue(&cookie); ) {
1210 if (jobQueue->Device() != device)
1211 continue;
1212 for (int32 i = jobQueue->ActiveJobIndex();
1213 KDiskDeviceJob *job = jobQueue->JobAt(i); i++) {
1214 if (job->Status() != B_DISK_DEVICE_JOB_IN_PROGRESS
1215 && job->Status() != B_DISK_DEVICE_JOB_SCHEDULED) {
1216 continue;
1218 KPartition *partition = FindPartition(job->ScopeID());
1219 if (!partition || partition->Device() != device)
1220 continue;
1221 partition->AddFlags(B_PARTITION_BUSY);
1224 // mark all anscestors of busy partitions descendant busy and all
1225 // descendants busy
1226 struct MarkBusyVisitor : KPartitionVisitor {
1227 virtual bool VisitPre(KPartition *partition)
1229 // parent busy => child busy
1230 if (partition->Parent() && partition->Parent()->IsBusy())
1231 partition->AddFlags(B_PARTITION_BUSY);
1232 return false;
1235 virtual bool VisitPost(KPartition *partition)
1237 // child [descendant] busy => parent descendant busy
1238 if ((partition->IsBusy() || partition->IsDescendantBusy())
1239 && partition->Parent()) {
1240 partition->Parent()->AddFlags(B_PARTITION_DESCENDANT_BUSY);
1242 return false;
1244 } visitor2;
1245 device->VisitEachDescendant(&visitor2);
1246 return B_OK;
1248 #endif
1251 status_t
1252 KDiskDeviceManager::_Scan(const char* path)
1254 DBG(OUT("KDiskDeviceManager::_Scan(%s)\n", path));
1255 status_t error = B_ENTRY_NOT_FOUND;
1256 struct stat st;
1257 if (lstat(path, &st) < 0) {
1258 return errno;
1260 if (S_ISDIR(st.st_mode)) {
1261 // a directory: iterate through its contents
1262 DIR* dir = opendir(path);
1263 if (!dir)
1264 return errno;
1265 while (dirent* entry = readdir(dir)) {
1266 // skip "." and ".."
1267 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
1268 continue;
1269 KPath entryPath;
1270 if (entryPath.SetPath(path) != B_OK
1271 || entryPath.Append(entry->d_name) != B_OK) {
1272 continue;
1274 if (_Scan(entryPath.Path()) == B_OK)
1275 error = B_OK;
1277 closedir(dir);
1278 } else {
1279 // not a directory
1280 // check, if it is named "raw"
1281 int32 len = strlen(path);
1282 int32 leafLen = strlen("/raw");
1283 if (len <= leafLen || strcmp(path + len - leafLen, "/raw"))
1284 return B_ERROR;
1285 if (FindDevice(path) != NULL) {
1286 // we already know this device
1287 return B_OK;
1290 DBG(OUT(" found device: %s\n", path));
1291 // create a KDiskDevice for it
1292 KDiskDevice* device = new(nothrow) KDiskDevice;
1293 if (!device)
1294 return B_NO_MEMORY;
1296 // init the KDiskDevice
1297 error = device->SetTo(path);
1298 // add the device
1299 if (error == B_OK && !_AddDevice(device))
1300 error = B_NO_MEMORY;
1301 // cleanup on error
1302 if (error != B_OK)
1303 delete device;
1305 return error;
1310 The device must be write locked, the manager must be locked.
1312 status_t
1313 KDiskDeviceManager::_ScanPartition(KPartition* partition, bool async,
1314 DiskSystemMap* restrictScan)
1316 // TODO: There's no reason why the manager needs to be locked anymore.
1317 if (!partition)
1318 return B_BAD_VALUE;
1320 // TODO: Reimplement asynchronous scanning, if we really need it.
1321 #if 0
1322 if (async) {
1323 // create a new job queue for the device
1324 KDiskDeviceJobQueue *jobQueue = new(nothrow) KDiskDeviceJobQueue;
1325 if (!jobQueue)
1326 return B_NO_MEMORY;
1327 jobQueue->SetDevice(partition->Device());
1329 // create a job for scanning the device and add it to the job queue
1330 KDiskDeviceJob *job = fJobFactory->CreateScanPartitionJob(partition->ID());
1331 if (!job) {
1332 delete jobQueue;
1333 return B_NO_MEMORY;
1336 if (!jobQueue->AddJob(job)) {
1337 delete jobQueue;
1338 delete job;
1339 return B_NO_MEMORY;
1342 // add the job queue
1343 status_t error = AddJobQueue(jobQueue);
1344 if (error != B_OK)
1345 delete jobQueue;
1347 return error;
1349 #endif
1351 // scan synchronously
1353 return _ScanPartition(partition, restrictScan);
1357 status_t
1358 KDiskDeviceManager::_ScanPartition(KPartition* partition,
1359 DiskSystemMap* restrictScan)
1361 // the partition's device must be write-locked
1362 if (partition == NULL)
1363 return B_BAD_VALUE;
1364 if (!partition->Device()->HasMedia() || partition->IsMounted())
1365 return B_OK;
1367 if (partition->CountChildren() > 0) {
1368 // Since this partition has already children, we don't scan it
1369 // again, but only its children.
1370 for (int32 i = 0; KPartition* child = partition->ChildAt(i); i++) {
1371 _ScanPartition(child, restrictScan);
1373 return B_OK;
1376 // This happens with some copy protected CDs. Just ignore the partition...
1377 if (partition->Offset() < 0)
1378 return B_BAD_DATA;
1380 DBG(
1381 KPath partitionPath;
1382 partition->GetPath(&partitionPath);
1383 OUT("KDiskDeviceManager::_ScanPartition(%s)\n", partitionPath.Path());
1386 // publish the partition
1387 status_t error = B_OK;
1388 if (!partition->IsPublished()) {
1389 error = partition->PublishDevice();
1390 if (error != B_OK)
1391 return error;
1394 DiskSystemMap* diskSystems = restrictScan;
1395 if (diskSystems == NULL)
1396 diskSystems = fDiskSystems;
1398 // find the disk system that returns the best priority for this partition
1399 float bestPriority = partition->DiskSystemPriority();
1400 KDiskSystem* bestDiskSystem = NULL;
1401 void* bestCookie = NULL;
1402 for (DiskSystemMap::Iterator iterator = diskSystems->Begin();
1403 iterator != diskSystems->End(); iterator++) {
1404 KDiskSystem* diskSystem = iterator->Value();
1405 if (diskSystem->Load() != B_OK)
1406 continue;
1408 DBG(OUT(" trying: %s\n", diskSystem->Name()));
1410 void* cookie = NULL;
1411 float priority = diskSystem->Identify(partition, &cookie);
1413 DBG(OUT(" returned: %g\n", priority));
1415 if (priority >= 0 && priority > bestPriority) {
1416 // new best disk system
1417 if (bestDiskSystem) {
1418 bestDiskSystem->FreeIdentifyCookie(partition, bestCookie);
1419 bestDiskSystem->Unload();
1421 bestPriority = priority;
1422 bestDiskSystem = diskSystem;
1423 bestCookie = cookie;
1424 } else {
1425 // disk system doesn't identify the partition or worse than our
1426 // current favorite
1427 if (priority >= 0)
1428 diskSystem->FreeIdentifyCookie(partition, cookie);
1429 diskSystem->Unload();
1433 // now, if we have found a disk system, let it scan the partition
1434 if (bestDiskSystem) {
1435 DBG(OUT(" scanning with: %s\n", bestDiskSystem->Name()));
1436 error = bestDiskSystem->Scan(partition, bestCookie);
1437 bestDiskSystem->FreeIdentifyCookie(partition, bestCookie);
1438 if (error == B_OK) {
1439 partition->SetDiskSystem(bestDiskSystem, bestPriority);
1440 for (int32 i = 0; KPartition* child = partition->ChildAt(i); i++)
1441 _ScanPartition(child, restrictScan);
1442 } else {
1443 // TODO: Handle the error.
1444 DBG(OUT(" scanning failed: %s\n", strerror(error)));
1447 // now we can safely unload the disk system -- it has been loaded by
1448 // the partition(s) and thus will not really be unloaded
1449 bestDiskSystem->Unload();
1450 } else {
1451 // contents not recognized
1452 // nothing to be done -- partitions are created as unrecognized
1455 return error;
1459 status_t
1460 KDiskDeviceManager::_AddRemoveMonitoring(const char* path, bool add)
1462 struct stat st;
1463 if (lstat(path, &st) < 0)
1464 return errno;
1466 status_t error = B_ENTRY_NOT_FOUND;
1467 if (S_ISDIR(st.st_mode)) {
1468 if (add) {
1469 error = add_node_listener(st.st_dev, st.st_ino, B_WATCH_DIRECTORY,
1470 *fDeviceWatcher);
1471 } else {
1472 error = remove_node_listener(st.st_dev, st.st_ino,
1473 *fDeviceWatcher);
1475 if (error != B_OK)
1476 return error;
1478 DIR* dir = opendir(path);
1479 if (!dir)
1480 return errno;
1482 while (dirent* entry = readdir(dir)) {
1483 // skip "." and ".."
1484 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
1485 continue;
1487 KPath entryPath;
1488 if (entryPath.SetPath(path) != B_OK
1489 || entryPath.Append(entry->d_name) != B_OK) {
1490 continue;
1493 if (_AddRemoveMonitoring(entryPath.Path(), add) == B_OK)
1494 error = B_OK;
1496 closedir(dir);
1499 return error;
1503 status_t
1504 KDiskDeviceManager::_CheckMediaStatus()
1506 while (!fTerminating) {
1507 int32 cookie = 0;
1508 while (KDiskDevice* device = RegisterNextDevice(&cookie)) {
1509 PartitionRegistrar _(device, true);
1510 DeviceWriteLocker locker(device);
1512 if (device->IsBusy(true))
1513 continue;
1515 bool hadMedia = device->HasMedia();
1516 bool changedMedia = device->MediaChanged();
1517 device->UpdateMediaStatusIfNeeded();
1519 // Detect if there was any status change since last check.
1520 if ((!device->MediaChanged() && (device->HasMedia() || !hadMedia))
1521 || !(hadMedia != device->HasMedia()
1522 || changedMedia != device->MediaChanged()))
1523 continue;
1525 device->MarkBusy(true);
1526 device->UninitializeMedia();
1528 if (device->MediaChanged()) {
1529 dprintf("Media changed from %s\n", device->Path());
1530 device->UpdateGeometry();
1531 _ScanPartition(device, false);
1532 _NotifyDeviceEvent(device, B_DEVICE_MEDIA_CHANGED,
1533 B_DEVICE_REQUEST_DEVICE);
1534 } else if (!device->HasMedia() && hadMedia) {
1535 dprintf("Media removed from %s\n", device->Path());
1538 device->UnmarkBusy(true);
1541 snooze(1000000);
1544 return 0;
1548 status_t
1549 KDiskDeviceManager::_CheckMediaStatusDaemon(void* self)
1551 return ((KDiskDeviceManager*)self)->_CheckMediaStatus();
1555 void
1556 KDiskDeviceManager::_NotifyDeviceEvent(KDiskDevice* device, int32 event,
1557 uint32 mask)
1559 char messageBuffer[512];
1560 KMessage message;
1561 message.SetTo(messageBuffer, sizeof(messageBuffer), B_DEVICE_UPDATE);
1562 message.AddInt32("event", event);
1563 message.AddInt32("id", device->ID());
1564 message.AddString("device", device->Path());
1566 fNotifications->Notify(message, mask);