* Copyright 2003-2009, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold, bonefish@cs.tu-berlin.de
* Axel Dörfler, axeld@pinc-software.de
*/
#include <DiskDeviceRoster.h>
#include <new>
#include <Directory.h>
#include <DiskDevice.h>
#include <DiskDevicePrivate.h>
#include <DiskSystem.h>
#include <Entry.h>
#include <FindDirectory.h>
#include <Message.h>
#include <Partition.h>
#include <Path.h>
#include <Volume.h>
#include <MessengerPrivate.h>
#include <syscalls.h>
#include <ddm_userland_interface_defs.h>
\brief An interface for iterating through the disk devices known to the
system and for a notification mechanism provided to listen to their
changes.
*/
static const directory_which kAddOnDirs[] = {
B_USER_NONPACKAGED_ADDONS_DIRECTORY,
B_USER_ADDONS_DIRECTORY,
B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY,
B_SYSTEM_ADDONS_DIRECTORY,
};
static const int32 kAddOnDirCount
= sizeof(kAddOnDirs) / sizeof(directory_which);
The object is ready to be used after construction.
*/
BDiskDeviceRoster::BDiskDeviceRoster()
: fDeviceCookie(0),
fDiskSystemCookie(0),
fJobCookie(0)
{
}
*/
BDiskDeviceRoster::~BDiskDeviceRoster()
{
}
\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)
{
if (!device)
return B_BAD_VALUE;
size_t neededSize = 0;
partition_id id = _kern_get_next_disk_device_id(&fDeviceCookie,
&neededSize);
if (id < 0)
return id;
return device->_SetTo(id, true, neededSize);
}
\return \c B_OK, if everything went fine, another error code otherwise.
*/
status_t
BDiskDeviceRoster::RewindDevices()
{
fDeviceCookie = 0;
return B_OK;
}
status_t
BDiskDeviceRoster::GetNextDiskSystem(BDiskSystem* system)
{
if (!system)
return B_BAD_VALUE;
user_disk_system_info info;
status_t error = _kern_get_next_disk_system_info(&fDiskSystemCookie,
&info);
if (error == B_OK)
error = system->_SetTo(&info);
return error;
}
status_t
BDiskDeviceRoster::RewindDiskSystems()
{
fDiskSystemCookie = 0;
return B_OK;
}
status_t
BDiskDeviceRoster::GetDiskSystem(BDiskSystem* system, const char* name)
{
if (!system)
return B_BAD_VALUE;
int32 cookie = 0;
user_disk_system_info info;
while (_kern_get_next_disk_system_info(&cookie, &info) == B_OK) {
if (!strcmp(name, info.name)
|| !strcmp(name, info.short_name)
|| !strcmp(name, info.pretty_name))
return system->_SetTo(&info);
}
return B_ENTRY_NOT_FOUND;
}
partition_id
BDiskDeviceRoster::RegisterFileDevice(const char* filename)
{
if (!filename)
return B_BAD_VALUE;
return _kern_register_file_device(filename);
}
status_t
BDiskDeviceRoster::UnregisterFileDevice(const char* filename)
{
if (!filename)
return B_BAD_VALUE;
return _kern_unregister_file_device(-1, filename);
}
status_t
BDiskDeviceRoster::UnregisterFileDevice(partition_id device)
{
if (device < 0)
return B_BAD_VALUE;
return _kern_unregister_file_device(device, NULL);
}
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 = fDeviceCookie;
fDeviceCookie = 0;
BDiskDevice deviceOnStack;
BDiskDevice* useDevice = device ? device : &deviceOnStack;
while (!terminatedEarly && GetNextDevice(useDevice) == B_OK)
terminatedEarly = visitor->Visit(useDevice);
fDeviceCookie = oldCookie;
if (!terminatedEarly)
useDevice->Unset();
}
return terminatedEarly;
}
subobjects.
The supplied visitor's Visit(BDiskDevice*) method is invoked for each
disk device and Visit(BPartition*) for each (non-disk device) 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 = fDeviceCookie;
fDeviceCookie = 0;
BDiskDevice deviceOnStack;
BDiskDevice* useDevice = device ? device : &deviceOnStack;
BPartition* foundPartition = NULL;
while (GetNextDevice(useDevice) == B_OK) {
foundPartition = useDevice->VisitEachDescendant(visitor);
if (foundPartition) {
terminatedEarly = true;
break;
}
}
fDeviceCookie = oldCookie;
if (!terminatedEarly)
useDevice->Unset();
else if (device && partition)
*partition = foundPartition;
}
return terminatedEarly;
}
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, int32)
{ return partition->IsMounted(); }
} filter;
PartitionFilterVisitor filterVisitor(visitor, &filter);
terminatedEarly
= VisitEachPartition(&filterVisitor, device, partition);
}
return terminatedEarly;
}
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, int32)
{ return partition->ContainsFileSystem(); }
} filter;
PartitionFilterVisitor filterVisitor(visitor, &filter);
terminatedEarly
= VisitEachPartition(&filterVisitor, device, partition);
}
return terminatedEarly;
}
*/
status_t
BDiskDeviceRoster::FindPartitionByVolume(const BVolume& volume,
BDiskDevice* device, BPartition** _partition)
{
class FindPartitionVisitor : public BDiskDeviceVisitor {
public:
FindPartitionVisitor(dev_t volume)
:
fVolume(volume)
{
}
virtual bool Visit(BDiskDevice* device)
{
return Visit(device, 0);
}
virtual bool Visit(BPartition* partition, int32 level)
{
BVolume volume;
return partition->GetVolume(&volume) == B_OK
&& volume.Device() == fVolume;
}
private:
dev_t fVolume;
} visitor(volume.Device());
if (VisitEachMountedPartition(&visitor, device, _partition))
return B_OK;
return B_ENTRY_NOT_FOUND;
}
*/
status_t
BDiskDeviceRoster::FindPartitionByMountPoint(const char* mountPoint,
BDiskDevice* device, BPartition** _partition)
{
BVolume volume(dev_for_path(mountPoint));
if (volume.InitCheck() == B_OK
&& FindPartitionByVolume(volume, device, _partition))
return B_OK;
return B_ENTRY_NOT_FOUND;
}
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
{
if (!device)
return B_BAD_VALUE;
return device->_SetTo(id, true, 0);
}
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
{
if (!device || !partition)
return B_BAD_VALUE;
status_t error = device->_SetTo(id, false, 0);
if (error != B_OK)
return error;
*partition = device->FindDescendant(id);
if (!*partition)
return B_ENTRY_NOT_FOUND;
return B_OK;
}
status_t
BDiskDeviceRoster::GetDeviceForPath(const char* filename, BDiskDevice* device)
{
if (!filename || !device)
return B_BAD_VALUE;
size_t neededSize = 0;
partition_id id = _kern_find_disk_device(filename, &neededSize);
if (id < 0)
return id;
return device->_SetTo(id, true, neededSize);
}
status_t
BDiskDeviceRoster::GetPartitionForPath(const char* filename,
BDiskDevice* device, BPartition** partition)
{
if (!filename || !device || !partition)
return B_BAD_VALUE;
size_t neededSize = 0;
partition_id id = _kern_find_partition(filename, &neededSize);
if (id < 0)
return id;
status_t error = device->_SetTo(id, false, neededSize);
if (error != B_OK)
return error;
*partition = device->FindDescendant(id);
if (!*partition)
return B_ENTRY_NOT_FOUND;
return B_OK;
}
status_t
BDiskDeviceRoster::GetFileDeviceForPath(const char* filename,
BDiskDevice* device)
{
if (!filename || !device)
return B_BAD_VALUE;
size_t neededSize = 0;
partition_id id = _kern_find_file_disk_device(filename, &neededSize);
if (id < 0)
return id;
return device->_SetTo(id, true, neededSize);
}
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)
{
if (eventMask == 0)
return B_BAD_VALUE;
BMessenger::Private messengerPrivate(target);
port_id port = messengerPrivate.Port();
int32 token = messengerPrivate.Token();
return _kern_start_watching_disks(eventMask, port, token);
}
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)
{
BMessenger::Private messengerPrivate(target);
port_id port = messengerPrivate.Port();
int32 token = messengerPrivate.Token();
return _kern_stop_watching_disks(port, token);
}
#if 0
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) {
bool found = false;
do {
AddOnImage image;
error = _GetNextAddOn(fPartitionAddOnDir, &image);
if (error == B_OK) {
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) {
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) {
error = _GetNextAddOnDir(&fPartitionAddOnDir,
&fPartitionAddOnDirIndex,
"partition");
}
} while (error == B_OK && !found);
}
return error;
}
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) {
bool found = false;
do {
AddOnImage image;
error = _GetNextAddOn(fFSAddOnDir, &image);
if (error == B_OK) {
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) {
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) {
error = _GetNextAddOnDir(&fFSAddOnDir, &fFSAddOnDirIndex,
"fs");
}
} while (error == B_OK && !found);
}
return error;
}
\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;
}
\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;
}
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);
BMessage request(B_REG_GET_DISK_DEVICE);
if (error == B_OK)
error = request.AddInt32(fieldName, id);
BMessage reply;
if (error == B_OK)
error = fManager.SendMessage(&request, &reply);
if (error == B_OK) {
status_t result = B_OK;
error = reply.FindInt32("result", &result);
if (error == B_OK)
error = result;
BMessage archive;
if (error == B_OK)
error = reply.FindMessage("device", &archive);
if (error == B_OK)
error = device->_Unarchive(&archive);
}
return error;
}
\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) {
bool found = false;
do {
error = _GetNextAddOn(*directory, image);
if (error == B_OK) {
found = true;
} else if (error == B_ENTRY_NOT_FOUND) {
error = _GetNextAddOnDir(directory, index, subdir);
}
} while (error == B_OK && !found);
}
return error;
}
\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) {
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;
}
\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);
if (error == B_OK) {
error = find_directory(kAddOnDirs[*index], path);
(*index)++;
}
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;
}
\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);
if (error == B_OK && !*directory) {
*directory = new BDirectory;
if (!*directory)
error = B_NO_MEMORY;
}
if (error == B_OK)
error = (*directory)->SetTo(path.Path());
if (error != B_OK && *directory) {
delete *directory;
*directory = NULL;
}
return error;
}
status_t
BDiskDeviceRoster::_LoadPartitionAddOn(const char *partitioningSystem,
AddOnImage *image, BDiskScannerPartitionAddOn **_addOn)
{
status_t error = partitioningSystem && image && _addOn
? B_OK : B_BAD_VALUE;
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) {
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) {
if (BDiskScannerPartitionAddOn *addOn = (*create_add_on)()) {
if (!strcmp(addOn->ShortName(), partitioningSystem)) {
*_addOn = addOn;
found = true;
} else
delete addOn;
}
}
}
}
if (directory)
delete directory;
if (error != B_OK && image)
image->Unload();
return error;
}
#endif