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