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