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