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 <ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@ -50,12 +52,12 @@ static const char* const kControlDeviceModuleName
|
|||||||
static const char* const kRawDeviceModuleName
|
static const char* const kRawDeviceModuleName
|
||||||
= "drivers/disk/virtual/ram_disk/raw/device_v1";
|
= "drivers/disk/virtual/ram_disk/raw/device_v1";
|
||||||
|
|
||||||
static const char* const kControlDeviceName
|
static const char* const kControlDeviceName = RAM_DISK_CONTROL_DEVICE_NAME;
|
||||||
= "disk/virtual/ram/control";
|
static const char* const kRawDeviceBaseName = RAM_DISK_RAW_DEVICE_BASE_NAME;
|
||||||
static const char* const kRawDeviceBaseName = "disk/virtual/ram";
|
|
||||||
|
|
||||||
static const char* const kFilePathItem = "ram_disk/file_path";
|
static const char* const kFilePathItem = "ram_disk/file_path";
|
||||||
static const char* const kDeviceSizeItem = "ram_disk/device_size";
|
static const char* const kDeviceSizeItem = "ram_disk/device_size";
|
||||||
|
static const char* const kDeviceIDItem = "ram_disk/id";
|
||||||
|
|
||||||
|
|
||||||
struct RawDevice;
|
struct RawDevice;
|
||||||
@ -65,6 +67,11 @@ struct device_manager_info* sDeviceManager;
|
|||||||
|
|
||||||
static RawDeviceList sDeviceList;
|
static RawDeviceList sDeviceList;
|
||||||
static mutex sDeviceListLock = MUTEX_INITIALIZER("ram disk device list");
|
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 {
|
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[] = {
|
device_attr attrs[] = {
|
||||||
{B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
|
{B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
|
||||||
{string: "RAM Disk Raw Device"}},
|
{string: "RAM Disk Raw Device"}},
|
||||||
{kFilePathItem, B_STRING_TYPE, {string: filePath}},
|
|
||||||
{kDeviceSizeItem, B_UINT64_TYPE, {ui64: deviceSize}},
|
{kDeviceSizeItem, B_UINT64_TYPE, {ui64: deviceSize}},
|
||||||
|
{kDeviceIDItem, B_UINT32_TYPE, {ui32: (uint32)id}},
|
||||||
|
{kFilePathItem, B_STRING_TYPE, {string: filePath}},
|
||||||
{NULL}
|
{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,
|
sDeviceManager->get_parent_node(Node()), kDriverModuleName, attrs,
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
|
if (error != B_OK) {
|
||||||
|
free_raw_device_id(id);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
_id = id;
|
||||||
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual status_t PublishDevice()
|
virtual status_t PublishDevice()
|
||||||
@ -127,7 +152,8 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
|
|||||||
RawDevice(device_node* node)
|
RawDevice(device_node* node)
|
||||||
:
|
:
|
||||||
Device(node),
|
Device(node),
|
||||||
fIndex(-1),
|
fID(-1),
|
||||||
|
fUnregistered(false),
|
||||||
fDeviceSize(0),
|
fDeviceSize(0),
|
||||||
fDeviceName(NULL),
|
fDeviceName(NULL),
|
||||||
fFilePath(NULL),
|
fFilePath(NULL),
|
||||||
@ -139,7 +165,7 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
|
|||||||
|
|
||||||
virtual ~RawDevice()
|
virtual ~RawDevice()
|
||||||
{
|
{
|
||||||
if (fIndex >= 0) {
|
if (fID >= 0) {
|
||||||
MutexLocker locker(sDeviceListLock);
|
MutexLocker locker(sDeviceListLock);
|
||||||
sDeviceList.Remove(this);
|
sDeviceList.Remove(this);
|
||||||
}
|
}
|
||||||
@ -148,12 +174,20 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
|
|||||||
free(fFilePath);
|
free(fFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 Index() const { return fIndex; }
|
int32 ID() const { return fID; }
|
||||||
off_t DeviceSize() const { return fDeviceSize; }
|
off_t DeviceSize() const { return fDeviceSize; }
|
||||||
const char* DeviceName() const { return fDeviceName; }
|
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;
|
fFilePath = filePath != NULL ? strdup(filePath) : NULL;
|
||||||
if (filePath != NULL && fFilePath == NULL)
|
if (filePath != NULL && fFilePath == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
@ -167,23 +201,10 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
|
|||||||
return B_BAD_VALUE;
|
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
|
// construct our device path
|
||||||
KPath path(kRawDeviceBaseName);
|
KPath path(kRawDeviceBaseName);
|
||||||
char buffer[32];
|
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);
|
status_t error = path.Append(buffer);
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
@ -191,6 +212,17 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
|
|||||||
|
|
||||||
fDeviceName = path.DetachBuffer();
|
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;
|
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()
|
status_t Flush()
|
||||||
{
|
{
|
||||||
static const size_t kPageCountPerIteration = 1024;
|
static const size_t kPageCountPerIteration = 1024;
|
||||||
@ -726,7 +767,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int32 fIndex;
|
int32 fID;
|
||||||
|
bool fUnregistered;
|
||||||
off_t fDeviceSize;
|
off_t fDeviceSize;
|
||||||
char* fDeviceName;
|
char* fDeviceName;
|
||||||
char* fFilePath;
|
char* fFilePath;
|
||||||
@ -756,96 +798,35 @@ private:
|
|||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static int32
|
||||||
parse_command_line(char* buffer, char**& _argv, int& _argc)
|
allocate_raw_device_id()
|
||||||
{
|
{
|
||||||
// Process the argument string. We split at whitespace, heeding quotes and
|
MutexLocker deviceListLocker(sDeviceListLock);
|
||||||
// escaped characters. The processed arguments are written to the given
|
for (size_t i = 0; i < sizeof(sUsedRawDeviceIDs) * 8; i++) {
|
||||||
// buffer, separated by single null chars.
|
if ((sUsedRawDeviceIDs & ((uint64)1 << i)) == 0) {
|
||||||
char* start = buffer;
|
sUsedRawDeviceIDs |= (uint64)1 << i;
|
||||||
char* out = buffer;
|
return (int32)i;
|
||||||
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++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pendingArgument) {
|
return -1;
|
||||||
*out = '\0';
|
|
||||||
argc++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// allocate argument vector
|
|
||||||
char** argv = new(std::nothrow) char*[argc + 1];
|
|
||||||
if (argv == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// fill vector
|
static void
|
||||||
start = buffer;
|
free_raw_device_id(int32 id)
|
||||||
for (int i = 0; i < argc; i++) {
|
{
|
||||||
argv[i] = start;
|
MutexLocker deviceListLocker(sDeviceListLock);
|
||||||
start += strlen(start) + 1;
|
sUsedRawDeviceIDs &= ~((uint64)1 << id);
|
||||||
}
|
|
||||||
argv[argc] = NULL;
|
|
||||||
|
|
||||||
_argv = argv;
|
|
||||||
_argc = argc;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static RawDevice*
|
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();
|
for (RawDeviceList::Iterator it = sDeviceList.GetIterator();
|
||||||
RawDevice* device = it.Next();) {
|
RawDevice* device = it.Next();) {
|
||||||
if (strcmp(device->DeviceName(), deviceName) == 0)
|
if (device->ID() == id)
|
||||||
return device;
|
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
|
// #pragma mark - driver
|
||||||
|
|
||||||
|
|
||||||
@ -890,6 +989,12 @@ ram_disk_driver_init_driver(device_node* node, void** _driverCookie)
|
|||||||
uint64 deviceSize;
|
uint64 deviceSize;
|
||||||
if (sDeviceManager->get_attr_uint64(node, kDeviceSizeItem, &deviceSize,
|
if (sDeviceManager->get_attr_uint64(node, kDeviceSizeItem, &deviceSize,
|
||||||
false) == B_OK) {
|
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;
|
const char* filePath = NULL;
|
||||||
sDeviceManager->get_attr_string(node, kFilePathItem, &filePath, false);
|
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)
|
if (device == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
status_t error = device->Init(filePath, deviceSize);
|
status_t error = device->Init(id, filePath, deviceSize);
|
||||||
if (error != B_OK) {
|
if (error != B_OK) {
|
||||||
delete device;
|
delete device;
|
||||||
return error;
|
return error;
|
||||||
@ -920,6 +1025,8 @@ static void
|
|||||||
ram_disk_driver_uninit_driver(void* driverCookie)
|
ram_disk_driver_uninit_driver(void* driverCookie)
|
||||||
{
|
{
|
||||||
Device* device = (Device*)driverCookie;
|
Device* device = (Device*)driverCookie;
|
||||||
|
if (RawDevice* rawDevice = dynamic_cast<RawDevice*>(device))
|
||||||
|
free_raw_device_id(rawDevice->ID());
|
||||||
delete device;
|
delete device;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -976,185 +1083,15 @@ static status_t
|
|||||||
ram_disk_control_device_read(void* cookie, off_t position, void* buffer,
|
ram_disk_control_device_read(void* cookie, off_t position, void* buffer,
|
||||||
size_t* _length)
|
size_t* _length)
|
||||||
{
|
{
|
||||||
*_length = 0;
|
return B_BAD_VALUE;
|
||||||
return B_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
ram_disk_control_device_write(void* cookie, off_t position, const void* data,
|
ram_disk_control_device_write(void* cookie, off_t position, const void* data,
|
||||||
size_t* _length)
|
size_t* _length)
|
||||||
{
|
{
|
||||||
ControlDevice* device = (ControlDevice*)cookie;
|
|
||||||
|
|
||||||
if (position != 0)
|
|
||||||
return B_BAD_VALUE;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1162,6 +1099,16 @@ static status_t
|
|||||||
ram_disk_control_device_control(void* cookie, uint32 op, void* buffer,
|
ram_disk_control_device_control(void* cookie, uint32 op, void* buffer,
|
||||||
size_t length)
|
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;
|
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_SET_INTERRUPTABLE_IO:
|
||||||
case B_FLUSH_DRIVE_CACHE:
|
case B_FLUSH_DRIVE_CACHE:
|
||||||
return B_OK;
|
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;
|
return B_BAD_VALUE;
|
||||||
|
@ -126,6 +126,11 @@ StdBinCommands
|
|||||||
urlwrapper.cpp
|
urlwrapper.cpp
|
||||||
: be $(TARGET_LIBSUPC++) : $(haiku-utils_rsrc) ;
|
: 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
|
# commands that need libbe.so and the stub catalog
|
||||||
StdBinCommands
|
StdBinCommands
|
||||||
clockconfig.cpp
|
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