diff --git a/src/tests/kits/storage/virtualdrive/Jamfile b/src/tests/kits/storage/virtualdrive/Jamfile index 301b109a8a..0d212d240a 100644 --- a/src/tests/kits/storage/virtualdrive/Jamfile +++ b/src/tests/kits/storage/virtualdrive/Jamfile @@ -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 = virtualdrive ; -# local devtarget = 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 + : virtualdrive + : installed-symlink ; diff --git a/src/tests/kits/storage/virtualdrive/mkvirtualdrive.cpp b/src/tests/kits/storage/virtualdrive/mkvirtualdrive.cpp new file mode 100644 index 0000000000..3482bb48e0 --- /dev/null +++ b/src/tests/kits/storage/virtualdrive/mkvirtualdrive.cpp @@ -0,0 +1,234 @@ +// mkvirtualdrive.cpp + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "virtualdrive.h" + +extern "C" const char * const*__libc_argv; + +const char *kUsage = +"Usage: %s [ --install ] [ --size ] \n" +" %s --uninstall \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 " + "`[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); +} + diff --git a/src/tests/kits/storage/virtualdrive/virtualdrive.c b/src/tests/kits/storage/virtualdrive/virtualdrive.c deleted file mode 100644 index bdb4e1e75c..0000000000 --- a/src/tests/kits/storage/virtualdrive/virtualdrive.c +++ /dev/null @@ -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 -// Ingo Weinhold -// ---------------------------------------------------------------------- - -#include -#include -#include -#include -#include "lock.h" - -/* -[2:07] 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] that's the disable-cache ioctl -[2:08] 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] thanks, I will try calling it -[2:08] and dont bother using dosfs as a host fs, it wont work -[2:09] 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] that ioctl is used on the swap file as well -[2:10] I'm currently allocating memory in the driver's write() function hook -[2:10] 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; -} - diff --git a/src/tests/kits/storage/virtualdrive/virtualdrive.cpp b/src/tests/kits/storage/virtualdrive/virtualdrive.cpp new file mode 100644 index 0000000000..3f12bb7c44 --- /dev/null +++ b/src/tests/kits/storage/virtualdrive/virtualdrive.cpp @@ -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 +// Ingo Weinhold +// ---------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "lock.h" +#include "virtualdrive.h" + +/* +[2:07] 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] that's the disable-cache ioctl +[2:08] 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] thanks, I will try calling it +[2:08] and dont bother using dosfs as a host fs, it wont work +[2:09] 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] that ioctl is used on the swap file as well +[2:10] I'm currently allocating memory in the driver's write() function hook +[2:10] 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; +} + diff --git a/src/tests/kits/storage/virtualdrive/virtualdrive.h b/src/tests/kits/storage/virtualdrive/virtualdrive.h new file mode 100644 index 0000000000..346d3d198c --- /dev/null +++ b/src/tests/kits/storage/virtualdrive/virtualdrive.h @@ -0,0 +1,36 @@ +// virtualdrive_ioctl.h + +#ifndef VIRTUAL_DRIVE_H +#define VIRTUAL_DRIVE_H + +#include + +// 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