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