* Copyright 2003-2017, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold, ingo_weinhold@gmx.de
* Axel Dörfler, axeld@pinc-software.de
*/
\brief Interface for userspace calls.
*/
#include <ddm_userland_interface.h>
#include <stdlib.h>
#include <AutoDeleter.h>
#include <fs/KPath.h>
#include <KDiskDevice.h>
#include <KDiskDeviceManager.h>
#include <KDiskDeviceUtils.h>
#include <KDiskSystem.h>
#include <KFileDiskDevice.h>
#include <syscall_args.h>
#include "UserDataWriter.h"
using namespace BPrivate::DiskDevice;
#define ERROR(x)
#define DUMMY_JOB_ID 0
indicating appropriate success or failure.
\param allowTruncation If \c true, does not return an error if
\a from is longer than \to. If \c false, returns \c B_NAME_TOO_LONG
if \a from is longer than \to.
*/
static status_t
ddm_strlcpy(char *to, const char *from, size_t size,
bool allowTruncation = false)
{
ssize_t fromLen = user_strlcpy(to, from, size);
if (fromLen < 0)
return fromLen;
if ((size_t)fromLen >= size && !allowTruncation)
return B_NAME_TOO_LONG;
return B_OK;
}
template<typename Type>
static inline status_t
copy_from_user_value(Type& value, const Type* userValue)
{
if (userValue == NULL)
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(userValue))
return B_BAD_ADDRESS;
return user_memcpy(&value, userValue, sizeof(Type));
}
template<typename Type>
static inline status_t
copy_to_user_value(Type* userValue, const Type& value)
{
if (userValue == NULL)
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(userValue))
return B_BAD_ADDRESS;
return user_memcpy(userValue, &value, sizeof(Type));
}
template<bool kAllowsNull>
struct UserStringParameter {
char* value;
inline UserStringParameter()
: value(NULL)
{
}
inline ~UserStringParameter()
{
free(value);
}
inline status_t Init(const char* userValue, size_t maxSize)
{
if (userValue == NULL) {
if (!kAllowsNull)
return B_BAD_VALUE;
return B_OK;
}
if (!IS_USER_ADDRESS(userValue))
return B_BAD_ADDRESS;
value = (char*)malloc(maxSize);
if (value == NULL)
return B_NO_MEMORY;
ssize_t bytesCopied = user_strlcpy(value, userValue, maxSize);
if (bytesCopied < 0)
return bytesCopied;
if ((size_t)bytesCopied >= maxSize)
return B_BUFFER_OVERFLOW;
return B_OK;
}
inline operator const char*()
{
return value;
}
inline operator char*()
{
return value;
}
};
#if 0
static void
move_descendants(KPartition *partition, off_t moveBy)
{
if (!partition)
return;
partition->SetOffset(partition->Offset() + moveBy);
for (int32 i = 0; KPartition *child = partition->ChildAt(i); i++)
move_descendants(child, moveBy);
}
static status_t
move_descendants_contents(KPartition *partition)
{
if (!partition)
return B_BAD_VALUE;
KDiskSystem *diskSystem = partition->DiskSystem();
if (diskSystem || partition->AlgorithmData()) {
status_t error = diskSystem->ShadowPartitionChanged(partition,
NULL, B_PARTITION_MOVE);
if (error != B_OK)
return error;
}
for (int32 i = 0; KPartition *child = partition->ChildAt(i); i++) {
status_t error = move_descendants_contents(child);
if (error != B_OK)
return error;
}
return B_OK;
}
#endif
partition_id
_user_get_next_disk_device_id(int32 *_cookie, size_t *neededSize)
{
int32 cookie;
status_t error = copy_from_user_value(cookie, _cookie);
if (error != B_OK)
return error;
partition_id id = B_ENTRY_NOT_FOUND;
KDiskDeviceManager *manager = KDiskDeviceManager::Default();
if (KDiskDevice *device = manager->RegisterNextDevice(&cookie)) {
PartitionRegistrar _(device, true);
id = device->ID();
if (neededSize != NULL) {
if (DeviceReadLocker locker = device) {
UserDataWriter writer;
device->WriteUserData(writer);
status_t status = copy_to_user_value(neededSize,
writer.AllocatedSize());
if (status != B_OK)
return status;
} else
id = B_ERROR;
}
}
error = copy_to_user_value(_cookie, cookie);
if (error != B_OK)
return error;
return id;
}
partition_id
_user_find_disk_device(const char *_filename, size_t *neededSize)
{
UserStringParameter<false> filename;
status_t error = filename.Init(_filename, B_PATH_NAME_LENGTH);
if (error != B_OK)
return error;
partition_id id = B_ENTRY_NOT_FOUND;
KDiskDeviceManager *manager = KDiskDeviceManager::Default();
if (KDiskDevice *device = manager->RegisterDevice(filename)) {
PartitionRegistrar _(device, true);
id = device->ID();
if (neededSize != NULL) {
if (DeviceReadLocker locker = device) {
UserDataWriter writer;
device->WriteUserData(writer);
error = copy_to_user_value(neededSize, writer.AllocatedSize());
if (error != B_OK)
return error;
} else
return B_ERROR;
}
}
return id;
}
partition_id
_user_find_partition(const char *_filename, size_t *neededSize)
{
UserStringParameter<false> filename;
status_t error = filename.Init(_filename, B_PATH_NAME_LENGTH);
if (error != B_OK)
return error;
KDiskDeviceManager *manager = KDiskDeviceManager::Default();
KPartition *partition = manager->RegisterPartition(filename);
if (partition == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar _(partition, true);
partition_id id = partition->ID();
if (neededSize != NULL) {
KDiskDevice *device = manager->RegisterDevice(partition->ID(), false);
if (device == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar _2(device, true);
if (DeviceReadLocker locker = device) {
UserDataWriter writer;
device->WriteUserData(writer);
error = copy_to_user_value(neededSize, writer.AllocatedSize());
if (error != B_OK)
return error;
} else
return B_ERROR;
}
return id;
}
partition_id
_user_find_file_disk_device(const char *_filename, size_t *neededSize)
{
UserStringParameter<false> filename;
status_t error = filename.Init(_filename, B_PATH_NAME_LENGTH);
if (error != B_OK)
return error;
KPath path(filename, KPath::NORMALIZE);
KDiskDeviceManager *manager = KDiskDeviceManager::Default();
KFileDiskDevice* device = manager->RegisterFileDevice(path.Path());
if (device == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar _(device, true);
partition_id id = device->ID();
if (neededSize != NULL) {
if (DeviceReadLocker locker = device) {
UserDataWriter writer;
device->WriteUserData(writer);
error = copy_to_user_value(neededSize, writer.AllocatedSize());
if (error != B_OK)
return error;
} else
return B_ERROR;
}
return id;
}
its partitions into the supplied buffer.
The function passes the buffer size required to hold the data back
through the \a _neededSize parameter, if the device could be found at
least and no serious error occured. If fails with \c B_BUFFER_OVERFLOW,
if the supplied buffer is too small or a \c NULL buffer is supplied
(and \c bufferSize is 0).
The device is identified by \a id. If \a deviceOnly is \c true, then
it must be the ID of a disk device, otherwise the disk device is
chosen, on which the partition \a id refers to resides.
\param id The ID of an arbitrary partition on the disk device (including
the disk device itself), whose data shall be returned
(if \a deviceOnly is \c false), or the ID of the disk device
itself (if \a deviceOnly is true).
\param deviceOnly Specifies whether only IDs of disk devices (\c true),
or also IDs of partitions (\c false) are accepted for \a id.
\param buffer The buffer into which the disk device data shall be written.
May be \c NULL.
\param bufferSize The size of \a buffer.
\param _neededSize Pointer to a variable into which the actually needed
buffer size is written. May be \c NULL.
\return
- \c B_OK: Everything went fine. The device was found and, if not \c NULL,
in \a _neededSize the actually needed buffer size is returned. And
\a buffer will contain the disk device data.
- \c B_BAD_VALUE: \c NULL \a buffer, but not 0 \a bufferSize.
- \c B_BUFFER_OVERFLOW: The supplied buffer was too small. \a _neededSize,
if not \c NULL, will contain the required buffer size.
- \c B_NO_MEMORY: Insufficient memory to complete the operation.
- \c B_ENTRY_NOT_FOUND: \a id is no valid disk device ID (if \a deviceOnly
is \c true) or not even a valid partition ID (if \a deviceOnly is
\c false).
- \c B_ERROR: An unexpected error occured.
- another error code...
*/
status_t
_user_get_disk_device_data(partition_id id, bool deviceOnly,
user_disk_device_data *buffer, size_t bufferSize, size_t *_neededSize)
{
if (buffer == NULL && bufferSize > 0)
return B_BAD_VALUE;
KDiskDeviceManager *manager = KDiskDeviceManager::Default();
KDiskDevice *device = manager->RegisterDevice(id, deviceOnly);
if (device == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar _(device, true);
if (DeviceReadLocker locker = device) {
UserDataWriter writer;
device->WriteUserData(writer);
size_t neededSize = writer.AllocatedSize();
if (_neededSize != NULL) {
status_t error = copy_ref_var_to_user(neededSize, _neededSize);
if (error != B_OK)
return error;
}
if (buffer == NULL || bufferSize < neededSize)
return B_BUFFER_OVERFLOW;
if (!IS_USER_ADDRESS(buffer))
return B_BAD_ADDRESS;
user_disk_device_data *kernelBuffer
= static_cast<user_disk_device_data*>(malloc(neededSize));
if (kernelBuffer == NULL)
return B_NO_MEMORY;
MemoryDeleter deleter(kernelBuffer);
writer.SetTo(kernelBuffer, bufferSize);
device->WriteUserData(writer);
if (writer.AllocatedSize() != neededSize) {
ERROR(("Size of written disk device user data changed from "
"%lu to %lu while device was locked!\n"));
return B_ERROR;
}
status_t error = writer.Relocate(buffer);
if (error != B_OK)
return error;
return user_memcpy(buffer, kernelBuffer, neededSize);
} else
return B_ERROR;
}
partition_id
_user_register_file_device(const char *_filename)
{
UserStringParameter<false> filename;
status_t error = filename.Init(_filename, B_PATH_NAME_LENGTH);
if (error != B_OK)
return error;
KPath path(filename, KPath::NORMALIZE);
KDiskDeviceManager *manager = KDiskDeviceManager::Default();
if (ManagerLocker locker = manager) {
KFileDiskDevice *device = manager->FindFileDevice(path.Path());
if (device != NULL)
return device->ID();
return manager->CreateFileDevice(path.Path());
}
return B_ERROR;
}
status_t
_user_unregister_file_device(partition_id deviceID, const char *_filename)
{
if (deviceID < 0 && _filename == NULL)
return B_BAD_VALUE;
KDiskDeviceManager *manager = KDiskDeviceManager::Default();
if (deviceID >= 0)
return manager->DeleteFileDevice(deviceID);
UserStringParameter<false> filename;
status_t error = filename.Init(_filename, B_PATH_NAME_LENGTH);
if (error != B_OK)
return error;
return manager->DeleteFileDevice(filename);
}
status_t
_user_get_file_disk_device_path(partition_id id, char* buffer,
size_t bufferSize)
{
if (id < 0 || buffer == NULL || bufferSize == 0)
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(buffer))
return B_BAD_ADDRESS;
KDiskDeviceManager *manager = KDiskDeviceManager::Default();
KDiskDevice *device = manager->RegisterDevice(id, true);
if (device != NULL) {
PartitionRegistrar _(device, true);
if (DeviceReadLocker locker = device) {
KFileDiskDevice* fileDevice
= dynamic_cast<KFileDiskDevice*>(device);
if (fileDevice == NULL)
return B_BAD_VALUE;
ssize_t copied = user_strlcpy(buffer, fileDevice->FilePath(),
bufferSize);
if (copied < 0)
return copied;
return (size_t)copied < bufferSize ? B_OK : B_BUFFER_OVERFLOW;
}
}
return B_ERROR;
}
status_t
_user_get_disk_system_info(disk_system_id id, user_disk_system_info *_info)
{
if (_info == NULL)
return B_BAD_VALUE;
KDiskDeviceManager *manager = KDiskDeviceManager::Default();
if (ManagerLocker locker = manager) {
KDiskSystem *diskSystem = manager->FindDiskSystem(id);
if (diskSystem != NULL) {
user_disk_system_info info;
diskSystem->GetInfo(&info);
return copy_to_user_value(_info, info);
}
}
return B_ENTRY_NOT_FOUND;
}
status_t
_user_get_next_disk_system_info(int32 *_cookie, user_disk_system_info *_info)
{
if (_info == NULL)
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(_info))
return B_BAD_ADDRESS;
int32 cookie;
status_t result = copy_from_user_value(cookie, _cookie);
if (result != B_OK)
return result;
result = B_ENTRY_NOT_FOUND;
KDiskDeviceManager *manager = KDiskDeviceManager::Default();
if (ManagerLocker locker = manager) {
KDiskSystem *diskSystem = manager->NextDiskSystem(&cookie);
if (diskSystem != NULL) {
user_disk_system_info info;
diskSystem->GetInfo(&info);
result = copy_to_user_value(_info, info);
}
}
status_t error = copy_to_user_value(_cookie, cookie);
if (error != B_OK)
result = error;
return result;
}
status_t
_user_find_disk_system(const char *_name, user_disk_system_info *_info)
{
if (_name == NULL || _info == NULL)
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(_name) || !IS_USER_ADDRESS(_info))
return B_BAD_ADDRESS;
char name[B_DISK_SYSTEM_NAME_LENGTH];
status_t error = ddm_strlcpy(name, _name, B_DISK_SYSTEM_NAME_LENGTH);
if (error != B_OK)
return error;
KDiskDeviceManager *manager = KDiskDeviceManager::Default();
if (ManagerLocker locker = manager) {
KDiskSystem *diskSystem = manager->FindDiskSystem(name);
if (diskSystem != NULL) {
user_disk_system_info info;
diskSystem->GetInfo(&info);
return copy_to_user_value(_info, info);
}
}
return B_ENTRY_NOT_FOUND;
}
status_t
_user_defragment_partition(partition_id partitionID, int32* _changeCounter)
{
int32 changeCounter;
status_t error = copy_from_user_value(changeCounter, _changeCounter);
if (error != B_OK)
return error;
KDiskDeviceManager* manager = KDiskDeviceManager::Default();
KPartition* partition = manager->WriteLockPartition(partitionID);
if (partition == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar1(partition, true);
PartitionRegistrar registrar2(partition->Device(), true);
DeviceWriteLocker locker(partition->Device(), true);
if (changeCounter != partition->ChangeCounter())
return B_BAD_VALUE;
KDiskSystem* diskSystem = partition->DiskSystem();
if (diskSystem == NULL)
return B_BAD_VALUE;
if (!partition->CheckAndMarkBusy(false))
return B_BUSY;
locker.Unlock();
error = diskSystem->Defragment(partition, DUMMY_JOB_ID);
locker.Lock();
partition->UnmarkBusy(false);
if (error != B_OK)
return error;
return copy_to_user_value(_changeCounter, partition->ChangeCounter());
}
status_t
_user_repair_partition(partition_id partitionID, int32* _changeCounter,
bool checkOnly)
{
int32 changeCounter;
status_t error = copy_from_user_value(changeCounter, _changeCounter);
if (error != B_OK)
return error;
KDiskDeviceManager* manager = KDiskDeviceManager::Default();
KPartition* partition = manager->WriteLockPartition(partitionID);
if (partition == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar1(partition, true);
PartitionRegistrar registrar2(partition->Device(), true);
DeviceWriteLocker locker(partition->Device(), true);
if (changeCounter != partition->ChangeCounter())
return B_BAD_VALUE;
KDiskSystem* diskSystem = partition->DiskSystem();
if (diskSystem == NULL)
return B_BAD_VALUE;
if (!partition->CheckAndMarkBusy(false))
return B_BUSY;
locker.Unlock();
error = diskSystem->Repair(partition, checkOnly, DUMMY_JOB_ID);
locker.Lock();
partition->UnmarkBusy(false);
if (error != B_OK)
return error;
return copy_to_user_value(_changeCounter, partition->ChangeCounter());
}
status_t
_user_resize_partition(partition_id partitionID, int32* _changeCounter,
partition_id childID, int32* _childChangeCounter, off_t size,
off_t contentSize)
{
int32 changeCounter;
int32 childChangeCounter;
status_t error = copy_from_user_value(changeCounter, _changeCounter);
if (error == B_OK)
error = copy_from_user_value(childChangeCounter, _childChangeCounter);
if (error != B_OK)
return error;
KDiskDeviceManager* manager = KDiskDeviceManager::Default();
KPartition* partition = manager->WriteLockPartition(partitionID);
if (partition == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar1(partition, true);
PartitionRegistrar registrar2(partition->Device(), true);
DeviceWriteLocker locker(partition->Device(), true);
KPartition* child = manager->RegisterPartition(childID);
if (child == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar3(child, true);
if (changeCounter != partition->ChangeCounter()
|| childChangeCounter != child->ChangeCounter()) {
return B_BAD_VALUE;
}
KDiskSystem* diskSystem = partition->DiskSystem();
if (diskSystem == NULL)
return B_BAD_VALUE;
if (child->Parent() != partition)
return B_BAD_VALUE;
if (size < 0 || contentSize < 0 || size < contentSize
|| size > partition->ContentSize()) {
return B_BAD_VALUE;
}
if (partition->IsBusy() || child->IsBusy())
return B_BUSY;
partition->SetBusy(true);
child->SetBusy(true);
locker.Unlock();
if (child->DiskSystem() && contentSize < child->ContentSize())
error = child->DiskSystem()->Resize(child, contentSize, DUMMY_JOB_ID);
if (error == B_OK && size != child->Size())
error = diskSystem->ResizeChild(child, size, DUMMY_JOB_ID);
if (error == B_OK && child->DiskSystem()
&& contentSize > child->ContentSize()) {
error = child->DiskSystem()->Resize(child, contentSize, DUMMY_JOB_ID);
}
locker.Lock();
partition->SetBusy(false);
child->SetBusy(false);
if (error != B_OK)
return error;
error = copy_to_user_value(_changeCounter, partition->ChangeCounter());
if (error == B_OK)
error = copy_to_user_value(_childChangeCounter, child->ChangeCounter());
return error;
}
status_t
_user_move_partition(partition_id partitionID, int32* changeCounter,
partition_id childID, int32* childChangeCounter, off_t newOffset,
partition_id* descendantIDs, int32* descendantChangeCounters,
int32 descendantCount)
{
#if 0
KDiskDeviceManager *manager = KDiskDeviceManager::Default();
KPartition *partition = manager->WriteLockPartition(partitionID);
if (!partition)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar1(partition, true);
PartitionRegistrar registrar2(partition->Device(), true);
DeviceWriteLocker locker(partition->Device(), true);
if (newOffset == partition->Offset())
return B_OK;
off_t proposedOffset = newOffset;
status_t error = validate_move_partition(partition, changeCounter,
&proposedOffset, true);
if (error != B_OK)
return error;
if (proposedOffset != newOffset)
return B_BAD_VALUE;
off_t moveBy = newOffset - partition->Offset();
move_descendants(partition, moveBy);
partition->Changed(B_PARTITION_CHANGED_OFFSET);
error = partition->Parent()->DiskSystem()->ShadowPartitionChanged(
partition->Parent(), partition, B_PARTITION_MOVE_CHILD);
if (error != B_OK)
return error;
return move_descendants_contents(partition);
#endif
return B_BAD_VALUE;
}
status_t
_user_set_partition_name(partition_id partitionID, int32* _changeCounter,
partition_id childID, int32* _childChangeCounter, const char* _name)
{
UserStringParameter<false> name;
int32 changeCounter;
int32 childChangeCounter;
status_t error = name.Init(_name, B_DISK_DEVICE_NAME_LENGTH);
if (error == B_OK)
error = copy_from_user_value(changeCounter, _changeCounter);
if (error == B_OK)
error = copy_from_user_value(childChangeCounter, _childChangeCounter);
if (error != B_OK)
return error;
KDiskDeviceManager* manager = KDiskDeviceManager::Default();
KPartition* partition = manager->WriteLockPartition(partitionID);
if (partition == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar1(partition, true);
PartitionRegistrar registrar2(partition->Device(), true);
DeviceWriteLocker locker(partition->Device(), true);
KPartition* child = manager->RegisterPartition(childID);
if (child == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar3(child, true);
if (changeCounter != partition->ChangeCounter()
|| childChangeCounter != child->ChangeCounter()) {
return B_BAD_VALUE;
}
KDiskSystem* diskSystem = partition->DiskSystem();
if (diskSystem == NULL)
return B_BAD_VALUE;
if (child->Parent() != partition)
return B_BAD_VALUE;
if (partition->IsBusy() || child->IsBusy())
return B_BUSY;
partition->SetBusy(true);
child->SetBusy(true);
locker.Unlock();
error = diskSystem->SetName(child, name.value, DUMMY_JOB_ID);
locker.Lock();
partition->SetBusy(false);
child->SetBusy(false);
if (error != B_OK)
return error;
error = copy_to_user_value(_changeCounter, partition->ChangeCounter());
if (error == B_OK)
error = copy_to_user_value(_childChangeCounter, child->ChangeCounter());
return error;
}
status_t
_user_set_partition_content_name(partition_id partitionID,
int32* _changeCounter, const char* _name)
{
UserStringParameter<true> name;
int32 changeCounter;
status_t error = name.Init(_name, B_DISK_DEVICE_NAME_LENGTH);
if (error == B_OK)
error = copy_from_user_value(changeCounter, _changeCounter);
if (error != B_OK)
return error;
KDiskDeviceManager* manager = KDiskDeviceManager::Default();
KPartition* partition = manager->WriteLockPartition(partitionID);
if (partition == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar1(partition, true);
PartitionRegistrar registrar2(partition->Device(), true);
DeviceWriteLocker locker(partition->Device(), true);
if (changeCounter != partition->ChangeCounter())
return B_BAD_VALUE;
KDiskSystem* diskSystem = partition->DiskSystem();
if (diskSystem == NULL)
return B_BAD_VALUE;
if (!partition->CheckAndMarkBusy(false))
return B_BUSY;
locker.Unlock();
error = diskSystem->SetContentName(partition, name.value, DUMMY_JOB_ID);
locker.Lock();
partition->UnmarkBusy(false);
if (error != B_OK)
return error;
return copy_to_user_value(_changeCounter, partition->ChangeCounter());
}
status_t
_user_set_partition_type(partition_id partitionID, int32* _changeCounter,
partition_id childID, int32* _childChangeCounter, const char* _type)
{
UserStringParameter<false> type;
int32 changeCounter;
int32 childChangeCounter;
status_t error = type.Init(_type, B_DISK_DEVICE_TYPE_LENGTH);
if (error == B_OK)
error = copy_from_user_value(changeCounter, _changeCounter);
if (error == B_OK)
error = copy_from_user_value(childChangeCounter, _childChangeCounter);
if (error != B_OK)
return error;
KDiskDeviceManager* manager = KDiskDeviceManager::Default();
KPartition* partition = manager->WriteLockPartition(partitionID);
if (partition == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar1(partition, true);
PartitionRegistrar registrar2(partition->Device(), true);
DeviceWriteLocker locker(partition->Device(), true);
KPartition* child = manager->RegisterPartition(childID);
if (child == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar3(child, true);
if (changeCounter != partition->ChangeCounter()
|| childChangeCounter != child->ChangeCounter()) {
return B_BAD_VALUE;
}
KDiskSystem* diskSystem = partition->DiskSystem();
if (diskSystem == NULL)
return B_BAD_VALUE;
if (child->Parent() != partition)
return B_BAD_VALUE;
if (partition->IsBusy() || child->IsBusy())
return B_BUSY;
partition->SetBusy(true);
child->SetBusy(true);
locker.Unlock();
error = diskSystem->SetType(child, type.value, DUMMY_JOB_ID);
locker.Lock();
partition->SetBusy(false);
child->SetBusy(false);
if (error != B_OK)
return error;
error = copy_to_user_value(_changeCounter, partition->ChangeCounter());
if (error == B_OK)
error = copy_to_user_value(_childChangeCounter, child->ChangeCounter());
return error;
}
status_t
_user_set_partition_parameters(partition_id partitionID, int32* _changeCounter,
partition_id childID, int32* _childChangeCounter, const char* _parameters)
{
UserStringParameter<true> parameters;
int32 changeCounter;
int32 childChangeCounter;
status_t error
= parameters.Init(_parameters, B_DISK_DEVICE_MAX_PARAMETER_SIZE);
if (error == B_OK)
error = copy_from_user_value(changeCounter, _changeCounter);
if (error == B_OK)
error = copy_from_user_value(childChangeCounter, _childChangeCounter);
if (error != B_OK)
return error;
KDiskDeviceManager* manager = KDiskDeviceManager::Default();
KPartition* partition = manager->WriteLockPartition(partitionID);
if (partition == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar1(partition, true);
PartitionRegistrar registrar2(partition->Device(), true);
DeviceWriteLocker locker(partition->Device(), true);
KPartition* child = manager->RegisterPartition(childID);
if (child == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar3(child, true);
if (changeCounter != partition->ChangeCounter()
|| childChangeCounter != child->ChangeCounter()) {
return B_BAD_VALUE;
}
KDiskSystem* diskSystem = partition->DiskSystem();
if (diskSystem == NULL)
return B_BAD_VALUE;
if (child->Parent() != partition)
return B_BAD_VALUE;
if (partition->IsBusy() || child->IsBusy())
return B_BUSY;
partition->SetBusy(true);
child->SetBusy(true);
locker.Unlock();
error = diskSystem->SetParameters(child, parameters.value, DUMMY_JOB_ID);
locker.Lock();
partition->SetBusy(false);
child->SetBusy(false);
if (error != B_OK)
return error;
error = copy_to_user_value(_changeCounter, partition->ChangeCounter());
if (error == B_OK)
error = copy_to_user_value(_childChangeCounter, child->ChangeCounter());
return error;
}
status_t
_user_set_partition_content_parameters(partition_id partitionID,
int32* _changeCounter, const char* _parameters)
{
UserStringParameter<true> parameters;
int32 changeCounter;
status_t error
= parameters.Init(_parameters, B_DISK_DEVICE_MAX_PARAMETER_SIZE);
if (error == B_OK)
error = copy_from_user_value(changeCounter, _changeCounter);
if (error != B_OK)
return error;
KDiskDeviceManager* manager = KDiskDeviceManager::Default();
KPartition* partition = manager->WriteLockPartition(partitionID);
if (partition == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar1(partition, true);
PartitionRegistrar registrar2(partition->Device(), true);
DeviceWriteLocker locker(partition->Device(), true);
if (changeCounter != partition->ChangeCounter())
return B_BAD_VALUE;
KDiskSystem* diskSystem = partition->DiskSystem();
if (diskSystem == NULL)
return B_BAD_VALUE;
if (!partition->CheckAndMarkBusy(true))
return B_BUSY;
locker.Unlock();
error = diskSystem->SetContentParameters(partition, parameters.value,
DUMMY_JOB_ID);
locker.Lock();
partition->UnmarkBusy(true);
if (error != B_OK)
return error;
return copy_to_user_value(_changeCounter, partition->ChangeCounter());
}
status_t
_user_initialize_partition(partition_id partitionID, int32* _changeCounter,
const char* _diskSystemName, const char* _name, const char* _parameters)
{
UserStringParameter<false> diskSystemName;
UserStringParameter<true> name;
UserStringParameter<true> parameters;
int32 changeCounter;
status_t error
= diskSystemName.Init(_diskSystemName, B_DISK_SYSTEM_NAME_LENGTH);
if (error == B_OK)
error = name.Init(_name, B_DISK_DEVICE_NAME_LENGTH);
if (error == B_OK)
error = parameters.Init(_parameters, B_DISK_DEVICE_MAX_PARAMETER_SIZE);
if (error == B_OK)
error = copy_from_user_value(changeCounter, _changeCounter);
if (error != B_OK)
return error;
KDiskDeviceManager* manager = KDiskDeviceManager::Default();
KPartition* partition = manager->WriteLockPartition(partitionID);
if (partition == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar1(partition, true);
PartitionRegistrar registrar2(partition->Device(), true);
DeviceWriteLocker locker(partition->Device(), true);
if (changeCounter != partition->ChangeCounter())
return B_BAD_VALUE;
if (partition->DiskSystem() != NULL)
return B_BAD_VALUE;
KDiskSystem *diskSystem = manager->LoadDiskSystem(diskSystemName.value,
true);
if (diskSystem == NULL)
return B_ENTRY_NOT_FOUND;
DiskSystemLoader loader(diskSystem, true);
if (!partition->CheckAndMarkBusy(true))
return B_BUSY;
locker.Unlock();
error = diskSystem->Initialize(partition, name.value, parameters.value,
DUMMY_JOB_ID);
locker.Lock();
partition->UnmarkBusy(true);
if (error != B_OK)
return error;
if (partition->DiskSystem() == NULL)
partition->SetDiskSystem(diskSystem);
return copy_to_user_value(_changeCounter, partition->ChangeCounter());
}
status_t
_user_uninitialize_partition(partition_id partitionID, int32* _changeCounter,
partition_id parentID, int32* _parentChangeCounter)
{
int32 changeCounter;
int32 parentChangeCounter;
bool haveParent = parentID >= 0;
status_t error = copy_from_user_value(changeCounter, _changeCounter);
if (haveParent && error == B_OK)
error = copy_from_user_value(parentChangeCounter, _parentChangeCounter);
if (error != B_OK)
return error;
KDiskDeviceManager* manager = KDiskDeviceManager::Default();
KPartition* partition = manager->WriteLockPartition(partitionID);
if (partition == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar1(partition, true);
PartitionRegistrar registrar2(partition->Device(), true);
DeviceWriteLocker locker(partition->Device(), true);
KPartition* parent = NULL;
if (haveParent)
parent = manager->RegisterPartition(parentID);
PartitionRegistrar registrar3(parent, true);
if (changeCounter != partition->ChangeCounter())
return B_BAD_VALUE;
if (haveParent && parentChangeCounter != parent->ChangeCounter())
return B_BAD_VALUE;
if (partition->DiskSystem() == NULL)
return B_BAD_VALUE;
if (!partition->CheckAndMarkBusy(true))
return B_BUSY;
if (partition->IsMounted() || partition->IsChildMounted())
return B_BAD_VALUE;
KDiskSystem* diskSystem = partition->DiskSystem();
locker.Unlock();
if (diskSystem != NULL)
diskSystem->Uninitialize(partition, DUMMY_JOB_ID);
locker.Lock();
error = partition->UninitializeContents(true);
partition->UnmarkBusy(true);
if (error != B_OK)
return error;
error = copy_to_user_value(_changeCounter, partition->ChangeCounter());
if (haveParent && error == B_OK)
error = copy_to_user_value(_parentChangeCounter, parent->ChangeCounter());
return error;
}
status_t
_user_create_child_partition(partition_id partitionID, int32* _changeCounter,
off_t offset, off_t size, const char* _type, const char* _name,
const char* _parameters, partition_id* childID, int32* childChangeCounter)
{
UserStringParameter<false> type;
UserStringParameter<true> name;
UserStringParameter<true> parameters;
int32 changeCounter;
status_t error = type.Init(_type, B_DISK_DEVICE_TYPE_LENGTH);
if (error == B_OK)
error = name.Init(_name, B_DISK_DEVICE_NAME_LENGTH);
if (error == B_OK)
error = parameters.Init(_parameters, B_DISK_DEVICE_MAX_PARAMETER_SIZE);
if (error == B_OK)
error = copy_from_user_value(changeCounter, _changeCounter);
if (error != B_OK)
return error;
KDiskDeviceManager* manager = KDiskDeviceManager::Default();
KPartition* partition = manager->WriteLockPartition(partitionID);
if (partition == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar1(partition, true);
PartitionRegistrar registrar2(partition->Device(), true);
DeviceWriteLocker locker(partition->Device(), true);
if (changeCounter != partition->ChangeCounter())
return B_BAD_VALUE;
KDiskSystem* diskSystem = partition->DiskSystem();
if (diskSystem == NULL)
return B_BAD_VALUE;
if (!partition->CheckAndMarkBusy(false))
return B_BUSY;
locker.Unlock();
KPartition *child = NULL;
error = diskSystem->CreateChild(partition, offset, size, type.value,
name.value, parameters.value, DUMMY_JOB_ID, &child, -1);
locker.Lock();
partition->UnmarkBusy(false);
if (error != B_OK)
return error;
if (child == NULL)
return B_ERROR;
child->UnmarkBusy(true);
error = copy_to_user_value(_changeCounter, partition->ChangeCounter());
if (error == B_OK)
error = copy_to_user_value(childID, child->ID());
return error;
}
status_t
_user_delete_child_partition(partition_id partitionID, int32* _changeCounter,
partition_id childID, int32 childChangeCounter)
{
int32 changeCounter;
status_t error;
if ((error = copy_from_user_value(changeCounter, _changeCounter)) != B_OK)
return error;
KDiskDeviceManager* manager = KDiskDeviceManager::Default();
KPartition* partition = manager->WriteLockPartition(partitionID);
if (partition == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar1(partition, true);
PartitionRegistrar registrar2(partition->Device(), true);
DeviceWriteLocker locker(partition->Device(), true);
KPartition* child = manager->RegisterPartition(childID);
if (child == NULL)
return B_ENTRY_NOT_FOUND;
PartitionRegistrar registrar3(child, true);
if (changeCounter != partition->ChangeCounter()
|| childChangeCounter != child->ChangeCounter()) {
return B_BAD_VALUE;
}
KDiskSystem* diskSystem = partition->DiskSystem();
if (diskSystem == NULL)
return B_BAD_VALUE;
if (child->Parent() != partition)
return B_BAD_VALUE;
if (partition->IsBusy() || !child->CheckAndMarkBusy(true))
return B_BUSY;
partition->SetBusy(true);
locker.Unlock();
error = diskSystem->DeleteChild(child, DUMMY_JOB_ID);
locker.Lock();
partition->SetBusy(false);
child->UnmarkBusy(true);
if (error != B_OK)
return error;
return copy_to_user_value(_changeCounter, partition->ChangeCounter());
}
status_t
_user_start_watching_disks(uint32 eventMask, port_id port, int32 token)
{
KDiskDeviceManager* manager = KDiskDeviceManager::Default();
return manager->Notifications().UpdateUserListener(eventMask, port, token);
}
status_t
_user_stop_watching_disks(port_id port, int32 token)
{
KDiskDeviceManager* manager = KDiskDeviceManager::Default();
return manager->Notifications().RemoveUserListeners(port, token);
}