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