Files
haiku-beta6/src/kits/storage/DiskDevice.cpp
T

816 lines
20 KiB
C++
Raw Normal View History

//----------------------------------------------------------------------
// This software is part of the OpenBeOS distribution and is covered
// by the OpenBeOS license.
//---------------------------------------------------------------------
2003-07-06 23:11:10 +00:00
#include <DiskDevice.h>
2003-02-16 18:07:23 +00:00
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <new.h>
2003-02-16 18:07:23 +00:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <DiskDevice.h>
2003-07-06 23:11:10 +00:00
#include <DiskDeviceVisitor.h>
2003-02-16 18:07:23 +00:00
#include <Drivers.h>
#include <Message.h>
2003-07-06 23:11:10 +00:00
#include <Path.h>
#include "ddm_userland_interface.h"
/*! \class BDiskDevice
\brief A BDiskDevice object represents a storage device.
*/
// constructor
/*! \brief Creates an uninitialized BDiskDevice object.
*/
BDiskDevice::BDiskDevice()
2003-07-06 23:11:10 +00:00
: fDeviceData(NULL)
{
}
// destructor
/*! \brief Frees all resources associated with this object.
*/
BDiskDevice::~BDiskDevice()
{
}
// HasMedia
/*! \brief Returns whether the device contains a media.
\return \c true, if the device contains a media, \c false otherwise.
*/
bool
BDiskDevice::HasMedia() const
{
return (fDeviceData
&& fDeviceData->device_flags & B_DISK_DEVICE_HAS_MEDIA);
}
// IsRemovableMedia
2003-07-06 23:11:10 +00:00
/*! \brief Returns whether the device media are removable.
\return \c true, if the device media are removable, \c false otherwise.
*/
2003-07-06 23:11:10 +00:00
bool
BDiskDevice::IsRemovableMedia() const
{
2003-07-06 23:11:10 +00:00
return (fDeviceData
&& fDeviceData->device_flags & B_DISK_DEVICE_REMOVABLE);
}
// IsReadOnlyMedia
2003-07-06 23:11:10 +00:00
bool
BDiskDevice::IsReadOnlyMedia() const
{
2003-07-06 23:11:10 +00:00
return (fDeviceData
&& fDeviceData->device_flags & B_DISK_DEVICE_READ_ONLY);
}
// IsWriteOnceMedia
bool
BDiskDevice::IsWriteOnceMedia() const
{
return (fDeviceData
&& fDeviceData->device_flags & B_DISK_DEVICE_WRITE_ONCE);
}
2003-07-06 23:11:10 +00:00
// Eject
/*! \brief Eject the device's media.
The device media must, of course, be removable, and the device must
support ejecting the media.
\param update If \c true, Update() is invoked after successful ejection.
\return
- \c B_OK: Everything went fine.
- \c B_NO_INIT: The device is not properly initialized.
- \c B_BAD_VALUE: The device media is not removable.
- other error codes
*/
2003-07-06 23:11:10 +00:00
status_t
BDiskDevice::Eject(bool update)
{
2003-07-06 23:11:10 +00:00
/* // get path
const char *path = Path();
status_t error = (path ? B_OK : B_NO_INIT);
// check whether the device media is removable
if (error == B_OK && !IsRemovable())
error = B_BAD_VALUE;
// open, eject and close the device
if (error == B_OK) {
int fd = open(path, O_RDONLY);
if (fd < 0 || ioctl(fd, B_EJECT_DEVICE) != 0)
error = errno;
if (fd >= 0)
close(fd);
}
if (error == B_OK && update)
error = Update();
return error;
*/
// not implemented
return B_ERROR;
}
2003-07-06 23:11:10 +00:00
// Update
/*! \brief Updates the object to reflect the latest changes to the device.
Note, that subobjects (BSessions, BPartitions) may be deleted during this
operation. It is also possible, that the device doesn't exist anymore --
e.g. if it is hot-pluggable. Then an error is returned and the object is
uninitialized.
\param updated Pointer to a bool variable which shall be set to \c true,
if the object needed to be updated and to \c false otherwise.
May be \c NULL. Is not touched, if the method fails.
\return \c B_OK, if the update went fine, another error code otherwise.
*/
2003-07-06 23:11:10 +00:00
status_t
BDiskDevice::Update(bool *updated)
{
2003-07-06 23:11:10 +00:00
/*
// get a messenger for the disk device manager
// TODO: Cache the messenger? Maybe add a global variable?
BMessenger manager;
status_t error = get_disk_device_messenger(&manager);
// compose request message
BMessage request(B_REG_UPDATE_DISK_DEVICE);
if (error == B_OK)
error = request.AddInt32("device_id", fUniqueID);
if (error == B_OK)
error = request.AddInt32("change_counter", fChangeCounter);
if (error == B_OK)
error = request.AddInt32("update_policy", B_REG_DEVICE_UPDATE_CHANGED);
// send request
BMessage reply;
if (error == B_OK)
error = manager.SendMessage(&request, &reply);
// analyze reply
bool upToDate = true;
if (error == B_OK) {
// result
status_t result = B_OK;
error = reply.FindInt32("result", &result);
if (error == B_OK)
error = result;
if (error == B_OK)
error = reply.FindBool("up_to_date", &upToDate);
}
// get the archived device, if not up to date
if (error == B_OK && !upToDate) {
BMessage archive;
error = reply.FindMessage("device", &archive);
if (error == B_OK)
error = _Update(&archive);
}
// set result / cleanup on error
if (error == B_OK) {
if (updated)
*updated = !upToDate;
} else
Unset();
return error;
*/
// not implemented
return B_ERROR;
}
2003-07-06 23:11:10 +00:00
// Unset
void
BDiskDevice::Unset()
{
free(fDeviceData);
fDeviceData = NULL;
}
// GetPath
status_t
BDiskDevice::GetPath(BPath *path) const
{
2003-07-06 23:11:10 +00:00
if (!path || !fDeviceData)
return B_BAD_VALUE;
return path->SetTo(fDeviceData->path);
}
2003-07-06 23:11:10 +00:00
// IsModified
bool
BDiskDevice::IsModified() const
{
// not implemented
return false;
}
// PrepareModifications
status_t
BDiskDevice::PrepareModifications()
{
// not implemented
return B_ERROR;
}
// CommitModifications
status_t
BDiskDevice::CommitModifications(bool synchronously,
BMessenger progressMessenger,
bool receiveCompleteProgressUpdates)
{
// not implemented
return B_ERROR;
}
// CancelModifications
status_t
BDiskDevice::CancelModifications()
{
// not implemented
return B_ERROR;
}
// _SetTo
2003-07-06 23:11:10 +00:00
status_t
BDiskDevice::_SetTo(partition_id id, size_t neededSize)
2003-07-06 23:11:10 +00:00
{
Unset();
// get the device data
void *buffer = NULL;
size_t bufferSize = 0;
if (neededSize > 0) {
// allocate initial buffer
buffer = malloc(neededSize);
if (!buffer)
return B_NO_MEMORY;
bufferSize = neededSize;
}
status_t error = B_OK;
do {
error = _kern_get_disk_device_data(id, false,
(user_disk_device_data*)buffer,
bufferSize, &neededSize);
if (error == B_BUFFER_OVERFLOW) {
// buffer to small re-allocate it
if (buffer)
free(buffer);
buffer = malloc(neededSize);
if (buffer)
bufferSize = neededSize;
else
error = B_NO_MEMORY;
}
} while (error == B_BUFFER_OVERFLOW);
// set the data
if (error == B_OK)
error = _SetTo((user_disk_device_data*)buffer);
2003-07-06 23:11:10 +00:00
// cleanup on error
if (error != B_OK)
free(buffer);
return error;
}
// _SetTo
2003-07-06 23:11:10 +00:00
status_t
BDiskDevice::_SetTo(user_disk_device_data *data)
2003-07-06 23:11:10 +00:00
{
Unset();
if (!data)
return B_BAD_VALUE;
fDeviceData = data;
status_t error = BPartition::_SetTo(this, NULL,
&fDeviceData->device_partition_data);
2003-07-06 23:11:10 +00:00
if (error != B_OK) {
// Don't call Unset() here. If _SetTo() fails, the caller retains
2003-07-06 23:11:10 +00:00
// ownership of the supplied data.
// TODO: Maybe introduce a _Unset() to avoid potential future
// problems.
fDeviceData = NULL;
}
return error;
}
// _AcceptVisitor
bool
BDiskDevice::_AcceptVisitor(BDiskDeviceVisitor *visitor, int32 level)
{
return visitor->Visit(this);
}
2003-07-06 23:11:10 +00:00
#if 0
// Path
/*! \brief Returns the path to the device.
\return The path to the device.
*/
const char *
BDiskDevice::Path() const
{
return (fPath[0] != '\0' ? fPath : NULL);
}
2003-02-16 18:07:23 +00:00
// skip_string_component
static
const char *
skip_string_component(const char *str, const char *component)
{
const char *remainder = NULL;
if (str && component) {
size_t len = strlen(component);
if (!strncmp(str, component, len))
remainder = str + len;
}
return remainder;
}
// GetName
/*! \brief Returns a human readable name for the device.
The method tries to interpret the device path, which it can do only for
floppy, IDE and SCSI devices, for others the device path is returned.
\param name Pointer to a pre-allocated BString to be set to the device
name.
\param includeBusID \c true, if the bus ID shall be included in the name
2003-02-16 18:07:23 +00:00
to be returned (of interest for SCSI devices).
\param includeLUN \c true, if the LUN shall be included in the name
2003-02-16 18:07:23 +00:00
to be returned (of interest for SCSI devices).
\return
- \c B_OK: Everything went fine.
- \c B_BAD_VALUE: \c NULL \a name.
- \c B_NO_INIT: The device is not properly initialized.
*/
2003-02-16 18:07:23 +00:00
status_t
BDiskDevice::GetName(BString *name, bool includeBusID, bool includeLUN) const
{
2003-02-16 18:07:23 +00:00
// check params and initialization
status_t error = (name ? B_OK : B_BAD_VALUE);
const char *path = Path();
if (error == B_OK && !path)
error = B_BAD_VALUE;
// analyze the device path
if (error == B_OK) {
bool recognized = false;
const char *prefix = "/dev/disk/";
size_t prefixLen = strlen(prefix);
if (!strncmp(path, prefix, prefixLen)) {
path = path + prefixLen;
// check first component
const char *tail = NULL;
// floppy
if (skip_string_component(path, "floppy/")) {
name->SetTo("floppy");
recognized = true;
// ide
} else if ((tail = skip_string_component(path, "ide/"))) {
int controller = 0;
bool master = false;
const char *_tail = tail;
if ((((tail = skip_string_component(_tail, "atapi/")))
|| ((tail = skip_string_component(_tail, "ata/"))))
&& isdigit(*tail)) {
controller = atoi(tail);
master = false;
if ((tail = strchr(tail, '/'))) {
tail++;
if (skip_string_component(tail, "master/")) {
master = true;
recognized = true;
} else if (skip_string_component(tail, "slave/"))
recognized = true;
}
}
if (recognized) {
name->SetTo("IDE ");
*name << (master ? "master" : "slave") << " bus:"
<< controller;
}
// scsi
} else if ((tail = skip_string_component(path, "scsi/"))) {
int bus = 0;
int id = 0;
int lun = 0;
if (sscanf(tail, "%d/%d/%d/raw", &bus, &id, &lun) == 3) {
recognized = true;
name->SetTo("SCSI");
if (includeBusID)
*name << " bus:" << bus;
*name << " id:" << id;
if (includeLUN)
*name << " lun:" << lun;
}
}
}
if (!recognized)
name->SetTo(path);
}
return error;
}
// GetName
/*! \brief Returns a human readable name for the device.
The method tries to interpret the device path, which it can do only for
floppy, IDE and SCSI devices, for others the device path is returned.
\param name Pointer to a pre-allocated char buffer into which the device
name shall be copied.
\param includeBusID \c true, if the bus ID shall be included in the name
2003-02-16 18:07:23 +00:00
to be returned (of interest for SCSI devices).
\param includeLUN \c true, if the LUN shall be included in the name
2003-02-16 18:07:23 +00:00
to be returned (of interest for SCSI devices).
- \c B_OK: Everything went fine.
- \c B_BAD_VALUE: \c NULL \a name.
- \c B_NO_INIT: The device is not properly initialized.
*/
2003-02-16 18:07:23 +00:00
status_t
BDiskDevice::GetName(char *name, bool includeBusID, bool includeLUN) const
{
2003-02-16 18:07:23 +00:00
status_t error = (name ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
BString _name;
error = GetName(&_name, includeBusID, includeLUN);
if (error == B_OK)
strcpy(name, _name.String());
}
return error;
}
// IsReadOnly
/*! \brief Returns whether the device is read-only.
\return \c true, if the device is read-only, \c false otherwise.
*/
bool
BDiskDevice::IsReadOnly() const
{
return fReadOnly;
}
// IsFloppy
/*! \brief Returns whether the device is a floppy device.
\return \c true, if the device is a floppy device, \c false otherwise.
*/
bool
BDiskDevice::IsFloppy() const
{
return !strcmp(fPath, "/dev/disk/floppy/raw");
}
// Type
/*! \brief Returns the type of the device.
The type may be one of the following (note, that not all types make sense
for storage device, but they are listed anyway):
- \c B_DISK: Hard disks, floppy disks, etc.
- \c B_TAPE: Tape drives.
- \c B_PRINTER: Printers.
- \c B_CPU: CPU devices.
- \c B_WORM: Write-once, read-many devives.
- \c B_CD: CD ROMs.
- \c B_SCANNER: Scanners.
- \c B_OPTICAL: Optical devices.
- \c B_JUKEBOX: Jukeboxes.
- \c B_NETWORK: Network devices.
\return The type of the device.
*/
uint8
BDiskDevice::Type() const
{
return fType;
}
// UniqueID
/*! \brief Returns a unique identifier for this device.
The ID is not persistent, i.e. in general won't be the same after
rebooting.
\see BDiskDeviceRoster::GetDeviceWithID().
\return A unique identifier for this device.
*/
int32
BDiskDevice::UniqueID() const
{
return fUniqueID;
}
// LowLevelFormat
/*! \brief Low level formats the device.
\return \c B_OK, if everything went fine, another error code otherwise.
*/
status_t
BDiskDevice::LowLevelFormat()
{
return B_ERROR; // not implemented
}
// VisitEachSession
/*! \brief Iterates through the device's sessions.
The supplied visitor's Visit(BSession*) is invoked for each session.
If Visit() returns \c true, the iteration is terminated and the BSession
object just visited is returned.
\param visitor The visitor.
\return The BSession object at which the iteration was terminated, or
\c NULL, if the iteration has not been terminated.
*/
BSession *
BDiskDevice::VisitEachSession(BDiskDeviceVisitor *visitor)
{
if (visitor) {
for (int32 i = 0; BSession *session = SessionAt(i); i++) {
if (visitor->Visit(session))
return session;
}
}
return NULL;
}
// VisitEachPartition
/*! \brief Iterates through the device's partitions.
The supplied visitor's Visit(BPartition*) is invoked for each partition.
If Visit() returns \c true, the iteration is terminated and the BPartition
object just visited is returned.
\param visitor The visitor.
\return The BPartition object at which the iteration was terminated, or
\c NULL, if the iteration has not been terminated.
*/
BPartition *
BDiskDevice::VisitEachPartition(BDiskDeviceVisitor *visitor)
{
if (visitor) {
for (int32 i = 0; BSession *session = SessionAt(i); i++) {
if (BPartition *partition = session->VisitEachPartition(visitor))
return partition;
}
}
return NULL;
}
// Traverse
/*! \brief Pre-order traverses the tree of the spanned by the BDiskDevice and
its subobjects.
The supplied visitor's Visit() is invoked for the device object itself,
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
BDiskDevice::Traverse(BDiskDeviceVisitor *visitor)
{
if (visitor) {
// visit the device itself
if (visitor->Visit(this))
return true;
for (int32 i = 0; BSession *session = SessionAt(i); i++) {
// visit the session
if (visitor->Visit(session))
return true;
// visit all partitions on the session
if (session->VisitEachPartition(visitor))
return true;
}
}
return false;
}
// SessionWithID
/*! \brief Returns the session on the device, that has a certain ID.
\param id The ID of the session to be returned.
\return The session with ID \a id, or \c NULL, if a session with that
ID does not exist on this device.
*/
BSession *
BDiskDevice::SessionWithID(int32 id)
{
IDFinderVisitor visitor(id);
return VisitEachSession(&visitor);
}
// PartitionWithID
/*! \brief Returns the partition on the device, that has a certain ID.
\param id The ID of the partition to be returned.
\return The partition with ID \a id, or \c NULL, if a partition with that
ID does not exist on this device.
*/
BPartition *
BDiskDevice::PartitionWithID(int32 id)
{
IDFinderVisitor visitor(id);
return VisitEachPartition(&visitor);
}
// copy constructor
/*! \brief Privatized copy constructor to avoid usage.
*/
BDiskDevice::BDiskDevice(const BDiskDevice &)
{
}
// =
/*! \brief Privatized assignment operator to avoid usage.
*/
BDiskDevice &
BDiskDevice::operator=(const BDiskDevice &)
{
return *this;
}
// find_string
static
status_t
find_string(BMessage *message, const char *name, char *buffer)
{
const char *str;
status_t error = message->FindString(name, &str);
if (error == B_OK)
strcpy(buffer, str);
return error;
}
// _Unarchive
status_t
BDiskDevice::_Unarchive(BMessage *archive)
{
//printf("BDiskDevice::_Unarchive()\n");
//archive->PrintToStream();
Unset();
status_t error = (archive ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
// ID and change counter
if (error == B_OK)
error = archive->FindInt32("id", &fUniqueID);
if (error == B_OK)
error = archive->FindInt32("change_counter", &fChangeCounter);
// geometry
if (error == B_OK)
error = archive->FindInt64("size", &fSize);
if (error == B_OK)
error = archive->FindInt32("block_size", &fBlockSize);
if (error == B_OK)
error = archive->FindInt8("type", (int8*)&fType);
if (error == B_OK)
error = archive->FindBool("removable", &fRemovable);
if (error == B_OK)
error = archive->FindBool("read_only", &fReadOnly);
// other data
if (error == B_OK)
error = find_string(archive, "path", fPath);
if (error == B_OK)
error = archive->FindInt32("media_status", &fMediaStatus);
// sessions
type_code fieldType;
int32 count = 0;
if (error == B_OK) {
if (archive->GetInfo("sessions", &fieldType, &count) != B_OK)
count = 0;
}
for (int32 i = 0; error == B_OK && i < count; i++) {
// get the archived session
BMessage sessionArchive;
error = archive->FindMessage("sessions", i, &sessionArchive);
// allocate a session object
BSession *session = NULL;
if (error == B_OK) {
session = new(nothrow) BSession;
if (!session)
error = B_NO_MEMORY;
}
// unarchive the session
if (error == B_OK)
error = session->_Unarchive(&sessionArchive);
// add the session
if (error == B_OK && !_AddSession(session))
error = B_NO_MEMORY;
// cleanup on error
if (error != B_OK && session)
delete session;
}
}
// cleanup on error
if (error != B_OK)
Unset();
//printf("BDiskDevice::_Unarchive() done: %s\n", strerror(error));
return error;
}
2003-02-16 18:07:23 +00:00
// _Update
status_t
BDiskDevice::_Update(BMessage *archive)
{
status_t error = (archive ? B_OK : B_BAD_VALUE);
bool upToDate = false;
if (error == B_OK) {
// ID and change counter
int32 id;
if (error == B_OK)
error = archive->FindInt32("id", &id);
if (error == B_OK && id != fUniqueID)
error = B_ERROR;
int32 changeCounter;
if (error == B_OK)
error = archive->FindInt32("change_counter", &changeCounter);
upToDate = (fChangeCounter == changeCounter);
fChangeCounter = changeCounter;
}
if (error == B_OK && !upToDate) {
// geometry
if (error == B_OK)
error = archive->FindInt64("size", &fSize);
if (error == B_OK)
error = archive->FindInt32("block_size", &fBlockSize);
if (error == B_OK)
error = archive->FindInt8("type", (int8*)&fType);
if (error == B_OK)
error = archive->FindBool("removable", &fRemovable);
if (error == B_OK)
error = archive->FindBool("read_only", &fReadOnly);
// other data
if (error == B_OK)
error = find_string(archive, "path", fPath);
if (error == B_OK)
error = archive->FindInt32("media_status", &fMediaStatus);
// sessions
// copy old session list and empty it
BObjectList<BSession> sessions;
for (int32 i = 0; BSession *session = fSessions.ItemAt(i); i++)
sessions.AddItem(session);
for (int32 i = fSessions.CountItems() - 1; i >= 0; i--)
fSessions.RemoveItemAt(i);
// get the session count
type_code fieldType;
int32 count = 0;
if (error == B_OK) {
if (archive->GetInfo("sessions", &fieldType, &count) != B_OK)
count = 0;
}
for (int32 i = 0; error == B_OK && i < count; i++) {
// get the archived session
BMessage sessionArchive;
error = archive->FindMessage("sessions", i, &sessionArchive);
// check, whether we do already know that session
int32 sessionID;
if (error == B_OK)
error = sessionArchive.FindInt32("id", &sessionID);
BSession *session = NULL;
for (int32 k = 0; k < sessions.CountItems(); k++) {
BSession *oldSession = sessions.ItemAt(i);
if (oldSession->UniqueID() == sessionID) {
session = oldSession;
break;
}
}
if (error == B_OK) {
if (session) {
// the session is known: just update it
error = session->_Update(&sessionArchive);
} else {
// the session is unknown
// allocate a session object
session = new(nothrow) BSession;
if (!session)
error = B_NO_MEMORY;
// unarchive the session
if (error == B_OK)
error = session->_Unarchive(&sessionArchive);
}
}
// add the session
if (error == B_OK && !_AddSession(session))
error = B_NO_MEMORY;
// cleanup on error
if (error != B_OK && session)
delete session;
}
// delete all obsolete sessions
for (int32 i = sessions.CountItems() - 1; i >= 0; i--)
delete sessions.RemoveItemAt(i);
}
// cleanup on error
if (error != B_OK)
Unset();
return error;
}
// _AddSession
bool
BDiskDevice::_AddSession(BSession *session)
{
bool result = fSessions.AddItem(session);
if (result)
session->_SetDevice(this);
return result;
}
2003-07-06 23:11:10 +00:00
#endif //0