17a424c39SIngo Weinhold // ---------------------------------------------------------------------- 27a424c39SIngo Weinhold // This software is part of the OpenBeOS distribution and is covered 37a424c39SIngo Weinhold // by the OpenBeOS license. 47a424c39SIngo Weinhold // 57a424c39SIngo Weinhold // File Name: virtualdrive.c 67a424c39SIngo Weinhold // 77a424c39SIngo Weinhold // Description: Driver that emulates virtual drives. 87a424c39SIngo Weinhold // 97a424c39SIngo Weinhold // Author: Marcus Overhagen <Marcus@Overhagen.de> 107a424c39SIngo Weinhold // Ingo Weinhold <bonefish@users.sf.net> 11*bc4f7808SAxel Dörfler // Axel Doerfler <axeld@pinc-software.de> 127a424c39SIngo Weinhold // ---------------------------------------------------------------------- 137a424c39SIngo Weinhold 147a424c39SIngo Weinhold #include <fcntl.h> 157a424c39SIngo Weinhold #include <errno.h> 167a424c39SIngo Weinhold #include <malloc.h> 177a424c39SIngo Weinhold #include <stdio.h> 187a424c39SIngo Weinhold #include <string.h> 197a424c39SIngo Weinhold #include <unistd.h> 207a424c39SIngo Weinhold 217a424c39SIngo Weinhold #include <KernelExport.h> 227a424c39SIngo Weinhold #include <Drivers.h> 237a424c39SIngo Weinhold #include <Errors.h> 247a424c39SIngo Weinhold 257a424c39SIngo Weinhold #include "lock.h" 267a424c39SIngo Weinhold #include "virtualdrive.h" 27*bc4f7808SAxel Dörfler #include "virtualdrive_icon.h" 287a424c39SIngo Weinhold 297a424c39SIngo Weinhold /* 307a424c39SIngo Weinhold [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 317a424c39SIngo Weinhold [2:07] <geist> that's the disable-cache ioctl 327a424c39SIngo Weinhold [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 337a424c39SIngo Weinhold [2:08] <marcus_o> thanks, I will try calling it 347a424c39SIngo Weinhold [2:08] <geist> and dont bother using dosfs as a host fs, it wont work 357a424c39SIngo Weinhold [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 367a424c39SIngo Weinhold [2:09] <geist> that ioctl is used on the swap file as well 377a424c39SIngo Weinhold [2:10] <marcus_o> I'm currently allocating memory in the driver's write() function hook 387a424c39SIngo Weinhold [2:10] <geist> cant do that 397a424c39SIngo Weinhold */ 407a424c39SIngo Weinhold 417a424c39SIngo Weinhold #define TRACE dprintf 427a424c39SIngo Weinhold //#define TRACE (void) 437a424c39SIngo Weinhold #define MB (1024LL * 1024LL) 447a424c39SIngo Weinhold 457a424c39SIngo Weinhold static int dev_index_for_path(const char *path); 467a424c39SIngo Weinhold 477a424c39SIngo Weinhold /* ----- 487a424c39SIngo Weinhold null-terminated array of device names supported by this driver 497a424c39SIngo Weinhold ----- */ 507a424c39SIngo Weinhold 51*bc4f7808SAxel Dörfler static const char *sVirtualDriveName[] = { 527a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/0", 537a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/1", 547a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/2", 557a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/3", 567a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/4", 577a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/5", 587a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/6", 597a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/7", 607a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/8", 617a424c39SIngo Weinhold VIRTUAL_DRIVE_DIRECTORY_REL "/9", 627a424c39SIngo Weinhold VIRTUAL_DRIVE_CONTROL_DEVICE_REL, 637a424c39SIngo Weinhold NULL 647a424c39SIngo Weinhold }; 657a424c39SIngo Weinhold 667a424c39SIngo Weinhold int32 api_version = B_CUR_DRIVER_API_VERSION; 67*bc4f7808SAxel Dörfler extern device_hooks sVirtualDriveHooks; 687a424c39SIngo Weinhold 697a424c39SIngo Weinhold lock driverlock; 707a424c39SIngo Weinhold 717a424c39SIngo Weinhold typedef struct device_info { 72*bc4f7808SAxel Dörfler int32 open_count; 737a424c39SIngo Weinhold int fd; 747a424c39SIngo Weinhold off_t size; 757a424c39SIngo Weinhold bool unused; 767a424c39SIngo Weinhold bool registered; 777a424c39SIngo Weinhold char file[B_PATH_NAME_LENGTH]; 787a424c39SIngo Weinhold const char *device_path; 797a424c39SIngo Weinhold device_geometry geometry; 807a424c39SIngo Weinhold } device_info; 817a424c39SIngo Weinhold 827a424c39SIngo Weinhold #define kDeviceCount 11 837a424c39SIngo Weinhold #define kDataDeviceCount (kDeviceCount - 1) 847a424c39SIngo Weinhold #define kControlDevice (kDeviceCount - 1) 857a424c39SIngo Weinhold struct device_info gDeviceInfos[kDeviceCount]; 867a424c39SIngo Weinhold 877a424c39SIngo Weinhold static int32 gRegistrationCount = 0; 887a424c39SIngo Weinhold static int gControlDeviceFD = -1; 897a424c39SIngo Weinhold 907a424c39SIngo Weinhold static thread_id gLockOwner = -1; 917a424c39SIngo Weinhold static int32 gLockOwnerNesting = 0; 927a424c39SIngo Weinhold 93*bc4f7808SAxel Dörfler 947a424c39SIngo Weinhold // lock_driver 957a424c39SIngo Weinhold void 967a424c39SIngo Weinhold lock_driver() 977a424c39SIngo Weinhold { 987a424c39SIngo Weinhold thread_id thread = find_thread(NULL); 997a424c39SIngo Weinhold if (gLockOwner != thread) { 1007a424c39SIngo Weinhold LOCK(driverlock); 1017a424c39SIngo Weinhold gLockOwner = thread; 1027a424c39SIngo Weinhold } 1037a424c39SIngo Weinhold gLockOwnerNesting++; 1047a424c39SIngo Weinhold } 1057a424c39SIngo Weinhold 106*bc4f7808SAxel Dörfler 1077a424c39SIngo Weinhold // unlock_driver 1087a424c39SIngo Weinhold void 1097a424c39SIngo Weinhold unlock_driver() 1107a424c39SIngo Weinhold { 1117a424c39SIngo Weinhold thread_id thread = find_thread(NULL); 1127a424c39SIngo Weinhold if (gLockOwner == thread && --gLockOwnerNesting == 0) { 1137a424c39SIngo Weinhold gLockOwner = -1; 1147a424c39SIngo Weinhold UNLOCK(driverlock); 1157a424c39SIngo Weinhold } 1167a424c39SIngo Weinhold } 1177a424c39SIngo Weinhold 1187a424c39SIngo Weinhold 1197a424c39SIngo Weinhold // is_valid_device_index 1207a424c39SIngo Weinhold static inline 1217a424c39SIngo Weinhold bool 1227a424c39SIngo Weinhold is_valid_device_index(int32 index) 1237a424c39SIngo Weinhold { 1247a424c39SIngo Weinhold return (index >= 0 && index < kDeviceCount); 1257a424c39SIngo Weinhold } 1267a424c39SIngo Weinhold 127*bc4f7808SAxel Dörfler 1287a424c39SIngo Weinhold // is_valid_data_device_index 1297a424c39SIngo Weinhold static inline 1307a424c39SIngo Weinhold bool 1317a424c39SIngo Weinhold is_valid_data_device_index(int32 index) 1327a424c39SIngo Weinhold { 1337a424c39SIngo Weinhold return (is_valid_device_index(index) && index != kControlDevice); 1347a424c39SIngo Weinhold } 1357a424c39SIngo Weinhold 136*bc4f7808SAxel Dörfler 137*bc4f7808SAxel Dörfler // dev_index_for_path 138*bc4f7808SAxel Dörfler static 139*bc4f7808SAxel Dörfler int 140*bc4f7808SAxel Dörfler dev_index_for_path(const char *path) 141*bc4f7808SAxel Dörfler { 142*bc4f7808SAxel Dörfler int i; 143*bc4f7808SAxel Dörfler for (i = 0; i < kDeviceCount; i++) { 144*bc4f7808SAxel Dörfler if (!strcmp(path, gDeviceInfos[i].device_path)) 145*bc4f7808SAxel Dörfler return i; 146*bc4f7808SAxel Dörfler } 147*bc4f7808SAxel Dörfler return -1; 148*bc4f7808SAxel Dörfler } 149*bc4f7808SAxel Dörfler 150*bc4f7808SAxel Dörfler 1517a424c39SIngo Weinhold // clear_device_info 1527a424c39SIngo Weinhold static 1537a424c39SIngo Weinhold void 1547a424c39SIngo Weinhold clear_device_info(int32 index) 1557a424c39SIngo Weinhold { 1567a424c39SIngo Weinhold TRACE("virtualdrive: clear_device_info(%ld)\n", index); 157*bc4f7808SAxel Dörfler 1587a424c39SIngo Weinhold device_info &info = gDeviceInfos[index]; 159*bc4f7808SAxel Dörfler info.open_count = 0; 1607a424c39SIngo Weinhold info.fd = -1; 1617a424c39SIngo Weinhold info.size = 0; 1627a424c39SIngo Weinhold info.unused = (index != kDeviceCount - 1); 1637a424c39SIngo Weinhold info.registered = !info.unused; 1647a424c39SIngo Weinhold info.file[0] = '\0'; 165*bc4f7808SAxel Dörfler info.device_path = sVirtualDriveName[index]; 1667a424c39SIngo Weinhold info.geometry.read_only = true; 1677a424c39SIngo Weinhold } 1687a424c39SIngo Weinhold 169*bc4f7808SAxel Dörfler 1707a424c39SIngo Weinhold // init_device_info 1717a424c39SIngo Weinhold static 1727a424c39SIngo Weinhold status_t 1737a424c39SIngo Weinhold init_device_info(int32 index, virtual_drive_info *initInfo) 1747a424c39SIngo Weinhold { 1757a424c39SIngo Weinhold if (!is_valid_data_device_index(index) || !initInfo) 1767a424c39SIngo Weinhold return B_BAD_VALUE; 177*bc4f7808SAxel Dörfler 1787a424c39SIngo Weinhold device_info &info = gDeviceInfos[index]; 1797a424c39SIngo Weinhold if (!info.unused) 1807a424c39SIngo Weinhold return B_BAD_VALUE; 181*bc4f7808SAxel Dörfler 1827a424c39SIngo Weinhold bool readOnly = (initInfo->use_geometry && initInfo->geometry.read_only); 183*bc4f7808SAxel Dörfler 1847a424c39SIngo Weinhold // open the file 1857a424c39SIngo Weinhold int fd = open(initInfo->file_name, (readOnly ? O_RDONLY : O_RDWR)); 1867a424c39SIngo Weinhold if (fd < 0) 1877a424c39SIngo Weinhold return errno; 188*bc4f7808SAxel Dörfler 1897a424c39SIngo Weinhold status_t error = B_OK; 190*bc4f7808SAxel Dörfler 1917a424c39SIngo Weinhold // get the file size 1927a424c39SIngo Weinhold off_t fileSize = 0; 1937a424c39SIngo Weinhold struct stat st; 1947a424c39SIngo Weinhold if (fstat(fd, &st) == 0) 1957a424c39SIngo Weinhold fileSize = st.st_size; 1967a424c39SIngo Weinhold else 1977a424c39SIngo Weinhold error = errno; 198*bc4f7808SAxel Dörfler 1997a424c39SIngo Weinhold // If we shall use the supplied geometry, we enlarge the file, if 2007a424c39SIngo Weinhold // necessary. Otherwise we fill in the geometry according to the size of the file. 2017a424c39SIngo Weinhold off_t size = 0; 2027a424c39SIngo Weinhold if (error == B_OK) { 2037a424c39SIngo Weinhold if (initInfo->use_geometry) { 2047a424c39SIngo Weinhold // use the supplied geometry 2057a424c39SIngo Weinhold info.geometry = initInfo->geometry; 2067a424c39SIngo Weinhold size = (off_t)info.geometry.bytes_per_sector 2077a424c39SIngo Weinhold * info.geometry.sectors_per_track 2087a424c39SIngo Weinhold * info.geometry.cylinder_count 2097a424c39SIngo Weinhold * info.geometry.head_count; 2107a424c39SIngo Weinhold if (size > fileSize) { 2117a424c39SIngo Weinhold if (!readOnly) { 2127a424c39SIngo Weinhold if (ftruncate(fd, size) != 0) 2137a424c39SIngo Weinhold error = errno; 2147a424c39SIngo Weinhold } else 2157a424c39SIngo Weinhold error = B_NOT_ALLOWED; 2167a424c39SIngo Weinhold } 2177a424c39SIngo Weinhold } else { 2187a424c39SIngo Weinhold // fill in the geometry 2197a424c39SIngo Weinhold // default to 512 bytes block size 2207a424c39SIngo Weinhold uint32 blockSize = 512; 2217a424c39SIngo Weinhold // Optimally we have only 1 block per sector and only one head. 2227a424c39SIngo Weinhold // Since we have only a uint32 for the cylinder count, this won't work 2237a424c39SIngo Weinhold // for files > 2TB. So, we set the head count to the minimally possible 2247a424c39SIngo Weinhold // value. 2257a424c39SIngo Weinhold off_t blocks = fileSize / blockSize; 2267a424c39SIngo Weinhold uint32 heads = (blocks + ULONG_MAX - 1) / ULONG_MAX; 2277a424c39SIngo Weinhold if (heads == 0) 2287a424c39SIngo Weinhold heads = 1; 2297a424c39SIngo Weinhold info.geometry.bytes_per_sector = blockSize; 2307a424c39SIngo Weinhold info.geometry.sectors_per_track = 1; 2317a424c39SIngo Weinhold info.geometry.cylinder_count = blocks / heads; 2327a424c39SIngo Weinhold info.geometry.head_count = heads; 2337a424c39SIngo Weinhold info.geometry.device_type = B_DISK; // TODO: Add a new constant. 2347a424c39SIngo Weinhold info.geometry.removable = false; 2357a424c39SIngo Weinhold info.geometry.read_only = false; 2367a424c39SIngo Weinhold info.geometry.write_once = false; 2377a424c39SIngo Weinhold size = (off_t)info.geometry.bytes_per_sector 2387a424c39SIngo Weinhold * info.geometry.sectors_per_track 2397a424c39SIngo Weinhold * info.geometry.cylinder_count 2407a424c39SIngo Weinhold * info.geometry.head_count; 2417a424c39SIngo Weinhold } 2427a424c39SIngo Weinhold } 243*bc4f7808SAxel Dörfler 244*bc4f7808SAxel Dörfler if (error == B_OK) { 245*bc4f7808SAxel Dörfler // Disable caching for underlying file! (else this driver will deadlock) 246*bc4f7808SAxel Dörfler // We probably cannot resize the file once the cache has been disabled! 247*bc4f7808SAxel Dörfler 248*bc4f7808SAxel Dörfler // This is a special reserved ioctl() opcode not defined anywhere in 249*bc4f7808SAxel Dörfler // the Be headers. 250*bc4f7808SAxel Dörfler if (ioctl(fd, 10000) != 0) { 251*bc4f7808SAxel Dörfler TRACE("virtualdrive: disable caching ioctl failed\n"); 252*bc4f7808SAxel Dörfler return errno; 253*bc4f7808SAxel Dörfler } 254*bc4f7808SAxel Dörfler } 255*bc4f7808SAxel Dörfler 2567a424c39SIngo Weinhold // fill in the rest of the device_info structure 2577a424c39SIngo Weinhold if (error == B_OK) { 258*bc4f7808SAxel Dörfler // open_count doesn't have to be changed here (virtualdrive_open() will do that for us) 2597a424c39SIngo Weinhold info.fd = fd; 2607a424c39SIngo Weinhold info.size = size; 2617a424c39SIngo Weinhold info.unused = false; 2627a424c39SIngo Weinhold info.registered = true; 2637a424c39SIngo Weinhold strcpy(info.file, initInfo->file_name); 264*bc4f7808SAxel Dörfler info.device_path = sVirtualDriveName[index]; 2657a424c39SIngo Weinhold } else { 2667a424c39SIngo Weinhold // cleanup on error 2677a424c39SIngo Weinhold close(fd); 268*bc4f7808SAxel Dörfler if (info.open_count == 0) 2697a424c39SIngo Weinhold clear_device_info(index); 2707a424c39SIngo Weinhold } 2717a424c39SIngo Weinhold return error; 2727a424c39SIngo Weinhold } 2737a424c39SIngo Weinhold 274*bc4f7808SAxel Dörfler 2757a424c39SIngo Weinhold // uninit_device_info 2767a424c39SIngo Weinhold static 2777a424c39SIngo Weinhold status_t 2787a424c39SIngo Weinhold uninit_device_info(int32 index) 2797a424c39SIngo Weinhold { 2807a424c39SIngo Weinhold if (!is_valid_data_device_index(index)) 2817a424c39SIngo Weinhold return B_BAD_VALUE; 282*bc4f7808SAxel Dörfler 2837a424c39SIngo Weinhold device_info &info = gDeviceInfos[index]; 2847a424c39SIngo Weinhold if (info.unused) 2857a424c39SIngo Weinhold return B_BAD_VALUE; 286*bc4f7808SAxel Dörfler 2877a424c39SIngo Weinhold close(info.fd); 2887a424c39SIngo Weinhold clear_device_info(index); 2897a424c39SIngo Weinhold return B_OK; 2907a424c39SIngo Weinhold } 2917a424c39SIngo Weinhold 292*bc4f7808SAxel Dörfler 293*bc4f7808SAxel Dörfler // #pragma mark - 294*bc4f7808SAxel Dörfler // public driver API 295*bc4f7808SAxel Dörfler 296*bc4f7808SAxel Dörfler 2977a424c39SIngo Weinhold status_t 2987a424c39SIngo Weinhold init_hardware(void) 2997a424c39SIngo Weinhold { 3007a424c39SIngo Weinhold TRACE("virtualdrive: init_hardware\n"); 3017a424c39SIngo Weinhold return B_OK; 3027a424c39SIngo Weinhold } 3037a424c39SIngo Weinhold 3047a424c39SIngo Weinhold 3057a424c39SIngo Weinhold status_t 3067a424c39SIngo Weinhold init_driver(void) 3077a424c39SIngo Weinhold { 3087a424c39SIngo Weinhold TRACE("virtualdrive: init\n"); 3097a424c39SIngo Weinhold 3107a424c39SIngo Weinhold new_lock(&driverlock, "virtualdrive lock"); 3117a424c39SIngo Weinhold 3127a424c39SIngo Weinhold // init the device infos 3137a424c39SIngo Weinhold for (int32 i = 0; i < kDeviceCount; i++) 3147a424c39SIngo Weinhold clear_device_info(i); 3157a424c39SIngo Weinhold 3167a424c39SIngo Weinhold return B_OK; 3177a424c39SIngo Weinhold } 3187a424c39SIngo Weinhold 3197a424c39SIngo Weinhold 3207a424c39SIngo Weinhold void 3217a424c39SIngo Weinhold uninit_driver(void) 3227a424c39SIngo Weinhold { 3237a424c39SIngo Weinhold TRACE("virtualdrive: uninit\n"); 3247a424c39SIngo Weinhold free_lock(&driverlock); 3257a424c39SIngo Weinhold } 3267a424c39SIngo Weinhold 3277a424c39SIngo Weinhold 328*bc4f7808SAxel Dörfler const char ** 329*bc4f7808SAxel Dörfler publish_devices(void) 330*bc4f7808SAxel Dörfler { 331*bc4f7808SAxel Dörfler TRACE("virtualdrive: publish_devices\n"); 332*bc4f7808SAxel Dörfler return sVirtualDriveName; 333*bc4f7808SAxel Dörfler } 334*bc4f7808SAxel Dörfler 335*bc4f7808SAxel Dörfler 336*bc4f7808SAxel Dörfler device_hooks * 337*bc4f7808SAxel Dörfler find_device(const char* name) 338*bc4f7808SAxel Dörfler { 339*bc4f7808SAxel Dörfler TRACE("virtualdrive: find_device(%s)\n", name); 340*bc4f7808SAxel Dörfler return &sVirtualDriveHooks; 341*bc4f7808SAxel Dörfler } 342*bc4f7808SAxel Dörfler 343*bc4f7808SAxel Dörfler 344*bc4f7808SAxel Dörfler // #pragma mark - 345*bc4f7808SAxel Dörfler // the device hooks 346*bc4f7808SAxel Dörfler 3477a424c39SIngo Weinhold 3487a424c39SIngo Weinhold static status_t 3497a424c39SIngo Weinhold virtualdrive_open(const char *name, uint32 flags, void **cookie) 3507a424c39SIngo Weinhold { 3517a424c39SIngo Weinhold TRACE("virtualdrive: open %s\n",name); 3527a424c39SIngo Weinhold 3537a424c39SIngo Weinhold *cookie = (void *)-1; 3547a424c39SIngo Weinhold 3557a424c39SIngo Weinhold lock_driver(); 3567a424c39SIngo Weinhold 3577a424c39SIngo Weinhold int32 devIndex = dev_index_for_path(name); 3587a424c39SIngo Weinhold 3597a424c39SIngo Weinhold TRACE("virtualdrive: devIndex %ld!\n", devIndex); 3607a424c39SIngo Weinhold 3617a424c39SIngo Weinhold if (!is_valid_device_index(devIndex)) { 3627a424c39SIngo Weinhold TRACE("virtualdrive: wrong index!\n"); 3637a424c39SIngo Weinhold unlock_driver(); 3647a424c39SIngo Weinhold return B_ERROR; 3657a424c39SIngo Weinhold } 3667a424c39SIngo Weinhold 3677a424c39SIngo Weinhold if (gDeviceInfos[devIndex].unused) { 3687a424c39SIngo Weinhold TRACE("virtualdrive: device is unused!\n"); 3697a424c39SIngo Weinhold unlock_driver(); 3707a424c39SIngo Weinhold return B_ERROR; 3717a424c39SIngo Weinhold } 3727a424c39SIngo Weinhold 3737a424c39SIngo Weinhold if (!gDeviceInfos[devIndex].registered) { 3747a424c39SIngo Weinhold TRACE("virtualdrive: device has been unregistered!\n"); 3757a424c39SIngo Weinhold unlock_driver(); 3767a424c39SIngo Weinhold return B_ERROR; 3777a424c39SIngo Weinhold } 3787a424c39SIngo Weinhold 3797a424c39SIngo Weinhold // store index in cookie 3807a424c39SIngo Weinhold *cookie = (void *)devIndex; 3817a424c39SIngo Weinhold 382*bc4f7808SAxel Dörfler gDeviceInfos[devIndex].open_count++; 3837a424c39SIngo Weinhold 3847a424c39SIngo Weinhold unlock_driver(); 3857a424c39SIngo Weinhold return B_OK; 3867a424c39SIngo Weinhold } 3877a424c39SIngo Weinhold 3887a424c39SIngo Weinhold 3897a424c39SIngo Weinhold static status_t 3907a424c39SIngo Weinhold virtualdrive_close(void *cookie) 3917a424c39SIngo Weinhold { 3927a424c39SIngo Weinhold TRACE("virtualdrive: close\n"); 3937a424c39SIngo Weinhold 3947a424c39SIngo Weinhold int32 devIndex = (int)cookie; 3957a424c39SIngo Weinhold 3967a424c39SIngo Weinhold TRACE("virtualdrive: devIndex = %ld\n", devIndex); 3977a424c39SIngo Weinhold if (!is_valid_data_device_index(devIndex)) 3987a424c39SIngo Weinhold return B_OK; 3997a424c39SIngo Weinhold 4007a424c39SIngo Weinhold lock_driver(); 4017a424c39SIngo Weinhold 402*bc4f7808SAxel Dörfler gDeviceInfos[devIndex].open_count--; 403*bc4f7808SAxel Dörfler if (gDeviceInfos[devIndex].open_count == 0 && !gDeviceInfos[devIndex].registered) { 4047a424c39SIngo Weinhold // The last FD is closed and the device has been unregistered. Free its info. 4057a424c39SIngo Weinhold uninit_device_info(devIndex); 4067a424c39SIngo Weinhold } 4077a424c39SIngo Weinhold 4087a424c39SIngo Weinhold unlock_driver(); 4097a424c39SIngo Weinhold 4107a424c39SIngo Weinhold return B_OK; 4117a424c39SIngo Weinhold } 4127a424c39SIngo Weinhold 4137a424c39SIngo Weinhold 4147a424c39SIngo Weinhold static status_t 4157a424c39SIngo Weinhold virtualdrive_read(void *cookie, off_t position, void *buffer, size_t *numBytes) 4167a424c39SIngo Weinhold { 4177a424c39SIngo Weinhold TRACE("virtualdrive: read pos = 0x%08Lx, bytes = 0x%08lx\n",position,*numBytes); 4187a424c39SIngo Weinhold // check parameters 4197a424c39SIngo Weinhold int devIndex = (int)cookie; 4207a424c39SIngo Weinhold if (devIndex == kControlDevice) { 4217a424c39SIngo Weinhold TRACE("virtualdrive: reading from control device not allowed\n"); 4227a424c39SIngo Weinhold return B_NOT_ALLOWED; 4237a424c39SIngo Weinhold } 4247a424c39SIngo Weinhold if (position < 0) 4257a424c39SIngo Weinhold return B_BAD_VALUE; 4267a424c39SIngo Weinhold lock_driver(); 4277a424c39SIngo Weinhold device_info &info = gDeviceInfos[devIndex]; 4287a424c39SIngo Weinhold // adjust position and numBytes according to the file size 4297a424c39SIngo Weinhold if (position > info.size) 4307a424c39SIngo Weinhold position = info.size; 4317a424c39SIngo Weinhold if (position + *numBytes > info.size) 4327a424c39SIngo Weinhold *numBytes = info.size - position; 4337a424c39SIngo Weinhold // read 4347a424c39SIngo Weinhold status_t error = B_OK; 4357a424c39SIngo Weinhold ssize_t bytesRead = read_pos(info.fd, position, buffer, *numBytes); 4367a424c39SIngo Weinhold if (bytesRead < 0) 4377a424c39SIngo Weinhold error = errno; 4387a424c39SIngo Weinhold else 4397a424c39SIngo Weinhold *numBytes = bytesRead; 4407a424c39SIngo Weinhold unlock_driver(); 4417a424c39SIngo Weinhold return error; 4427a424c39SIngo Weinhold } 4437a424c39SIngo Weinhold 4447a424c39SIngo Weinhold 4457a424c39SIngo Weinhold static status_t 4467a424c39SIngo Weinhold virtualdrive_write(void *cookie, off_t position, const void *buffer, size_t *numBytes) 4477a424c39SIngo Weinhold { 4487a424c39SIngo Weinhold TRACE("virtualdrive: write pos = 0x%08Lx, bytes = 0x%08lx\n",position,*numBytes); 4497a424c39SIngo Weinhold // check parameters 4507a424c39SIngo Weinhold int devIndex = (int)cookie; 4517a424c39SIngo Weinhold if (devIndex == kControlDevice) { 4527a424c39SIngo Weinhold TRACE("virtualdrive: writing to control device not allowed\n"); 4537a424c39SIngo Weinhold return B_NOT_ALLOWED; 4547a424c39SIngo Weinhold } 4557a424c39SIngo Weinhold if (position < 0) 4567a424c39SIngo Weinhold return B_BAD_VALUE; 4577a424c39SIngo Weinhold lock_driver(); 4587a424c39SIngo Weinhold device_info &info = gDeviceInfos[devIndex]; 4597a424c39SIngo Weinhold // adjust position and numBytes according to the file size 4607a424c39SIngo Weinhold if (position > info.size) 4617a424c39SIngo Weinhold position = info.size; 4627a424c39SIngo Weinhold if (position + *numBytes > info.size) 4637a424c39SIngo Weinhold *numBytes = info.size - position; 4647a424c39SIngo Weinhold // read 4657a424c39SIngo Weinhold status_t error = B_OK; 4667a424c39SIngo Weinhold ssize_t bytesRead = write_pos(info.fd, position, buffer, *numBytes); 4677a424c39SIngo Weinhold if (bytesRead < 0) 4687a424c39SIngo Weinhold error = errno; 4697a424c39SIngo Weinhold else 4707a424c39SIngo Weinhold *numBytes = bytesRead; 4717a424c39SIngo Weinhold unlock_driver(); 4727a424c39SIngo Weinhold return error; 4737a424c39SIngo Weinhold } 4747a424c39SIngo Weinhold 4757a424c39SIngo Weinhold 4767a424c39SIngo Weinhold static status_t 4777a424c39SIngo Weinhold virtualdrive_control(void *cookie, uint32 op, void *arg, size_t len) 4787a424c39SIngo Weinhold { 4797a424c39SIngo Weinhold TRACE("virtualdrive: ioctl\n"); 4807a424c39SIngo Weinhold 4817a424c39SIngo Weinhold int devIndex = (int)cookie; 4827a424c39SIngo Weinhold device_info &info = gDeviceInfos[devIndex]; 4837a424c39SIngo Weinhold 4847a424c39SIngo Weinhold if (devIndex == kControlDevice || info.unused) { 4857a424c39SIngo Weinhold // control device or unused data device 4867a424c39SIngo Weinhold switch (op) { 4877a424c39SIngo Weinhold case B_GET_DEVICE_SIZE: 4887a424c39SIngo Weinhold case B_SET_NONBLOCKING_IO: 4897a424c39SIngo Weinhold case B_SET_BLOCKING_IO: 4907a424c39SIngo Weinhold case B_GET_READ_STATUS: 4917a424c39SIngo Weinhold case B_GET_WRITE_STATUS: 4927a424c39SIngo Weinhold case B_GET_ICON: 4937a424c39SIngo Weinhold case B_GET_GEOMETRY: 4947a424c39SIngo Weinhold case B_GET_BIOS_GEOMETRY: 4957a424c39SIngo Weinhold case B_GET_MEDIA_STATUS: 4967a424c39SIngo Weinhold case B_SET_UNINTERRUPTABLE_IO: 4977a424c39SIngo Weinhold case B_SET_INTERRUPTABLE_IO: 4987a424c39SIngo Weinhold case B_FLUSH_DRIVE_CACHE: 4997a424c39SIngo Weinhold case B_GET_BIOS_DRIVE_ID: 5007a424c39SIngo Weinhold case B_GET_DRIVER_FOR_DEVICE: 5017a424c39SIngo Weinhold case B_SET_DEVICE_SIZE: 5027a424c39SIngo Weinhold case B_SET_PARTITION: 5037a424c39SIngo Weinhold case B_FORMAT_DEVICE: 5047a424c39SIngo Weinhold case B_EJECT_DEVICE: 5057a424c39SIngo Weinhold case B_LOAD_MEDIA: 5067a424c39SIngo Weinhold case B_GET_NEXT_OPEN_DEVICE: 5077a424c39SIngo Weinhold TRACE("virtualdrive: another ioctl: %lx (%lu)\n", op, op); 5087a424c39SIngo Weinhold return B_BAD_VALUE; 509*bc4f7808SAxel Dörfler 5107a424c39SIngo Weinhold case VIRTUAL_DRIVE_REGISTER_FILE: 5117a424c39SIngo Weinhold { 5127a424c39SIngo Weinhold TRACE("virtualdrive: VIRTUAL_DRIVE_REGISTER_FILE\n"); 513*bc4f7808SAxel Dörfler 5147a424c39SIngo Weinhold virtual_drive_info *driveInfo = (virtual_drive_info *)arg; 515*bc4f7808SAxel Dörfler if (devIndex != kControlDevice || driveInfo == NULL 516*bc4f7808SAxel Dörfler || driveInfo->magic != VIRTUAL_DRIVE_MAGIC 517*bc4f7808SAxel Dörfler || driveInfo->drive_info_size != sizeof(virtual_drive_info)) 518*bc4f7808SAxel Dörfler return B_BAD_VALUE; 519*bc4f7808SAxel Dörfler 5207a424c39SIngo Weinhold status_t error = B_ERROR; 521*bc4f7808SAxel Dörfler int32 i; 522*bc4f7808SAxel Dörfler 5237a424c39SIngo Weinhold lock_driver(); 524*bc4f7808SAxel Dörfler 525*bc4f7808SAxel Dörfler // first, look if we already have opened that file and see 526*bc4f7808SAxel Dörfler // if it's available to us which happens when it has been 527*bc4f7808SAxel Dörfler // halted but is still in use by other components 528*bc4f7808SAxel Dörfler for (i = 0; i < kDataDeviceCount; i++) { 529*bc4f7808SAxel Dörfler if (!gDeviceInfos[i].unused 530*bc4f7808SAxel Dörfler && gDeviceInfos[i].fd == -1 531*bc4f7808SAxel Dörfler && !gDeviceInfos[i].registered 532*bc4f7808SAxel Dörfler && !strcmp(gDeviceInfos[i].file, driveInfo->file_name)) { 533*bc4f7808SAxel Dörfler // mark device as unused, so that init_device_info() will succeed 534*bc4f7808SAxel Dörfler gDeviceInfos[i].unused = true; 535*bc4f7808SAxel Dörfler error = B_OK; 536*bc4f7808SAxel Dörfler break; 537*bc4f7808SAxel Dörfler } 538*bc4f7808SAxel Dörfler } 539*bc4f7808SAxel Dörfler 540*bc4f7808SAxel Dörfler if (error != B_OK) { 541*bc4f7808SAxel Dörfler // find an unused data device 542*bc4f7808SAxel Dörfler for (i = 0; i < kDataDeviceCount; i++) { 5437a424c39SIngo Weinhold if (gDeviceInfos[i].unused) { 544*bc4f7808SAxel Dörfler error = B_OK; 545*bc4f7808SAxel Dörfler break; 546*bc4f7808SAxel Dörfler } 547*bc4f7808SAxel Dörfler } 548*bc4f7808SAxel Dörfler } 549*bc4f7808SAxel Dörfler 550*bc4f7808SAxel Dörfler if (error == B_OK) { 551*bc4f7808SAxel Dörfler // we found a device slot, let's initialize it 5527a424c39SIngo Weinhold error = init_device_info(i, driveInfo); 5537a424c39SIngo Weinhold if (error == B_OK) { 5547a424c39SIngo Weinhold // return the device path 5557a424c39SIngo Weinhold strcpy(driveInfo->device_name, "/dev/"); 556*bc4f7808SAxel Dörfler strcat(driveInfo->device_name, gDeviceInfos[i].device_path); 557*bc4f7808SAxel Dörfler 5587a424c39SIngo Weinhold // on the first registration we need to open the 5597a424c39SIngo Weinhold // control device to stay loaded 5607a424c39SIngo Weinhold if (gRegistrationCount++ == 0) { 5617a424c39SIngo Weinhold char path[B_PATH_NAME_LENGTH]; 5627a424c39SIngo Weinhold strcpy(path, "/dev/"); 5637a424c39SIngo Weinhold strcat(path, info.device_path); 5647a424c39SIngo Weinhold gControlDeviceFD = open(path, O_RDONLY); 5657a424c39SIngo Weinhold } 5667a424c39SIngo Weinhold } 5677a424c39SIngo Weinhold } 568*bc4f7808SAxel Dörfler 5697a424c39SIngo Weinhold unlock_driver(); 5707a424c39SIngo Weinhold return error; 5717a424c39SIngo Weinhold } 5727a424c39SIngo Weinhold case VIRTUAL_DRIVE_UNREGISTER_FILE: 5737a424c39SIngo Weinhold case VIRTUAL_DRIVE_GET_INFO: 5747a424c39SIngo Weinhold TRACE("virtualdrive: VIRTUAL_DRIVE_UNREGISTER_FILE/" 5757a424c39SIngo Weinhold "VIRTUAL_DRIVE_GET_INFO\n"); 576*bc4f7808SAxel Dörfler // these are called on used data files only! 5777a424c39SIngo Weinhold return B_BAD_VALUE; 578*bc4f7808SAxel Dörfler 5797a424c39SIngo Weinhold default: 5807a424c39SIngo Weinhold TRACE("virtualdrive: unknown ioctl: %lx (%lu)\n", op, op); 5817a424c39SIngo Weinhold return B_BAD_VALUE; 5827a424c39SIngo Weinhold } 5837a424c39SIngo Weinhold } else { 5847a424c39SIngo Weinhold // used data device 5857a424c39SIngo Weinhold switch (op) { 5867a424c39SIngo Weinhold case B_GET_DEVICE_SIZE: 5877a424c39SIngo Weinhold TRACE("virtualdrive: B_GET_DEVICE_SIZE\n"); 5887a424c39SIngo Weinhold *(size_t*)arg = info.size; 5897a424c39SIngo Weinhold return B_OK; 5907a424c39SIngo Weinhold 5917a424c39SIngo Weinhold case B_SET_NONBLOCKING_IO: 5927a424c39SIngo Weinhold TRACE("virtualdrive: B_SET_NONBLOCKING_IO\n"); 5937a424c39SIngo Weinhold return B_OK; 5947a424c39SIngo Weinhold 5957a424c39SIngo Weinhold case B_SET_BLOCKING_IO: 5967a424c39SIngo Weinhold TRACE("virtualdrive: B_SET_BLOCKING_IO\n"); 5977a424c39SIngo Weinhold return B_OK; 5987a424c39SIngo Weinhold 5997a424c39SIngo Weinhold case B_GET_READ_STATUS: 6007a424c39SIngo Weinhold TRACE("virtualdrive: B_GET_READ_STATUS\n"); 6017a424c39SIngo Weinhold *(bool*)arg = true; 6027a424c39SIngo Weinhold return B_OK; 6037a424c39SIngo Weinhold 6047a424c39SIngo Weinhold case B_GET_WRITE_STATUS: 6057a424c39SIngo Weinhold TRACE("virtualdrive: B_GET_WRITE_STATUS\n"); 6067a424c39SIngo Weinhold *(bool*)arg = true; 6077a424c39SIngo Weinhold return B_OK; 6087a424c39SIngo Weinhold 6097a424c39SIngo Weinhold case B_GET_ICON: 610*bc4f7808SAxel Dörfler { 6117a424c39SIngo Weinhold TRACE("virtualdrive: B_GET_ICON\n"); 612*bc4f7808SAxel Dörfler device_icon *icon = (device_icon *)arg; 613*bc4f7808SAxel Dörfler 614*bc4f7808SAxel Dörfler if (icon->icon_size == kPrimaryImageWidth) { 615*bc4f7808SAxel Dörfler memcpy(icon->icon_data, kPrimaryImageBits, kPrimaryImageWidth * kPrimaryImageHeight); 616*bc4f7808SAxel Dörfler } else if (icon->icon_size == kSecondaryImageWidth) { 617*bc4f7808SAxel Dörfler memcpy(icon->icon_data, kSecondaryImageBits, kSecondaryImageWidth * kSecondaryImageHeight); 618*bc4f7808SAxel Dörfler } else 619*bc4f7808SAxel Dörfler return B_ERROR; 620*bc4f7808SAxel Dörfler 6217a424c39SIngo Weinhold return B_OK; 622*bc4f7808SAxel Dörfler } 6237a424c39SIngo Weinhold 6247a424c39SIngo Weinhold case B_GET_GEOMETRY: 6257a424c39SIngo Weinhold TRACE("virtualdrive: B_GET_GEOMETRY\n"); 6267a424c39SIngo Weinhold *(device_geometry *)arg = info.geometry; 6277a424c39SIngo Weinhold return B_OK; 6287a424c39SIngo Weinhold 6297a424c39SIngo Weinhold case B_GET_BIOS_GEOMETRY: 6307a424c39SIngo Weinhold { 6317a424c39SIngo Weinhold TRACE("virtualdrive: B_GET_BIOS_GEOMETRY\n"); 6327a424c39SIngo Weinhold device_geometry *dg = (device_geometry *)arg; 6337a424c39SIngo Weinhold dg->bytes_per_sector = 512; 6347a424c39SIngo Weinhold dg->sectors_per_track = info.size / (512 * 1024); 6357a424c39SIngo Weinhold dg->cylinder_count = 1024; 6367a424c39SIngo Weinhold dg->head_count = 1; 6377a424c39SIngo Weinhold dg->device_type = info.geometry.device_type; 6387a424c39SIngo Weinhold dg->removable = info.geometry.removable; 6397a424c39SIngo Weinhold dg->read_only = info.geometry.read_only; 6407a424c39SIngo Weinhold dg->write_once = info.geometry.write_once; 6417a424c39SIngo Weinhold return B_OK; 6427a424c39SIngo Weinhold } 6437a424c39SIngo Weinhold 6447a424c39SIngo Weinhold case B_GET_MEDIA_STATUS: 6457a424c39SIngo Weinhold TRACE("virtualdrive: B_GET_MEDIA_STATUS\n"); 6467a424c39SIngo Weinhold *(status_t*)arg = B_NO_ERROR; 6477a424c39SIngo Weinhold return B_OK; 6487a424c39SIngo Weinhold 6497a424c39SIngo Weinhold case B_SET_UNINTERRUPTABLE_IO: 6507a424c39SIngo Weinhold TRACE("virtualdrive: B_SET_UNINTERRUPTABLE_IO\n"); 6517a424c39SIngo Weinhold return B_OK; 6527a424c39SIngo Weinhold 6537a424c39SIngo Weinhold case B_SET_INTERRUPTABLE_IO: 6547a424c39SIngo Weinhold TRACE("virtualdrive: B_SET_INTERRUPTABLE_IO\n"); 6557a424c39SIngo Weinhold return B_OK; 6567a424c39SIngo Weinhold 6577a424c39SIngo Weinhold case B_FLUSH_DRIVE_CACHE: 6587a424c39SIngo Weinhold TRACE("virtualdrive: B_FLUSH_DRIVE_CACHE\n"); 6597a424c39SIngo Weinhold return B_OK; 6607a424c39SIngo Weinhold 6617a424c39SIngo Weinhold case B_GET_BIOS_DRIVE_ID: 6627a424c39SIngo Weinhold TRACE("virtualdrive: B_GET_BIOS_DRIVE_ID\n"); 6637a424c39SIngo Weinhold *(uint8*)arg = 0xF8; 6647a424c39SIngo Weinhold return B_OK; 6657a424c39SIngo Weinhold 6667a424c39SIngo Weinhold case B_GET_DRIVER_FOR_DEVICE: 6677a424c39SIngo Weinhold case B_SET_DEVICE_SIZE: 6687a424c39SIngo Weinhold case B_SET_PARTITION: 6697a424c39SIngo Weinhold case B_FORMAT_DEVICE: 6707a424c39SIngo Weinhold case B_EJECT_DEVICE: 6717a424c39SIngo Weinhold case B_LOAD_MEDIA: 6727a424c39SIngo Weinhold case B_GET_NEXT_OPEN_DEVICE: 6737a424c39SIngo Weinhold TRACE("virtualdrive: another ioctl: %lx (%lu)\n", op, op); 6747a424c39SIngo Weinhold return B_BAD_VALUE; 675*bc4f7808SAxel Dörfler 6767a424c39SIngo Weinhold case VIRTUAL_DRIVE_REGISTER_FILE: 6777a424c39SIngo Weinhold TRACE("virtualdrive: VIRTUAL_DRIVE_REGISTER_FILE (data)\n"); 6787a424c39SIngo Weinhold return B_BAD_VALUE; 6797a424c39SIngo Weinhold case VIRTUAL_DRIVE_UNREGISTER_FILE: 6807a424c39SIngo Weinhold { 6817a424c39SIngo Weinhold TRACE("virtualdrive: VIRTUAL_DRIVE_UNREGISTER_FILE\n"); 6827a424c39SIngo Weinhold lock_driver(); 683*bc4f7808SAxel Dörfler 684*bc4f7808SAxel Dörfler bool immediately = (bool)arg; 685*bc4f7808SAxel Dörfler bool wasRegistered = info.registered; 686*bc4f7808SAxel Dörfler 687*bc4f7808SAxel Dörfler info.registered = false; 688*bc4f7808SAxel Dörfler 6897a424c39SIngo Weinhold // on the last unregistration we need to close the 6907a424c39SIngo Weinhold // control device 6917a424c39SIngo Weinhold if (wasRegistered && --gRegistrationCount == 0) { 6927a424c39SIngo Weinhold close(gControlDeviceFD); 6937a424c39SIngo Weinhold gControlDeviceFD = -1; 6947a424c39SIngo Weinhold } 695*bc4f7808SAxel Dörfler 696*bc4f7808SAxel Dörfler // if we "immediately" is true, we will stop our service immediately 697*bc4f7808SAxel Dörfler // and close the underlying file, open it for other uses 698*bc4f7808SAxel Dörfler if (immediately) { 699*bc4f7808SAxel Dörfler TRACE("virtualdrive: close file descriptor\n"); 700*bc4f7808SAxel Dörfler // we cannot use uninit_device_info() here, since that does 701*bc4f7808SAxel Dörfler // a little too much and would open the device for other 702*bc4f7808SAxel Dörfler // uses. 703*bc4f7808SAxel Dörfler close(info.fd); 704*bc4f7808SAxel Dörfler info.fd = -1; 705*bc4f7808SAxel Dörfler } 706*bc4f7808SAxel Dörfler 7077a424c39SIngo Weinhold unlock_driver(); 7087a424c39SIngo Weinhold return B_OK; 7097a424c39SIngo Weinhold } 7107a424c39SIngo Weinhold case VIRTUAL_DRIVE_GET_INFO: 7117a424c39SIngo Weinhold { 7127a424c39SIngo Weinhold TRACE("virtualdrive: VIRTUAL_DRIVE_GET_INFO\n"); 713*bc4f7808SAxel Dörfler 7147a424c39SIngo Weinhold virtual_drive_info *driveInfo = (virtual_drive_info *)arg; 715*bc4f7808SAxel Dörfler if (driveInfo == NULL 716*bc4f7808SAxel Dörfler || driveInfo->magic != VIRTUAL_DRIVE_MAGIC 717*bc4f7808SAxel Dörfler || driveInfo->drive_info_size != sizeof(virtual_drive_info)) 718*bc4f7808SAxel Dörfler return B_BAD_VALUE; 719*bc4f7808SAxel Dörfler 7207a424c39SIngo Weinhold strcpy(driveInfo->file_name, info.file); 7217a424c39SIngo Weinhold strcpy(driveInfo->device_name, "/dev/"); 7227a424c39SIngo Weinhold strcat(driveInfo->device_name, info.device_path); 7237a424c39SIngo Weinhold driveInfo->geometry = info.geometry; 7247a424c39SIngo Weinhold driveInfo->use_geometry = true; 725*bc4f7808SAxel Dörfler driveInfo->halted = info.fd == -1; 7267a424c39SIngo Weinhold return B_OK; 7277a424c39SIngo Weinhold } 728*bc4f7808SAxel Dörfler 7297a424c39SIngo Weinhold default: 7307a424c39SIngo Weinhold TRACE("virtualdrive: unknown ioctl: %lx (%lu)\n", op, op); 7317a424c39SIngo Weinhold return B_BAD_VALUE; 7327a424c39SIngo Weinhold } 7337a424c39SIngo Weinhold } 7347a424c39SIngo Weinhold 7357a424c39SIngo Weinhold } 7367a424c39SIngo Weinhold 7377a424c39SIngo Weinhold 7387a424c39SIngo Weinhold static status_t 7397a424c39SIngo Weinhold virtualdrive_free(void *cookie) 7407a424c39SIngo Weinhold { 7417a424c39SIngo Weinhold TRACE("virtualdrive: free\n"); 7427a424c39SIngo Weinhold return B_OK; 7437a424c39SIngo Weinhold } 7447a424c39SIngo Weinhold 7457a424c39SIngo Weinhold 7467a424c39SIngo Weinhold /* ----- 7477a424c39SIngo Weinhold function pointers for the device hooks entry points 7487a424c39SIngo Weinhold ----- */ 7497a424c39SIngo Weinhold 750*bc4f7808SAxel Dörfler device_hooks sVirtualDriveHooks = { 7517a424c39SIngo Weinhold virtualdrive_open, /* -> open entry point */ 7527a424c39SIngo Weinhold virtualdrive_close, /* -> close entry point */ 7537a424c39SIngo Weinhold virtualdrive_free, /* -> free cookie */ 7547a424c39SIngo Weinhold virtualdrive_control, /* -> control entry point */ 7557a424c39SIngo Weinhold virtualdrive_read, /* -> read entry point */ 7567a424c39SIngo Weinhold virtualdrive_write /* -> write entry point */ 7577a424c39SIngo Weinhold }; 7587a424c39SIngo Weinhold 759