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