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