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