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