xref: /haiku/src/kits/storage/disk_device/DiskDeviceRoster.cpp (revision 4720c31bb08f5c6d1c8ddb616463c6fba9b350a1)
1 /*
2  * Copyright 2003-2009, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Ingo Weinhold, bonefish@cs.tu-berlin.de
7  *		Axel Dörfler, axeld@pinc-software.de
8  */
9 
10 #include <DiskDeviceRoster.h>
11 
12 #include <new>
13 
14 #include <Directory.h>
15 #include <DiskDevice.h>
16 #include <DiskDevicePrivate.h>
17 #include <DiskSystem.h>
18 #include <Entry.h>
19 #include <FindDirectory.h>
20 #include <Message.h>
21 #include <Partition.h>
22 #include <Path.h>
23 #include <Volume.h>
24 
25 #include <MessengerPrivate.h>
26 
27 #include <syscalls.h>
28 #include <ddm_userland_interface_defs.h>
29 
30 
31 /*!	\class BDiskDeviceRoster
32 	\brief An interface for iterating through the disk devices known to the
33 		   system and for a notification mechanism provided to listen to their
34 		   changes.
35 */
36 
37 /*!	\brief find_directory constants of the add-on dirs to be searched. */
38 static const directory_which kAddOnDirs[] = {
39 	B_USER_ADDONS_DIRECTORY,
40 	B_COMMON_ADDONS_DIRECTORY,
41 	B_BEOS_ADDONS_DIRECTORY
42 };
43 /*!	\brief Size of the kAddOnDirs array. */
44 static const int32 kAddOnDirCount
45 	= sizeof(kAddOnDirs) / sizeof(directory_which);
46 
47 
48 /*!	\brief Creates a BDiskDeviceRoster object.
49 
50 	The object is ready to be used after construction.
51 */
52 BDiskDeviceRoster::BDiskDeviceRoster()
53 	: fDeviceCookie(0),
54 	  fDiskSystemCookie(0),
55 	  fJobCookie(0)
56 //	  fPartitionAddOnDir(NULL),
57 //	  fFSAddOnDir(NULL),
58 //	  fPartitionAddOnDirIndex(0),
59 //	  fFSAddOnDirIndex(0)
60 {
61 }
62 
63 
64 /*!	\brief Frees all resources associated with the object.
65 */
66 BDiskDeviceRoster::~BDiskDeviceRoster()
67 {
68 //	if (fPartitionAddOnDir)
69 //		delete fPartitionAddOnDir;
70 //	if (fFSAddOnDir)
71 //		delete fFSAddOnDir;
72 }
73 
74 
75 /*!	\brief Returns the next BDiskDevice.
76 	\param device Pointer to a pre-allocated BDiskDevice to be initialized to
77 		   represent the next device.
78 	\return
79 	- \c B_OK: Everything went fine.
80 	- \c B_ENTRY_NOT_FOUND: The end of the list of devices had already been
81 	  reached.
82 	- another error code
83 */
84 status_t
85 BDiskDeviceRoster::GetNextDevice(BDiskDevice* device)
86 {
87 	if (!device)
88 		return B_BAD_VALUE;
89 
90 	size_t neededSize = 0;
91 	partition_id id = _kern_get_next_disk_device_id(&fDeviceCookie,
92 		&neededSize);
93 	if (id < 0)
94 		return id;
95 
96 	return device->_SetTo(id, true, neededSize);
97 }
98 
99 
100 /*!	\brief Rewinds the device list iterator.
101 	\return \c B_OK, if everything went fine, another error code otherwise.
102 */
103 status_t
104 BDiskDeviceRoster::RewindDevices()
105 {
106 	fDeviceCookie = 0;
107 	return B_OK;
108 }
109 
110 
111 status_t
112 BDiskDeviceRoster::GetNextDiskSystem(BDiskSystem* system)
113 {
114 	if (!system)
115 		return B_BAD_VALUE;
116 	user_disk_system_info info;
117 	status_t error = _kern_get_next_disk_system_info(&fDiskSystemCookie,
118 		&info);
119 	if (error == B_OK)
120 		error = system->_SetTo(&info);
121 	return error;
122 }
123 
124 
125 status_t
126 BDiskDeviceRoster::RewindDiskSystems()
127 {
128 	fDiskSystemCookie = 0;
129 	return B_OK;
130 }
131 
132 
133 status_t
134 BDiskDeviceRoster::GetDiskSystem(BDiskSystem* system, const char* name)
135 {
136 	if (!system)
137 		return B_BAD_VALUE;
138 
139 	int32 cookie = 0;
140 	user_disk_system_info info;
141 	while (_kern_get_next_disk_system_info(&cookie, &info) == B_OK) {
142 		if (!strcmp(name, info.name)
143 			|| !strcmp(name, info.short_name)
144 			|| !strcmp(name, info.pretty_name))
145 			return system->_SetTo(&info);
146 	}
147 
148 	return B_ENTRY_NOT_FOUND;
149 }
150 
151 
152 partition_id
153 BDiskDeviceRoster::RegisterFileDevice(const char* filename)
154 {
155 	if (!filename)
156 		return B_BAD_VALUE;
157 	return _kern_register_file_device(filename);
158 }
159 
160 
161 status_t
162 BDiskDeviceRoster::UnregisterFileDevice(const char* filename)
163 {
164 	if (!filename)
165 		return B_BAD_VALUE;
166 	return _kern_unregister_file_device(-1, filename);
167 }
168 
169 
170 status_t
171 BDiskDeviceRoster::UnregisterFileDevice(partition_id device)
172 {
173 	if (device < 0)
174 		return B_BAD_VALUE;
175 	return _kern_unregister_file_device(device, NULL);
176 }
177 
178 
179 /*!	\brief Iterates through the all devices.
180 
181 	The supplied visitor's Visit(BDiskDevice*) is invoked for each device.
182 	If Visit() returns \c true, the iteration is terminated and this method
183 	returns \c true. If supplied, \a device is set to the concerned device.
184 
185 	\param visitor The visitor.
186 	\param device Pointer to a pre-allocated BDiskDevice to be initialized
187 		   to the device at which the iteration was terminated.
188 		   May be \c NULL.
189 	\return \c true, if the iteration was terminated, \c false otherwise.
190 */
191 bool
192 BDiskDeviceRoster::VisitEachDevice(BDiskDeviceVisitor* visitor,
193 	BDiskDevice* device)
194 {
195 	bool terminatedEarly = false;
196 	if (visitor) {
197 		int32 oldCookie = fDeviceCookie;
198 		fDeviceCookie = 0;
199 		BDiskDevice deviceOnStack;
200 		BDiskDevice* useDevice = device ? device : &deviceOnStack;
201 		while (!terminatedEarly && GetNextDevice(useDevice) == B_OK)
202 			terminatedEarly = visitor->Visit(useDevice);
203 		fDeviceCookie = oldCookie;
204 		if (!terminatedEarly)
205 			useDevice->Unset();
206 	}
207 	return terminatedEarly;
208 }
209 
210 
211 /*!	\brief Pre-order traverses the trees spanned by the BDiskDevices and their
212 		   subobjects.
213 
214 	The supplied visitor's Visit(BDiskDevice*) method is invoked for each
215 	disk device and Visit(BPartition*) for each (non-disk device) partition.
216 	If Visit() returns \c true, the iteration is terminated and this method
217 	returns \c true. If supplied, \a device is set to the concerned device
218 	and in \a partition the pointer to the partition object is returned.
219 
220 	\param visitor The visitor.
221 	\param device Pointer to a pre-allocated BDiskDevice to be initialized
222 		   to the device at which the iteration was terminated.
223 		   May be \c NULL.
224 	\param partition Pointer to a pre-allocated BPartition pointer to be set
225 		   to the partition at which the iteration was terminated.
226 		   May be \c NULL.
227 	\return \c true, if the iteration was terminated, \c false otherwise.
228 */
229 bool
230 BDiskDeviceRoster::VisitEachPartition(BDiskDeviceVisitor* visitor,
231 	BDiskDevice* device, BPartition** partition)
232 {
233 	bool terminatedEarly = false;
234 	if (visitor) {
235 		int32 oldCookie = fDeviceCookie;
236 		fDeviceCookie = 0;
237 		BDiskDevice deviceOnStack;
238 		BDiskDevice* useDevice = device ? device : &deviceOnStack;
239 		BPartition* foundPartition = NULL;
240 		while (GetNextDevice(useDevice) == B_OK) {
241 			foundPartition = useDevice->VisitEachDescendant(visitor);
242 			if (foundPartition) {
243 				terminatedEarly = true;
244 				break;
245 			}
246 		}
247 		fDeviceCookie = oldCookie;
248 		if (!terminatedEarly)
249 			useDevice->Unset();
250 		else if (device && partition)
251 			*partition = foundPartition;
252 	}
253 	return terminatedEarly;
254 }
255 
256 
257 /*!	\brief Iterates through the all devices' partitions that are mounted.
258 
259 	The supplied visitor's Visit(BPartition*) is invoked for each mounted
260 	partition.
261 	If Visit() returns \c true, the iteration is terminated and this method
262 	returns \c true. If supplied, \a device is set to the concerned device
263 	and in \a partition the pointer to the partition object is returned.
264 
265 	\param visitor The visitor.
266 	\param device Pointer to a pre-allocated BDiskDevice to be initialized
267 		   to the device at which the iteration was terminated.
268 		   May be \c NULL.
269 	\param partition Pointer to a pre-allocated BPartition pointer to be set
270 		   to the partition at which the iteration was terminated.
271 		   May be \c NULL.
272 	\return \c true, if the iteration was terminated, \c false otherwise.
273 */
274 bool
275 BDiskDeviceRoster::VisitEachMountedPartition(BDiskDeviceVisitor* visitor,
276 	BDiskDevice* device, BPartition** partition)
277 {
278 	bool terminatedEarly = false;
279 	if (visitor) {
280 		struct MountedPartitionFilter : public PartitionFilter {
281 			virtual bool Filter(BPartition *partition, int32)
282 				{ return partition->IsMounted(); }
283 		} filter;
284 		PartitionFilterVisitor filterVisitor(visitor, &filter);
285 		terminatedEarly
286 			= VisitEachPartition(&filterVisitor, device, partition);
287 	}
288 	return terminatedEarly;
289 }
290 
291 
292 /*!	\brief Iterates through the all devices' partitions that are mountable.
293 
294 	The supplied visitor's Visit(BPartition*) is invoked for each mountable
295 	partition.
296 	If Visit() returns \c true, the iteration is terminated and this method
297 	returns \c true. If supplied, \a device is set to the concerned device
298 	and in \a partition the pointer to the partition object is returned.
299 
300 	\param visitor The visitor.
301 	\param device Pointer to a pre-allocated BDiskDevice to be initialized
302 		   to the device at which the iteration was terminated.
303 		   May be \c NULL.
304 	\param partition Pointer to a pre-allocated BPartition pointer to be set
305 		   to the partition at which the iteration was terminated.
306 		   May be \c NULL.
307 	\return \c true, if the iteration was terminated, \c false otherwise.
308 */
309 bool
310 BDiskDeviceRoster::VisitEachMountablePartition(BDiskDeviceVisitor* visitor,
311 	BDiskDevice* device, BPartition** partition)
312 {
313 	bool terminatedEarly = false;
314 	if (visitor) {
315 		struct MountablePartitionFilter : public PartitionFilter {
316 			virtual bool Filter(BPartition *partition, int32)
317 				{ return partition->ContainsFileSystem(); }
318 		} filter;
319 		PartitionFilterVisitor filterVisitor(visitor, &filter);
320 		terminatedEarly
321 			= VisitEachPartition(&filterVisitor, device, partition);
322 	}
323 	return terminatedEarly;
324 }
325 
326 
327 /*!	\brief Finds a BPartition by BVolume.
328 */
329 status_t
330 BDiskDeviceRoster::FindPartitionByVolume(BVolume* volume, BDiskDevice* device,
331 	BPartition** _partition)
332 {
333 	class FindPartitionVisitor : public BDiskDeviceVisitor {
334 	public:
335 		FindPartitionVisitor(dev_t volume)
336 			:
337 			fVolume(volume)
338 		{
339 		}
340 
341 		virtual bool Visit(BDiskDevice* device)
342 		{
343 			return Visit(device, 0);
344 		}
345 
346 		virtual bool Visit(BPartition* partition, int32 level)
347 		{
348 			BVolume volume;
349 			return partition->GetVolume(&volume) == B_OK
350 				&& volume.Device() == fVolume;
351 		}
352 
353 	private:
354 		dev_t	fVolume;
355 	} visitor(volume->Device());
356 
357 	if (VisitEachMountedPartition(&visitor, device, _partition))
358 		return B_OK;
359 
360 	return B_ENTRY_NOT_FOUND;
361 }
362 
363 
364 /*!	\brief Finds a BPartition by mount path.
365 */
366 status_t
367 BDiskDeviceRoster::FindPartitionByMountPoint(const char* mountPoint,
368 	BDiskDevice* device, BPartition** _partition)
369 {
370 	BVolume volume(dev_for_path(mountPoint));
371 	if (volume.InitCheck() == B_OK
372 		&& FindPartitionByVolume(&volume, device, _partition))
373 		return B_OK;
374 
375 	return B_ENTRY_NOT_FOUND;
376 }
377 
378 
379 /*!	\brief Returns a BDiskDevice for a given ID.
380 
381 	The supplied \a device is initialized to the device identified by \a id.
382 
383 	\param id The ID of the device to be retrieved.
384 	\param device Pointer to a pre-allocated BDiskDevice to be initialized
385 		   to the device identified by \a id.
386 	\return
387 	- \c B_OK: Everything went fine.
388 	- \c B_ENTRY_NOT_FOUND: A device with ID \a id could not be found.
389 	- other error codes
390 */
391 status_t
392 BDiskDeviceRoster::GetDeviceWithID(int32 id, BDiskDevice* device) const
393 {
394 	if (!device)
395 		return B_BAD_VALUE;
396 	return device->_SetTo(id, true, 0);
397 }
398 
399 
400 /*!	\brief Returns a BPartition for a given ID.
401 
402 	The supplied \a device is initialized to the device the partition
403 	identified by \a id resides on, and \a partition is set to point to the
404 	respective BPartition.
405 
406 	\param id The ID of the partition to be retrieved.
407 	\param device Pointer to a pre-allocated BDiskDevice to be initialized
408 		   to the device the partition identified by \a id resides on.
409 	\param partition Pointer to a pre-allocated BPartition pointer to be set
410 		   to the partition identified by \a id.
411 	\return
412 	- \c B_OK: Everything went fine.
413 	- \c B_ENTRY_NOT_FOUND: A partition with ID \a id could not be found.
414 	- other error codes
415 */
416 status_t
417 BDiskDeviceRoster::GetPartitionWithID(int32 id, BDiskDevice* device,
418 	BPartition** partition) const
419 {
420 	if (!device || !partition)
421 		return B_BAD_VALUE;
422 
423 	// retrieve the device data
424 	status_t error = device->_SetTo(id, false, 0);
425 	if (error != B_OK)
426 		return error;
427 
428 	// find the partition object
429 	*partition = device->FindDescendant(id);
430 	if (!*partition)	// should never happen!
431 		return B_ENTRY_NOT_FOUND;
432 
433 	return B_OK;
434 }
435 
436 
437 status_t
438 BDiskDeviceRoster::GetDeviceForPath(const char* filename, BDiskDevice* device)
439 {
440 	if (!filename || !device)
441 		return B_BAD_VALUE;
442 
443 	// get the device ID
444 	size_t neededSize = 0;
445 	partition_id id = _kern_find_disk_device(filename, &neededSize);
446 	if (id < 0)
447 		return id;
448 
449 	// retrieve the device data
450 	return device->_SetTo(id, true, neededSize);
451 }
452 
453 
454 status_t
455 BDiskDeviceRoster::GetPartitionForPath(const char* filename,
456 	BDiskDevice* device, BPartition** partition)
457 {
458 	if (!filename || !device || !partition)
459 		return B_BAD_VALUE;
460 
461 	// get the partition ID
462 	size_t neededSize = 0;
463 	partition_id id = _kern_find_partition(filename, &neededSize);
464 	if (id < 0)
465 		return id;
466 
467 	// retrieve the device data
468 	status_t error = device->_SetTo(id, false, neededSize);
469 	if (error != B_OK)
470 		return error;
471 
472 	// find the partition object
473 	*partition = device->FindDescendant(id);
474 	if (!*partition)	// should never happen!
475 		return B_ENTRY_NOT_FOUND;
476 	return B_OK;
477 }
478 
479 
480 status_t
481 BDiskDeviceRoster::GetFileDeviceForPath(const char* filename,
482 	BDiskDevice* device)
483 {
484 	if (!filename || !device)
485 		return B_BAD_VALUE;
486 
487 	// get the device ID
488 	size_t neededSize = 0;
489 	partition_id id = _kern_find_file_disk_device(filename, &neededSize);
490 	if (id < 0)
491 		return id;
492 
493 	// retrieve the device data
494 	return device->_SetTo(id, true, neededSize);
495 }
496 
497 
498 /*!	\brief Adds a target to the list of targets to be notified on disk device
499 		   events.
500 
501 	\todo List the event mask flags, the events and describe the layout of the
502 		  notification message.
503 
504 	If \a target is already listening to events, this method replaces the
505 	former event mask with \a eventMask.
506 
507 	\param target A BMessenger identifying the target to which the events
508 		   shall be sent.
509 	\param eventMask A mask specifying on which events the target shall be
510 		   notified.
511 	\return \c B_OK, if everything went fine, another error code otherwise.
512 */
513 status_t
514 BDiskDeviceRoster::StartWatching(BMessenger target, uint32 eventMask)
515 {
516 	if (eventMask == 0)
517 		return B_BAD_VALUE;
518 
519 	BMessenger::Private messengerPrivate(target);
520 	port_id port = messengerPrivate.Port();
521 	int32 token = messengerPrivate.Token();
522 
523 	return _kern_start_watching_disks(eventMask, port, token);
524 }
525 
526 
527 /*!	\brief Remove a target from the list of targets to be notified on disk
528 		   device events.
529 	\param target A BMessenger identifying the target to which notfication
530 		   message shall not longer be sent.
531 	\return \c B_OK, if everything went fine, another error code otherwise.
532 */
533 status_t
534 BDiskDeviceRoster::StopWatching(BMessenger target)
535 {
536 	BMessenger::Private messengerPrivate(target);
537 	port_id port = messengerPrivate.Port();
538 	int32 token = messengerPrivate.Token();
539 
540 	return _kern_stop_watching_disks(port, token);
541 }
542 
543 #if 0
544 
545 /*!	\brief Returns the next partitioning system capable of partitioning.
546 
547 	The returned \a shortName can be passed to BSession::Partition().
548 
549 	\param shortName Pointer to a pre-allocation char buffer, of size
550 		   \c B_FILE_NAME_LENGTH or larger into which the short name of the
551 		   partitioning system shall be written.
552 	\param longName Pointer to a pre-allocation char buffer, of size
553 		   \c B_FILE_NAME_LENGTH or larger into which the long name of the
554 		   partitioning system shall be written. May be \c NULL.
555 	\return
556 	- \c B_OK: Everything went fine.
557 	- \c B_BAD_VALUE: \c NULL \a shortName.
558 	- \c B_ENTRY_NOT_FOUND: End of the list has been reached.
559 	- other error codes
560 */
561 status_t
562 BDiskDeviceRoster::GetNextPartitioningSystem(char *shortName, char *longName)
563 {
564 	status_t error = (shortName ? B_OK : B_BAD_VALUE);
565 	if (error == B_OK) {
566 		// search until an add-on has been found or the end of all directories
567 		// has been reached
568 		bool found = false;
569 		do {
570 			// get the next add-on in the current dir
571 			AddOnImage image;
572 			error = _GetNextAddOn(fPartitionAddOnDir, &image);
573 			if (error == B_OK) {
574 				// add-on loaded: get the function that creates an add-on
575 				// object
576 				BDiskScannerPartitionAddOn *(*create_add_on)();
577 				if (get_image_symbol(image.ID(), "create_ds_partition_add_on",
578 									 B_SYMBOL_TYPE_TEXT,
579 									 (void**)&create_add_on) == B_OK) {
580 					// create the add-on object and copy the requested data
581 					if (BDiskScannerPartitionAddOn *addOn
582 						= (*create_add_on)()) {
583 						const char *addOnShortName = addOn->ShortName();
584 						const char *addOnLongName = addOn->LongName();
585 						if (addOnShortName && addOnLongName) {
586 							strcpy(shortName, addOnShortName);
587 							if (longName)
588 								strcpy(longName, addOnLongName);
589 							found = true;
590 						}
591 						delete addOn;
592 					}
593 				}
594 			} else if (error == B_ENTRY_NOT_FOUND) {
595 				// end of the current directory has been reached, try next dir
596 				error = _GetNextAddOnDir(&fPartitionAddOnDir,
597 										 &fPartitionAddOnDirIndex,
598 										 "partition");
599 			}
600 		} while (error == B_OK && !found);
601 	}
602 	return error;
603 }
604 
605 
606 /*!	\brief Returns the next file system capable of initializing.
607 
608 	The returned \a shortName can be passed to BPartition::Initialize().
609 
610 	\param shortName Pointer to a pre-allocation char buffer, of size
611 		   \c B_FILE_NAME_LENGTH or larger into which the short name of the
612 		   file system shall be written.
613 	\param longName Pointer to a pre-allocation char buffer, of size
614 		   \c B_FILE_NAME_LENGTH or larger into which the long name of the
615 		   file system shall be written. May be \c NULL.
616 	\return
617 	- \c B_OK: Everything went fine.
618 	- \c B_BAD_VALUE: \c NULL \a shortName.
619 	- \c B_ENTRY_NOT_FOUND: End of the list has been reached.
620 	- other error codes
621 */
622 status_t
623 BDiskDeviceRoster::GetNextFileSystem(char *shortName, char *longName)
624 {
625 	status_t error = (shortName ? B_OK : B_BAD_VALUE);
626 	if (error == B_OK) {
627 		// search until an add-on has been found or the end of all directories
628 		// has been reached
629 		bool found = false;
630 		do {
631 			// get the next add-on in the current dir
632 			AddOnImage image;
633 			error = _GetNextAddOn(fFSAddOnDir, &image);
634 			if (error == B_OK) {
635 				// add-on loaded: get the function that creates an add-on
636 				// object
637 				BDiskScannerFSAddOn *(*create_add_on)();
638 				if (get_image_symbol(image.ID(), "create_ds_fs_add_on",
639 									 B_SYMBOL_TYPE_TEXT,
640 									 (void**)&create_add_on) == B_OK) {
641 					// create the add-on object and copy the requested data
642 					if (BDiskScannerFSAddOn *addOn = (*create_add_on)()) {
643 						const char *addOnShortName = addOn->ShortName();
644 						const char *addOnLongName = addOn->LongName();
645 						if (addOnShortName && addOnLongName) {
646 							strcpy(shortName, addOnShortName);
647 							if (longName)
648 								strcpy(longName, addOnLongName);
649 							found = true;
650 						}
651 						delete addOn;
652 					}
653 				}
654 			} else if (error == B_ENTRY_NOT_FOUND) {
655 				// end of the current directory has been reached, try next dir
656 				error = _GetNextAddOnDir(&fFSAddOnDir, &fFSAddOnDirIndex,
657 										 "fs");
658 			}
659 		} while (error == B_OK && !found);
660 	}
661 	return error;
662 }
663 
664 
665 /*!	\brief Rewinds the partitioning system list iterator.
666 	\return \c B_OK, if everything went fine, another error code otherwise.
667 */
668 status_t
669 BDiskDeviceRoster::RewindPartitiningSystems()
670 {
671 	if (fPartitionAddOnDir) {
672 		delete fPartitionAddOnDir;
673 		fPartitionAddOnDir = NULL;
674 	}
675 	fPartitionAddOnDirIndex = 0;
676 	return B_OK;
677 }
678 
679 
680 /*!	\brief Rewinds the file system list iterator.
681 	\return \c B_OK, if everything went fine, another error code otherwise.
682 */
683 status_t
684 BDiskDeviceRoster::RewindFileSystems()
685 {
686 	if (fFSAddOnDir) {
687 		delete fFSAddOnDir;
688 		fFSAddOnDir = NULL;
689 	}
690 	fFSAddOnDirIndex = 0;
691 	return B_OK;
692 }
693 
694 
695 /*!	\brief Returns a BDiskDevice for a given device, session or partition ID.
696 
697 	The supplied \a device is initialized to the device the object identified
698 	by \a id belongs to.
699 
700 	\param fieldName "device_id", "sesison_id" or "partition_id" according to
701 		   the type of object the device shall be retrieved for.
702 	\param id The ID of the device, session or partition to be retrieved.
703 	\param device Pointer to a pre-allocated BDiskDevice to be initialized
704 		   to the device to be retrieved.
705 	\return
706 	- \c B_OK: Everything went fine.
707 	- \c B_ENTRY_NOT_FOUND: A device, session or partition respectively with
708 		 ID \a id could not be found.
709 	- other error codes
710 */
711 status_t
712 BDiskDeviceRoster::_GetObjectWithID(const char *fieldName, int32 id,
713 	BDiskDevice *device) const
714 {
715 	status_t error = (device ? B_OK : B_BAD_VALUE);
716 	// compose request message
717 	BMessage request(B_REG_GET_DISK_DEVICE);
718 	if (error == B_OK)
719 		error = request.AddInt32(fieldName, id);
720 	// send request
721 	BMessage reply;
722 	if (error == B_OK)
723 		error = fManager.SendMessage(&request, &reply);
724 	// analyze reply
725 	if (error == B_OK) {
726 		// result
727 		status_t result = B_OK;
728 		error = reply.FindInt32("result", &result);
729 		if (error == B_OK)
730 			error = result;
731 		// device
732 		BMessage archive;
733 		if (error == B_OK)
734 			error = reply.FindMessage("device", &archive);
735 		if (error == B_OK)
736 			error = device->_Unarchive(&archive);
737 	}
738 	return error;
739 }
740 
741 
742 /*!	\brief Finds and loads the next add-on of an add-on subdirectory.
743 	\param directory The add-on directory.
744 	\param image Pointer to an image_id into which the image ID of the loaded
745 		   add-on shall be written.
746 	\return
747 	- \c B_OK: Everything went fine.
748 	- \c B_ENTRY_NOT_FOUND: End of directory.
749 	- other error codes
750 */
751 status_t
752 BDiskDeviceRoster::_GetNextAddOn(BDirectory **directory, int32 *index,
753 	const char *subdir, AddOnImage *image)
754 {
755 	status_t error = (directory && index && subdir && image
756 					  ? B_OK : B_BAD_VALUE);
757 	if (error == B_OK) {
758 		// search until an add-on has been found or the end of all directories
759 		// has been reached
760 		bool found = false;
761 		do {
762 			// get the next add-on in the current dir
763 			error = _GetNextAddOn(*directory, image);
764 			if (error == B_OK) {
765 				found = true;
766 			} else if (error == B_ENTRY_NOT_FOUND) {
767 				// end of the current directory has been reached, try next dir
768 				error = _GetNextAddOnDir(directory, index, subdir);
769 			}
770 		} while (error == B_OK && !found);
771 	}
772 	return error;
773 }
774 
775 
776 /*!	\brief Finds and loads the next add-on of an add-on subdirectory.
777 	\param directory The add-on directory.
778 	\param image Pointer to an image_id into which the image ID of the loaded
779 		   add-on shall be written.
780 	\return
781 	- \c B_OK: Everything went fine.
782 	- \c B_ENTRY_NOT_FOUND: End of directory.
783 	- other error codes
784 */
785 status_t
786 BDiskDeviceRoster::_GetNextAddOn(BDirectory *directory, AddOnImage *image)
787 {
788 	status_t error = (directory ? B_OK : B_ENTRY_NOT_FOUND);
789 	if (error == B_OK) {
790 		// iterate through the entry list and try to load the entries
791 		bool found = false;
792 		while (error == B_OK && !found) {
793 			BEntry entry;
794 			error = directory->GetNextEntry(&entry);
795 			BPath path;
796 			if (error == B_OK && entry.GetPath(&path) == B_OK)
797 				found = (image->Load(path.Path()) == B_OK);
798 		}
799 	}
800 	return error;
801 }
802 
803 
804 /*!	\brief Gets the next add-on directory path.
805 	\param path Pointer to a BPath to be set to the found directory.
806 	\param index Pointer to an index into the kAddOnDirs array indicating
807 		   which add-on dir shall be retrieved next.
808 	\param subdir Name of the subdirectory (in the "disk_scanner" subdirectory
809 		   of the add-on directory) \a directory shall be set to.
810 	\return
811 	- \c B_OK: Everything went fine.
812 	- \c B_ENTRY_NOT_FOUND: End of directory list.
813 	- other error codes
814 */
815 status_t
816 BDiskDeviceRoster::_GetNextAddOnDir(BPath *path, int32 *index,
817 	const char *subdir)
818 {
819 	status_t error = (*index < kAddOnDirCount ? B_OK : B_ENTRY_NOT_FOUND);
820 	// get the add-on dir path
821 	if (error == B_OK) {
822 		error = find_directory(kAddOnDirs[*index], path);
823 		(*index)++;
824 	}
825 	// construct the subdirectory path
826 	if (error == B_OK) {
827 		error = path->Append("disk_scanner");
828 		if (error == B_OK)
829 			error = path->Append(subdir);
830 	}
831 if (error == B_OK)
832 printf("  next add-on dir: `%s'\n", path->Path());
833 	return error;
834 }
835 
836 
837 /*!	\brief Gets the next add-on directory.
838 	\param directory Pointer to a BDirectory* to be set to the found directory.
839 	\param index Pointer to an index into the kAddOnDirs array indicating
840 		   which add-on dir shall be retrieved next.
841 	\param subdir Name of the subdirectory (in the "disk_scanner" subdirectory
842 		   of the add-on directory) \a directory shall be set to.
843 	\return
844 	- \c B_OK: Everything went fine.
845 	- \c B_ENTRY_NOT_FOUND: End of directory list.
846 	- other error codes
847 */
848 status_t
849 BDiskDeviceRoster::_GetNextAddOnDir(BDirectory **directory, int32 *index,
850 	const char *subdir)
851 {
852 	BPath path;
853 	status_t error = _GetNextAddOnDir(&path, index, subdir);
854 	// create a BDirectory object, if there is none yet.
855 	if (error == B_OK && !*directory) {
856 		*directory = new BDirectory;
857 		if (!*directory)
858 			error = B_NO_MEMORY;
859 	}
860 	// init the directory
861 	if (error == B_OK)
862 		error = (*directory)->SetTo(path.Path());
863 	// cleanup on error
864 	if (error != B_OK && *directory) {
865 		delete *directory;
866 		*directory = NULL;
867 	}
868 	return error;
869 }
870 
871 
872 status_t
873 BDiskDeviceRoster::_LoadPartitionAddOn(const char *partitioningSystem,
874 	AddOnImage *image, BDiskScannerPartitionAddOn **_addOn)
875 {
876 	status_t error = partitioningSystem && image && _addOn
877 		? B_OK : B_BAD_VALUE;
878 
879 	// load the image
880 	bool found = false;
881 	BPath path;
882 	BDirectory *directory = NULL;
883 	int32 index = 0;
884 	while (error == B_OK && !found) {
885 		error = _GetNextAddOn(&directory, &index, "partition", image);
886 		if (error == B_OK) {
887 			// add-on loaded: get the function that creates an add-on
888 			// object
889 			BDiskScannerPartitionAddOn *(*create_add_on)();
890 			if (get_image_symbol(image->ID(), "create_ds_partition_add_on",
891 								 B_SYMBOL_TYPE_TEXT,
892 								 (void**)&create_add_on) == B_OK) {
893 				// create the add-on object and copy the requested data
894 				if (BDiskScannerPartitionAddOn *addOn = (*create_add_on)()) {
895 					if (!strcmp(addOn->ShortName(), partitioningSystem)) {
896 						*_addOn = addOn;
897 						found = true;
898 					} else
899 						delete addOn;
900 				}
901 			}
902 		}
903 	}
904 	// cleanup
905 	if (directory)
906 		delete directory;
907 	if (error != B_OK && image)
908 		image->Unload();
909 	return error;
910 }
911 
912 #endif	// 0
913