* Renamed virtualdrive.c to .cpp to be able to use the saner syntax.
* Improved the driver: It now publishes a control device and ten `data' devices. Via an ioctl one can assign an arbitrary file to a free data device. (The devices are published in /dev/misc instead of /dev/disk to prevent them from being scanned by the Tracker and DriveSetup.) * Added a small command line tool to (un)register files that way. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@3649 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
cc74989eb6
commit
7a424c3923
@ -1,16 +1,14 @@
|
||||
SubDir OBOS_TOP src tests kits storage virtualdrive ;
|
||||
|
||||
R5KernelAddon virtualdrive : kernel drivers bin : virtualdrive.c ;
|
||||
R5KernelAddon virtualdrive : kernel drivers bin : virtualdrive.cpp ;
|
||||
|
||||
{
|
||||
# local bintarget = <installed-bin>virtualdrive ;
|
||||
# local devtarget = <installed-dev>virtualdrive ;
|
||||
# LOCATE on $(bintarget) = /boot/home/config/add-ons/kernel/drivers/bin ;
|
||||
# LOCATE on $(devtarget)
|
||||
# = /boot/home/config/add-ons/kernel/drivers/dev/disk/virtual ;
|
||||
# File $(bintarget) : virtualdrive ;
|
||||
# RelSymLink $(devtarget) : $(bintarget) ;
|
||||
# NotFile install_virtualdrive ;
|
||||
# Depends install_virtualdrive : $(devtarget) ;
|
||||
}
|
||||
SimpleTest mkvirtualdrive : mkvirtualdrive.cpp : be ;
|
||||
|
||||
# Installation
|
||||
OBOSInstall install_virtualdrive
|
||||
: /boot/home/config/add-ons/kernel/drivers/bin
|
||||
: virtualdrive ;
|
||||
OBOSInstallRelSymLink install_virtualdrive
|
||||
: /boot/home/config/add-ons/kernel/drivers/dev/misc
|
||||
: <installed>virtualdrive
|
||||
: installed-symlink ;
|
||||
|
234
src/tests/kits/storage/virtualdrive/mkvirtualdrive.cpp
Normal file
234
src/tests/kits/storage/virtualdrive/mkvirtualdrive.cpp
Normal file
@ -0,0 +1,234 @@
|
||||
// mkvirtualdrive.cpp
|
||||
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <Path.h>
|
||||
|
||||
#include "virtualdrive.h"
|
||||
|
||||
extern "C" const char * const*__libc_argv;
|
||||
|
||||
const char *kUsage =
|
||||
"Usage: %s [ --install ] [ --size <size> ] <file>\n"
|
||||
" %s --uninstall <device>\n"
|
||||
" %s ( --help | -h )\n"
|
||||
;
|
||||
|
||||
// print_usage
|
||||
static
|
||||
void
|
||||
print_usage(bool error = false)
|
||||
{
|
||||
const char *name = "mkvirtualdrive";
|
||||
BPath path;
|
||||
if (path.SetTo(__libc_argv[0]) == B_OK)
|
||||
name = path.Leaf();
|
||||
fprintf((error ? stderr : stdout), kUsage, name, name, name);
|
||||
}
|
||||
|
||||
// parse_size
|
||||
static
|
||||
bool
|
||||
parse_size(const char *str, off_t *_size)
|
||||
{
|
||||
if (!str || !_size)
|
||||
return false;
|
||||
int32 len = strlen(str);
|
||||
char buffer[22];
|
||||
if (len == 0 || len + 1 > (int32)sizeof(buffer))
|
||||
return false;
|
||||
strcpy(buffer, str);
|
||||
// check the last char for one of the suffixes
|
||||
off_t suffix = 1;
|
||||
if (!isdigit(buffer[len - 1])) {
|
||||
switch (buffer[len - 1]) {
|
||||
case 'K':
|
||||
suffix = 1LL << 10;
|
||||
break;
|
||||
case 'M':
|
||||
suffix = 1LL << 20;
|
||||
break;
|
||||
case 'G':
|
||||
suffix = 1LL << 30;
|
||||
break;
|
||||
case 'T':
|
||||
suffix = 1LL << 40;
|
||||
break;
|
||||
case 'P':
|
||||
suffix = 1LL << 50;
|
||||
break;
|
||||
case 'E':
|
||||
suffix = 1LL << 60;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
buffer[len - 1] = '\0';
|
||||
len--;
|
||||
if (len == 0)
|
||||
return false;
|
||||
}
|
||||
// all other chars must be digits
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (!isdigit(buffer[i]))
|
||||
return false;
|
||||
}
|
||||
off_t size = atoll(buffer);
|
||||
*_size = size * suffix;
|
||||
// check for overflow
|
||||
if (*_size / suffix != size)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// install_file
|
||||
status_t
|
||||
install_file(const char *file, off_t size)
|
||||
{
|
||||
// open the control device
|
||||
int fd = open(VIRTUAL_DRIVE_CONTROL_DEVICE, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Failed to open control device: %s\n",
|
||||
strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
// set up the info
|
||||
virtual_drive_info info;
|
||||
strcpy(info.file_name, file);
|
||||
if (size >= 0) {
|
||||
info.use_geometry = true;
|
||||
// fill in the geometry
|
||||
// default to 512 bytes block size
|
||||
uint32 blockSize = 512;
|
||||
// Optimally we have only 1 block per sector and only one head.
|
||||
// Since we have only a uint32 for the cylinder count, this won't work
|
||||
// for files > 2TB. So, we set the head count to the minimally possible
|
||||
// value.
|
||||
off_t blocks = size / blockSize;
|
||||
uint32 heads = (blocks + ULONG_MAX - 1) / ULONG_MAX;
|
||||
if (heads == 0)
|
||||
heads = 1;
|
||||
info.geometry.bytes_per_sector = blockSize;
|
||||
info.geometry.sectors_per_track = 1;
|
||||
info.geometry.cylinder_count = blocks / heads;
|
||||
info.geometry.head_count = heads;
|
||||
info.geometry.device_type = B_DISK; // TODO: Add a new constant.
|
||||
info.geometry.removable = false;
|
||||
info.geometry.read_only = false;
|
||||
info.geometry.write_once = false;
|
||||
} else {
|
||||
info.use_geometry = false;
|
||||
}
|
||||
// issue the ioctl
|
||||
status_t error = B_OK;
|
||||
if (ioctl(fd, VIRTUAL_DRIVE_REGISTER_FILE, &info) != 0) {
|
||||
error = errno;
|
||||
fprintf(stderr, "Failed to install device: %s\n", strerror(error));
|
||||
} else {
|
||||
printf("File `%s' registered as device `%s'.\n", file,
|
||||
info.device_name);
|
||||
}
|
||||
// close the control device
|
||||
close(fd);
|
||||
return error;
|
||||
}
|
||||
|
||||
// uninstall_file
|
||||
status_t
|
||||
uninstall_file(const char *device)
|
||||
{
|
||||
// open the device
|
||||
int fd = open(device, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Failed to open device `%s': %s\n",
|
||||
device, strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
// issue the ioctl
|
||||
status_t error = B_OK;
|
||||
if (ioctl(fd, VIRTUAL_DRIVE_UNREGISTER_FILE) != 0) {
|
||||
error = errno;
|
||||
fprintf(stderr, "Failed to uninstall device: %s\n", strerror(error));
|
||||
}
|
||||
// close the control device
|
||||
close(fd);
|
||||
return error;
|
||||
}
|
||||
|
||||
// main
|
||||
int
|
||||
main(int argc, const char **argv)
|
||||
{
|
||||
status_t error = B_OK;
|
||||
int argIndex = 1;
|
||||
bool install = true;
|
||||
off_t size = -1;
|
||||
// parse options
|
||||
for (; error == B_OK && argIndex < argc
|
||||
&& argv[argIndex][0] == '-'; argIndex++) {
|
||||
const char *arg = argv[argIndex];
|
||||
if (arg[1] == '-') {
|
||||
// "--" option
|
||||
arg += 2;
|
||||
if (!strcmp(arg, "install")) {
|
||||
install = true;
|
||||
} else if (!strcmp(arg, "size")) {
|
||||
argIndex++;
|
||||
if (argIndex >= argc) {
|
||||
fprintf(stderr, "Parameter expected for `--%s'.\n", arg);
|
||||
print_usage(1);
|
||||
return 1;
|
||||
}
|
||||
if (!parse_size(argv[argIndex], &size)) {
|
||||
fprintf(stderr, "Parameter for `--%s' must be of the form "
|
||||
"`<number>[K|M|G|T|P|E]'.\n", arg);
|
||||
print_usage(1);
|
||||
return 1;
|
||||
}
|
||||
} else if (!strcmp(arg, "uninstall")) {
|
||||
install = false;
|
||||
} else if (!strcmp(arg, "help")) {
|
||||
print_usage();
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "Invalid option `-%s'.\n", arg);
|
||||
print_usage(true);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
// "-" options
|
||||
arg++;
|
||||
int32 count = strlen(arg);
|
||||
for (int i = 0; error == B_OK && i < count; i++) {
|
||||
switch (arg[i]) {
|
||||
case 'h':
|
||||
print_usage();
|
||||
return 0;
|
||||
default:
|
||||
fprintf(stderr, "Invalid option `-%c'.\n", arg[i]);
|
||||
print_usage(true);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// parse rest (the file name)
|
||||
if (argIndex != argc - 1) {
|
||||
print_usage(true);
|
||||
return 1;
|
||||
}
|
||||
const char *file = argv[argIndex];
|
||||
// do the job
|
||||
if (install)
|
||||
error = install_file(file, size);
|
||||
else
|
||||
error = uninstall_file(file);
|
||||
return (error == B_OK ? 0 : 1);
|
||||
}
|
||||
|
@ -1,444 +0,0 @@
|
||||
// ----------------------------------------------------------------------
|
||||
// This software is part of the OpenBeOS distribution and is covered
|
||||
// by the OpenBeOS license.
|
||||
//
|
||||
// File Name: virtualdrive.c
|
||||
//
|
||||
// Description: Driver that emulates virtual drives.
|
||||
//
|
||||
// Author: Marcus Overhagen <Marcus@Overhagen.de>
|
||||
// Ingo Weinhold <bonefish@users.sf.net>
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <Drivers.h>
|
||||
#include <Errors.h>
|
||||
#include <malloc.h>
|
||||
#include "lock.h"
|
||||
|
||||
/*
|
||||
[2:07] <geist> when you open the file in the driver, use stat() to see if it's a file. if it is, call ioctl 10000 on the underlying file
|
||||
[2:07] <geist> that's the disable-cache ioctl
|
||||
[2:08] <geist> bfs is probably doing the same algorithm, and seeing that you are a device and not a file, and so it doesn't call 10000 on you
|
||||
[2:08] <marcus_o> thanks, I will try calling it
|
||||
[2:08] <geist> and dont bother using dosfs as a host fs, it wont work
|
||||
[2:09] <geist> bfs is the only fs that's reasonably safe being reentered like that, but only if the underlying one is opened with the cache disabled on that file
|
||||
[2:09] <geist> that ioctl is used on the swap file as well
|
||||
[2:10] <marcus_o> I'm currently allocating memory in the driver's write() function hook
|
||||
[2:10] <geist> cant do that
|
||||
*/
|
||||
|
||||
#define TRACE dprintf
|
||||
//#define TRACE (void)
|
||||
#define MB (1024LL * 1024LL)
|
||||
|
||||
static int dev_num_for_path(const char *path);
|
||||
|
||||
int32 api_version = B_CUR_DRIVER_API_VERSION;
|
||||
|
||||
lock driverlock;
|
||||
|
||||
typedef struct device_inf {
|
||||
int32 opencount;
|
||||
uint8 *buffer;
|
||||
int fd;
|
||||
int64 size;
|
||||
bool hidden;
|
||||
char file[1024];
|
||||
const char device_path[1024];
|
||||
} device_inf;
|
||||
|
||||
struct device_inf device_info[] =
|
||||
{
|
||||
{ 0, 0, 0, 64 * MB, false, "/tmp/virtualdrive64", "disk/virtual/0/raw" },
|
||||
{ 0, 0, 0, 32 * MB, false, "/tmp/virtualdrive32", "disk/virtual/1/raw" },
|
||||
};
|
||||
|
||||
const int kDeviceCount = sizeof(device_info) / sizeof(device_inf);
|
||||
|
||||
/* ----------
|
||||
init_hardware - called once the first time the driver is loaded
|
||||
----- */
|
||||
status_t
|
||||
init_hardware (void)
|
||||
{
|
||||
TRACE("virtualdrive: init_hardware\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
init_driver - optional function - called every time the driver
|
||||
is loaded.
|
||||
----- */
|
||||
status_t
|
||||
init_driver (void)
|
||||
{
|
||||
TRACE("virtualdrive: init\n");
|
||||
|
||||
new_lock(&driverlock, "virtualdrive lock");
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
uninit_driver - optional function - called every time the driver
|
||||
is unloaded
|
||||
----- */
|
||||
void
|
||||
uninit_driver (void)
|
||||
{
|
||||
TRACE("virtualdrive: uninit\n");
|
||||
free_lock(&driverlock);
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
virtualdrive_open - handle open() calls
|
||||
----- */
|
||||
|
||||
static status_t
|
||||
virtualdrive_open (const char *name, uint32 flags, void** cookie)
|
||||
{
|
||||
int dev_num;
|
||||
TRACE("virtualdrive: open %s\n",name);
|
||||
|
||||
*cookie = (void *) -1;
|
||||
|
||||
if (0 == strcmp(name,"disk/virtual/virtualdrive")) {
|
||||
TRACE("virtualdrive: rejected!\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
LOCK(driverlock);
|
||||
|
||||
dev_num = dev_num_for_path(name);
|
||||
|
||||
TRACE("virtualdrive: dev_num %d!\n",dev_num);
|
||||
|
||||
if (dev_num < 0 || dev_num >= kDeviceCount) {
|
||||
TRACE("virtualdrive: wrong index!\n");
|
||||
UNLOCK(driverlock);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (device_info[dev_num].hidden) {
|
||||
TRACE("virtualdrive: device is hidden!\n");
|
||||
UNLOCK(driverlock);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// store index in cookie
|
||||
*cookie = (void *) dev_num;
|
||||
|
||||
device_info[dev_num].opencount++;
|
||||
|
||||
if (device_info[dev_num].opencount == 1) {
|
||||
char c;
|
||||
const char *file;
|
||||
file = device_info[dev_num].file;
|
||||
TRACE("virtualdrive: opening %s\n",file);
|
||||
device_info[dev_num].fd = open(file,O_RDWR | O_CREAT,0666);
|
||||
if (device_info[dev_num].fd < 0) {
|
||||
device_info[dev_num].opencount--;
|
||||
TRACE("virtualdrive: file open failed!\n");
|
||||
// remove index from cookie
|
||||
*cookie = (void *) -1;
|
||||
UNLOCK(driverlock);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// disable caching for underlying file! (else this driver will deadlock)
|
||||
if (0 != ioctl(device_info[dev_num].fd,10000)) {
|
||||
TRACE("virtualdrive: disable caching ioctl failed\n");
|
||||
}
|
||||
// set file size
|
||||
lseek(device_info[dev_num].fd,device_info[dev_num].size - 1,0);
|
||||
read(device_info[dev_num].fd,&c,1);
|
||||
lseek(device_info[dev_num].fd,device_info[dev_num].size - 1,0);
|
||||
write(device_info[dev_num].fd,&c,1);
|
||||
|
||||
// device_info[dev_num].buffer = (uint8*) malloc(65536);
|
||||
TRACE("virtualdrive: file opened\n");
|
||||
}
|
||||
|
||||
UNLOCK(driverlock);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
virtualdrive_close - handle close() calls
|
||||
----- */
|
||||
|
||||
static status_t
|
||||
virtualdrive_close (void* cookie)
|
||||
{
|
||||
int dev_num;
|
||||
TRACE("virtualdrive: close\n");
|
||||
|
||||
dev_num = (int)cookie;
|
||||
|
||||
TRACE("virtualdrive: dev_num = %d\n",dev_num);
|
||||
if (dev_num < 0 || dev_num >= kDeviceCount)
|
||||
return B_OK;
|
||||
|
||||
LOCK(driverlock);
|
||||
|
||||
device_info[dev_num].opencount--;
|
||||
|
||||
if (device_info[dev_num].opencount == 0) {
|
||||
close(device_info[dev_num].fd);
|
||||
// free(device_info[dev_num].buffer);
|
||||
}
|
||||
|
||||
UNLOCK(driverlock);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
virtualdrive_read - handle read() calls
|
||||
----- */
|
||||
|
||||
static status_t
|
||||
virtualdrive_read (void* cookie, off_t position, void *buffer, size_t* num_bytes)
|
||||
{
|
||||
int fd;
|
||||
TRACE("virtualdrive: read pos = 0x%08Lx, bytes = 0x%08lx\n",position,*num_bytes);
|
||||
fd = device_info[(int) cookie].fd;
|
||||
if (fd < 0)
|
||||
return B_ERROR;
|
||||
if (position & 511)
|
||||
return B_ERROR;
|
||||
if (*num_bytes & 511)
|
||||
return B_ERROR;
|
||||
|
||||
LOCK(driverlock);
|
||||
lseek(fd,position,0);
|
||||
read(fd,buffer,*num_bytes);
|
||||
UNLOCK(driverlock);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
virtualdrive_write - handle write() calls
|
||||
----- */
|
||||
|
||||
static status_t
|
||||
virtualdrive_write (void* cookie, off_t position, const void* buffer, size_t* num_bytes)
|
||||
{
|
||||
int fd;
|
||||
TRACE("virtualdrive: write pos = 0x%08Lx, bytes = 0x%08lx\n",position,*num_bytes);
|
||||
fd = device_info[(int) cookie].fd;
|
||||
if (fd < 0)
|
||||
return B_ERROR;
|
||||
if (position & 511)
|
||||
return B_ERROR;
|
||||
if (*num_bytes & 511)
|
||||
return B_ERROR;
|
||||
|
||||
LOCK(driverlock);
|
||||
lseek(fd,position,0);
|
||||
write(fd,buffer,*num_bytes);
|
||||
UNLOCK(driverlock);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
virtualdrive_control - handle ioctl calls
|
||||
----- */
|
||||
|
||||
static status_t
|
||||
virtualdrive_control (void* cookie, uint32 op, void* arg, size_t len)
|
||||
{
|
||||
device_geometry *dg;
|
||||
partition_info *pi;
|
||||
struct device_inf *dev;
|
||||
|
||||
TRACE("virtualdrive: ioctl\n");
|
||||
|
||||
dev = &device_info[(int)cookie];
|
||||
|
||||
switch (op) {
|
||||
case B_GET_DEVICE_SIZE:
|
||||
TRACE("virtualdrive: free\n");
|
||||
*(size_t*)arg = dev->size;
|
||||
return B_OK;
|
||||
|
||||
case B_SET_NONBLOCKING_IO:
|
||||
TRACE("virtualdrive: B_SET_NONBLOCKING_IO\n");
|
||||
return B_OK;
|
||||
|
||||
case B_SET_BLOCKING_IO:
|
||||
TRACE("virtualdrive: B_SET_BLOCKING_IO\n");
|
||||
return B_OK;
|
||||
|
||||
case B_GET_READ_STATUS:
|
||||
TRACE("virtualdrive: B_GET_READ_STATUS\n");
|
||||
*(bool*)arg = true;
|
||||
return B_OK;
|
||||
|
||||
case B_GET_WRITE_STATUS:
|
||||
TRACE("virtualdrive: B_GET_WRITE_STATUS\n");
|
||||
*(bool*)arg = true;
|
||||
return B_OK;
|
||||
|
||||
case B_GET_ICON:
|
||||
TRACE("virtualdrive: B_GET_ICON\n");
|
||||
return B_OK;
|
||||
|
||||
case B_GET_GEOMETRY:
|
||||
TRACE("virtualdrive: B_GET_GEOMETRY\n");
|
||||
dg = (device_geometry *)arg;
|
||||
dg->bytes_per_sector = 512;
|
||||
dg->sectors_per_track = dev->size / 512;
|
||||
dg->cylinder_count = 1;
|
||||
dg->head_count = 1;
|
||||
dg->device_type = B_DISK;
|
||||
dg->removable = false;
|
||||
dg->read_only = false;
|
||||
dg->write_once = false;
|
||||
return B_OK;
|
||||
|
||||
case B_GET_BIOS_GEOMETRY:
|
||||
TRACE("virtualdrive: B_GET_BIOS_GEOMETRY\n");
|
||||
dg = (device_geometry *)arg;
|
||||
dg->bytes_per_sector = 512;
|
||||
dg->sectors_per_track = dev->size / (512 * 1024);
|
||||
dg->cylinder_count = 1024;
|
||||
dg->head_count = 1;
|
||||
dg->device_type = B_DISK;
|
||||
dg->removable = false;
|
||||
dg->read_only = false;
|
||||
dg->write_once = false;
|
||||
return B_OK;
|
||||
|
||||
case B_GET_PARTITION_INFO:
|
||||
// this one never gets called
|
||||
TRACE("virtualdrive: B_GET_PARTITION_INFO\n");
|
||||
pi = (partition_info *)arg;
|
||||
pi->offset = 0;
|
||||
pi->size = dev->size;
|
||||
pi->logical_block_size = 512;
|
||||
pi->session = 1;
|
||||
pi->partition = 1;
|
||||
strcpy(pi->device,"/boot/home/dummy");
|
||||
return B_OK;
|
||||
|
||||
case B_GET_MEDIA_STATUS:
|
||||
TRACE("virtualdrive: B_GET_MEDIA_STATUS\n");
|
||||
*(status_t*)arg = B_NO_ERROR;
|
||||
return B_OK;
|
||||
|
||||
case B_SET_UNINTERRUPTABLE_IO:
|
||||
TRACE("virtualdrive: B_SET_UNINTERRUPTABLE_IO\n");
|
||||
return B_OK;
|
||||
|
||||
case B_SET_INTERRUPTABLE_IO:
|
||||
TRACE("virtualdrive: B_SET_INTERRUPTABLE_IO\n");
|
||||
return B_OK;
|
||||
|
||||
case B_FLUSH_DRIVE_CACHE:
|
||||
TRACE("virtualdrive: B_FLUSH_DRIVE_CACHE\n");
|
||||
return B_OK;
|
||||
|
||||
case B_GET_BIOS_DRIVE_ID:
|
||||
TRACE("virtualdrive: B_GET_BIOS_DRIVE_ID\n");
|
||||
*(uint8*)arg = 0xF8;
|
||||
return B_OK;
|
||||
|
||||
case B_GET_DRIVER_FOR_DEVICE:
|
||||
case B_SET_DEVICE_SIZE:
|
||||
case B_SET_PARTITION:
|
||||
case B_FORMAT_DEVICE:
|
||||
case B_EJECT_DEVICE:
|
||||
case B_LOAD_MEDIA:
|
||||
case B_GET_NEXT_OPEN_DEVICE:
|
||||
TRACE("virtualdrive: another ioctl: %lx (%lu)\n", op, op);
|
||||
return B_BAD_VALUE;
|
||||
default:
|
||||
TRACE("virtualdrive: unknown ioctl: %lx (%lu)\n", op, op);
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* -----
|
||||
virtualdrive_free - called after the last device is closed, and after
|
||||
all i/o is complete.
|
||||
----- */
|
||||
static status_t
|
||||
virtualdrive_free (void* cookie)
|
||||
{
|
||||
TRACE("virtualdrive: free\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* -----
|
||||
null-terminated array of device names supported by this driver
|
||||
----- */
|
||||
|
||||
static const char *virtualdrive_name[] = {
|
||||
// "disk/virtual/virtualdrive",
|
||||
"disk/virtual/0/raw",
|
||||
"disk/virtual/1/raw",
|
||||
NULL
|
||||
};
|
||||
|
||||
/* -----
|
||||
function pointers for the device hooks entry points
|
||||
----- */
|
||||
|
||||
device_hooks virtualdrive_hooks = {
|
||||
virtualdrive_open, /* -> open entry point */
|
||||
virtualdrive_close, /* -> close entry point */
|
||||
virtualdrive_free, /* -> free cookie */
|
||||
virtualdrive_control, /* -> control entry point */
|
||||
virtualdrive_read, /* -> read entry point */
|
||||
virtualdrive_write /* -> write entry point */
|
||||
};
|
||||
|
||||
/* ----------
|
||||
publish_devices - return a null-terminated array of devices
|
||||
supported by this driver.
|
||||
----- */
|
||||
|
||||
const char**
|
||||
publish_devices()
|
||||
{
|
||||
TRACE("virtualdrive: publish_devices\n");
|
||||
return virtualdrive_name;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
find_device - return ptr to device hooks structure for a
|
||||
given device name
|
||||
----- */
|
||||
|
||||
device_hooks*
|
||||
find_device(const char* name)
|
||||
{
|
||||
return &virtualdrive_hooks;
|
||||
}
|
||||
|
||||
// dev_num_for_path
|
||||
static
|
||||
int
|
||||
dev_num_for_path(const char *path)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < kDeviceCount; i++) {
|
||||
if (!strcmp(path, device_info[i].device_path))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
699
src/tests/kits/storage/virtualdrive/virtualdrive.cpp
Normal file
699
src/tests/kits/storage/virtualdrive/virtualdrive.cpp
Normal file
@ -0,0 +1,699 @@
|
||||
// ----------------------------------------------------------------------
|
||||
// This software is part of the OpenBeOS distribution and is covered
|
||||
// by the OpenBeOS license.
|
||||
//
|
||||
// File Name: virtualdrive.c
|
||||
//
|
||||
// Description: Driver that emulates virtual drives.
|
||||
//
|
||||
// Author: Marcus Overhagen <Marcus@Overhagen.de>
|
||||
// Ingo Weinhold <bonefish@users.sf.net>
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <Drivers.h>
|
||||
#include <Errors.h>
|
||||
|
||||
#include "lock.h"
|
||||
#include "virtualdrive.h"
|
||||
|
||||
/*
|
||||
[2:07] <geist> when you open the file in the driver, use stat() to see if it's a file. if it is, call ioctl 10000 on the underlying file
|
||||
[2:07] <geist> that's the disable-cache ioctl
|
||||
[2:08] <geist> bfs is probably doing the same algorithm, and seeing that you are a device and not a file, and so it doesn't call 10000 on you
|
||||
[2:08] <marcus_o> thanks, I will try calling it
|
||||
[2:08] <geist> and dont bother using dosfs as a host fs, it wont work
|
||||
[2:09] <geist> bfs is the only fs that's reasonably safe being reentered like that, but only if the underlying one is opened with the cache disabled on that file
|
||||
[2:09] <geist> that ioctl is used on the swap file as well
|
||||
[2:10] <marcus_o> I'm currently allocating memory in the driver's write() function hook
|
||||
[2:10] <geist> cant do that
|
||||
*/
|
||||
|
||||
#define TRACE dprintf
|
||||
//#define TRACE (void)
|
||||
#define MB (1024LL * 1024LL)
|
||||
|
||||
static int dev_index_for_path(const char *path);
|
||||
|
||||
/* -----
|
||||
null-terminated array of device names supported by this driver
|
||||
----- */
|
||||
|
||||
static const char *virtualdrive_name[] = {
|
||||
VIRTUAL_DRIVE_DIRECTORY_REL "/0",
|
||||
VIRTUAL_DRIVE_DIRECTORY_REL "/1",
|
||||
VIRTUAL_DRIVE_DIRECTORY_REL "/2",
|
||||
VIRTUAL_DRIVE_DIRECTORY_REL "/3",
|
||||
VIRTUAL_DRIVE_DIRECTORY_REL "/4",
|
||||
VIRTUAL_DRIVE_DIRECTORY_REL "/5",
|
||||
VIRTUAL_DRIVE_DIRECTORY_REL "/6",
|
||||
VIRTUAL_DRIVE_DIRECTORY_REL "/7",
|
||||
VIRTUAL_DRIVE_DIRECTORY_REL "/8",
|
||||
VIRTUAL_DRIVE_DIRECTORY_REL "/9",
|
||||
VIRTUAL_DRIVE_CONTROL_DEVICE_REL,
|
||||
NULL
|
||||
};
|
||||
|
||||
int32 api_version = B_CUR_DRIVER_API_VERSION;
|
||||
|
||||
lock driverlock;
|
||||
|
||||
typedef struct device_info {
|
||||
int32 opencount;
|
||||
int fd;
|
||||
off_t size;
|
||||
bool unused;
|
||||
bool registered;
|
||||
char file[B_PATH_NAME_LENGTH];
|
||||
const char *device_path;
|
||||
device_geometry geometry;
|
||||
} device_info;
|
||||
|
||||
#define kDeviceCount 11
|
||||
#define kDataDeviceCount (kDeviceCount - 1)
|
||||
#define kControlDevice (kDeviceCount - 1)
|
||||
struct device_info gDeviceInfos[kDeviceCount];
|
||||
|
||||
static int32 gRegistrationCount = 0;
|
||||
static int gControlDeviceFD = -1;
|
||||
|
||||
static thread_id gLockOwner = -1;
|
||||
static int32 gLockOwnerNesting = 0;
|
||||
|
||||
// lock_driver
|
||||
void
|
||||
lock_driver()
|
||||
{
|
||||
thread_id thread = find_thread(NULL);
|
||||
if (gLockOwner != thread) {
|
||||
LOCK(driverlock);
|
||||
gLockOwner = thread;
|
||||
}
|
||||
gLockOwnerNesting++;
|
||||
}
|
||||
|
||||
// unlock_driver
|
||||
void
|
||||
unlock_driver()
|
||||
{
|
||||
thread_id thread = find_thread(NULL);
|
||||
if (gLockOwner == thread && --gLockOwnerNesting == 0) {
|
||||
gLockOwner = -1;
|
||||
UNLOCK(driverlock);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// is_valid_device_index
|
||||
static inline
|
||||
bool
|
||||
is_valid_device_index(int32 index)
|
||||
{
|
||||
return (index >= 0 && index < kDeviceCount);
|
||||
}
|
||||
|
||||
// is_valid_data_device_index
|
||||
static inline
|
||||
bool
|
||||
is_valid_data_device_index(int32 index)
|
||||
{
|
||||
return (is_valid_device_index(index) && index != kControlDevice);
|
||||
}
|
||||
|
||||
// clear_device_info
|
||||
static
|
||||
void
|
||||
clear_device_info(int32 index)
|
||||
{
|
||||
TRACE("virtualdrive: clear_device_info(%ld)\n", index);
|
||||
device_info &info = gDeviceInfos[index];
|
||||
info.opencount = 0;
|
||||
info.fd = -1;
|
||||
info.size = 0;
|
||||
info.unused = (index != kDeviceCount - 1);
|
||||
info.registered = !info.unused;
|
||||
info.file[0] = '\0';
|
||||
info.device_path = virtualdrive_name[index];
|
||||
info.geometry.read_only = true;
|
||||
}
|
||||
|
||||
// init_device_info
|
||||
static
|
||||
status_t
|
||||
init_device_info(int32 index, virtual_drive_info *initInfo)
|
||||
{
|
||||
if (!is_valid_data_device_index(index) || !initInfo)
|
||||
return B_BAD_VALUE;
|
||||
device_info &info = gDeviceInfos[index];
|
||||
if (!info.unused)
|
||||
return B_BAD_VALUE;
|
||||
bool readOnly = (initInfo->use_geometry && initInfo->geometry.read_only);
|
||||
// open the file
|
||||
int fd = open(initInfo->file_name, (readOnly ? O_RDONLY : O_RDWR));
|
||||
if (fd < 0)
|
||||
return errno;
|
||||
// disable caching for underlying file! (else this driver will deadlock)
|
||||
status_t error = B_OK;
|
||||
if (ioctl(fd, 10000) != 0) {
|
||||
error = errno;
|
||||
TRACE("virtualdrive: disable caching ioctl failed\n");
|
||||
}
|
||||
// get the file size
|
||||
off_t fileSize = 0;
|
||||
if (error == B_OK) {
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == 0)
|
||||
fileSize = st.st_size;
|
||||
else
|
||||
error = errno;
|
||||
}
|
||||
// If we shall use the supplied geometry, we enlarge the file, if
|
||||
// necessary. Otherwise we fill in the geometry according to the size of the file.
|
||||
off_t size = 0;
|
||||
if (error == B_OK) {
|
||||
if (initInfo->use_geometry) {
|
||||
// use the supplied geometry
|
||||
info.geometry = initInfo->geometry;
|
||||
size = (off_t)info.geometry.bytes_per_sector
|
||||
* info.geometry.sectors_per_track
|
||||
* info.geometry.cylinder_count
|
||||
* info.geometry.head_count;
|
||||
if (size > fileSize) {
|
||||
if (!readOnly) {
|
||||
if (ftruncate(fd, size) != 0)
|
||||
error = errno;
|
||||
} else
|
||||
error = B_NOT_ALLOWED;
|
||||
}
|
||||
} else {
|
||||
// fill in the geometry
|
||||
// default to 512 bytes block size
|
||||
uint32 blockSize = 512;
|
||||
// Optimally we have only 1 block per sector and only one head.
|
||||
// Since we have only a uint32 for the cylinder count, this won't work
|
||||
// for files > 2TB. So, we set the head count to the minimally possible
|
||||
// value.
|
||||
off_t blocks = fileSize / blockSize;
|
||||
uint32 heads = (blocks + ULONG_MAX - 1) / ULONG_MAX;
|
||||
if (heads == 0)
|
||||
heads = 1;
|
||||
info.geometry.bytes_per_sector = blockSize;
|
||||
info.geometry.sectors_per_track = 1;
|
||||
info.geometry.cylinder_count = blocks / heads;
|
||||
info.geometry.head_count = heads;
|
||||
info.geometry.device_type = B_DISK; // TODO: Add a new constant.
|
||||
info.geometry.removable = false;
|
||||
info.geometry.read_only = false;
|
||||
info.geometry.write_once = false;
|
||||
size = (off_t)info.geometry.bytes_per_sector
|
||||
* info.geometry.sectors_per_track
|
||||
* info.geometry.cylinder_count
|
||||
* info.geometry.head_count;
|
||||
}
|
||||
}
|
||||
// fill in the rest of the device_info structure
|
||||
if (error == B_OK) {
|
||||
info.opencount = 0;
|
||||
info.fd = fd;
|
||||
info.size = size;
|
||||
info.unused = false;
|
||||
info.registered = true;
|
||||
strcpy(info.file, initInfo->file_name);
|
||||
info.device_path = virtualdrive_name[index];
|
||||
} else {
|
||||
// cleanup on error
|
||||
close(fd);
|
||||
clear_device_info(index);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// uninit_device_info
|
||||
static
|
||||
status_t
|
||||
uninit_device_info(int32 index)
|
||||
{
|
||||
if (!is_valid_data_device_index(index))
|
||||
return B_BAD_VALUE;
|
||||
device_info &info = gDeviceInfos[index];
|
||||
if (info.unused)
|
||||
return B_BAD_VALUE;
|
||||
close(info.fd);
|
||||
clear_device_info(index);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
init_hardware - called once the first time the driver is loaded
|
||||
----- */
|
||||
status_t
|
||||
init_hardware (void)
|
||||
{
|
||||
TRACE("virtualdrive: init_hardware\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
init_driver - optional function - called every time the driver
|
||||
is loaded.
|
||||
----- */
|
||||
status_t
|
||||
init_driver (void)
|
||||
{
|
||||
TRACE("virtualdrive: init\n");
|
||||
|
||||
new_lock(&driverlock, "virtualdrive lock");
|
||||
|
||||
// init the device infos
|
||||
for (int32 i = 0; i < kDeviceCount; i++)
|
||||
clear_device_info(i);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
uninit_driver - optional function - called every time the driver
|
||||
is unloaded
|
||||
----- */
|
||||
void
|
||||
uninit_driver (void)
|
||||
{
|
||||
TRACE("virtualdrive: uninit\n");
|
||||
free_lock(&driverlock);
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
virtualdrive_open - handle open() calls
|
||||
----- */
|
||||
|
||||
static status_t
|
||||
virtualdrive_open (const char *name, uint32 flags, void** cookie)
|
||||
{
|
||||
TRACE("virtualdrive: open %s\n",name);
|
||||
|
||||
*cookie = (void *) -1;
|
||||
|
||||
lock_driver();
|
||||
|
||||
int32 devIndex = dev_index_for_path(name);
|
||||
|
||||
TRACE("virtualdrive: devIndex %ld!\n", devIndex);
|
||||
|
||||
if (!is_valid_device_index(devIndex)) {
|
||||
TRACE("virtualdrive: wrong index!\n");
|
||||
unlock_driver();
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (gDeviceInfos[devIndex].unused) {
|
||||
TRACE("virtualdrive: device is unused!\n");
|
||||
unlock_driver();
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
if (!gDeviceInfos[devIndex].registered) {
|
||||
TRACE("virtualdrive: device has been unregistered!\n");
|
||||
unlock_driver();
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// store index in cookie
|
||||
*cookie = (void *) devIndex;
|
||||
|
||||
gDeviceInfos[devIndex].opencount++;
|
||||
|
||||
unlock_driver();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
virtualdrive_close - handle close() calls
|
||||
----- */
|
||||
|
||||
static status_t
|
||||
virtualdrive_close (void* cookie)
|
||||
{
|
||||
TRACE("virtualdrive: close\n");
|
||||
|
||||
int32 devIndex = (int)cookie;
|
||||
|
||||
TRACE("virtualdrive: devIndex = %ld\n", devIndex);
|
||||
if (!is_valid_data_device_index(devIndex))
|
||||
return B_OK;
|
||||
|
||||
lock_driver();
|
||||
|
||||
gDeviceInfos[devIndex].opencount--;
|
||||
if (gDeviceInfos[devIndex].opencount == 0 && !gDeviceInfos[devIndex].registered) {
|
||||
// The last FD is closed and the device has been unregistered. Free its info.
|
||||
uninit_device_info(devIndex);
|
||||
}
|
||||
|
||||
unlock_driver();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
virtualdrive_read - handle read() calls
|
||||
----- */
|
||||
|
||||
static status_t
|
||||
virtualdrive_read (void* cookie, off_t position, void *buffer, size_t* numBytes)
|
||||
{
|
||||
TRACE("virtualdrive: read pos = 0x%08Lx, bytes = 0x%08lx\n",position,*numBytes);
|
||||
// check parameters
|
||||
int devIndex = (int)cookie;
|
||||
if (devIndex == kControlDevice) {
|
||||
TRACE("virtualdrive: reading from control device not allowed\n");
|
||||
return B_NOT_ALLOWED;
|
||||
}
|
||||
if (position < 0)
|
||||
return B_BAD_VALUE;
|
||||
lock_driver();
|
||||
device_info &info = gDeviceInfos[devIndex];
|
||||
// adjust position and numBytes according to the file size
|
||||
if (position > info.size)
|
||||
position = info.size;
|
||||
if (position + *numBytes > info.size)
|
||||
*numBytes = info.size - position;
|
||||
// read
|
||||
status_t error = B_OK;
|
||||
ssize_t bytesRead = read_pos(info.fd, position, buffer, *numBytes);
|
||||
if (bytesRead < 0)
|
||||
error = errno;
|
||||
else
|
||||
*numBytes = bytesRead;
|
||||
unlock_driver();
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
virtualdrive_write - handle write() calls
|
||||
----- */
|
||||
|
||||
static status_t
|
||||
virtualdrive_write(void* cookie, off_t position, const void* buffer, size_t* numBytes)
|
||||
{
|
||||
TRACE("virtualdrive: write pos = 0x%08Lx, bytes = 0x%08lx\n",position,*numBytes);
|
||||
// check parameters
|
||||
int devIndex = (int)cookie;
|
||||
if (devIndex == kControlDevice) {
|
||||
TRACE("virtualdrive: writing to control device not allowed\n");
|
||||
return B_NOT_ALLOWED;
|
||||
}
|
||||
if (position < 0)
|
||||
return B_BAD_VALUE;
|
||||
lock_driver();
|
||||
device_info &info = gDeviceInfos[devIndex];
|
||||
// adjust position and numBytes according to the file size
|
||||
if (position > info.size)
|
||||
position = info.size;
|
||||
if (position + *numBytes > info.size)
|
||||
*numBytes = info.size - position;
|
||||
// read
|
||||
status_t error = B_OK;
|
||||
ssize_t bytesRead = write_pos(info.fd, position, buffer, *numBytes);
|
||||
if (bytesRead < 0)
|
||||
error = errno;
|
||||
else
|
||||
*numBytes = bytesRead;
|
||||
unlock_driver();
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/* ----------
|
||||
virtualdrive_control - handle ioctl calls
|
||||
----- */
|
||||
|
||||
static status_t
|
||||
virtualdrive_control (void* cookie, uint32 op, void* arg, size_t len)
|
||||
{
|
||||
TRACE("virtualdrive: ioctl\n");
|
||||
|
||||
int devIndex = (int)cookie;
|
||||
device_info &info = gDeviceInfos[devIndex];
|
||||
|
||||
if (devIndex == kControlDevice || info.unused) {
|
||||
// control device or unused data device
|
||||
switch (op) {
|
||||
case B_GET_DEVICE_SIZE:
|
||||
case B_SET_NONBLOCKING_IO:
|
||||
case B_SET_BLOCKING_IO:
|
||||
case B_GET_READ_STATUS:
|
||||
case B_GET_WRITE_STATUS:
|
||||
case B_GET_ICON:
|
||||
case B_GET_GEOMETRY:
|
||||
case B_GET_BIOS_GEOMETRY:
|
||||
case B_GET_MEDIA_STATUS:
|
||||
case B_SET_UNINTERRUPTABLE_IO:
|
||||
case B_SET_INTERRUPTABLE_IO:
|
||||
case B_FLUSH_DRIVE_CACHE:
|
||||
case B_GET_BIOS_DRIVE_ID:
|
||||
case B_GET_DRIVER_FOR_DEVICE:
|
||||
case B_SET_DEVICE_SIZE:
|
||||
case B_SET_PARTITION:
|
||||
case B_FORMAT_DEVICE:
|
||||
case B_EJECT_DEVICE:
|
||||
case B_LOAD_MEDIA:
|
||||
case B_GET_NEXT_OPEN_DEVICE:
|
||||
TRACE("virtualdrive: another ioctl: %lx (%lu)\n", op, op);
|
||||
return B_BAD_VALUE;
|
||||
case VIRTUAL_DRIVE_REGISTER_FILE:
|
||||
{
|
||||
TRACE("virtualdrive: VIRTUAL_DRIVE_REGISTER_FILE\n");
|
||||
if (devIndex != kControlDevice || !arg)
|
||||
return B_BAD_VALUE;
|
||||
// find an unused data device and initialize it
|
||||
virtual_drive_info *driveInfo = (virtual_drive_info*)arg;
|
||||
status_t error = B_ERROR;
|
||||
lock_driver();
|
||||
for (int32 i = 0; i < kDataDeviceCount; i++) {
|
||||
if (gDeviceInfos[i].unused) {
|
||||
error = init_device_info(i, driveInfo);
|
||||
if (error == B_OK) {
|
||||
// return the device path
|
||||
strcpy(driveInfo->device_name, "/dev/");
|
||||
strcat(driveInfo->device_name,
|
||||
gDeviceInfos[i].device_path);
|
||||
// on the first registration we need to open the
|
||||
// control device to stay loaded
|
||||
if (gRegistrationCount++ == 0) {
|
||||
char path[B_PATH_NAME_LENGTH];
|
||||
strcpy(path, "/dev/");
|
||||
strcat(path, info.device_path);
|
||||
gControlDeviceFD = open(path, O_RDONLY);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
unlock_driver();
|
||||
return error;
|
||||
}
|
||||
case VIRTUAL_DRIVE_UNREGISTER_FILE:
|
||||
case VIRTUAL_DRIVE_GET_INFO:
|
||||
TRACE("virtualdrive: VIRTUAL_DRIVE_UNREGISTER_FILE/"
|
||||
"VIRTUAL_DRIVE_GET_INFO\n");
|
||||
return B_BAD_VALUE;
|
||||
default:
|
||||
TRACE("virtualdrive: unknown ioctl: %lx (%lu)\n", op, op);
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
} else {
|
||||
// used data device
|
||||
switch (op) {
|
||||
case B_GET_DEVICE_SIZE:
|
||||
TRACE("virtualdrive: B_GET_DEVICE_SIZE\n");
|
||||
*(size_t*)arg = info.size;
|
||||
return B_OK;
|
||||
|
||||
case B_SET_NONBLOCKING_IO:
|
||||
TRACE("virtualdrive: B_SET_NONBLOCKING_IO\n");
|
||||
return B_OK;
|
||||
|
||||
case B_SET_BLOCKING_IO:
|
||||
TRACE("virtualdrive: B_SET_BLOCKING_IO\n");
|
||||
return B_OK;
|
||||
|
||||
case B_GET_READ_STATUS:
|
||||
TRACE("virtualdrive: B_GET_READ_STATUS\n");
|
||||
*(bool*)arg = true;
|
||||
return B_OK;
|
||||
|
||||
case B_GET_WRITE_STATUS:
|
||||
TRACE("virtualdrive: B_GET_WRITE_STATUS\n");
|
||||
*(bool*)arg = true;
|
||||
return B_OK;
|
||||
|
||||
case B_GET_ICON:
|
||||
TRACE("virtualdrive: B_GET_ICON\n");
|
||||
return B_OK;
|
||||
|
||||
case B_GET_GEOMETRY:
|
||||
TRACE("virtualdrive: B_GET_GEOMETRY\n");
|
||||
*(device_geometry *)arg = info.geometry;
|
||||
return B_OK;
|
||||
|
||||
case B_GET_BIOS_GEOMETRY:
|
||||
{
|
||||
TRACE("virtualdrive: B_GET_BIOS_GEOMETRY\n");
|
||||
device_geometry *dg = (device_geometry *)arg;
|
||||
dg->bytes_per_sector = 512;
|
||||
dg->sectors_per_track = info.size / (512 * 1024);
|
||||
dg->cylinder_count = 1024;
|
||||
dg->head_count = 1;
|
||||
dg->device_type = info.geometry.device_type;
|
||||
dg->removable = info.geometry.removable;
|
||||
dg->read_only = info.geometry.read_only;
|
||||
dg->write_once = info.geometry.write_once;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
case B_GET_MEDIA_STATUS:
|
||||
TRACE("virtualdrive: B_GET_MEDIA_STATUS\n");
|
||||
*(status_t*)arg = B_NO_ERROR;
|
||||
return B_OK;
|
||||
|
||||
case B_SET_UNINTERRUPTABLE_IO:
|
||||
TRACE("virtualdrive: B_SET_UNINTERRUPTABLE_IO\n");
|
||||
return B_OK;
|
||||
|
||||
case B_SET_INTERRUPTABLE_IO:
|
||||
TRACE("virtualdrive: B_SET_INTERRUPTABLE_IO\n");
|
||||
return B_OK;
|
||||
|
||||
case B_FLUSH_DRIVE_CACHE:
|
||||
TRACE("virtualdrive: B_FLUSH_DRIVE_CACHE\n");
|
||||
return B_OK;
|
||||
|
||||
case B_GET_BIOS_DRIVE_ID:
|
||||
TRACE("virtualdrive: B_GET_BIOS_DRIVE_ID\n");
|
||||
*(uint8*)arg = 0xF8;
|
||||
return B_OK;
|
||||
|
||||
case B_GET_DRIVER_FOR_DEVICE:
|
||||
case B_SET_DEVICE_SIZE:
|
||||
case B_SET_PARTITION:
|
||||
case B_FORMAT_DEVICE:
|
||||
case B_EJECT_DEVICE:
|
||||
case B_LOAD_MEDIA:
|
||||
case B_GET_NEXT_OPEN_DEVICE:
|
||||
TRACE("virtualdrive: another ioctl: %lx (%lu)\n", op, op);
|
||||
return B_BAD_VALUE;
|
||||
case VIRTUAL_DRIVE_REGISTER_FILE:
|
||||
TRACE("virtualdrive: VIRTUAL_DRIVE_REGISTER_FILE (data)\n");
|
||||
return B_BAD_VALUE;
|
||||
case VIRTUAL_DRIVE_UNREGISTER_FILE:
|
||||
{
|
||||
TRACE("virtualdrive: VIRTUAL_DRIVE_UNREGISTER_FILE\n");
|
||||
lock_driver();
|
||||
bool wasRegistered = gDeviceInfos[devIndex].registered;
|
||||
gDeviceInfos[devIndex].registered = false;
|
||||
// on the last unregistration we need to close the
|
||||
// control device
|
||||
if (wasRegistered && --gRegistrationCount == 0) {
|
||||
close(gControlDeviceFD);
|
||||
gControlDeviceFD = -1;
|
||||
}
|
||||
unlock_driver();
|
||||
return B_OK;
|
||||
}
|
||||
case VIRTUAL_DRIVE_GET_INFO:
|
||||
{
|
||||
TRACE("virtualdrive: VIRTUAL_DRIVE_GET_INFO\n");
|
||||
if (!arg)
|
||||
return B_BAD_VALUE;
|
||||
virtual_drive_info *driveInfo = (virtual_drive_info*)arg;
|
||||
strcpy(driveInfo->file_name, info.file);
|
||||
strcpy(driveInfo->device_name, "/dev/");
|
||||
strcat(driveInfo->device_name, info.device_path);
|
||||
driveInfo->geometry = info.geometry;
|
||||
driveInfo->use_geometry = true;
|
||||
return B_OK;
|
||||
}
|
||||
default:
|
||||
TRACE("virtualdrive: unknown ioctl: %lx (%lu)\n", op, op);
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* -----
|
||||
virtualdrive_free - called after the last device is closed, and after
|
||||
all i/o is complete.
|
||||
----- */
|
||||
static status_t
|
||||
virtualdrive_free (void* cookie)
|
||||
{
|
||||
TRACE("virtualdrive: free\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* -----
|
||||
function pointers for the device hooks entry points
|
||||
----- */
|
||||
|
||||
device_hooks virtualdrive_hooks = {
|
||||
virtualdrive_open, /* -> open entry point */
|
||||
virtualdrive_close, /* -> close entry point */
|
||||
virtualdrive_free, /* -> free cookie */
|
||||
virtualdrive_control, /* -> control entry point */
|
||||
virtualdrive_read, /* -> read entry point */
|
||||
virtualdrive_write /* -> write entry point */
|
||||
};
|
||||
|
||||
/* ----------
|
||||
publish_devices - return a null-terminated array of devices
|
||||
supported by this driver.
|
||||
----- */
|
||||
|
||||
const char**
|
||||
publish_devices()
|
||||
{
|
||||
TRACE("virtualdrive: publish_devices\n");
|
||||
return virtualdrive_name;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
find_device - return ptr to device hooks structure for a
|
||||
given device name
|
||||
----- */
|
||||
|
||||
device_hooks*
|
||||
find_device(const char* name)
|
||||
{
|
||||
TRACE("virtualdrive: find_device(%s)\n", name);
|
||||
return &virtualdrive_hooks;
|
||||
}
|
||||
|
||||
// dev_index_for_path
|
||||
static
|
||||
int
|
||||
dev_index_for_path(const char *path)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < kDeviceCount; i++) {
|
||||
if (!strcmp(path, gDeviceInfos[i].device_path))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
36
src/tests/kits/storage/virtualdrive/virtualdrive.h
Normal file
36
src/tests/kits/storage/virtualdrive/virtualdrive.h
Normal file
@ -0,0 +1,36 @@
|
||||
// virtualdrive_ioctl.h
|
||||
|
||||
#ifndef VIRTUAL_DRIVE_H
|
||||
#define VIRTUAL_DRIVE_H
|
||||
|
||||
#include <Drivers.h>
|
||||
|
||||
// virtualdrive device directory and control device, "/dev" relative
|
||||
#define VIRTUAL_DRIVE_DIRECTORY_REL "misc/virtualdrive"
|
||||
#define VIRTUAL_DRIVE_CONTROL_DEVICE_REL VIRTUAL_DRIVE_DIRECTORY_REL \
|
||||
"/control"
|
||||
// virtualdrive device directory and control device, absolute
|
||||
#define VIRTUAL_DRIVE_DIRECTORY "/dev/" \
|
||||
VIRTUAL_DRIVE_DIRECTORY_REL
|
||||
#define VIRTUAL_DRIVE_CONTROL_DEVICE "/dev/" \
|
||||
VIRTUAL_DRIVE_CONTROL_DEVICE_REL
|
||||
|
||||
#define VIRTUAL_DRIVE_IOCTL_BASE (B_DEVICE_OP_CODES_END + 10001)
|
||||
|
||||
enum {
|
||||
VIRTUAL_DRIVE_REGISTER_FILE = VIRTUAL_DRIVE_IOCTL_BASE,
|
||||
// on control device: virtual_drive_info*, fills in device_name
|
||||
VIRTUAL_DRIVE_UNREGISTER_FILE,
|
||||
// on data device: none
|
||||
VIRTUAL_DRIVE_GET_INFO,
|
||||
// on data device: virtual_drive_info*
|
||||
};
|
||||
|
||||
typedef struct virtual_drive_info {
|
||||
char file_name[B_PATH_NAME_LENGTH];
|
||||
char device_name[B_PATH_NAME_LENGTH];
|
||||
device_geometry geometry;
|
||||
bool use_geometry;
|
||||
} virtual_drive_info;
|
||||
|
||||
#endif // VIRTUAL_DRIVE_H
|
Loading…
Reference in New Issue
Block a user