* Copyright 2003-2007, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <DiskDevice.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <new>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <DiskDevice.h>
#include <DiskDeviceVisitor.h>
#include <Drivers.h>
#include <Message.h>
#include <Path.h>
#include <syscalls.h>
#include <ddm_userland_interface_defs.h>
#include "DiskDeviceJob.h"
#include "DiskDeviceJobGenerator.h"
#include "DiskDeviceJobQueue.h"
#include <DiskSystemAddOnManager.h>
#undef TRACE
#ifdef TRACE_DISK_DEVICE
# define TRACE(x...) printf(x)
#else
# define TRACE(x...) do {} while (false)
#endif
\brief A BDiskDevice object represents a storage device.
*/
*/
BDiskDevice::BDiskDevice()
:
fDeviceData(NULL)
{
}
*/
BDiskDevice::~BDiskDevice()
{
CancelModifications();
}
\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) != 0;
}
\return \c true, if the device media are removable, \c false otherwise.
*/
bool
BDiskDevice::IsRemovableMedia() const
{
return fDeviceData
&& (fDeviceData->device_flags & B_DISK_DEVICE_REMOVABLE) != 0;
}
bool
BDiskDevice::IsReadOnlyMedia() const
{
return fDeviceData
&& (fDeviceData->device_flags & B_DISK_DEVICE_READ_ONLY) != 0;
}
bool
BDiskDevice::IsWriteOnceMedia() const
{
return fDeviceData
&& (fDeviceData->device_flags & B_DISK_DEVICE_WRITE_ONCE) != 0;
}
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
*/
status_t
BDiskDevice::Eject(bool update)
{
if (fDeviceData == NULL)
return B_NO_INIT;
if (!IsRemovableMedia())
return B_BAD_VALUE;
int fd = open(fDeviceData->path, O_RDONLY);
if (fd < 0)
return errno;
status_t status = B_OK;
if (ioctl(fd, B_EJECT_DEVICE) != 0)
status = errno;
close(fd);
if (status == B_OK && update)
status = Update();
return status;
}
status_t
BDiskDevice::SetTo(partition_id id)
{
return _SetTo(id, true, 0);
}
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.
*/
status_t
BDiskDevice::Update(bool* updated)
{
if (InitCheck() != B_OK)
return InitCheck();
user_disk_device_data* data = NULL;
status_t error = _GetData(ID(), true, 0, &data);
if (error == B_OK)
error = _Update(data, updated);
if (error != B_OK && data)
free(data);
return error;
}
void
BDiskDevice::Unset()
{
BPartition::_Unset();
free(fDeviceData);
fDeviceData = NULL;
}
status_t
BDiskDevice::InitCheck() const
{
return fDeviceData ? B_OK : B_NO_INIT;
}
status_t
BDiskDevice::GetPath(BPath* path) const
{
if (!path || !fDeviceData)
return B_BAD_VALUE;
return path->SetTo(fDeviceData->path);
}
bool
BDiskDevice::IsModified() const
{
if (InitCheck() != B_OK)
return false;
struct IsModifiedVisitor : public BDiskDeviceVisitor {
virtual bool Visit(BDiskDevice* device)
{
return Visit(device, 0);
}
virtual bool Visit(BPartition* partition, int32 level)
{
return partition->_IsModified();
}
} visitor;
return (VisitEachDescendant(&visitor) != NULL);
}
*
* Subsequent modifications are performed on so-called \a shadow structure
* and not written to device until \ref CommitModifications is called.
*
* \note This call locks the device. You need to call \ref CommitModifications
* or \ref CancelModifications to unlock it.
*/
status_t
BDiskDevice::PrepareModifications()
{
TRACE("%p->BDiskDevice::PrepareModifications()\n", this);
status_t error = InitCheck();
if (error != B_OK) {
TRACE(" InitCheck() failed\n");
return error;
}
if (fDelegate) {
TRACE(" already prepared!\n");
return B_BAD_VALUE;
}
error = DiskSystemAddOnManager::Default()->LoadDiskSystems();
if (error != B_OK) {
TRACE(" failed to load disk systems\n");
return error;
}
error = _CreateDelegates();
if (error == B_OK)
error = _InitDelegates();
else
TRACE(" failed to create delegates\n");
if (error != B_OK) {
TRACE(" failed to init delegates\n");
_DeleteDelegates();
DiskSystemAddOnManager::Default()->UnloadDiskSystems();
}
return error;
}
*
* Creates a set of jobs that perform all the changes done after
* \ref PrepareModifications. The changes are then written to device.
* Deletes and recreates all BPartition children to apply the changes,
* so cached pointers to BPartition objects cannot be used after this
* call.
* Unlocks the device for further use.
*/
status_t
BDiskDevice::CommitModifications(bool synchronously,
BMessenger progressMessenger, bool receiveCompleteProgressUpdates)
{
status_t error = InitCheck();
if (error != B_OK)
return error;
if (!fDelegate)
return B_BAD_VALUE;
DiskDeviceJobQueue jobQueue;
error = DiskDeviceJobGenerator(this, &jobQueue).GenerateJobs();
if (error == B_OK)
error = jobQueue.Execute();
_DeleteDelegates();
DiskSystemAddOnManager::Default()->UnloadDiskSystems();
if (error == B_OK)
error = _SetTo(ID(), true, 0);
return error;
}
*
* Nothing is written on the device and it is unlocked for further use.
*/
status_t
BDiskDevice::CancelModifications()
{
status_t error = InitCheck();
if (error != B_OK)
return error;
if (!fDelegate)
return B_BAD_VALUE;
_DeleteDelegates();
DiskSystemAddOnManager::Default()->UnloadDiskSystems();
if (error == B_OK)
error = _SetTo(ID(), true, 0);
return error;
}
up by a file.
*/
bool
BDiskDevice::IsFile() const
{
return fDeviceData
&& (fDeviceData->device_flags & B_DISK_DEVICE_IS_FILE) != 0;
}
status_t
BDiskDevice::GetFilePath(BPath* path) const
{
if (path == NULL)
return B_BAD_VALUE;
if (!IsFile())
return B_BAD_TYPE;
char pathBuffer[B_PATH_NAME_LENGTH];
status_t status = _kern_get_file_disk_device_path(
fDeviceData->device_partition_data.id, pathBuffer, sizeof(pathBuffer));
if (status != B_OK)
return status;
return path->SetTo(pathBuffer);
}
*/
BDiskDevice::BDiskDevice(const BDiskDevice&)
{
}
*/
BDiskDevice&
BDiskDevice::operator=(const BDiskDevice&)
{
return *this;
}
status_t
BDiskDevice::_GetData(partition_id id, bool deviceOnly, size_t neededSize,
user_disk_device_data** data)
{
void* buffer = NULL;
size_t bufferSize = 0;
if (neededSize > 0) {
buffer = malloc(neededSize);
if (!buffer)
return B_NO_MEMORY;
bufferSize = neededSize;
}
status_t error = B_OK;
do {
error = _kern_get_disk_device_data(id, deviceOnly,
(user_disk_device_data*)buffer, bufferSize, &neededSize);
if (error == B_BUFFER_OVERFLOW) {
free(buffer);
buffer = malloc(neededSize);
if (buffer)
bufferSize = neededSize;
else
error = B_NO_MEMORY;
}
} while (error == B_BUFFER_OVERFLOW);
if (error == B_OK)
*data = (user_disk_device_data*)buffer;
else
free(buffer);
return error;
}
status_t
BDiskDevice::_SetTo(partition_id id, bool deviceOnly, size_t neededSize)
{
Unset();
user_disk_device_data* data = NULL;
status_t error = _GetData(id, deviceOnly, neededSize, &data);
if (error == B_OK)
error = _SetTo(data);
if (error != B_OK && data)
free(data);
return error;
}
status_t
BDiskDevice::_SetTo(user_disk_device_data* data)
{
Unset();
if (!data)
return B_BAD_VALUE;
fDeviceData = data;
status_t error = BPartition::_SetTo(this, NULL,
&fDeviceData->device_partition_data);
if (error != B_OK) {
fDeviceData = NULL;
Unset();
}
return error;
}
status_t
BDiskDevice::_Update(user_disk_device_data* data, bool* updated)
{
if (!data || !fDeviceData || ID() != data->device_partition_data.id)
return B_BAD_VALUE;
bool _updated;
if (!updated)
updated = &_updated;
*updated = false;
_ClearUserData(&data->device_partition_data);
status_t error = _RemoveObsoleteDescendants(&data->device_partition_data,
updated);
if (error != B_OK)
return error;
error = BPartition::_Update(&data->device_partition_data, updated);
if (error == B_OK) {
user_disk_device_data* oldData = fDeviceData;
fDeviceData = data;
if (data->device_flags != oldData->device_flags
|| strcmp(data->path, oldData->path)) {
*updated = true;
}
free(oldData);
}
return error;
}
bool
BDiskDevice::_AcceptVisitor(BDiskDeviceVisitor* visitor, int32 level)
{
return visitor->Visit(this);
}
void
BDiskDevice::_ClearUserData(user_partition_data* data)
{
data->user_data = NULL;
for (int i = 0; i < data->child_count; i++)
_ClearUserData(data->children[i]);
}