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.
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"
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>
29 #include <node_monitor.h>
30 #include <Notifications.h>
31 #include <util/kernel_cpp.h>
48 // directories for partitioning and file system modules
49 static const char* kPartitioningSystemPrefix
= "partitioning_systems";
50 static const char* kFileSystemPrefix
= "file_systems";
54 KDiskDeviceManager
* KDiskDeviceManager::sDefaultManager
= NULL
;
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
82 struct KDiskDeviceManager::PartitionMap
: VectorMap
<partition_id
, KPartition
*,
83 VectorMapEntryStrategy::ImplicitKey
<partition_id
, KPartition
*,
88 struct KDiskDeviceManager::DeviceMap
: VectorMap
<partition_id
, KDiskDevice
*,
89 VectorMapEntryStrategy::ImplicitKey
<partition_id
, KDiskDevice
*,
94 struct KDiskDeviceManager::DiskSystemMap
: VectorMap
<disk_system_id
,
96 VectorMapEntryStrategy::ImplicitKey
<disk_system_id
, KDiskSystem
*,
101 struct KDiskDeviceManager::PartitionSet
: VectorSet
<KPartition
*> {
105 class KDiskDeviceManager::DiskSystemWatcher
: public NotificationListener
{
107 DiskSystemWatcher(KDiskDeviceManager
* 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();
125 KDiskDeviceManager
* fManager
;
129 class KDiskDeviceManager::DeviceWatcher
: public NotificationListener
{
135 virtual ~DeviceWatcher()
139 virtual void EventOccurred(NotificationService
& service
,
140 const KMessage
* event
)
142 int32 opcode
= event
->GetInt32("opcode", -1);
144 case B_ENTRY_CREATED
:
145 case B_ENTRY_REMOVED
:
147 device_event
* deviceEvent
= new(std::nothrow
) device_event
;
148 if (deviceEvent
== NULL
)
151 const char* name
= event
->GetString("name", NULL
);
153 deviceEvent
->name
= strdup(name
);
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);
163 if (vfs_stat_node_ref(deviceEvent
->device
, deviceEvent
->node
,
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);
173 remove_node_listener(deviceEvent
->device
,
174 deviceEvent
->node
, *this);
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
);
186 resume_thread(thread
);
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
) {
211 if (event
->opcode
== B_ENTRY_CREATED
)
212 KDiskDeviceManager::Default()->CreateDevice(path
.Path());
214 KDiskDeviceManager::Default()->DeleteDevice(path
.Path());
223 class KDiskDeviceManager::DiskNotifications
224 : public DefaultUserNotificationService
{
227 : DefaultUserNotificationService("disk devices")
231 virtual ~DiskNotifications()
240 KDiskDeviceManager::KDiskDeviceManager()
242 fDevices(new(nothrow
) DeviceMap
),
243 fPartitions(new(nothrow
) PartitionMap
),
244 fDiskSystems(new(nothrow
) DiskSystemMap
),
245 fObsoletePartitions(new(nothrow
) PartitionSet
),
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
)
257 fNotifications
->Register();
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()
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()));
315 fNotifications
->Unregister();
317 // delete the containers
321 delete fObsoletePartitions
;
326 KDiskDeviceManager::InitCheck() const
328 if (fPartitions
== NULL
|| fDevices
== NULL
|| fDiskSystems
== NULL
329 || fObsoletePartitions
== NULL
|| fNotifications
== NULL
)
336 /*! This creates the system's default DiskDeviceManager.
337 The creation is not thread-safe, and shouldn't be done more than once.
340 KDiskDeviceManager::CreateDefault()
342 if (sDefaultManager
!= NULL
)
345 sDefaultManager
= new(nothrow
) KDiskDeviceManager
;
346 if (sDefaultManager
== NULL
)
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.
357 KDiskDeviceManager::DeleteDefault()
359 delete sDefaultManager
;
360 sDefaultManager
= NULL
;
365 KDiskDeviceManager::Default()
367 return sDefaultManager
;
372 KDiskDeviceManager::Lock()
374 return recursive_lock_lock(&fLock
) == B_OK
;
379 KDiskDeviceManager::Unlock()
381 recursive_lock_unlock(&fLock
);
385 DefaultUserNotificationService
&
386 KDiskDeviceManager::Notifications()
388 return *fNotifications
;
393 KDiskDeviceManager::Notify(const KMessage
& event
, uint32 eventMask
)
395 fNotifications
->Notify(event
, eventMask
);
400 KDiskDeviceManager::FindDevice(const char* path
)
402 for (int32 cookie
= 0; KDiskDevice
* device
= NextDevice(&cookie
); ) {
403 if (device
->Path() && !strcmp(path
, device
->Path()))
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())
423 KDiskDeviceManager::FindPartition(const char* path
)
427 if (partitionPath
.InitCheck() != B_OK
)
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
) {
444 KDiskDeviceManager::FindPartition(partition_id id
)
446 PartitionMap::Iterator iterator
= fPartitions
->Find(id
);
447 if (iterator
!= fPartitions
->End())
448 return iterator
->Value();
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())) {
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
)) {
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
)
490 KDiskDeviceManager::RegisterDevice(partition_id id
, bool deviceOnly
)
492 if (ManagerLocker locker
= this) {
493 if (KDiskDevice
* device
= FindDevice(id
, deviceOnly
)) {
503 KDiskDeviceManager::RegisterNextDevice(int32
* cookie
)
508 if (ManagerLocker locker
= this) {
509 if (KDiskDevice
* device
= NextDevice(cookie
)) {
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();
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
)
540 KDiskDeviceManager::RegisterPartition(partition_id id
)
542 if (ManagerLocker locker
= this) {
543 if (KPartition
* partition
= FindPartition(id
)) {
544 partition
->Register();
553 KDiskDeviceManager::RegisterFileDevice(const char* filePath
)
555 if (ManagerLocker locker
= this) {
556 if (KFileDiskDevice
* device
= FindFileDevice(filePath
)) {
566 KDiskDeviceManager::ReadLockDevice(partition_id id
, bool deviceOnly
)
569 KDiskDevice
* device
= RegisterDevice(id
, deviceOnly
);
573 if (device
->ReadLock())
575 device
->Unregister();
581 KDiskDeviceManager::WriteLockDevice(partition_id id
, bool deviceOnly
)
584 KDiskDevice
* device
= RegisterDevice(id
, deviceOnly
);
588 if (device
->WriteLock())
590 device
->Unregister();
596 KDiskDeviceManager::ReadLockPartition(partition_id id
)
598 // register partition
599 KPartition
* partition
= RegisterPartition(id
);
602 // get and register the device
603 KDiskDevice
* device
= NULL
;
604 if (ManagerLocker locker
= this) {
605 device
= partition
->Device();
610 if (device
&& device
->ReadLock()) {
611 // final check, if the partition still belongs to the device
612 if (partition
->Device() == device
)
614 device
->ReadUnlock();
616 // cleanup on failure
618 device
->Unregister();
619 partition
->Unregister();
625 KDiskDeviceManager::WriteLockPartition(partition_id id
)
627 // register partition
628 KPartition
* partition
= RegisterPartition(id
);
631 // get and register the device
632 KDiskDevice
* device
= NULL
;
633 if (ManagerLocker locker
= this) {
634 device
= partition
->Device();
639 if (device
&& device
->WriteLock()) {
640 // final check, if the partition still belongs to the device
641 if (partition
->Device() == device
)
643 device
->WriteUnlock();
645 // cleanup on failure
647 device
->Unregister();
648 partition
->Unregister();
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);
670 KDiskDeviceManager::CreateDevice(const char* path
, bool* newlyCreated
)
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
681 *newlyCreated
= false;
686 // create a KDiskDevice for it
687 device
= new(nothrow
) KDiskDevice
;
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())
699 if (error
== B_OK
&& !_AddDevice(device
))
704 deviceLocker
.Unlock();
710 // scan for partitions
711 _ScanPartition(device
, false);
712 device
->UnmarkBusy(true);
714 _NotifyDeviceEvent(device
, B_DEVICE_ADDED
,
715 B_DEVICE_REQUEST_DEVICE_LIST
);
718 *newlyCreated
= true;
729 KDiskDeviceManager::DeleteDevice(const char* path
)
731 KDiskDevice
* device
= FindDevice(path
);
733 return B_ENTRY_NOT_FOUND
;
735 PartitionRegistrar
_(device
, false);
736 if (DeviceWriteLocker locker
= device
) {
737 if (_RemoveDevice(device
))
746 KDiskDeviceManager::CreateFileDevice(const char* filePath
, bool* newlyCreated
)
751 // normalize the file path
752 KPath normalizedFilePath
;
753 status_t error
= normalizedFilePath
.SetTo(filePath
, KPath::NORMALIZE
);
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
))) {
763 *newlyCreated
= false;
768 // allocate a KFileDiskDevice
769 device
= new(nothrow
) KFileDiskDevice
;
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())
781 if (error
== B_OK
&& !_AddDevice(device
))
786 _ScanPartition(device
, false);
787 device
->UnmarkBusy(true);
789 _NotifyDeviceEvent(device
, B_DEVICE_ADDED
,
790 B_DEVICE_REQUEST_DEVICE_LIST
);
793 *newlyCreated
= true;
798 // cleanup on failure
799 deviceLocker
.Unlock();
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
))
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
))
838 KDiskDeviceManager::CountDevices()
840 return fDevices
->Count();
845 KDiskDeviceManager::NextDevice(int32
* cookie
)
850 DeviceMap::Iterator it
= fDevices
->FindClose(*cookie
, false);
851 if (it
!= fDevices
->End()) {
852 KDiskDevice
* device
= it
->Value();
853 *cookie
= device
->ID() + 1;
861 KDiskDeviceManager::PartitionAdded(KPartition
* partition
)
863 return partition
&& fPartitions
->Put(partition
->ID(), partition
) == B_OK
;
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();
883 KDiskDeviceManager::DeletePartition(KPartition
* partition
)
885 if (partition
&& partition
->IsObsolete()
886 && partition
->CountReferences() == 0
887 && partition
->PrepareForDeletion()
888 && fObsoletePartitions
->Remove(partition
)) {
897 KDiskDeviceManager::FindDiskSystem(const char* name
, bool byPrettyName
)
899 for (int32 cookie
= 0; KDiskSystem
* diskSystem
= NextDiskSystem(&cookie
);) {
901 if (strcmp(name
, diskSystem
->PrettyName()) == 0)
904 if (strcmp(name
, diskSystem
->Name()) == 0)
913 KDiskDeviceManager::FindDiskSystem(disk_system_id id
)
915 DiskSystemMap::Iterator it
= fDiskSystems
->Find(id
);
916 if (it
!= fDiskSystems
->End())
923 KDiskDeviceManager::CountDiskSystems()
925 return fDiskSystems
->Count();
930 KDiskDeviceManager::NextDiskSystem(int32
* cookie
)
935 DiskSystemMap::Iterator it
= fDiskSystems
->FindClose(*cookie
, false);
936 if (it
!= fDiskSystems
->End()) {
937 KDiskSystem
* diskSystem
= it
->Value();
938 *cookie
= diskSystem
->ID() + 1;
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
)
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
)
972 KDiskDeviceManager::LoadNextDiskSystem(int32
* cookie
)
977 if (ManagerLocker locker
= this) {
978 if (KDiskSystem
* diskSystem
= NextDiskSystem(cookie
)) {
979 if (diskSystem
->Load() == B_OK
) {
980 *cookie
= diskSystem
->ID() + 1;
990 KDiskDeviceManager::InitialDeviceScan()
992 status_t error
= B_ERROR
;
995 if (ManagerLocker locker
= this) {
996 error
= _Scan("/dev/disk");
1001 // scan the devices for partitions
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);
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);
1040 KDiskDeviceManager::_RescanDiskSystems(DiskSystemMap
& addedSystems
,
1043 void* cookie
= open_module_list(fileSystems
1044 ? kFileSystemPrefix
: kPartitioningSystemPrefix
);
1050 if (name
.InitCheck() != B_OK
)
1052 size_t nameLength
= name
.BufferSize();
1053 if (read_next_module_name(cookie
, name
.LockBuffer(),
1054 &nameLength
) != B_OK
) {
1057 name
.UnlockBuffer();
1059 if (FindDiskSystem(name
.Path()))
1063 DBG(OUT("file system: %s\n", name
.Path()));
1064 _AddFileSystem(name
.Path());
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
);
1079 /*! Rescan the existing disk systems. This is called after the boot device
1080 has become available.
1083 KDiskDeviceManager::RescanDiskSystems()
1085 DiskSystemMap addedSystems
;
1089 // rescan for partitioning and file systems
1090 _RescanDiskSystems(addedSystems
, false);
1091 _RescanDiskSystems(addedSystems
, true);
1095 // rescan existing devices with the new disk systems
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);
1116 KDiskDeviceManager::_AddPartitioningSystem(const char* name
)
1121 KDiskSystem
* diskSystem
= new(nothrow
) KPartitioningSystem(name
);
1124 return _AddDiskSystem(diskSystem
);
1129 KDiskDeviceManager::_AddFileSystem(const char* name
)
1134 KDiskSystem
* diskSystem
= new(nothrow
) KFileSystem(name
);
1138 return _AddDiskSystem(diskSystem
);
1143 KDiskDeviceManager::_AddDiskSystem(KDiskSystem
* diskSystem
)
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
)));
1152 error
= fDiskSystems
->Put(diskSystem
->ID(), diskSystem
);
1155 DBG(OUT("KDiskDeviceManager::_AddDiskSystem() done: %s\n",
1162 KDiskDeviceManager::_AddDevice(KDiskDevice
* device
)
1164 if (!device
|| !PartitionAdded(device
))
1166 if (fDevices
->Put(device
->ID(), device
) == B_OK
)
1168 PartitionRemoved(device
);
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
);
1189 The device must be write locked, the manager must be locked.
1192 KDiskDeviceManager::_UpdateBusyPartitions(KDiskDevice
*device
)
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
);
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
)
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
) {
1218 KPartition
*partition
= FindPartition(job
->ScopeID());
1219 if (!partition
|| partition
->Device() != device
)
1221 partition
->AddFlags(B_PARTITION_BUSY
);
1224 // mark all anscestors of busy partitions descendant busy and all
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
);
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
);
1245 device
->VisitEachDescendant(&visitor2
);
1252 KDiskDeviceManager::_Scan(const char* path
)
1254 DBG(OUT("KDiskDeviceManager::_Scan(%s)\n", path
));
1255 status_t error
= B_ENTRY_NOT_FOUND
;
1257 if (lstat(path
, &st
) < 0) {
1260 if (S_ISDIR(st
.st_mode
)) {
1261 // a directory: iterate through its contents
1262 DIR* dir
= opendir(path
);
1265 while (dirent
* entry
= readdir(dir
)) {
1266 // skip "." and ".."
1267 if (!strcmp(entry
->d_name
, ".") || !strcmp(entry
->d_name
, ".."))
1270 if (entryPath
.SetPath(path
) != B_OK
1271 || entryPath
.Append(entry
->d_name
) != B_OK
) {
1274 if (_Scan(entryPath
.Path()) == B_OK
)
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"))
1285 if (FindDevice(path
) != NULL
) {
1286 // we already know this device
1290 DBG(OUT(" found device: %s\n", path
));
1291 // create a KDiskDevice for it
1292 KDiskDevice
* device
= new(nothrow
) KDiskDevice
;
1296 // init the KDiskDevice
1297 error
= device
->SetTo(path
);
1299 if (error
== B_OK
&& !_AddDevice(device
))
1300 error
= B_NO_MEMORY
;
1310 The device must be write locked, the manager must be locked.
1313 KDiskDeviceManager::_ScanPartition(KPartition
* partition
, bool async
,
1314 DiskSystemMap
* restrictScan
)
1316 // TODO: There's no reason why the manager needs to be locked anymore.
1320 // TODO: Reimplement asynchronous scanning, if we really need it.
1323 // create a new job queue for the device
1324 KDiskDeviceJobQueue
*jobQueue
= new(nothrow
) KDiskDeviceJobQueue
;
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());
1336 if (!jobQueue
->AddJob(job
)) {
1342 // add the job queue
1343 status_t error
= AddJobQueue(jobQueue
);
1351 // scan synchronously
1353 return _ScanPartition(partition
, restrictScan
);
1358 KDiskDeviceManager::_ScanPartition(KPartition
* partition
,
1359 DiskSystemMap
* restrictScan
)
1361 // the partition's device must be write-locked
1362 if (partition
== NULL
)
1364 if (!partition
->Device()->HasMedia() || partition
->IsMounted())
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
);
1376 // This happens with some copy protected CDs. Just ignore the partition...
1377 if (partition
->Offset() < 0)
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();
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
)
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
;
1425 // disk system doesn't identify the partition or worse than our
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
);
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();
1451 // contents not recognized
1452 // nothing to be done -- partitions are created as unrecognized
1460 KDiskDeviceManager::_AddRemoveMonitoring(const char* path
, bool add
)
1463 if (lstat(path
, &st
) < 0)
1466 status_t error
= B_ENTRY_NOT_FOUND
;
1467 if (S_ISDIR(st
.st_mode
)) {
1469 error
= add_node_listener(st
.st_dev
, st
.st_ino
, B_WATCH_DIRECTORY
,
1472 error
= remove_node_listener(st
.st_dev
, st
.st_ino
,
1478 DIR* dir
= opendir(path
);
1482 while (dirent
* entry
= readdir(dir
)) {
1483 // skip "." and ".."
1484 if (!strcmp(entry
->d_name
, ".") || !strcmp(entry
->d_name
, ".."))
1488 if (entryPath
.SetPath(path
) != B_OK
1489 || entryPath
.Append(entry
->d_name
) != B_OK
) {
1493 if (_AddRemoveMonitoring(entryPath
.Path(), add
) == B_OK
)
1504 KDiskDeviceManager::_CheckMediaStatus()
1506 while (!fTerminating
) {
1508 while (KDiskDevice
* device
= RegisterNextDevice(&cookie
)) {
1509 PartitionRegistrar
_(device
, true);
1510 DeviceWriteLocker
locker(device
);
1512 if (device
->IsBusy(true))
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()))
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);
1549 KDiskDeviceManager::_CheckMediaStatusDaemon(void* self
)
1551 return ((KDiskDeviceManager
*)self
)->_CheckMediaStatus();
1556 KDiskDeviceManager::_NotifyDeviceEvent(KDiskDevice
* device
, int32 event
,
1559 char messageBuffer
[512];
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
);