ramdisk: Switch to CLI command for user interface
* Drop the old "echo to control device" interface in favor of an ioctl interface. * Add CLI program "ramdisk" to manage RAM disks.
This commit is contained in:
parent
5df58b522b
commit
25a83d13b9
47
headers/private/file_systems/ram_disk/ram_disk.h
Normal file
47
headers/private/file_systems/ram_disk/ram_disk.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _PRIVATE_FILE_SYSTEMS_RAM_DISK_RAM_DISK_H
|
||||
#define _PRIVATE_FILE_SYSTEMS_RAM_DISK_RAM_DISK_H
|
||||
|
||||
|
||||
#include <Drivers.h>
|
||||
#include <StorageDefs.h>
|
||||
|
||||
|
||||
#define RAM_DISK_CONTROL_DEVICE_NAME "disk/virtual/ram/control"
|
||||
#define RAM_DISK_RAW_DEVICE_BASE_NAME "disk/virtual/ram"
|
||||
|
||||
|
||||
enum {
|
||||
RAM_DISK_IOCTL_REGISTER = B_DEVICE_OP_CODES_END + 1,
|
||||
RAM_DISK_IOCTL_UNREGISTER,
|
||||
RAM_DISK_IOCTL_FLUSH,
|
||||
RAM_DISK_IOCTL_INFO
|
||||
};
|
||||
|
||||
|
||||
struct ram_disk_ioctl_register {
|
||||
uint64 size;
|
||||
char path[B_PATH_NAME_LENGTH];
|
||||
|
||||
// return value
|
||||
int32 id;
|
||||
};
|
||||
|
||||
|
||||
struct ram_disk_ioctl_unregister {
|
||||
int32 id;
|
||||
};
|
||||
|
||||
|
||||
struct ram_disk_ioctl_info {
|
||||
// return values
|
||||
int32 id;
|
||||
uint64 size;
|
||||
char path[B_PATH_NAME_LENGTH];
|
||||
};
|
||||
|
||||
|
||||
#endif // _PRIVATE_FILE_SYSTEMS_RAM_DISK_RAM_DISK_H
|
@ -4,6 +4,8 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <file_systems/ram_disk/ram_disk.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
@ -50,12 +52,12 @@ static const char* const kControlDeviceModuleName
|
||||
static const char* const kRawDeviceModuleName
|
||||
= "drivers/disk/virtual/ram_disk/raw/device_v1";
|
||||
|
||||
static const char* const kControlDeviceName
|
||||
= "disk/virtual/ram/control";
|
||||
static const char* const kRawDeviceBaseName = "disk/virtual/ram";
|
||||
static const char* const kControlDeviceName = RAM_DISK_CONTROL_DEVICE_NAME;
|
||||
static const char* const kRawDeviceBaseName = RAM_DISK_RAW_DEVICE_BASE_NAME;
|
||||
|
||||
static const char* const kFilePathItem = "ram_disk/file_path";
|
||||
static const char* const kDeviceSizeItem = "ram_disk/device_size";
|
||||
static const char* const kDeviceIDItem = "ram_disk/id";
|
||||
|
||||
|
||||
struct RawDevice;
|
||||
@ -65,6 +67,11 @@ struct device_manager_info* sDeviceManager;
|
||||
|
||||
static RawDeviceList sDeviceList;
|
||||
static mutex sDeviceListLock = MUTEX_INITIALIZER("ram disk device list");
|
||||
static uint64 sUsedRawDeviceIDs = 0;
|
||||
|
||||
|
||||
static int32 allocate_raw_device_id();
|
||||
static void free_raw_device_id(int32 id);
|
||||
|
||||
|
||||
struct Device {
|
||||
@ -100,19 +107,37 @@ struct ControlDevice : Device {
|
||||
{
|
||||
}
|
||||
|
||||
status_t Register(const char* filePath, uint64 deviceSize)
|
||||
status_t Register(const char* filePath, uint64 deviceSize, int32& _id)
|
||||
{
|
||||
int32 id = allocate_raw_device_id();
|
||||
if (id < 0)
|
||||
return B_BUSY;
|
||||
|
||||
device_attr attrs[] = {
|
||||
{B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
|
||||
{string: "RAM Disk Raw Device"}},
|
||||
{kFilePathItem, B_STRING_TYPE, {string: filePath}},
|
||||
{kDeviceSizeItem, B_UINT64_TYPE, {ui64: deviceSize}},
|
||||
{kDeviceIDItem, B_UINT32_TYPE, {ui32: (uint32)id}},
|
||||
{kFilePathItem, B_STRING_TYPE, {string: filePath}},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
return sDeviceManager->register_node(
|
||||
// If filePath is NULL, remove the attribute.
|
||||
if (filePath == NULL) {
|
||||
size_t count = sizeof(attrs) / sizeof(attrs[0]);
|
||||
memset(attrs + count - 2, 0, sizeof(attrs[0]));
|
||||
}
|
||||
|
||||
status_t error = sDeviceManager->register_node(
|
||||
sDeviceManager->get_parent_node(Node()), kDriverModuleName, attrs,
|
||||
NULL, NULL);
|
||||
if (error != B_OK) {
|
||||
free_raw_device_id(id);
|
||||
return error;
|
||||
}
|
||||
|
||||
_id = id;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
virtual status_t PublishDevice()
|
||||
@ -127,7 +152,8 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
|
||||
RawDevice(device_node* node)
|
||||
:
|
||||
Device(node),
|
||||
fIndex(-1),
|
||||
fID(-1),
|
||||
fUnregistered(false),
|
||||
fDeviceSize(0),
|
||||
fDeviceName(NULL),
|
||||
fFilePath(NULL),
|
||||
@ -139,7 +165,7 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
|
||||
|
||||
virtual ~RawDevice()
|
||||
{
|
||||
if (fIndex >= 0) {
|
||||
if (fID >= 0) {
|
||||
MutexLocker locker(sDeviceListLock);
|
||||
sDeviceList.Remove(this);
|
||||
}
|
||||
@ -148,12 +174,20 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
|
||||
free(fFilePath);
|
||||
}
|
||||
|
||||
int32 Index() const { return fIndex; }
|
||||
int32 ID() const { return fID; }
|
||||
off_t DeviceSize() const { return fDeviceSize; }
|
||||
const char* DeviceName() const { return fDeviceName; }
|
||||
|
||||
status_t Init(const char* filePath, uint64 deviceSize)
|
||||
bool IsUnregistered() const { return fUnregistered; }
|
||||
|
||||
void SetUnregistered(bool unregistered)
|
||||
{
|
||||
fUnregistered = unregistered;
|
||||
}
|
||||
|
||||
status_t Init(int32 id, const char* filePath, uint64 deviceSize)
|
||||
{
|
||||
fID = id;
|
||||
fFilePath = filePath != NULL ? strdup(filePath) : NULL;
|
||||
if (filePath != NULL && fFilePath == NULL)
|
||||
return B_NO_MEMORY;
|
||||
@ -167,23 +201,10 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
// find a free slot
|
||||
fIndex = 0;
|
||||
RawDevice* nextDevice = NULL;
|
||||
MutexLocker locker(sDeviceListLock);
|
||||
for (RawDeviceList::Iterator it = sDeviceList.GetIterator();
|
||||
(nextDevice = it.Next()) != NULL;) {
|
||||
if (nextDevice->Index() > fIndex)
|
||||
break;
|
||||
fIndex = nextDevice->Index() + 1;
|
||||
}
|
||||
|
||||
sDeviceList.InsertBefore(nextDevice, this);
|
||||
|
||||
// construct our device path
|
||||
KPath path(kRawDeviceBaseName);
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "%" B_PRId32 "/raw", fIndex);
|
||||
snprintf(buffer, sizeof(buffer), "%" B_PRId32 "/raw", fID);
|
||||
|
||||
status_t error = path.Append(buffer);
|
||||
if (error != B_OK)
|
||||
@ -191,6 +212,17 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
|
||||
|
||||
fDeviceName = path.DetachBuffer();
|
||||
|
||||
// insert into device list
|
||||
RawDevice* nextDevice = NULL;
|
||||
MutexLocker locker(sDeviceListLock);
|
||||
for (RawDeviceList::Iterator it = sDeviceList.GetIterator();
|
||||
(nextDevice = it.Next()) != NULL;) {
|
||||
if (nextDevice->ID() > fID)
|
||||
break;
|
||||
}
|
||||
|
||||
sDeviceList.InsertBefore(nextDevice, this);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@ -268,6 +300,15 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
|
||||
}
|
||||
}
|
||||
|
||||
void GetInfo(ram_disk_ioctl_info& _info) const
|
||||
{
|
||||
_info.id = fID;
|
||||
_info.size = fDeviceSize;
|
||||
memset(&_info.path, 0, sizeof(_info.path));
|
||||
if (fFilePath != NULL)
|
||||
strlcpy(_info.path, fFilePath, sizeof(_info.path));
|
||||
}
|
||||
|
||||
status_t Flush()
|
||||
{
|
||||
static const size_t kPageCountPerIteration = 1024;
|
||||
@ -726,7 +767,8 @@ private:
|
||||
}
|
||||
|
||||
private:
|
||||
int32 fIndex;
|
||||
int32 fID;
|
||||
bool fUnregistered;
|
||||
off_t fDeviceSize;
|
||||
char* fDeviceName;
|
||||
char* fFilePath;
|
||||
@ -756,96 +798,35 @@ private:
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static bool
|
||||
parse_command_line(char* buffer, char**& _argv, int& _argc)
|
||||
static int32
|
||||
allocate_raw_device_id()
|
||||
{
|
||||
// Process the argument string. We split at whitespace, heeding quotes and
|
||||
// escaped characters. The processed arguments are written to the given
|
||||
// buffer, separated by single null chars.
|
||||
char* start = buffer;
|
||||
char* out = buffer;
|
||||
bool pendingArgument = false;
|
||||
int argc = 0;
|
||||
while (*start != '\0') {
|
||||
// ignore whitespace
|
||||
if (isspace(*start)) {
|
||||
if (pendingArgument) {
|
||||
*out = '\0';
|
||||
out++;
|
||||
argc++;
|
||||
pendingArgument = false;
|
||||
}
|
||||
start++;
|
||||
continue;
|
||||
}
|
||||
|
||||
pendingArgument = true;
|
||||
|
||||
if (*start == '"' || *start == '\'') {
|
||||
// quoted text -- continue until closing quote
|
||||
char quote = *start;
|
||||
start++;
|
||||
while (*start != '\0' && *start != quote) {
|
||||
if (*start == '\\' && quote == '"') {
|
||||
start++;
|
||||
if (*start == '\0')
|
||||
break;
|
||||
}
|
||||
*out = *start;
|
||||
start++;
|
||||
out++;
|
||||
}
|
||||
|
||||
if (*start != '\0')
|
||||
start++;
|
||||
} else {
|
||||
// unquoted text
|
||||
if (*start == '\\') {
|
||||
// escaped char
|
||||
start++;
|
||||
if (start == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
*out = *start;
|
||||
start++;
|
||||
out++;
|
||||
MutexLocker deviceListLocker(sDeviceListLock);
|
||||
for (size_t i = 0; i < sizeof(sUsedRawDeviceIDs) * 8; i++) {
|
||||
if ((sUsedRawDeviceIDs & ((uint64)1 << i)) == 0) {
|
||||
sUsedRawDeviceIDs |= (uint64)1 << i;
|
||||
return (int32)i;
|
||||
}
|
||||
}
|
||||
|
||||
if (pendingArgument) {
|
||||
*out = '\0';
|
||||
argc++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// allocate argument vector
|
||||
char** argv = new(std::nothrow) char*[argc + 1];
|
||||
if (argv == NULL)
|
||||
return false;
|
||||
|
||||
// fill vector
|
||||
start = buffer;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
argv[i] = start;
|
||||
start += strlen(start) + 1;
|
||||
}
|
||||
argv[argc] = NULL;
|
||||
|
||||
_argv = argv;
|
||||
_argc = argc;
|
||||
return true;
|
||||
static void
|
||||
free_raw_device_id(int32 id)
|
||||
{
|
||||
MutexLocker deviceListLocker(sDeviceListLock);
|
||||
sUsedRawDeviceIDs &= ~((uint64)1 << id);
|
||||
}
|
||||
|
||||
|
||||
static RawDevice*
|
||||
find_raw_device(const char* deviceName)
|
||||
find_raw_device(int32 id)
|
||||
{
|
||||
if (strncmp(deviceName, "/dev/", 5) == 0)
|
||||
deviceName += 5;
|
||||
|
||||
for (RawDeviceList::Iterator it = sDeviceList.GetIterator();
|
||||
RawDevice* device = it.Next();) {
|
||||
if (strcmp(device->DeviceName(), deviceName) == 0)
|
||||
if (device->ID() == id)
|
||||
return device;
|
||||
}
|
||||
|
||||
@ -853,6 +834,124 @@ find_raw_device(const char* deviceName)
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ioctl_register(ControlDevice* controlDevice, ram_disk_ioctl_register* request)
|
||||
{
|
||||
KPath path;
|
||||
uint64 deviceSize = 0;
|
||||
|
||||
if (request->path[0] != '\0') {
|
||||
// check if the path is null-terminated
|
||||
if (strnlen(request->path, sizeof(request->path))
|
||||
== sizeof(request->path)) {
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
// get a normalized file path
|
||||
status_t error = path.SetTo(request->path, true);
|
||||
if (error != B_OK) {
|
||||
dprintf("ramdisk: register: Invalid path \"%s\": %s\n",
|
||||
request->path, strerror(error));
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.Path(), &st) != 0) {
|
||||
dprintf("ramdisk: register: Failed to stat \"%s\": %s\n",
|
||||
path.Path(), strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
dprintf("ramdisk: register: \"%s\" is not a file!\n", path.Path());
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
deviceSize = st.st_size;
|
||||
} else {
|
||||
deviceSize = request->size;
|
||||
}
|
||||
|
||||
return controlDevice->Register(path.Length() > 0 ? path.Path() : NULL,
|
||||
deviceSize, request->id);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ioctl_unregister(ControlDevice* controlDevice,
|
||||
ram_disk_ioctl_unregister* request)
|
||||
{
|
||||
// find the device in the list and unregister it
|
||||
MutexLocker locker(sDeviceListLock);
|
||||
RawDevice* device = find_raw_device(request->id);
|
||||
if (device == NULL)
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
// mark unregistered before we unlock
|
||||
if (device->IsUnregistered())
|
||||
return B_BUSY;
|
||||
device->SetUnregistered(true);
|
||||
locker.Unlock();
|
||||
|
||||
device_node* node = device->Node();
|
||||
status_t error = sDeviceManager->unpublish_device(node,
|
||||
device->DeviceName());
|
||||
if (error != B_OK) {
|
||||
dprintf("ramdisk: unregister: Failed to unpublish device \"%s\": %s\n",
|
||||
device->DeviceName(), strerror(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
error = sDeviceManager->unregister_node(node);
|
||||
// Note: B_BUSY is OK. The node will removed as soon as possible.
|
||||
if (error != B_OK && error != B_BUSY) {
|
||||
dprintf("ramdisk: unregister: Failed to unregister node for device %"
|
||||
B_PRId32 ": %s\n", request->id, strerror(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
ioctl_info(RawDevice* device, ram_disk_ioctl_info* request)
|
||||
{
|
||||
device->GetInfo(*request);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
template<typename DeviceType, typename Request>
|
||||
static status_t
|
||||
handle_ioctl(DeviceType* device,
|
||||
status_t (*handler)(DeviceType*, Request*), void* buffer)
|
||||
{
|
||||
// copy request to the kernel heap
|
||||
if (buffer == NULL || !IS_USER_ADDRESS(buffer))
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
Request* request = new(std::nothrow) Request;
|
||||
if (request == NULL)
|
||||
return B_NO_MEMORY;
|
||||
ObjectDeleter<Request> requestDeleter(request);
|
||||
|
||||
if (user_memcpy(request, buffer, sizeof(Request)) != B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
// handle the ioctl
|
||||
status_t error = handler(device, request);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// copy the request back to userland
|
||||
if (user_memcpy(buffer, request, sizeof(Request)) != B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - driver
|
||||
|
||||
|
||||
@ -890,6 +989,12 @@ ram_disk_driver_init_driver(device_node* node, void** _driverCookie)
|
||||
uint64 deviceSize;
|
||||
if (sDeviceManager->get_attr_uint64(node, kDeviceSizeItem, &deviceSize,
|
||||
false) == B_OK) {
|
||||
int32 id = -1;
|
||||
sDeviceManager->get_attr_uint32(node, kDeviceIDItem, (uint32*)&id,
|
||||
false);
|
||||
if (id < 0)
|
||||
return B_ERROR;
|
||||
|
||||
const char* filePath = NULL;
|
||||
sDeviceManager->get_attr_string(node, kFilePathItem, &filePath, false);
|
||||
|
||||
@ -897,7 +1002,7 @@ ram_disk_driver_init_driver(device_node* node, void** _driverCookie)
|
||||
if (device == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
status_t error = device->Init(filePath, deviceSize);
|
||||
status_t error = device->Init(id, filePath, deviceSize);
|
||||
if (error != B_OK) {
|
||||
delete device;
|
||||
return error;
|
||||
@ -920,6 +1025,8 @@ static void
|
||||
ram_disk_driver_uninit_driver(void* driverCookie)
|
||||
{
|
||||
Device* device = (Device*)driverCookie;
|
||||
if (RawDevice* rawDevice = dynamic_cast<RawDevice*>(device))
|
||||
free_raw_device_id(rawDevice->ID());
|
||||
delete device;
|
||||
}
|
||||
|
||||
@ -976,185 +1083,15 @@ static status_t
|
||||
ram_disk_control_device_read(void* cookie, off_t position, void* buffer,
|
||||
size_t* _length)
|
||||
{
|
||||
*_length = 0;
|
||||
return B_OK;
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static status_t
|
||||
ram_disk_control_device_write(void* cookie, off_t position, const void* data,
|
||||
size_t* _length)
|
||||
{
|
||||
ControlDevice* device = (ControlDevice*)cookie;
|
||||
|
||||
if (position != 0)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// copy data to stack buffer
|
||||
char* buffer = (char*)malloc(*_length + 1);
|
||||
if (buffer == NULL)
|
||||
return B_NO_MEMORY;
|
||||
MemoryDeleter bufferDeleter(buffer);
|
||||
|
||||
if (IS_USER_ADDRESS(data)) {
|
||||
if (user_memcpy(buffer, data, *_length) != B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
} else
|
||||
memcpy(buffer, data, *_length);
|
||||
|
||||
buffer[*_length] = '\0';
|
||||
|
||||
// parse arguments
|
||||
char** argv;
|
||||
int argc;
|
||||
if (!parse_command_line(buffer, argv, argc))
|
||||
return B_NO_MEMORY;
|
||||
ArrayDeleter<char*> argvDeleter(argv);
|
||||
|
||||
if (argc == 0) {
|
||||
dprintf("\"help\" for usage!\n");
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
// execute command
|
||||
if (strcmp(argv[0], "help") == 0) {
|
||||
// help
|
||||
dprintf("register (<path> | -s <size>)\n");
|
||||
dprintf(" Registers a new ram disk device backed by file <path> or\n"
|
||||
" an unbacked ram disk device with size <size>.\n");
|
||||
dprintf("unregister <device>\n");
|
||||
dprintf(" Unregisters <device>.\n");
|
||||
dprintf("flush <device>\n");
|
||||
dprintf(" Writes <device> changes back to the file associated with\n"
|
||||
" it, if any.\n");
|
||||
} else if (strcmp(argv[0], "register") == 0) {
|
||||
// register
|
||||
if (argc < 2 || argc > 3 || (argc == 3 && strcmp(argv[1], "-s") != 0)) {
|
||||
dprintf("Usage: register (<path> | -s <size>)\n");
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
KPath path;
|
||||
uint64 deviceSize = 0;
|
||||
|
||||
if (argc == 2) {
|
||||
// get a normalized file path
|
||||
status_t error = path.SetTo(argv[1], true);
|
||||
if (error != B_OK) {
|
||||
dprintf("Invalid path \"%s\": %s\n", argv[1], strerror(error));
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.Path(), &st) != 0) {
|
||||
dprintf("Failed to stat \"%s\": %s\n", path.Path(),
|
||||
strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
dprintf("\"%s\" is not a file!\n", path.Path());
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
deviceSize = st.st_size;
|
||||
} else {
|
||||
// parse size argument
|
||||
const char* sizeString = argv[2];
|
||||
char* end;
|
||||
deviceSize = strtoll(sizeString, &end, 0);
|
||||
if (end == sizeString) {
|
||||
dprintf("Invalid size argument: \"%s\"\n", sizeString);
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
switch (*end) {
|
||||
case 'g':
|
||||
deviceSize *= 1024;
|
||||
case 'm':
|
||||
deviceSize *= 1024;
|
||||
case 'k':
|
||||
deviceSize *= 1024;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return device->Register(path.Length() > 0 ? path.Path() : NULL,
|
||||
deviceSize);
|
||||
} else if (strcmp(argv[0], "unregister") == 0) {
|
||||
// unregister
|
||||
if (argc != 2) {
|
||||
dprintf("Usage: unregister <device>\n");
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
const char* deviceName = argv[1];
|
||||
|
||||
// find the device in the list and unregister it
|
||||
MutexLocker locker(sDeviceListLock);
|
||||
if (RawDevice* device = find_raw_device(deviceName)) {
|
||||
// TODO: Race condition: We should mark the device as going to
|
||||
// be unregistered, so no one else can try the same after we
|
||||
// unlock!
|
||||
locker.Unlock();
|
||||
// TODO: The following doesn't work! unpublish_device(), as per implementation
|
||||
// (partially commented out) and unregister_node() returns B_BUSY.
|
||||
status_t error = sDeviceManager->unpublish_device(
|
||||
device->Node(), device->DeviceName());
|
||||
if (error != B_OK) {
|
||||
dprintf("Failed to unpublish device \"%s\": %s\n",
|
||||
deviceName, strerror(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
error = sDeviceManager->unregister_node(device->Node());
|
||||
if (error != B_OK) {
|
||||
dprintf("Failed to unregister node \"%s\": %s\n",
|
||||
deviceName, strerror(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
dprintf("Device \"%s\" not found!\n", deviceName);
|
||||
return B_BAD_VALUE;
|
||||
} else if (strcmp(argv[0], "flush") == 0) {
|
||||
// flush
|
||||
if (argc != 2) {
|
||||
dprintf("Usage: unregister <device>\n");
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
const char* deviceName = argv[1];
|
||||
|
||||
// find the device in the list and flush it
|
||||
MutexLocker locker(sDeviceListLock);
|
||||
RawDevice* device = find_raw_device(deviceName);
|
||||
if (device == NULL) {
|
||||
dprintf("Device \"%s\" not found!\n", deviceName);
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
// TODO: Race condition: Once we unlock someone could unregister the
|
||||
// device. We should probably open the device by path, and use a
|
||||
// special ioctl.
|
||||
locker.Unlock();
|
||||
|
||||
status_t error = device->Flush();
|
||||
if (error != B_OK) {
|
||||
dprintf("Failed to flush device: %s\n", strerror(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
} else {
|
||||
dprintf("Invalid command \"%s\"!\n", argv[0]);
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
|
||||
@ -1162,6 +1099,16 @@ static status_t
|
||||
ram_disk_control_device_control(void* cookie, uint32 op, void* buffer,
|
||||
size_t length)
|
||||
{
|
||||
ControlDevice* device = (ControlDevice*)cookie;
|
||||
|
||||
switch (op) {
|
||||
case RAM_DISK_IOCTL_REGISTER:
|
||||
return handle_ioctl(device, &ioctl_register, buffer);
|
||||
|
||||
case RAM_DISK_IOCTL_UNREGISTER:
|
||||
return handle_ioctl(device, &ioctl_unregister, buffer);
|
||||
}
|
||||
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
@ -1347,6 +1294,21 @@ ram_disk_raw_device_control(void* _cookie, uint32 op, void* buffer,
|
||||
case B_SET_INTERRUPTABLE_IO:
|
||||
case B_FLUSH_DRIVE_CACHE:
|
||||
return B_OK;
|
||||
|
||||
case RAM_DISK_IOCTL_FLUSH:
|
||||
{
|
||||
status_t error = device->Flush();
|
||||
if (error != B_OK) {
|
||||
dprintf("ramdisk: flush: Failed to flush device: %s\n",
|
||||
strerror(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
case RAM_DISK_IOCTL_INFO:
|
||||
return handle_ioctl(device, &ioctl_info, buffer);
|
||||
}
|
||||
|
||||
return B_BAD_VALUE;
|
||||
|
@ -126,6 +126,11 @@ StdBinCommands
|
||||
urlwrapper.cpp
|
||||
: be $(TARGET_LIBSUPC++) : $(haiku-utils_rsrc) ;
|
||||
|
||||
# standard commands that need libbe.so, libsupc++.so, and libshared.a
|
||||
StdBinCommands
|
||||
ramdisk.cpp
|
||||
: libshared.a be $(TARGET_LIBSUPC++) : $(haiku-utils_rsrc) ;
|
||||
|
||||
# commands that need libbe.so and the stub catalog
|
||||
StdBinCommands
|
||||
clockconfig.cpp
|
||||
|
477
src/bin/ramdisk.cpp
Normal file
477
src/bin/ramdisk.cpp
Normal file
@ -0,0 +1,477 @@
|
||||
/*
|
||||
* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <Entry.h>
|
||||
#include <Path.h>
|
||||
#include <String.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <TextTable.h>
|
||||
|
||||
#include <file_systems/ram_disk/ram_disk.h>
|
||||
|
||||
|
||||
extern const char* __progname;
|
||||
static const char* kProgramName = __progname;
|
||||
|
||||
static const char* const kUsage =
|
||||
"Usage: %s <command> [ <options> ]\n"
|
||||
"Controls RAM disk devices.\n"
|
||||
"\n"
|
||||
"Commands:\n"
|
||||
" create (-s <size> | <path>)\n"
|
||||
" Creates a new RAM disk.\n"
|
||||
" delete <id>\n"
|
||||
" Deletes an existing RAM disk.\n"
|
||||
" flush <id>\n"
|
||||
" Writes modified data of an existing RAM disk back to its file.\n"
|
||||
" help\n"
|
||||
" Print this usage info.\n"
|
||||
" list\n"
|
||||
" List all RAM disks.\n"
|
||||
;
|
||||
|
||||
static const char* const kCreateUsage =
|
||||
"Usage: %s %s (-s <size> | <path>)\n"
|
||||
"Creates a new RAM disk device. If the <size> argument is specified, a\n"
|
||||
"new zeroed RAM disk with that size (in bytes, suffixes 'k', 'm', 'g' are\n"
|
||||
"interpreted as KiB, MiB, GiB) is registered.\n"
|
||||
"Alternatively a file path can be specified. In that case the RAM disk \n"
|
||||
"data are initially read from that file and at any later point the\n"
|
||||
"modified RAM disk data can be written back to the same file upon request\n"
|
||||
"(via the \"flush\" command). The size of the RAM disk is implied by that\n"
|
||||
"of the file.\n"
|
||||
;
|
||||
|
||||
static const char* const kDeleteUsage =
|
||||
"Usage: %s %s <id>\n"
|
||||
"Deletes the existing RAM disk with ID <id>. All modified data will be\n"
|
||||
"lost.\n"
|
||||
;
|
||||
|
||||
static const char* const kFlushUsage =
|
||||
"Usage: %s %s <id>\n"
|
||||
"Writes all modified data of the RAM disk with ID <id> back to the file\n"
|
||||
"specified when the RAM disk was created. Fails, if the RAM disk had been\n"
|
||||
"created without an associated file.\n"
|
||||
;
|
||||
|
||||
static const char* const kListUsage =
|
||||
"Usage: %s %s\n"
|
||||
"Lists all existing RAM disks.\n"
|
||||
;
|
||||
|
||||
static const char* const kRamDiskControlDevicePath
|
||||
= "/dev/" RAM_DISK_CONTROL_DEVICE_NAME;
|
||||
static const char* const kRamDiskRawDeviceBasePath
|
||||
= "/dev/" RAM_DISK_RAW_DEVICE_BASE_NAME;
|
||||
|
||||
static const char* sCommandName = NULL;
|
||||
static const char* sCommandUsage = NULL;
|
||||
|
||||
|
||||
static void
|
||||
print_usage_and_exit(bool error)
|
||||
{
|
||||
if (sCommandUsage != NULL) {
|
||||
fprintf(error ? stderr : stdout, sCommandUsage, kProgramName,
|
||||
sCommandName);
|
||||
} else
|
||||
fprintf(error ? stderr : stdout, kUsage, kProgramName);
|
||||
exit(error ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
execute_control_device_ioctl(int operation, void* request)
|
||||
{
|
||||
// open the ram disk control device
|
||||
int fd = open(kRamDiskControlDevicePath, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Error: Failed to open RAM disk control device \"%s\": "
|
||||
"%s\n", kRamDiskControlDevicePath, strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
FileDescriptorCloser fdCloser(fd);
|
||||
|
||||
// issue the request
|
||||
if (ioctl(fd, operation, request) < 0)
|
||||
return errno;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
command_register(int argc, const char* const* argv)
|
||||
{
|
||||
sCommandUsage = kCreateUsage;
|
||||
|
||||
int64 deviceSize = -1;
|
||||
|
||||
while (true) {
|
||||
static struct option sLongOptions[] = {
|
||||
{ "size", required_argument, 0, 's' },
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
opterr = 0; // don't print errors
|
||||
int c = getopt_long(argc, (char**)argv, "+s:h", sLongOptions, NULL);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_usage_and_exit(false);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
{
|
||||
const char* sizeString = optarg;
|
||||
char* end;
|
||||
deviceSize = strtoll(sizeString, &end, 0);
|
||||
if (end != sizeString && deviceSize > 0) {
|
||||
int64 originalDeviceSize = deviceSize;
|
||||
switch (*end) {
|
||||
case 'g':
|
||||
deviceSize *= 1024;
|
||||
case 'm':
|
||||
deviceSize *= 1024;
|
||||
case 'k':
|
||||
deviceSize *= 1024;
|
||||
end++;
|
||||
break;
|
||||
case '\0':
|
||||
break;
|
||||
default:
|
||||
deviceSize = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (deviceSize > 0 && originalDeviceSize > deviceSize)
|
||||
deviceSize = -1;
|
||||
}
|
||||
|
||||
if (deviceSize <= 0) {
|
||||
fprintf(stderr, "Error: Invalid size argument: \"%s\"\n",
|
||||
sizeString);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check maximum size
|
||||
system_info info;
|
||||
get_system_info(&info);
|
||||
if (deviceSize / B_PAGE_SIZE > (int64)info.max_pages * 2 / 3) {
|
||||
fprintf(stderr, "Error: Given RAM disk size too large.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
print_usage_and_exit(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The remaining optional argument is the file path. It may only be
|
||||
// specified, if no size has been specified.
|
||||
const char* path = optind < argc ? argv[optind++] : NULL;
|
||||
if (optind < argc || (deviceSize >= 0) == (path != NULL))
|
||||
print_usage_and_exit(true);
|
||||
|
||||
// prepare the request
|
||||
ram_disk_ioctl_register request;
|
||||
request.size = (uint64)deviceSize;
|
||||
request.path[0] = '\0';
|
||||
request.id = -1;
|
||||
|
||||
if (path != NULL) {
|
||||
// verify the path
|
||||
BEntry entry;
|
||||
status_t error = entry.SetTo(path, true);
|
||||
if (error == B_OK && !entry.Exists())
|
||||
error = B_ENTRY_NOT_FOUND;
|
||||
if (error != B_OK) {
|
||||
fprintf(stderr, "Error: Failed to resolve path \"%s\": %s\n",
|
||||
path, strerror(error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!entry.IsFile()) {
|
||||
fprintf(stderr, "Error: \"%s\" is not a file.\n", path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
BPath normalizedPath;
|
||||
error = entry.GetPath(&normalizedPath);
|
||||
if (error != B_OK) {
|
||||
fprintf(stderr, "Error: Failed to normalize path \"%s\": %s\n",
|
||||
path, strerror(error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strlcpy(request.path, normalizedPath.Path(), sizeof(request.path))
|
||||
>= sizeof(request.path)) {
|
||||
fprintf(stderr, "Error: Normalized path too long.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
status_t error = execute_control_device_ioctl(RAM_DISK_IOCTL_REGISTER,
|
||||
&request);
|
||||
if (error != B_OK) {
|
||||
fprintf(stderr, "Error: Failed to create RAM disk device: %s\n",
|
||||
strerror(error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("RAM disk device created as \"%s/%" B_PRId32 "/raw\"\n",
|
||||
kRamDiskRawDeviceBasePath, request.id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
command_unregister(int argc, const char* const* argv)
|
||||
{
|
||||
sCommandUsage = kDeleteUsage;
|
||||
|
||||
while (true) {
|
||||
static struct option sLongOptions[] = {
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
opterr = 0; // don't print errors
|
||||
int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_usage_and_exit(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
print_usage_and_exit(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The remaining argument is the device ID.
|
||||
if (optind + 1 != argc)
|
||||
print_usage_and_exit(true);
|
||||
|
||||
const char* idString = argv[optind++];
|
||||
char* end;
|
||||
long long id = strtol(idString, &end, 0);
|
||||
if (end == idString || *end != '\0' || id < 0 || id > INT32_MAX) {
|
||||
fprintf(stderr, "Error: Invalid ID \"%s\".\n", idString);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check whether the raw device for that ID exists
|
||||
BString path;
|
||||
path.SetToFormat("%s/%s/raw", kRamDiskRawDeviceBasePath, idString);
|
||||
struct stat st;
|
||||
if (lstat(path, &st) != 0) {
|
||||
fprintf(stderr, "Error: No RAM disk with ID %s.\n", idString);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// issue the request
|
||||
ram_disk_ioctl_unregister request;
|
||||
request.id = (int32)id;
|
||||
|
||||
status_t error = execute_control_device_ioctl(RAM_DISK_IOCTL_UNREGISTER,
|
||||
&request);
|
||||
if (error != B_OK) {
|
||||
fprintf(stderr, "Error: Failed to delete RAM disk device: %s\n",
|
||||
strerror(error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
command_flush(int argc, const char* const* argv)
|
||||
{
|
||||
sCommandUsage = kFlushUsage;
|
||||
|
||||
while (true) {
|
||||
static struct option sLongOptions[] = {
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
opterr = 0; // don't print errors
|
||||
int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_usage_and_exit(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
print_usage_and_exit(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The remaining argument is the device ID.
|
||||
if (optind + 1 != argc)
|
||||
print_usage_and_exit(true);
|
||||
|
||||
const char* idString = argv[optind++];
|
||||
char* end;
|
||||
long long id = strtol(idString, &end, 0);
|
||||
if (end == idString || *end != '\0' || id < 0 || id > INT32_MAX) {
|
||||
fprintf(stderr, "Error: Invalid ID \"%s\".\n", idString);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// open the raw device
|
||||
BString path;
|
||||
path.SetToFormat("%s/%s/raw", kRamDiskRawDeviceBasePath, idString);
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Error: Failed to open RAM disk device \"%s\"\n",
|
||||
path.String());
|
||||
return 1;
|
||||
}
|
||||
FileDescriptorCloser fdCloser(fd);
|
||||
|
||||
// issue the request
|
||||
if (ioctl(fd, RAM_DISK_IOCTL_FLUSH, NULL) < 0) {
|
||||
fprintf(stderr, "Error: Failed to flush RAM disk device: %s\n",
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
command_list(int argc, const char* const* argv)
|
||||
{
|
||||
sCommandUsage = kListUsage;
|
||||
|
||||
while (true) {
|
||||
static struct option sLongOptions[] = {
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
opterr = 0; // don't print errors
|
||||
int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_usage_and_exit(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
print_usage_and_exit(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// There shouldn't be any remaining arguments.
|
||||
if (optind != argc)
|
||||
print_usage_and_exit(true);
|
||||
|
||||
// iterate through the RAM disk device directory and search for raw devices
|
||||
DIR* dir = opendir(kRamDiskRawDeviceBasePath);
|
||||
if (dir == NULL) {
|
||||
fprintf(stderr, "Error: Failed to open RAM disk device directory: %s\n",
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
CObjectDeleter<DIR, int> dirCloser(dir, &closedir);
|
||||
|
||||
TextTable table;
|
||||
table.AddColumn("ID", B_ALIGN_RIGHT);
|
||||
table.AddColumn("Size", B_ALIGN_RIGHT);
|
||||
table.AddColumn("Associated file");
|
||||
|
||||
while (dirent* entry = readdir(dir)) {
|
||||
// check, if the entry name could be an ID
|
||||
const char* idString = entry->d_name;
|
||||
char* end;
|
||||
long long id = strtol(idString, &end, 0);
|
||||
if (end == idString || *end != '\0' || id < 0 || id > INT32_MAX)
|
||||
continue;
|
||||
|
||||
// open the raw device
|
||||
BString path;
|
||||
path.SetToFormat("%s/%s/raw", kRamDiskRawDeviceBasePath, idString);
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
continue;
|
||||
FileDescriptorCloser fdCloser(fd);
|
||||
|
||||
// issue the request
|
||||
ram_disk_ioctl_info request;
|
||||
if (ioctl(fd, RAM_DISK_IOCTL_INFO, &request) < 0)
|
||||
continue;
|
||||
|
||||
int32 rowIndex = table.CountRows();
|
||||
table.SetTextAt(rowIndex, 0, BString() << request.id);
|
||||
table.SetTextAt(rowIndex, 1, BString() << request.size);
|
||||
table.SetTextAt(rowIndex, 2, request.path);
|
||||
}
|
||||
|
||||
if (table.CountRows() > 0)
|
||||
table.Print(INT32_MAX);
|
||||
else
|
||||
printf("No RAM disks.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, const char* const* argv)
|
||||
{
|
||||
if (argc < 2)
|
||||
print_usage_and_exit(true);
|
||||
|
||||
if (strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0
|
||||
|| strcmp(argv[1], "-h") == 0) {
|
||||
print_usage_and_exit(false);
|
||||
}
|
||||
|
||||
sCommandName = argv[1];
|
||||
|
||||
if (strcmp(sCommandName, "create") == 0)
|
||||
return command_register(argc - 1, argv + 1);
|
||||
if (strcmp(sCommandName, "delete") == 0)
|
||||
return command_unregister(argc - 1, argv + 1);
|
||||
if (strcmp(sCommandName, "flush") == 0)
|
||||
return command_flush(argc - 1, argv + 1);
|
||||
if (strcmp(sCommandName, "list") == 0)
|
||||
return command_list(argc - 1, argv + 1);
|
||||
|
||||
print_usage_and_exit(true);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user