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. */
38static 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. */
45static 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*/
53BDiskDeviceRoster::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*/
67BDiskDeviceRoster::~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*/
85status_t
86BDiskDeviceRoster::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*/
104status_t
105BDiskDeviceRoster::RewindDevices()
106{
107	fDeviceCookie = 0;
108	return B_OK;
109}
110
111
112status_t
113BDiskDeviceRoster::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
126status_t
127BDiskDeviceRoster::RewindDiskSystems()
128{
129	fDiskSystemCookie = 0;
130	return B_OK;
131}
132
133
134status_t
135BDiskDeviceRoster::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
153partition_id
154BDiskDeviceRoster::RegisterFileDevice(const char* filename)
155{
156	if (!filename)
157		return B_BAD_VALUE;
158	return _kern_register_file_device(filename);
159}
160
161
162status_t
163BDiskDeviceRoster::UnregisterFileDevice(const char* filename)
164{
165	if (!filename)
166		return B_BAD_VALUE;
167	return _kern_unregister_file_device(-1, filename);
168}
169
170
171status_t
172BDiskDeviceRoster::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*/
192bool
193BDiskDeviceRoster::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*/
230bool
231BDiskDeviceRoster::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*/
275bool
276BDiskDeviceRoster::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*/
310bool
311BDiskDeviceRoster::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*/
330status_t
331BDiskDeviceRoster::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*/
367status_t
368BDiskDeviceRoster::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*/
392status_t
393BDiskDeviceRoster::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*/
417status_t
418BDiskDeviceRoster::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
438status_t
439BDiskDeviceRoster::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
455status_t
456BDiskDeviceRoster::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
481status_t
482BDiskDeviceRoster::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*/
514status_t
515BDiskDeviceRoster::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*/
534status_t
535BDiskDeviceRoster::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*/
562status_t
563BDiskDeviceRoster::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*/
623status_t
624BDiskDeviceRoster::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*/
669status_t
670BDiskDeviceRoster::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*/
684status_t
685BDiskDeviceRoster::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*/
712status_t
713BDiskDeviceRoster::_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*/
752status_t
753BDiskDeviceRoster::_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*/
786status_t
787BDiskDeviceRoster::_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*/
816status_t
817BDiskDeviceRoster::_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	}
832if (error == B_OK)
833printf("  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*/
849status_t
850BDiskDeviceRoster::_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
873status_t
874BDiskDeviceRoster::_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