#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
BDiskDevice::BDiskDevice()
:
fDeviceData(NULL)
{
}
BDiskDevice::~BDiskDevice()
{
CancelModifications();
}
bool
BDiskDevice::HasMedia() const
{
return fDeviceData
&& (fDeviceData->device_flags & B_DISK_DEVICE_HAS_MEDIA) != 0;
}
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;
}
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);
}
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);
}
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;
}
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;
}
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;
}
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]);
}