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