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_ADDONS_DIRECTORY,
40	B_COMMON_ADDONS_DIRECTORY,
41	B_SYSTEM_ADDONS_DIRECTORY,
42};
43/*!	\brief Size of the kAddOnDirs array. */
44static 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*/
52BDiskDeviceRoster::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*/
66BDiskDeviceRoster::~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*/
84status_t
85BDiskDeviceRoster::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*/
103status_t
104BDiskDeviceRoster::RewindDevices()
105{
106	fDeviceCookie = 0;
107	return B_OK;
108}
109
110
111status_t
112BDiskDeviceRoster::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
125status_t
126BDiskDeviceRoster::RewindDiskSystems()
127{
128	fDiskSystemCookie = 0;
129	return B_OK;
130}
131
132
133status_t
134BDiskDeviceRoster::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
152partition_id
153BDiskDeviceRoster::RegisterFileDevice(const char* filename)
154{
155	if (!filename)
156		return B_BAD_VALUE;
157	return _kern_register_file_device(filename);
158}
159
160
161status_t
162BDiskDeviceRoster::UnregisterFileDevice(const char* filename)
163{
164	if (!filename)
165		return B_BAD_VALUE;
166	return _kern_unregister_file_device(-1, filename);
167}
168
169
170status_t
171BDiskDeviceRoster::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*/
191bool
192BDiskDeviceRoster::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*/
229bool
230BDiskDeviceRoster::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*/
274bool
275BDiskDeviceRoster::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*/
309bool
310BDiskDeviceRoster::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*/
329status_t
330BDiskDeviceRoster::FindPartitionByVolume(const BVolume& volume,
331	BDiskDevice* device, 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*/
366status_t
367BDiskDeviceRoster::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*/
391status_t
392BDiskDeviceRoster::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*/
416status_t
417BDiskDeviceRoster::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
437status_t
438BDiskDeviceRoster::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
454status_t
455BDiskDeviceRoster::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
480status_t
481BDiskDeviceRoster::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*/
513status_t
514BDiskDeviceRoster::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*/
533status_t
534BDiskDeviceRoster::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*/
561status_t
562BDiskDeviceRoster::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*/
622status_t
623BDiskDeviceRoster::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*/
668status_t
669BDiskDeviceRoster::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*/
683status_t
684BDiskDeviceRoster::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*/
711status_t
712BDiskDeviceRoster::_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*/
751status_t
752BDiskDeviceRoster::_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*/
785status_t
786BDiskDeviceRoster::_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*/
815status_t
816BDiskDeviceRoster::_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	}
831if (error == B_OK)
832printf("  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*/
848status_t
849BDiskDeviceRoster::_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
872status_t
873BDiskDeviceRoster::_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