Files
haiku-beta6/src/kits/storage/DiskDeviceRoster.cpp
T
2003-02-24 00:41:21 +00:00

947 lines
29 KiB
C++

//----------------------------------------------------------------------
// This software is part of the OpenBeOS distribution and is covered
// by the OpenBeOS license.
//---------------------------------------------------------------------
#include <new.h>
#include <Directory.h>
#include <DiskDevice.h>
#include <DiskDevicePrivate.h>
#include <DiskDeviceRoster.h>
#include <DiskScannerAddOn.h>
#include <Entry.h>
#include <FindDirectory.h>
#include <Message.h>
#include <Partition.h>
#include <Path.h>
#include <RegistrarDefs.h>
#include <RosterPrivate.h>
#include <Session.h>
#include "AddOnImage.h"
//----------------------------------------------------------------------------
// Hack to make BDiskDeviceRoster communicate with our registrar.
BRoster _be_roster;
const BRoster *be_roster = &_be_roster;
// init_roster
struct _InitRoster {
_InitRoster()
{
bool initialized = false;
// find the registrar port
port_id rosterPort = find_port(kRosterPortName);
port_info info;
if (rosterPort >= 0 && get_port_info(rosterPort, &info) == B_OK) {
// construct the roster messenger
struct {
port_id fPort;
int32 fHandlerToken;
team_id fTeam;
int32 extra0;
int32 extra1;
bool fPreferredTarget;
bool extra2;
bool extra3;
bool extra4;
} fakeMessenger;
fakeMessenger.fPort = rosterPort;
fakeMessenger.fHandlerToken = 0;
fakeMessenger.fTeam = info.team;
fakeMessenger.fPreferredTarget = true;
BMessenger mainMessenger = *(BMessenger*)&fakeMessenger;
// ask for the MIME messenger
BMessage reply;
status_t error = mainMessenger.SendMessage(
B_REG_GET_MIME_MESSENGER, &reply);
if (error == B_OK && reply.what == B_REG_SUCCESS) {
BMessenger mimeMessenger;
reply.FindMessenger("messenger", &mimeMessenger);
BRoster::Private(_be_roster).SetTo(mainMessenger,
mimeMessenger);
initialized = true;
} else {
}
}
if (!initialized) {
printf("initializing be_roster failed!\n");
exit(1);
}
}
} _InitRosterObject;
//----------------------------------------------------------------------------
/*! \class BDiskDeviceRoster
\brief An interface for iterating through the disk devices known to the
system and for a notification mechanism provided to listen to their
changes.
*/
/*! \brief find_directory constants of the add-on dirs to be searched. */
static const directory_which kAddOnDirs[] = {
B_USER_ADDONS_DIRECTORY,
// B_COMMON_ADDONS_DIRECTORY,
B_BEOS_ADDONS_DIRECTORY
};
/*! \brief Size of the kAddOnDirs array. */
static const int32 kAddOnDirCount
= sizeof(kAddOnDirs) / sizeof(directory_which);
// constructor
/*! \brief Creates a BDiskDeviceRoster object.
The object is ready to be used after construction.
*/
BDiskDeviceRoster::BDiskDeviceRoster()
: fManager(),
fCookie(0),
fPartitionAddOnDir(NULL),
fFSAddOnDir(NULL),
fPartitionAddOnDirIndex(0),
fFSAddOnDirIndex(0)
{
get_disk_device_messenger(&fManager);
}
// destructor
/*! \brief Frees all resources associated with the object.
*/
BDiskDeviceRoster::~BDiskDeviceRoster()
{
if (fPartitionAddOnDir)
delete fPartitionAddOnDir;
if (fFSAddOnDir)
delete fFSAddOnDir;
}
// GetNextDevice
/*! \brief Returns the next BDiskDevice.
\param device Pointer to a pre-allocated BDiskDevice to be initialized to
represent the next device.
\return
- \c B_OK: Everything went fine.
- \c B_ENTRY_NOT_FOUND: The end of the list of devices had already been
reached.
- another error code
*/
status_t
BDiskDeviceRoster::GetNextDevice(BDiskDevice *device)
{
//printf("BDiskDeviceRoster::GetNextDevice()\n");
status_t error = (device ? B_OK : B_BAD_VALUE);
// compose request message
BMessage request(B_REG_NEXT_DISK_DEVICE);
if (error == B_OK)
error = request.AddInt32("cookie", fCookie);
// send request
BMessage reply;
if (error == B_OK)
error = fManager.SendMessage(&request, &reply);
// analyze reply
if (error == B_OK) {
//reply.PrintToStream();
// result
status_t result = B_OK;
error = reply.FindInt32("result", &result);
//printf(" check: %s\n", strerror(error));
if (error == B_OK)
error = result;
//printf(" check: %s\n", strerror(error));
// cookie
if (error == B_OK)
error = reply.FindInt32("cookie", &fCookie);
//printf(" check: %s\n", strerror(error));
// device
BMessage archive;
if (error == B_OK)
error = reply.FindMessage("device", &archive);
//printf(" check: %s\n", strerror(error));
if (error == B_OK)
error = device->_Unarchive(&archive);
//printf(" check: %s\n", strerror(error));
}
//printf("BDiskDeviceRoster::GetNextDevice() done: %s\n", strerror(error));
return error;
}
// Rewind
/*! \brief Rewinds the device list iterator.
\return \c B_OK, if everything went fine, another error code otherwise.
*/
status_t
BDiskDeviceRoster::Rewind()
{
fCookie = 0;
return B_OK;
}
// VisitEachDevice
/*! \brief Iterates through the all devices.
The supplied visitor's Visit(BDiskDevice*) is invoked for each device.
If Visit() returns \c true, the iteration is terminated and this method
returns \c true. If supplied, \a device is set to the concerned device.
\param visitor The visitor.
\param device Pointer to a pre-allocated BDiskDevice to be initialized
to the device at which the iteration was terminated.
May be \c NULL.
\return \c true, if the iteration was terminated, \c false otherwise.
*/
bool
BDiskDeviceRoster::VisitEachDevice(BDiskDeviceVisitor *visitor,
BDiskDevice *device)
{
bool terminatedEarly = false;
if (visitor) {
int32 oldCookie = fCookie;
fCookie = 0;
BDiskDevice deviceOnStack;
BDiskDevice *useDevice = (device ? device : &deviceOnStack);
while (!terminatedEarly && GetNextDevice(useDevice) == B_OK)
terminatedEarly = visitor->Visit(useDevice);
fCookie = oldCookie;
if (!terminatedEarly)
useDevice->Unset();
}
return terminatedEarly;
}
// VisitEachSession
/*! \brief Iterates through the all devices' sessions.
The supplied visitor's Visit(BSession*) is invoked for each session.
If Visit() returns \c true, the iteration is terminated and this method
returns \c true. If supplied, \a device is set to the concerned device
and in \a session the pointer to the session object is returned.
\param visitor The visitor.
\param device Pointer to a pre-allocated BDiskDevice to be initialized
to the device at which the iteration was terminated.
May be \c NULL.
\param session Pointer to a pre-allocated BSession pointer to be set
to the session at which the iteration was terminated.
May be \c NULL.
\return \c true, if the iteration was terminated, \c false otherwise.
*/
bool
BDiskDeviceRoster::VisitEachSession(BDiskDeviceVisitor *visitor,
BDiskDevice *device, BSession **session)
{
bool terminatedEarly = false;
if (visitor) {
int32 oldCookie = fCookie;
fCookie = 0;
BDiskDevice deviceOnStack;
BDiskDevice *useDevice = (device ? device : &deviceOnStack);
BSession *foundSession = NULL;
while (!foundSession && GetNextDevice(useDevice) == B_OK)
foundSession = useDevice->VisitEachSession(visitor);
fCookie = oldCookie;
if (!terminatedEarly)
useDevice->Unset();
else if (device && session)
*session = foundSession;
}
return terminatedEarly;
}
// VisitEachPartition
/*! \brief Iterates through the all devices' partitions.
The supplied visitor's Visit(BPartition*) is invoked for each partition.
If Visit() returns \c true, the iteration is terminated and this method
returns \c true. If supplied, \a device is set to the concerned device
and in \a partition the pointer to the partition object is returned.
\param visitor The visitor.
\param device Pointer to a pre-allocated BDiskDevice to be initialized
to the device at which the iteration was terminated.
May be \c NULL.
\param partition Pointer to a pre-allocated BPartition pointer to be set
to the partition at which the iteration was terminated.
May be \c NULL.
\return \c true, if the iteration was terminated, \c false otherwise.
*/
bool
BDiskDeviceRoster::VisitEachPartition(BDiskDeviceVisitor *visitor,
BDiskDevice *device,
BPartition **partition)
{
bool terminatedEarly = false;
if (visitor) {
int32 oldCookie = fCookie;
fCookie = 0;
BDiskDevice deviceOnStack;
BDiskDevice *useDevice = (device ? device : &deviceOnStack);
BPartition *foundPartition = NULL;
while (!foundPartition && GetNextDevice(useDevice) == B_OK)
foundPartition = useDevice->VisitEachPartition(visitor);
fCookie = oldCookie;
if (!terminatedEarly)
useDevice->Unset();
else if (device && partition)
*partition = foundPartition;
}
return terminatedEarly;
}
// Traverse
/*! \brief Pre-order traverses the trees of the spanned by the BDiskDevices
and their subobjects.
The supplied visitor's Visit() is invoked for each device, for each
session and for each partition.
If Visit() returns \c true, the iteration is terminated and this method
returns \c true as well.
\param visitor The visitor.
\return \c true, if the iteration was terminated, \c false otherwise.
*/
bool
BDiskDeviceRoster::Traverse(BDiskDeviceVisitor *visitor)
{
bool terminatedEarly = false;
if (visitor) {
int32 oldCookie = fCookie;
fCookie = 0;
BDiskDevice device;
while (!terminatedEarly && GetNextDevice(&device) == B_OK)
terminatedEarly = device.Traverse(visitor);
fCookie = oldCookie;
}
return terminatedEarly;
}
// VisitEachMountedPartition
/*! \brief Iterates through the all devices' partitions that are mounted.
The supplied visitor's Visit(BPartition*) is invoked for each mounted
partition.
If Visit() returns \c true, the iteration is terminated and this method
returns \c true. If supplied, \a device is set to the concerned device
and in \a partition the pointer to the partition object is returned.
\param visitor The visitor.
\param device Pointer to a pre-allocated BDiskDevice to be initialized
to the device at which the iteration was terminated.
May be \c NULL.
\param partition Pointer to a pre-allocated BPartition pointer to be set
to the partition at which the iteration was terminated.
May be \c NULL.
\return \c true, if the iteration was terminated, \c false otherwise.
*/
bool
BDiskDeviceRoster::VisitEachMountedPartition(BDiskDeviceVisitor *visitor,
BDiskDevice *device,
BPartition **partition)
{
bool terminatedEarly = false;
if (visitor) {
struct MountedPartitionFilter : public PartitionFilter {
virtual bool Filter(BPartition *partition)
{ return partition->IsMounted(); }
} filter;
PartitionFilterVisitor filterVisitor(visitor, &filter);
terminatedEarly
= VisitEachPartition(&filterVisitor, device, partition);
}
return terminatedEarly;
}
// VisitEachMountablePartition
/*! \brief Iterates through the all devices' partitions that are mountable.
The supplied visitor's Visit(BPartition*) is invoked for each mountable
partition.
If Visit() returns \c true, the iteration is terminated and this method
returns \c true. If supplied, \a device is set to the concerned device
and in \a partition the pointer to the partition object is returned.
\param visitor The visitor.
\param device Pointer to a pre-allocated BDiskDevice to be initialized
to the device at which the iteration was terminated.
May be \c NULL.
\param partition Pointer to a pre-allocated BPartition pointer to be set
to the partition at which the iteration was terminated.
May be \c NULL.
\return \c true, if the iteration was terminated, \c false otherwise.
*/
bool
BDiskDeviceRoster::VisitEachMountablePartition(BDiskDeviceVisitor *visitor,
BDiskDevice *device,
BPartition **partition)
{
bool terminatedEarly = false;
if (visitor) {
struct MountablePartitionFilter : public PartitionFilter {
virtual bool Filter(BPartition *partition)
{ return partition->ContainsFileSystem(); }
} filter;
PartitionFilterVisitor filterVisitor(visitor, &filter);
terminatedEarly
= VisitEachPartition(&filterVisitor, device, partition);
}
return terminatedEarly;
}
// VisitEachInitializablePartition
/*! \brief Iterates through the all devices' partitions that are initializable.
The supplied visitor's Visit(BPartition*) is invoked for each
initializable partition.
If Visit() returns \c true, the iteration is terminated and this method
returns \c true. If supplied, \a device is set to the concerned device
and in \a partition the pointer to the partition object is returned.
\param visitor The visitor.
\param device Pointer to a pre-allocated BDiskDevice to be initialized
to the device at which the iteration was terminated.
May be \c NULL.
\param partition Pointer to a pre-allocated BPartition pointer to be set
to the partition at which the iteration was terminated.
May be \c NULL.
\return \c true, if the iteration was terminated, \c false otherwise.
*/
bool
BDiskDeviceRoster::VisitEachInitializablePartition(BDiskDeviceVisitor *visitor,
BDiskDevice *device,
BPartition **partition)
{
bool terminatedEarly = false;
if (visitor) {
struct InitializablePartitionFilter : public PartitionFilter {
virtual bool Filter(BPartition *partition)
{ return !partition->IsHidden(); }
} filter;
PartitionFilterVisitor filterVisitor(visitor, &filter);
terminatedEarly
= VisitEachPartition(&filterVisitor, device, partition);
}
return terminatedEarly;
}
// GetDeviceWithID
/*! \brief Returns a BDiskDevice for a given ID.
The supplied \a device is initialized to the device identified by \a id.
\param id The ID of the device to be retrieved.
\param device Pointer to a pre-allocated BDiskDevice to be initialized
to the device identified by \a id.
\return
- \c B_OK: Everything went fine.
- \c B_ENTRY_NOT_FOUND: A device with ID \a id could not be found.
- other error codes
*/
status_t
BDiskDeviceRoster::GetDeviceWithID(int32 id, BDiskDevice *device) const
{
return _GetObjectWithID("device_id", id, device);
}
// GetSessionWithID
/*! \brief Returns a BSession for a given ID.
The supplied \a device is initialized to the device the session identified
by \a id resides on, and \a session is set to point to the respective
BSession.
\param id The ID of the session to be retrieved.
\param device Pointer to a pre-allocated BDiskDevice to be initialized
to the device the session identified by \a id resides on.
\param session Pointer to a pre-allocated BSession pointer to be set to
the session identified by \a id.
\return
- \c B_OK: Everything went fine.
- \c B_ENTRY_NOT_FOUND: A session with ID \a id could not be found.
- other error codes
*/
status_t
BDiskDeviceRoster::GetSessionWithID(int32 id, BDiskDevice *device,
BSession **session) const
{
status_t error = (device && session ? B_OK : B_BAD_VALUE);
if (error == B_OK)
error = _GetObjectWithID("session_id", id, device);
if (error == B_OK)
*session = device->SessionWithID(id);
return error;
}
// GetPartitionWithID
/*! \brief Returns a BPartition for a given ID.
The supplied \a device is initialized to the device the partition
identified by \a id resides on, and \a partition is set to point to the
respective BPartition.
\param id The ID of the partition to be retrieved.
\param device Pointer to a pre-allocated BDiskDevice to be initialized
to the device the partition identified by \a id resides on.
\param partition Pointer to a pre-allocated BPartition pointer to be set
to the partition identified by \a id.
\return
- \c B_OK: Everything went fine.
- \c B_ENTRY_NOT_FOUND: A partition with ID \a id could not be found.
- other error codes
*/
status_t
BDiskDeviceRoster::GetPartitionWithID(int32 id, BDiskDevice *device,
BPartition **partition) const
{
status_t error = (device && partition ? B_OK : B_BAD_VALUE);
if (error == B_OK)
error = _GetObjectWithID("partition_id", id, device);
if (error == B_OK)
*partition = device->PartitionWithID(id);
return error;
}
// StartWatching
/*! \brief Adds a target to the list of targets to be notified on disk device
events.
\todo List the event mask flags, the events and describe the layout of the
notification message.
If \a target is already listening to events, this method replaces the
former event mask with \a eventMask.
\param target A BMessenger identifying the target to which the events
shall be sent.
\param eventMask A mask specifying on which events the target shall be
notified.
\return \c B_OK, if everything went fine, another error code otherwise.
*/
status_t
BDiskDeviceRoster::StartWatching(BMessenger target, uint32 eventMask)
{
status_t error = B_OK;
// compose request message
BMessage request(B_REG_DEVICE_START_WATCHING);
if (error == B_OK)
error = request.AddMessenger("target", target);
if (error == B_OK)
error = request.AddInt32("events", (int32)eventMask);
// send request
BMessage reply;
if (error == B_OK)
error = fManager.SendMessage(&request, &reply);
// analyze reply
if (error == B_OK) {
// result
status_t result = B_OK;
error = reply.FindInt32("result", &result);
if (error == B_OK)
error = result;
}
return error;
}
// StopWatching
/*! \brief Remove a target from the list of targets to be notified on disk
device events.
\param target A BMessenger identifying the target to which notfication
message shall not longer be sent.
\return \c B_OK, if everything went fine, another error code otherwise.
*/
status_t
BDiskDeviceRoster::StopWatching(BMessenger target)
{
status_t error = B_OK;
// compose request message
BMessage request(B_REG_DEVICE_STOP_WATCHING);
if (error == B_OK)
error = request.AddMessenger("target", target);
// send request
BMessage reply;
if (error == B_OK)
error = fManager.SendMessage(&request, &reply);
// analyze reply
if (error == B_OK) {
// result
status_t result = B_OK;
error = reply.FindInt32("result", &result);
if (error == B_OK)
error = result;
}
return error;
}
// GetNextPartitioningSystem
/*! \brief Returns the next partitioning system capable of partitioning.
The returned \a shortName can be passed to BSession::Partition().
\param shortName Pointer to a pre-allocation char buffer, of size
\c B_FILE_NAME_LENGTH or larger into which the short name of the
partitioning system shall be written.
\param longName Pointer to a pre-allocation char buffer, of size
\c B_FILE_NAME_LENGTH or larger into which the long name of the
partitioning system shall be written. May be \c NULL.
\return
- \c B_OK: Everything went fine.
- \c B_BAD_VALUE: \c NULL \a shortName.
- \c B_ENTRY_NOT_FOUND: End of the list has been reached.
- other error codes
*/
status_t
BDiskDeviceRoster::GetNextPartitioningSystem(char *shortName, char *longName)
{
status_t error = (shortName ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
// search until an add-on has been found or the end of all directories
// has been reached
bool found = false;
do {
// get the next add-on in the current dir
AddOnImage image;
error = _GetNextAddOn(fPartitionAddOnDir, &image);
if (error == B_OK) {
// add-on loaded: get the function that creates an add-on
// object
BDiskScannerPartitionAddOn *(*create_add_on)();
if (get_image_symbol(image.ID(), "create_ds_partition_add_on",
B_SYMBOL_TYPE_TEXT,
(void**)&create_add_on) == B_OK) {
// create the add-on object and copy the requested data
if (BDiskScannerPartitionAddOn *addOn
= (*create_add_on)()) {
const char *addOnShortName = addOn->ShortName();
const char *addOnLongName = addOn->LongName();
if (addOnShortName && addOnLongName) {
strcpy(shortName, addOnShortName);
if (longName)
strcpy(longName, addOnLongName);
found = true;
}
delete addOn;
}
}
} else if (error == B_ENTRY_NOT_FOUND) {
// end of the current directory has been reached, try next dir
error = _GetNextAddOnDir(&fPartitionAddOnDir,
&fPartitionAddOnDirIndex,
"partition");
}
} while (error == B_OK && !found);
}
return error;
}
// GetNextFileSystem
/*! \brief Returns the next file system capable of initializing.
The returned \a shortName can be passed to BPartition::Initialize().
\param shortName Pointer to a pre-allocation char buffer, of size
\c B_FILE_NAME_LENGTH or larger into which the short name of the
file system shall be written.
\param longName Pointer to a pre-allocation char buffer, of size
\c B_FILE_NAME_LENGTH or larger into which the long name of the
file system shall be written. May be \c NULL.
\return
- \c B_OK: Everything went fine.
- \c B_BAD_VALUE: \c NULL \a shortName.
- \c B_ENTRY_NOT_FOUND: End of the list has been reached.
- other error codes
*/
status_t
BDiskDeviceRoster::GetNextFileSystem(char *shortName, char *longName)
{
status_t error = (shortName ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
// search until an add-on has been found or the end of all directories
// has been reached
bool found = false;
do {
// get the next add-on in the current dir
AddOnImage image;
error = _GetNextAddOn(fFSAddOnDir, &image);
if (error == B_OK) {
// add-on loaded: get the function that creates an add-on
// object
BDiskScannerFSAddOn *(*create_add_on)();
if (get_image_symbol(image.ID(), "create_ds_fs_add_on",
B_SYMBOL_TYPE_TEXT,
(void**)&create_add_on) == B_OK) {
// create the add-on object and copy the requested data
if (BDiskScannerFSAddOn *addOn = (*create_add_on)()) {
const char *addOnShortName = addOn->ShortName();
const char *addOnLongName = addOn->LongName();
if (addOnShortName && addOnLongName) {
strcpy(shortName, addOnShortName);
if (longName)
strcpy(longName, addOnLongName);
found = true;
}
delete addOn;
}
}
} else if (error == B_ENTRY_NOT_FOUND) {
// end of the current directory has been reached, try next dir
error = _GetNextAddOnDir(&fFSAddOnDir, &fFSAddOnDirIndex,
"fs");
}
} while (error == B_OK && !found);
}
return error;
}
// RewindPartitiningSystems
/*! \brief Rewinds the partitioning system list iterator.
\return \c B_OK, if everything went fine, another error code otherwise.
*/
status_t
BDiskDeviceRoster::RewindPartitiningSystems()
{
if (fPartitionAddOnDir) {
delete fPartitionAddOnDir;
fPartitionAddOnDir = NULL;
}
fPartitionAddOnDirIndex = 0;
return B_OK;
}
// RewindFileSystems
/*! \brief Rewinds the file system list iterator.
\return \c B_OK, if everything went fine, another error code otherwise.
*/
status_t
BDiskDeviceRoster::RewindFileSystems()
{
if (fFSAddOnDir) {
delete fFSAddOnDir;
fFSAddOnDir = NULL;
}
fFSAddOnDirIndex = 0;
return B_OK;
}
// _GetObjectWithID
/*! \brief Returns a BDiskDevice for a given device, session or partition ID.
The supplied \a device is initialized to the device the object identified
by \a id belongs to.
\param fieldName "device_id", "sesison_id" or "partition_id" according to
the type of object the device shall be retrieved for.
\param id The ID of the device, session or partition to be retrieved.
\param device Pointer to a pre-allocated BDiskDevice to be initialized
to the device to be retrieved.
\return
- \c B_OK: Everything went fine.
- \c B_ENTRY_NOT_FOUND: A device, session or partition respectively with
ID \a id could not be found.
- other error codes
*/
status_t
BDiskDeviceRoster::_GetObjectWithID(const char *fieldName, int32 id,
BDiskDevice *device) const
{
status_t error = (device ? B_OK : B_BAD_VALUE);
// compose request message
BMessage request(B_REG_GET_DISK_DEVICE);
if (error == B_OK)
error = request.AddInt32(fieldName, id);
// send request
BMessage reply;
if (error == B_OK)
error = fManager.SendMessage(&request, &reply);
// analyze reply
if (error == B_OK) {
// result
status_t result = B_OK;
error = reply.FindInt32("result", &result);
if (error == B_OK)
error = result;
// device
BMessage archive;
if (error == B_OK)
error = reply.FindMessage("device", &archive);
if (error == B_OK)
error = device->_Unarchive(&archive);
}
return error;
}
// _GetNextAddOn
/*! \brief Finds and loads the next add-on of an add-on subdirectory.
\param directory The add-on directory.
\param image Pointer to an image_id into which the image ID of the loaded
add-on shall be written.
\return
- \c B_OK: Everything went fine.
- \c B_ENTRY_NOT_FOUND: End of directory.
- other error codes
*/
status_t
BDiskDeviceRoster::_GetNextAddOn(BDirectory **directory, int32 *index,
const char *subdir, AddOnImage *image)
{
status_t error = (directory && index && subdir && image
? B_OK : B_BAD_VALUE);
if (error == B_OK) {
// search until an add-on has been found or the end of all directories
// has been reached
bool found = false;
do {
// get the next add-on in the current dir
error = _GetNextAddOn(*directory, image);
if (error == B_OK) {
found = true;
} else if (error == B_ENTRY_NOT_FOUND) {
// end of the current directory has been reached, try next dir
error = _GetNextAddOnDir(directory, index, subdir);
}
} while (error == B_OK && !found);
}
return error;
}
// _GetNextAddOn
/*! \brief Finds and loads the next add-on of an add-on subdirectory.
\param directory The add-on directory.
\param image Pointer to an image_id into which the image ID of the loaded
add-on shall be written.
\return
- \c B_OK: Everything went fine.
- \c B_ENTRY_NOT_FOUND: End of directory.
- other error codes
*/
status_t
BDiskDeviceRoster::_GetNextAddOn(BDirectory *directory, AddOnImage *image)
{
status_t error = (directory ? B_OK : B_ENTRY_NOT_FOUND);
if (error == B_OK) {
// iterate through the entry list and try to load the entries
bool found = false;
while (error == B_OK && !found) {
BEntry entry;
error = directory->GetNextEntry(&entry);
BPath path;
if (error == B_OK && entry.GetPath(&path) == B_OK)
found = (image->Load(path.Path()) == B_OK);
}
}
return error;
}
// _GetNextAddOnDir
/*! \brief Gets the next add-on directory path.
\param path Pointer to a BPath to be set to the found directory.
\param index Pointer to an index into the kAddOnDirs array indicating
which add-on dir shall be retrieved next.
\param subdir Name of the subdirectory (in the "disk_scanner" subdirectory
of the add-on directory) \a directory shall be set to.
\return
- \c B_OK: Everything went fine.
- \c B_ENTRY_NOT_FOUND: End of directory list.
- other error codes
*/
status_t
BDiskDeviceRoster::_GetNextAddOnDir(BPath *path, int32 *index,
const char *subdir)
{
status_t error = (*index < kAddOnDirCount ? B_OK : B_ENTRY_NOT_FOUND);
// get the add-on dir path
if (error == B_OK) {
error = find_directory(kAddOnDirs[*index], path);
(*index)++;
}
// construct the subdirectory path
if (error == B_OK) {
error = path->Append("disk_scanner");
if (error == B_OK)
error = path->Append(subdir);
}
if (error == B_OK)
printf(" next add-on dir: `%s'\n", path->Path());
return error;
}
// _GetNextAddOnDir
/*! \brief Gets the next add-on directory.
\param directory Pointer to a BDirectory* to be set to the found directory.
\param index Pointer to an index into the kAddOnDirs array indicating
which add-on dir shall be retrieved next.
\param subdir Name of the subdirectory (in the "disk_scanner" subdirectory
of the add-on directory) \a directory shall be set to.
\return
- \c B_OK: Everything went fine.
- \c B_ENTRY_NOT_FOUND: End of directory list.
- other error codes
*/
status_t
BDiskDeviceRoster::_GetNextAddOnDir(BDirectory **directory, int32 *index,
const char *subdir)
{
BPath path;
status_t error = _GetNextAddOnDir(&path, index, subdir);
// create a BDirectory object, if there is none yet.
if (error == B_OK && !*directory) {
*directory = new BDirectory;
if (!*directory)
error = B_NO_MEMORY;
}
// init the directory
if (error == B_OK)
error = (*directory)->SetTo(path.Path());
// cleanup on error
if (error != B_OK && *directory) {
delete *directory;
*directory = NULL;
}
return error;
}
// _LoadPartitionAddOn
status_t
BDiskDeviceRoster::_LoadPartitionAddOn(const char *partitioningSystem,
AddOnImage *image,
BDiskScannerPartitionAddOn **_addOn)
{
status_t error = (partitioningSystem && image && _addOn
? B_OK : B_BAD_VALUE);
// load the image
bool found = false;
BPath path;
BDirectory *directory = NULL;
int32 index = 0;
while (error == B_OK && !found) {
error = _GetNextAddOn(&directory, &index, "partition", image);
if (error == B_OK) {
// add-on loaded: get the function that creates an add-on
// object
BDiskScannerPartitionAddOn *(*create_add_on)();
if (get_image_symbol(image->ID(), "create_ds_partition_add_on",
B_SYMBOL_TYPE_TEXT,
(void**)&create_add_on) == B_OK) {
// create the add-on object and copy the requested data
if (BDiskScannerPartitionAddOn *addOn = (*create_add_on)()) {
if (!strcmp(addOn->ShortName(), partitioningSystem)) {
*_addOn = addOn;
found = true;
} else
delete addOn;
}
}
}
}
// cleanup
if (directory)
delete directory;
if (error != B_OK && image)
image->Unload();
return error;
}