* 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:
Ingo Weinhold 2003-06-24 23:45:03 +00:00
parent cc74989eb6
commit 7a424c3923
5 changed files with 979 additions and 456 deletions

View File

@ -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 ;

View 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);
}

View File

@ -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;
}

View 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;
}

View 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