xref: /haiku/src/system/kernel/disk_device_manager/KDiskDeviceManager.cpp (revision acc71e7c7c666824557eacb3bd42a9aa802c1196)
1 /*
2  * Copyright 2004-2009, Haiku, Inc. All rights reserved.
3  * Copyright 2003-2004, Ingo Weinhold, bonefish@cs.tu-berlin.de. All rights reserved.
4  *
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 #include "KDiskDevice.h"
10 #include "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"
19 
20 #include <VectorMap.h>
21 #include <VectorSet.h>
22 
23 #include <DiskDeviceRoster.h>
24 #include <KernelExport.h>
25 #include <NodeMonitor.h>
26 
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>
33 
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>
41 
42 // debugging
43 //#define DBG(x)
44 #define DBG(x) x
45 #define OUT dprintf
46 
47 
48 // directories for partitioning and file system modules
49 static const char* kPartitioningSystemPrefix = "partitioning_systems";
50 static const char* kFileSystemPrefix = "file_systems";
51 
52 
53 // singleton instance
54 KDiskDeviceManager* KDiskDeviceManager::sDefaultManager = NULL;
55 
56 
57 struct device_event {
58 	int32					opcode;
59 	const char*				name;
60 	dev_t					device;
61 	ino_t					directory;
62 	ino_t					node;
63 	NotificationListener*	listener;
64 };
65 
66 
67 struct GetPartitionID {
68 	inline partition_id operator()(const KPartition* partition) const
69 	{
70 		return partition->ID();
71 	}
72 };
73 
74 
75 struct GetDiskSystemID {
76 	inline disk_system_id operator()(const KDiskSystem* system) const
77 	{
78 		return system->ID();
79 	}
80 };
81 
82 
83 struct KDiskDeviceManager::PartitionMap : VectorMap<partition_id, KPartition*,
84 	VectorMapEntryStrategy::ImplicitKey<partition_id, KPartition*,
85 		GetPartitionID> > {
86 };
87 
88 
89 struct KDiskDeviceManager::DeviceMap : VectorMap<partition_id, KDiskDevice*,
90 	VectorMapEntryStrategy::ImplicitKey<partition_id, KDiskDevice*,
91 		GetPartitionID> > {
92 };
93 
94 
95 struct KDiskDeviceManager::DiskSystemMap : VectorMap<disk_system_id,
96 	KDiskSystem*,
97 	VectorMapEntryStrategy::ImplicitKey<disk_system_id, KDiskSystem*,
98 		GetDiskSystemID> > {
99 };
100 
101 
102 struct KDiskDeviceManager::PartitionSet : VectorSet<KPartition*> {
103 };
104 
105 
106 class KDiskDeviceManager::DiskSystemWatcher : public NotificationListener {
107 public:
108 	DiskSystemWatcher(KDiskDeviceManager* manager)
109 		:
110 		fManager(manager)
111 	{
112 	}
113 
114 	virtual ~DiskSystemWatcher()
115 	{
116 	}
117 
118 	virtual void EventOccured(NotificationService& service,
119 		const KMessage* event)
120 	{
121 		if (event->GetInt32("opcode", -1) != B_ENTRY_REMOVED)
122 			fManager->RescanDiskSystems();
123 	}
124 
125 private:
126 	KDiskDeviceManager* fManager;
127 };
128 
129 
130 class KDiskDeviceManager::DeviceWatcher : public NotificationListener {
131 public:
132 	DeviceWatcher()
133 	{
134 	}
135 
136 	virtual ~DeviceWatcher()
137 	{
138 	}
139 
140 	virtual void EventOccured(NotificationService& service,
141 		const KMessage* event)
142 	{
143 		int32 opcode = event->GetInt32("opcode", -1);
144 		switch (opcode) {
145 			case B_ENTRY_CREATED:
146 			case B_ENTRY_REMOVED:
147 			{
148 				device_event* deviceEvent = new(std::nothrow) device_event;
149 				if (deviceEvent == NULL)
150 					break;
151 
152 				const char* name = event->GetString("name", NULL);
153 				if (name != NULL)
154 					deviceEvent->name = strdup(name);
155 				else
156 					deviceEvent->name = NULL;
157 
158 				deviceEvent->opcode = opcode;
159 				deviceEvent->device = event->GetInt32("device", -1);
160 				deviceEvent->directory = event->GetInt64("directory", -1);
161 				deviceEvent->node = event->GetInt64("node", -1);
162 				deviceEvent->listener = this;
163 
164 				// TODO: a real in-kernel DPC mechanism would be preferred...
165 				thread_id thread = spawn_kernel_thread(_HandleDeviceEvent,
166 					"device event", B_NORMAL_PRIORITY, deviceEvent);
167 				if (thread < 0)
168 					delete deviceEvent;
169 				else
170 					resume_thread(thread);
171 				break;
172 			}
173 
174 			default:
175 				break;
176 		}
177 	}
178 
179 	static status_t _HandleDeviceEvent(void* _event)
180 	{
181 		device_event* event = (device_event*)_event;
182 
183 		struct stat st;
184 		if (vfs_stat_node_ref(event->device, event->node, &st) != 0) {
185 			delete event;
186 			return B_ERROR;
187 		}
188 
189 		if (S_ISDIR(st.st_mode)) {
190 			if (event->opcode == B_ENTRY_CREATED) {
191 				add_node_listener(event->device, event->node, B_WATCH_DIRECTORY,
192 					*event->listener);
193 			} else {
194 				remove_node_listener(event->device, event->node,
195 					*event->listener);
196 			}
197 		} else if (strcmp(event->name, "raw") == 0) {
198 			// a new raw device was added/removed
199 			KPath path(B_PATH_NAME_LENGTH + 1);
200 			if (path.InitCheck() != B_OK
201 				|| vfs_entry_ref_to_path(event->device, event->directory,
202 						event->name, path.LockBuffer(),
203 				path.BufferSize()) != B_OK) {
204 				delete event;
205 				return B_ERROR;
206 			}
207 
208 			path.UnlockBuffer();
209 
210 			if (event->opcode == B_ENTRY_CREATED)
211 				KDiskDeviceManager::Default()->CreateDevice(path.Path());
212 			else
213 				KDiskDeviceManager::Default()->DeleteDevice(path.Path());
214 		}
215 
216 		delete event;
217 		return B_OK;
218 	}
219 };
220 
221 
222 class KDiskDeviceManager::DiskNotifications
223 	: public DefaultUserNotificationService {
224 public:
225 	DiskNotifications()
226 		: DefaultUserNotificationService("disk devices")
227 	{
228 	}
229 
230 	virtual ~DiskNotifications()
231 	{
232 	}
233 };
234 
235 
236 //	#pragma mark -
237 
238 
239 KDiskDeviceManager::KDiskDeviceManager()
240 	:
241 	fLock("disk device manager"),
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)
251 {
252 	if (InitCheck() != B_OK)
253 		return;
254 
255 	RescanDiskSystems();
256 
257 	fMediaChecker = spawn_kernel_thread(_CheckMediaStatusDaemon,
258 		"media checker", B_NORMAL_PRIORITY, this);
259 	if (fMediaChecker >= 0)
260 		resume_thread(fMediaChecker);
261 
262 	DBG(OUT("number of disk systems: %ld\n", CountDiskSystems()));
263 	// TODO: Watch the disk systems and the relevant directories.
264 }
265 
266 
267 KDiskDeviceManager::~KDiskDeviceManager()
268 {
269 	fTerminating = true;
270 
271 	status_t result;
272 	wait_for_thread(fMediaChecker, &result);
273 
274 	// stop all node monitoring
275 	_AddRemoveMonitoring("/dev/disk", false);
276 	delete fDeviceWatcher;
277 
278 	// remove all devices
279 	for (int32 cookie = 0; KDiskDevice* device = NextDevice(&cookie);) {
280 		PartitionRegistrar _(device);
281 		_RemoveDevice(device);
282 	}
283 
284 	// some sanity checks
285 	if (fPartitions->Count() > 0) {
286 		DBG(OUT("WARNING: There are still %ld unremoved partitions!\n",
287 			fPartitions->Count()));
288 		for (PartitionMap::Iterator it = fPartitions->Begin();
289 				it != fPartitions->End(); ++it) {
290 			DBG(OUT("         partition: %ld\n", it->Value()->ID()));
291 		}
292 	}
293 	if (fObsoletePartitions->Count() > 0) {
294 		DBG(OUT("WARNING: There are still %ld obsolete partitions!\n",
295 				fObsoletePartitions->Count()));
296 		for (PartitionSet::Iterator it = fObsoletePartitions->Begin();
297 				it != fObsoletePartitions->End(); ++it) {
298 			DBG(OUT("         partition: %ld\n", (*it)->ID()));
299 		}
300 	}
301 	// remove all disk systems
302 	for (int32 cookie = 0; KDiskSystem* diskSystem = NextDiskSystem(&cookie);) {
303 		fDiskSystems->Remove(diskSystem->ID());
304 		if (diskSystem->IsLoaded()) {
305 			DBG(OUT("WARNING: Disk system `%s' (%ld) is still loaded!\n",
306 				diskSystem->Name(), diskSystem->ID()));
307 		} else
308 			delete diskSystem;
309 	}
310 
311 	// delete the containers
312 	delete fPartitions;
313 	delete fDevices;
314 	delete fDiskSystems;
315 	delete fObsoletePartitions;
316 }
317 
318 
319 status_t
320 KDiskDeviceManager::InitCheck() const
321 {
322 	if (fPartitions == NULL || fDevices == NULL || fDiskSystems == NULL
323 		|| fObsoletePartitions == NULL || fNotifications == NULL)
324 		return B_NO_MEMORY;
325 
326 	return fLock.Sem() >= 0 ? B_OK : fLock.Sem();
327 }
328 
329 
330 /*!	This creates the system's default DiskDeviceManager.
331 	The creation is not thread-safe, and shouldn't be done more than once.
332 */
333 status_t
334 KDiskDeviceManager::CreateDefault()
335 {
336 	if (sDefaultManager != NULL)
337 		return B_OK;
338 
339 	sDefaultManager = new(nothrow) KDiskDeviceManager;
340 	if (sDefaultManager == NULL)
341 		return B_NO_MEMORY;
342 
343 	return sDefaultManager->InitCheck();
344 }
345 
346 
347 /*!	This deletes the default DiskDeviceManager. The deletion is not
348 	thread-safe either, you should make sure that it's called only once.
349 */
350 void
351 KDiskDeviceManager::DeleteDefault()
352 {
353 	delete sDefaultManager;
354 	sDefaultManager = NULL;
355 }
356 
357 
358 KDiskDeviceManager*
359 KDiskDeviceManager::Default()
360 {
361 	return sDefaultManager;
362 }
363 
364 
365 bool
366 KDiskDeviceManager::Lock()
367 {
368 	return fLock.Lock();
369 }
370 
371 
372 void
373 KDiskDeviceManager::Unlock()
374 {
375 	fLock.Unlock();
376 }
377 
378 
379 DefaultUserNotificationService&
380 KDiskDeviceManager::Notifications()
381 {
382 	return *fNotifications;
383 }
384 
385 
386 void
387 KDiskDeviceManager::Notify(const KMessage& event, uint32 eventMask)
388 {
389 	fNotifications->Notify(event, eventMask);
390 }
391 
392 
393 KDiskDevice*
394 KDiskDeviceManager::FindDevice(const char* path)
395 {
396 	for (int32 cookie = 0; KDiskDevice* device = NextDevice(&cookie); ) {
397 		if (device->Path() && !strcmp(path, device->Path()))
398 			return device;
399 	}
400 	return NULL;
401 }
402 
403 
404 KDiskDevice*
405 KDiskDeviceManager::FindDevice(partition_id id, bool deviceOnly)
406 {
407 	if (KPartition* partition = FindPartition(id)) {
408 		KDiskDevice* device = partition->Device();
409 		if (!deviceOnly || id == device->ID())
410 			return device;
411 	}
412 	return NULL;
413 }
414 
415 
416 KPartition*
417 KDiskDeviceManager::FindPartition(const char* path)
418 {
419 	// TODO: Optimize!
420 	KPath partitionPath;
421 	if (partitionPath.InitCheck() != B_OK)
422 		return NULL;
423 
424 	for (PartitionMap::Iterator iterator = fPartitions->Begin();
425 			iterator != fPartitions->End(); ++iterator) {
426 		KPartition* partition = iterator->Value();
427 		if (partition->GetPath(&partitionPath) == B_OK
428 			&& partitionPath == path) {
429 			return partition;
430 		}
431 	}
432 
433 	return NULL;
434 }
435 
436 
437 KPartition*
438 KDiskDeviceManager::FindPartition(partition_id id)
439 {
440 	PartitionMap::Iterator iterator = fPartitions->Find(id);
441 	if (iterator != fPartitions->End())
442 		return iterator->Value();
443 
444 	return NULL;
445 }
446 
447 
448 KFileDiskDevice*
449 KDiskDeviceManager::FindFileDevice(const char* filePath)
450 {
451 	for (int32 cookie = 0; KDiskDevice* device = NextDevice(&cookie); ) {
452 		KFileDiskDevice* fileDevice = dynamic_cast<KFileDiskDevice*>(device);
453 		if (fileDevice && fileDevice->FilePath()
454 			&& !strcmp(filePath, fileDevice->FilePath())) {
455 			return fileDevice;
456 		}
457 	}
458 	return NULL;
459 }
460 
461 
462 KDiskDevice*
463 KDiskDeviceManager::RegisterDevice(const char* path)
464 {
465 	if (ManagerLocker locker = this) {
466 		for (int32 i = 0; i < 2; i++) {
467 			if (KDiskDevice* device = FindDevice(path)) {
468 				device->Register();
469 				return device;
470 			}
471 
472 			// if the device is not known yet, create it and try again
473 			const char* leaf = strrchr(path, '/');
474 			if (i == 0 && !strncmp(path, "/dev/disk", 9)
475 				&& !strcmp(leaf + 1, "raw") && CreateDevice(path) < B_OK)
476 				break;
477 		}
478 	}
479 	return NULL;
480 }
481 
482 
483 KDiskDevice*
484 KDiskDeviceManager::RegisterDevice(partition_id id, bool deviceOnly)
485 {
486 	if (ManagerLocker locker = this) {
487 		if (KDiskDevice* device = FindDevice(id, deviceOnly)) {
488 			device->Register();
489 			return device;
490 		}
491 	}
492 	return NULL;
493 }
494 
495 
496 KDiskDevice*
497 KDiskDeviceManager::RegisterNextDevice(int32* cookie)
498 {
499 	if (!cookie)
500 		return NULL;
501 
502 	if (ManagerLocker locker = this) {
503 		if (KDiskDevice* device = NextDevice(cookie)) {
504 			device->Register();
505 			return device;
506 		}
507 	}
508 	return NULL;
509 }
510 
511 
512 KPartition*
513 KDiskDeviceManager::RegisterPartition(const char* path)
514 {
515 	if (ManagerLocker locker = this) {
516 		for (int32 i = 0; i < 2; i++) {
517 			if (KPartition* partition = FindPartition(path)) {
518 				partition->Register();
519 				return partition;
520 			}
521 
522 			// if the device is not known yet, create it and try again
523 			const char* leaf = strrchr(path, '/');
524 			if (i == 0 && !strncmp(path, "/dev/disk", 9)
525 				&& !strcmp(leaf + 1, "raw") && CreateDevice(path) < B_OK)
526 				break;
527 		}
528 	}
529 	return NULL;
530 }
531 
532 
533 KPartition*
534 KDiskDeviceManager::RegisterPartition(partition_id id)
535 {
536 	if (ManagerLocker locker = this) {
537 		if (KPartition* partition = FindPartition(id)) {
538 			partition->Register();
539 			return partition;
540 		}
541 	}
542 	return NULL;
543 }
544 
545 
546 KFileDiskDevice*
547 KDiskDeviceManager::RegisterFileDevice(const char* filePath)
548 {
549 	if (ManagerLocker locker = this) {
550 		if (KFileDiskDevice* device = FindFileDevice(filePath)) {
551 			device->Register();
552 			return device;
553 		}
554 	}
555 	return NULL;
556 }
557 
558 
559 KDiskDevice*
560 KDiskDeviceManager::ReadLockDevice(partition_id id, bool deviceOnly)
561 {
562 	// register device
563 	KDiskDevice* device = RegisterDevice(id, deviceOnly);
564 	if (!device)
565 		return NULL;
566 	// lock device
567 	if (device->ReadLock())
568 		return device;
569 	device->Unregister();
570 	return NULL;
571 }
572 
573 
574 KDiskDevice*
575 KDiskDeviceManager::WriteLockDevice(partition_id id, bool deviceOnly)
576 {
577 	// register device
578 	KDiskDevice* device = RegisterDevice(id, deviceOnly);
579 	if (!device)
580 		return NULL;
581 	// lock device
582 	if (device->WriteLock())
583 		return device;
584 	device->Unregister();
585 	return NULL;
586 }
587 
588 
589 KPartition*
590 KDiskDeviceManager::ReadLockPartition(partition_id id)
591 {
592 	// register partition
593 	KPartition* partition = RegisterPartition(id);
594 	if (!partition)
595 		return NULL;
596 	// get and register the device
597 	KDiskDevice* device = NULL;
598 	if (ManagerLocker locker = this) {
599 		device = partition->Device();
600 		if (device)
601 			device->Register();
602 	}
603 	// lock the device
604 	if (device && device->ReadLock()) {
605 		// final check, if the partition still belongs to the device
606 		if (partition->Device() == device)
607 			return partition;
608 		device->ReadUnlock();
609 	}
610 	// cleanup on failure
611 	if (device)
612 		device->Unregister();
613 	partition->Unregister();
614 	return NULL;
615 }
616 
617 
618 KPartition*
619 KDiskDeviceManager::WriteLockPartition(partition_id id)
620 {
621 	// register partition
622 	KPartition* partition = RegisterPartition(id);
623 	if (!partition)
624 		return NULL;
625 	// get and register the device
626 	KDiskDevice* device = NULL;
627 	if (ManagerLocker locker = this) {
628 		device = partition->Device();
629 		if (device)
630 			device->Register();
631 	}
632 	// lock the device
633 	if (device && device->WriteLock()) {
634 		// final check, if the partition still belongs to the device
635 		if (partition->Device() == device)
636 			return partition;
637 		device->WriteUnlock();
638 	}
639 	// cleanup on failure
640 	if (device)
641 		device->Unregister();
642 	partition->Unregister();
643 	return NULL;
644 }
645 
646 
647 status_t
648 KDiskDeviceManager::ScanPartition(KPartition* partition)
649 {
650 // TODO: This won't do. Locking the DDM while scanning the partition is not a
651 // good idea. Even locking the device doesn't feel right. Marking the partition
652 // busy and passing the disk system a temporary clone of the partition_data
653 // should work as well.
654 	if (DeviceWriteLocker deviceLocker = partition->Device()) {
655 		if (ManagerLocker locker = this)
656 			return _ScanPartition(partition, false);
657 	}
658 
659 	return B_ERROR;
660 }
661 
662 
663 partition_id
664 KDiskDeviceManager::CreateDevice(const char* path, bool* newlyCreated)
665 {
666 	if (!path)
667 		return B_BAD_VALUE;
668 
669 	status_t error = B_ERROR;
670 	if (ManagerLocker locker = this) {
671 		KDiskDevice* device = FindDevice(path);
672 		if (device != NULL) {
673 			// we already know this device
674 			if (newlyCreated)
675 				*newlyCreated = false;
676 
677 			return device->ID();
678 		}
679 
680 		// create a KDiskDevice for it
681 		device = new(nothrow) KDiskDevice;
682 		if (!device)
683 			return B_NO_MEMORY;
684 
685 		// initialize and add the device
686 		error = device->SetTo(path);
687 
688 		// Note: Here we are allowed to lock a device although already having
689 		// the manager locked, since it is not yet added to the manager.
690 		DeviceWriteLocker deviceLocker(device);
691 		if (error == B_OK && !deviceLocker.IsLocked())
692 			error = B_ERROR;
693 		if (error == B_OK && !_AddDevice(device))
694 			error = B_NO_MEMORY;
695 
696 		// cleanup on error
697 		if (error != B_OK) {
698 			delete device;
699 			return error;
700 		}
701 
702 		if (error == B_OK) {
703 			// scan for partitions
704 			_ScanPartition(device, false);
705 			device->UnmarkBusy(true);
706 
707 			_NotifyDeviceEvent(device, B_DEVICE_ADDED,
708 				B_DEVICE_REQUEST_DEVICE_LIST);
709 
710 			if (newlyCreated)
711 				*newlyCreated = true;
712 
713 			return device->ID();
714 		}
715 	}
716 
717 	return error;
718 }
719 
720 
721 status_t
722 KDiskDeviceManager::DeleteDevice(const char* path)
723 {
724 	KDiskDevice* device = FindDevice(path);
725 	if (device == NULL)
726 		return B_ENTRY_NOT_FOUND;
727 
728 	PartitionRegistrar _(device, false);
729 	if (DeviceWriteLocker locker = device) {
730 		if (_RemoveDevice(device))
731 			return B_OK;
732 	}
733 
734 	return B_ERROR;
735 }
736 
737 
738 partition_id
739 KDiskDeviceManager::CreateFileDevice(const char* filePath, bool* newlyCreated)
740 {
741 	if (!filePath)
742 		return B_BAD_VALUE;
743 
744 	// normalize the file path
745 	KPath normalizedFilePath;
746 	status_t error = normalizedFilePath.SetTo(filePath, true);
747 	if (error != B_OK)
748 		return error;
749 	filePath = normalizedFilePath.Path();
750 
751 	KFileDiskDevice* device = NULL;
752 	if (ManagerLocker locker = this) {
753 		// check, if the device does already exist
754 		if ((device = FindFileDevice(filePath))) {
755 			if (newlyCreated)
756 				*newlyCreated = false;
757 
758 			return device->ID();
759 		}
760 
761 		// allocate a KFileDiskDevice
762 		device = new(nothrow) KFileDiskDevice;
763 		if (!device)
764 			return B_NO_MEMORY;
765 
766 		// initialize and add the device
767 		error = device->SetTo(filePath);
768 
769 		// Note: Here we are allowed to lock a device although already having
770 		// the manager locked, since it is not yet added to the manager.
771 		DeviceWriteLocker deviceLocker(device);
772 		if (error == B_OK && !deviceLocker.IsLocked())
773 			error = B_ERROR;
774 		if (error == B_OK && !_AddDevice(device))
775 			error = B_NO_MEMORY;
776 
777 		// scan device
778 		if (error == B_OK) {
779 			_ScanPartition(device, false);
780 			device->UnmarkBusy(true);
781 
782 			_NotifyDeviceEvent(device, B_DEVICE_ADDED,
783 				B_DEVICE_REQUEST_DEVICE_LIST);
784 
785 			if (newlyCreated)
786 				*newlyCreated = true;
787 
788 			return device->ID();
789 		}
790 
791 		// cleanup on failure
792 		delete device;
793 	} else
794 		error = B_ERROR;
795 	return error;
796 }
797 
798 
799 status_t
800 KDiskDeviceManager::DeleteFileDevice(const char* filePath)
801 {
802 	if (KFileDiskDevice* device = RegisterFileDevice(filePath)) {
803 		PartitionRegistrar _(device, true);
804 		if (DeviceWriteLocker locker = device) {
805 			if (_RemoveDevice(device))
806 				return B_OK;
807 		}
808 	}
809 	return B_ERROR;
810 }
811 
812 
813 status_t
814 KDiskDeviceManager::DeleteFileDevice(partition_id id)
815 {
816 	if (KDiskDevice* device = RegisterDevice(id)) {
817 		PartitionRegistrar _(device, true);
818 		if (!dynamic_cast<KFileDiskDevice*>(device) || id != device->ID())
819 			return B_ENTRY_NOT_FOUND;
820 		if (DeviceWriteLocker locker = device) {
821 			if (_RemoveDevice(device))
822 				return B_OK;
823 		}
824 	}
825 	return B_ERROR;
826 }
827 
828 
829 int32
830 KDiskDeviceManager::CountDevices()
831 {
832 	return fDevices->Count();
833 }
834 
835 
836 KDiskDevice*
837 KDiskDeviceManager::NextDevice(int32* cookie)
838 {
839 	if (!cookie)
840 		return NULL;
841 
842 	DeviceMap::Iterator it = fDevices->FindClose(*cookie, false);
843 	if (it != fDevices->End()) {
844 		KDiskDevice* device = it->Value();
845 		*cookie = device->ID() + 1;
846 		return device;
847 	}
848 	return NULL;
849 }
850 
851 
852 bool
853 KDiskDeviceManager::PartitionAdded(KPartition* partition)
854 {
855 	return partition && fPartitions->Put(partition->ID(), partition) == B_OK;
856 }
857 
858 
859 bool
860 KDiskDeviceManager::PartitionRemoved(KPartition* partition)
861 {
862 	if (partition && partition->PrepareForRemoval()
863 		&& fPartitions->Remove(partition->ID())) {
864 		// TODO: If adding the partition to the obsolete list fails (due to lack
865 		// of memory), we can't do anything about it. We will leak memory then.
866 		fObsoletePartitions->Insert(partition);
867 		partition->MarkObsolete();
868 		return true;
869 	}
870 	return false;
871 }
872 
873 
874 bool
875 KDiskDeviceManager::DeletePartition(KPartition* partition)
876 {
877 	if (partition && partition->IsObsolete()
878 		&& partition->CountReferences() == 0
879 		&& partition->PrepareForDeletion()
880 		&& fObsoletePartitions->Remove(partition)) {
881 		delete partition;
882 		return true;
883 	}
884 	return false;
885 }
886 
887 
888 KDiskSystem*
889 KDiskDeviceManager::FindDiskSystem(const char* name, bool byPrettyName)
890 {
891 	for (int32 cookie = 0; KDiskSystem* diskSystem = NextDiskSystem(&cookie);) {
892 		if (byPrettyName) {
893 			if (strcmp(name, diskSystem->PrettyName()) == 0)
894 				return diskSystem;
895 		} else {
896 			if (strcmp(name, diskSystem->Name()) == 0)
897 				return diskSystem;
898 		}
899 	}
900 	return NULL;
901 }
902 
903 
904 KDiskSystem*
905 KDiskDeviceManager::FindDiskSystem(disk_system_id id)
906 {
907 	DiskSystemMap::Iterator it = fDiskSystems->Find(id);
908 	if (it != fDiskSystems->End())
909 		return it->Value();
910 	return NULL;
911 }
912 
913 
914 int32
915 KDiskDeviceManager::CountDiskSystems()
916 {
917 	return fDiskSystems->Count();
918 }
919 
920 
921 KDiskSystem*
922 KDiskDeviceManager::NextDiskSystem(int32* cookie)
923 {
924 	if (!cookie)
925 		return NULL;
926 
927 	DiskSystemMap::Iterator it = fDiskSystems->FindClose(*cookie, false);
928 	if (it != fDiskSystems->End()) {
929 		KDiskSystem* diskSystem = it->Value();
930 		*cookie = diskSystem->ID() + 1;
931 		return diskSystem;
932 	}
933 	return NULL;
934 }
935 
936 
937 KDiskSystem*
938 KDiskDeviceManager::LoadDiskSystem(const char* name, bool byPrettyName)
939 {
940 	KDiskSystem* diskSystem = NULL;
941 	if (ManagerLocker locker = this) {
942 		diskSystem = FindDiskSystem(name, byPrettyName);
943 		if (diskSystem && diskSystem->Load() != B_OK)
944 			diskSystem = NULL;
945 	}
946 	return diskSystem;
947 }
948 
949 
950 KDiskSystem*
951 KDiskDeviceManager::LoadDiskSystem(disk_system_id id)
952 {
953 	KDiskSystem* diskSystem = NULL;
954 	if (ManagerLocker locker = this) {
955 		diskSystem = FindDiskSystem(id);
956 		if (diskSystem && diskSystem->Load() != B_OK)
957 			diskSystem = NULL;
958 	}
959 	return diskSystem;
960 }
961 
962 
963 KDiskSystem*
964 KDiskDeviceManager::LoadNextDiskSystem(int32* cookie)
965 {
966 	if (!cookie)
967 		return NULL;
968 
969 	if (ManagerLocker locker = this) {
970 		if (KDiskSystem* diskSystem = NextDiskSystem(cookie)) {
971 			if (diskSystem->Load() == B_OK) {
972 				*cookie = diskSystem->ID() + 1;
973 				return diskSystem;
974 			}
975 		}
976 	}
977 	return NULL;
978 }
979 
980 
981 status_t
982 KDiskDeviceManager::InitialDeviceScan()
983 {
984 	status_t error = B_ERROR;
985 
986 	// scan for devices
987 	if (ManagerLocker locker = this) {
988 		error = _Scan("/dev/disk");
989 		if (error != B_OK)
990 			return error;
991 	}
992 
993 	// scan the devices for partitions
994 	int32 cookie = 0;
995 	while (KDiskDevice* device = RegisterNextDevice(&cookie)) {
996 		PartitionRegistrar _(device, true);
997 		if (DeviceWriteLocker deviceLocker = device) {
998 			if (ManagerLocker locker = this) {
999 				error = _ScanPartition(device, false);
1000 				device->UnmarkBusy(true);
1001 				if (error != B_OK)
1002 					break;
1003 			} else
1004 				return B_ERROR;
1005 		} else
1006 			return B_ERROR;
1007 	}
1008 	return error;
1009 }
1010 
1011 
1012 status_t
1013 KDiskDeviceManager::StartMonitoring()
1014 {
1015 	// do another scan, this will populate the devfs directories
1016 	InitialDeviceScan();
1017 
1018 	// start monitoring the disk systems
1019 	fDiskSystemWatcher = new(std::nothrow) DiskSystemWatcher(this);
1020 	if (fDiskSystemWatcher != NULL) {
1021 		start_watching_modules(kFileSystemPrefix, *fDiskSystemWatcher);
1022 		start_watching_modules(kPartitioningSystemPrefix,
1023 			*fDiskSystemWatcher);
1024 	}
1025 
1026 	// start monitoring all dirs under /dev/disk
1027 	return _AddRemoveMonitoring("/dev/disk", true);
1028 }
1029 
1030 
1031 status_t
1032 KDiskDeviceManager::_RescanDiskSystems(DiskSystemMap& addedSystems,
1033 	bool fileSystems)
1034 {
1035 	void* cookie = open_module_list(fileSystems
1036 		? kFileSystemPrefix : kPartitioningSystemPrefix);
1037 	if (cookie == NULL)
1038 		return B_NO_MEMORY;
1039 
1040 	while (true) {
1041 		KPath name;
1042 		if (name.InitCheck() != B_OK)
1043 			break;
1044 		size_t nameLength = name.BufferSize();
1045 		if (read_next_module_name(cookie, name.LockBuffer(),
1046 				&nameLength) != B_OK) {
1047 			break;
1048 		}
1049 		name.UnlockBuffer();
1050 
1051 		if (FindDiskSystem(name.Path()))
1052 			continue;
1053 
1054 		if (fileSystems) {
1055 			DBG(OUT("file system: %s\n", name.Path()));
1056 			_AddFileSystem(name.Path());
1057 		} else {
1058 			DBG(OUT("partitioning system: %s\n", name.Path()));
1059 			_AddPartitioningSystem(name.Path());
1060 		}
1061 
1062 		if (KDiskSystem* system = FindDiskSystem(name.Path()))
1063 			addedSystems.Put(system->ID(), system);
1064 	}
1065 
1066 	close_module_list(cookie);
1067 	return B_OK;
1068 }
1069 
1070 
1071 /*!	Rescan the existing disk systems. This is called after the boot device
1072 	has become available.
1073 */
1074 status_t
1075 KDiskDeviceManager::RescanDiskSystems()
1076 {
1077 	DiskSystemMap addedSystems;
1078 
1079 	Lock();
1080 
1081 	// rescan for partitioning and file systems
1082 	_RescanDiskSystems(addedSystems, false);
1083 	_RescanDiskSystems(addedSystems, true);
1084 
1085 	Unlock();
1086 
1087 	// rescan existing devices with the new disk systems
1088 	int32 cookie = 0;
1089 	while (KDiskDevice* device = RegisterNextDevice(&cookie)) {
1090 		PartitionRegistrar _(device, true);
1091 		if (DeviceWriteLocker deviceLocker = device) {
1092 			if (ManagerLocker locker = this) {
1093 				status_t status = _ScanPartition(device, false, &addedSystems);
1094 				device->UnmarkBusy(true);
1095 				if (status != B_OK)
1096 					break;
1097 			} else
1098 				return B_ERROR;
1099 		} else
1100 			return B_ERROR;
1101 	}
1102 
1103 	return B_OK;
1104 }
1105 
1106 
1107 status_t
1108 KDiskDeviceManager::_AddPartitioningSystem(const char* name)
1109 {
1110 	if (!name)
1111 		return B_BAD_VALUE;
1112 
1113 	KDiskSystem* diskSystem = new(nothrow) KPartitioningSystem(name);
1114 	if (!diskSystem)
1115 		return B_NO_MEMORY;
1116 	return _AddDiskSystem(diskSystem);
1117 }
1118 
1119 
1120 status_t
1121 KDiskDeviceManager::_AddFileSystem(const char* name)
1122 {
1123 	if (!name)
1124 		return B_BAD_VALUE;
1125 
1126 	KDiskSystem* diskSystem = new(nothrow) KFileSystem(name);
1127 	if (!diskSystem)
1128 		return B_NO_MEMORY;
1129 
1130 	return _AddDiskSystem(diskSystem);
1131 }
1132 
1133 
1134 status_t
1135 KDiskDeviceManager::_AddDiskSystem(KDiskSystem* diskSystem)
1136 {
1137 	if (!diskSystem)
1138 		return B_BAD_VALUE;
1139 	DBG(OUT("KDiskDeviceManager::_AddDiskSystem(%s)\n", diskSystem->Name()));
1140 	status_t error = diskSystem->Init();
1141 	DBG(if (error != B_OK)
1142 		OUT("  initialization failed: %s\n", strerror(error)));
1143 	if (error == B_OK)
1144 		error = fDiskSystems->Put(diskSystem->ID(), diskSystem);
1145 	if (error != B_OK)
1146 		delete diskSystem;
1147 	DBG(OUT("KDiskDeviceManager::_AddDiskSystem() done: %s\n",
1148 		strerror(error)));
1149 	return error;
1150 }
1151 
1152 
1153 bool
1154 KDiskDeviceManager::_AddDevice(KDiskDevice* device)
1155 {
1156 	if (!device || !PartitionAdded(device))
1157 		return false;
1158 	if (fDevices->Put(device->ID(), device) == B_OK)
1159 		return true;
1160 	PartitionRemoved(device);
1161 	return false;
1162 }
1163 
1164 
1165 bool
1166 KDiskDeviceManager::_RemoveDevice(KDiskDevice* device)
1167 {
1168 	if (device != NULL && fDevices->Remove(device->ID())
1169 		&& PartitionRemoved(device)) {
1170 		_NotifyDeviceEvent(device, B_DEVICE_REMOVED,
1171 			B_DEVICE_REQUEST_DEVICE_LIST);
1172 		return true;
1173 	}
1174 
1175 	return false;
1176 }
1177 
1178 
1179 #if 0
1180 /*!
1181 	The device must be write locked, the manager must be locked.
1182 */
1183 status_t
1184 KDiskDeviceManager::_UpdateBusyPartitions(KDiskDevice *device)
1185 {
1186 	if (!device)
1187 		return B_BAD_VALUE;
1188 	// mark all partitions un-busy
1189 	struct UnmarkBusyVisitor : KPartitionVisitor {
1190 		virtual bool VisitPre(KPartition *partition)
1191 		{
1192 			partition->ClearFlags(B_PARTITION_BUSY
1193 								  | B_PARTITION_DESCENDANT_BUSY);
1194 			return false;
1195 		}
1196 	} visitor;
1197 	device->VisitEachDescendant(&visitor);
1198 	// Iterate through all job queues and all jobs scheduled or in
1199 	// progress and mark their scope busy.
1200 	for (int32 cookie = 0;
1201 		 KDiskDeviceJobQueue *jobQueue = NextJobQueue(&cookie); ) {
1202 		if (jobQueue->Device() != device)
1203 			continue;
1204 		for (int32 i = jobQueue->ActiveJobIndex();
1205 			 KDiskDeviceJob *job = jobQueue->JobAt(i); i++) {
1206 			if (job->Status() != B_DISK_DEVICE_JOB_IN_PROGRESS
1207 				&& job->Status() != B_DISK_DEVICE_JOB_SCHEDULED) {
1208 				continue;
1209 			}
1210 			KPartition *partition = FindPartition(job->ScopeID());
1211 			if (!partition || partition->Device() != device)
1212 				continue;
1213 			partition->AddFlags(B_PARTITION_BUSY);
1214 		}
1215 	}
1216 	// mark all anscestors of busy partitions descendant busy and all
1217 	// descendants busy
1218 	struct MarkBusyVisitor : KPartitionVisitor {
1219 		virtual bool VisitPre(KPartition *partition)
1220 		{
1221 			// parent busy => child busy
1222 			if (partition->Parent() && partition->Parent()->IsBusy())
1223 				partition->AddFlags(B_PARTITION_BUSY);
1224 			return false;
1225 		}
1226 
1227 		virtual bool VisitPost(KPartition *partition)
1228 		{
1229 			// child [descendant] busy => parent descendant busy
1230 			if ((partition->IsBusy() || partition->IsDescendantBusy())
1231 				&& partition->Parent()) {
1232 				partition->Parent()->AddFlags(B_PARTITION_DESCENDANT_BUSY);
1233 			}
1234 			return false;
1235 		}
1236 	} visitor2;
1237 	device->VisitEachDescendant(&visitor2);
1238 	return B_OK;
1239 }
1240 #endif
1241 
1242 
1243 status_t
1244 KDiskDeviceManager::_Scan(const char* path)
1245 {
1246 	DBG(OUT("KDiskDeviceManager::_Scan(%s)\n", path));
1247 	status_t error = B_ENTRY_NOT_FOUND;
1248 	struct stat st;
1249 	if (lstat(path, &st) < 0) {
1250 		return errno;
1251 	}
1252 	if (S_ISDIR(st.st_mode)) {
1253 		// a directory: iterate through its contents
1254 		DIR* dir = opendir(path);
1255 		if (!dir)
1256 			return errno;
1257 		while (dirent* entry = readdir(dir)) {
1258 			// skip "." and ".."
1259 			if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
1260 				continue;
1261 			KPath entryPath;
1262 			if (entryPath.SetPath(path) != B_OK
1263 				|| entryPath.Append(entry->d_name) != B_OK) {
1264 				continue;
1265 			}
1266 			if (_Scan(entryPath.Path()) == B_OK)
1267 				error = B_OK;
1268 		}
1269 		closedir(dir);
1270 	} else {
1271 		// not a directory
1272 		// check, if it is named "raw"
1273 		int32 len = strlen(path);
1274 		int32 leafLen = strlen("/raw");
1275 		if (len <= leafLen || strcmp(path + len - leafLen, "/raw"))
1276 			return B_ERROR;
1277 		if (FindDevice(path) != NULL) {
1278 			// we already know this device
1279 			return B_OK;
1280 		}
1281 
1282 		DBG(OUT("  found device: %s\n", path));
1283 		// create a KDiskDevice for it
1284 		KDiskDevice* device = new(nothrow) KDiskDevice;
1285 		if (!device)
1286 			return B_NO_MEMORY;
1287 
1288 		// init the KDiskDevice
1289 		error = device->SetTo(path);
1290 		// add the device
1291 		if (error == B_OK && !_AddDevice(device))
1292 			error = B_NO_MEMORY;
1293 		// cleanup on error
1294 		if (error != B_OK)
1295 			delete device;
1296 	}
1297 	return error;
1298 }
1299 
1300 
1301 /*!
1302 	The device must be write locked, the manager must be locked.
1303 */
1304 status_t
1305 KDiskDeviceManager::_ScanPartition(KPartition* partition, bool async,
1306 	DiskSystemMap* restrictScan)
1307 {
1308 // TODO: There's no reason why the manager needs to be locked anymore.
1309 	if (!partition)
1310 		return B_BAD_VALUE;
1311 
1312 // TODO: Reimplement asynchronous scanning, if we really need it.
1313 #if 0
1314 	if (async) {
1315 		// create a new job queue for the device
1316 		KDiskDeviceJobQueue *jobQueue = new(nothrow) KDiskDeviceJobQueue;
1317 		if (!jobQueue)
1318 			return B_NO_MEMORY;
1319 		jobQueue->SetDevice(partition->Device());
1320 
1321 		// create a job for scanning the device and add it to the job queue
1322 		KDiskDeviceJob *job = fJobFactory->CreateScanPartitionJob(partition->ID());
1323 		if (!job) {
1324 			delete jobQueue;
1325 			return B_NO_MEMORY;
1326 		}
1327 
1328 		if (!jobQueue->AddJob(job)) {
1329 			delete jobQueue;
1330 			delete job;
1331 			return B_NO_MEMORY;
1332 		}
1333 
1334 		// add the job queue
1335 		status_t error = AddJobQueue(jobQueue);
1336 		if (error != B_OK)
1337 			delete jobQueue;
1338 
1339 		return error;
1340 	}
1341 #endif
1342 
1343 	// scan synchronously
1344 
1345 	return _ScanPartition(partition, restrictScan);
1346 }
1347 
1348 
1349 status_t
1350 KDiskDeviceManager::_ScanPartition(KPartition* partition,
1351 	DiskSystemMap* restrictScan)
1352 {
1353 	// the partition's device must be write-locked
1354 	if (partition == NULL)
1355 		return B_BAD_VALUE;
1356 	if (!partition->Device()->HasMedia() || partition->IsMounted())
1357 		return B_OK;
1358 
1359 	if (partition->CountChildren() > 0) {
1360 		// Since this partition has already children, we don't scan it
1361 		// again, but only its children.
1362 		for (int32 i = 0; KPartition* child = partition->ChildAt(i); i++) {
1363 			_ScanPartition(child, restrictScan);
1364 		}
1365 		return B_OK;
1366 	}
1367 
1368 	DBG(
1369 		KPath partitionPath;
1370 		partition->GetPath(&partitionPath);
1371 		OUT("KDiskDeviceManager::_ScanPartition(%s)\n", partitionPath.Path());
1372 	)
1373 
1374 	// publish the partition
1375 	status_t error = B_OK;
1376 	if (!partition->IsPublished()) {
1377 		error = partition->PublishDevice();
1378 		if (error != B_OK)
1379 			return error;
1380 	}
1381 
1382 	DiskSystemMap* diskSystems = restrictScan;
1383 	if (diskSystems == NULL)
1384 		diskSystems = fDiskSystems;
1385 
1386 	// find the disk system that returns the best priority for this partition
1387 	float bestPriority = partition->DiskSystemPriority();
1388 	KDiskSystem* bestDiskSystem = NULL;
1389 	void* bestCookie = NULL;
1390 	for (DiskSystemMap::Iterator iterator = diskSystems->Begin();
1391 			iterator != diskSystems->End(); iterator++) {
1392 		KDiskSystem* diskSystem = iterator->Value();
1393 		if (diskSystem->Load() != B_OK)
1394 			continue;
1395 
1396 		DBG(OUT("  trying: %s\n", diskSystem->Name()));
1397 
1398 		void* cookie = NULL;
1399 		float priority = diskSystem->Identify(partition, &cookie);
1400 
1401 		DBG(OUT("  returned: %g\n", priority));
1402 
1403 		if (priority >= 0 && priority > bestPriority) {
1404 			// new best disk system
1405 			if (bestDiskSystem) {
1406 				bestDiskSystem->FreeIdentifyCookie(partition, bestCookie);
1407 				bestDiskSystem->Unload();
1408 			}
1409 			bestPriority = priority;
1410 			bestDiskSystem = diskSystem;
1411 			bestCookie = cookie;
1412 		} else {
1413 			// disk system doesn't identify the partition or worse than our
1414 			// current favorite
1415 			if (priority >= 0)
1416 				diskSystem->FreeIdentifyCookie(partition, cookie);
1417 			diskSystem->Unload();
1418 		}
1419 	}
1420 
1421 	// now, if we have found a disk system, let it scan the partition
1422 	if (bestDiskSystem) {
1423 		DBG(OUT("  scanning with: %s\n", bestDiskSystem->Name()));
1424 		error = bestDiskSystem->Scan(partition, bestCookie);
1425 		bestDiskSystem->FreeIdentifyCookie(partition, bestCookie);
1426 		if (error == B_OK) {
1427 			partition->SetDiskSystem(bestDiskSystem, bestPriority);
1428 			for (int32 i = 0; KPartition* child = partition->ChildAt(i); i++)
1429 				_ScanPartition(child, restrictScan);
1430 		} else {
1431 			// TODO: Handle the error.
1432 			DBG(OUT("  scanning failed: %s\n", strerror(error)));
1433 		}
1434 
1435 		// now we can safely unload the disk system -- it has been loaded by
1436 		// the partition(s) and thus will not really be unloaded
1437 		bestDiskSystem->Unload();
1438 	} else {
1439 		// contents not recognized
1440 		// nothing to be done -- partitions are created as unrecognized
1441 	}
1442 
1443 	return error;
1444 }
1445 
1446 
1447 status_t
1448 KDiskDeviceManager::_AddRemoveMonitoring(const char* path, bool add)
1449 {
1450 	struct stat st;
1451 	if (lstat(path, &st) < 0)
1452 		return errno;
1453 
1454 	status_t error = B_ENTRY_NOT_FOUND;
1455 	if (S_ISDIR(st.st_mode)) {
1456 		if (add) {
1457 			error = add_node_listener(st.st_dev, st.st_ino, B_WATCH_DIRECTORY,
1458 				*fDeviceWatcher);
1459 		} else {
1460 			error = remove_node_listener(st.st_dev, st.st_ino,
1461 				*fDeviceWatcher);
1462 		}
1463 		if (error != B_OK)
1464 			return error;
1465 
1466 		DIR* dir = opendir(path);
1467 		if (!dir)
1468 			return errno;
1469 
1470 		while (dirent* entry = readdir(dir)) {
1471 			// skip "." and ".."
1472 			if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
1473 				continue;
1474 
1475 			KPath entryPath;
1476 			if (entryPath.SetPath(path) != B_OK
1477 				|| entryPath.Append(entry->d_name) != B_OK) {
1478 				continue;
1479 			}
1480 
1481 			if (_AddRemoveMonitoring(entryPath.Path(), add) == B_OK)
1482 				error = B_OK;
1483 		}
1484 		closedir(dir);
1485 	}
1486 
1487 	return error;
1488 }
1489 
1490 
1491 status_t
1492 KDiskDeviceManager::_CheckMediaStatus()
1493 {
1494 	while (!fTerminating) {
1495 		int32 cookie = 0;
1496 		while (KDiskDevice* device = RegisterNextDevice(&cookie)) {
1497 			PartitionRegistrar _(device, true);
1498 			DeviceWriteLocker locker(device);
1499 
1500 			if (device->IsBusy(true))
1501 				continue;
1502 
1503 			bool hadMedia = device->HasMedia();
1504 			bool changedMedia = device->MediaChanged();
1505 			device->UpdateMediaStatusIfNeeded();
1506 
1507 			// Detect if there was any status change since last check.
1508 			if ((!device->MediaChanged() && (device->HasMedia() || !hadMedia))
1509 				|| !(hadMedia != device->HasMedia()
1510 					|| changedMedia != device->MediaChanged()))
1511 				continue;
1512 
1513 			device->MarkBusy(true);
1514 			device->UninitializeMedia();
1515 
1516 			if (device->MediaChanged()) {
1517 				dprintf("Media changed from %s\n", device->Path());
1518 				device->UpdateGeometry();
1519 				_ScanPartition(device, false);
1520 				_NotifyDeviceEvent(device, B_DEVICE_MEDIA_CHANGED,
1521 					B_DEVICE_REQUEST_DEVICE);
1522 			} else if (!device->HasMedia() && hadMedia) {
1523 				dprintf("Media removed from %s\n", device->Path());
1524 			}
1525 
1526 			device->UnmarkBusy(true);
1527 		}
1528 
1529 		snooze(1000000);
1530 	}
1531 
1532 	return 0;
1533 }
1534 
1535 
1536 status_t
1537 KDiskDeviceManager::_CheckMediaStatusDaemon(void* self)
1538 {
1539 	return ((KDiskDeviceManager*)self)->_CheckMediaStatus();
1540 }
1541 
1542 
1543 void
1544 KDiskDeviceManager::_NotifyDeviceEvent(KDiskDevice* device, int32 event,
1545 	uint32 mask)
1546 {
1547 	char messageBuffer[512];
1548 	KMessage message;
1549 	message.SetTo(messageBuffer, sizeof(messageBuffer), B_DEVICE_UPDATE);
1550 	message.AddInt32("event", event);
1551 	message.AddInt32("id", device->ID());
1552 	message.AddString("device", device->Path());
1553 
1554 	fNotifications->Notify(message, mask);
1555 }
1556 
1557