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