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