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