diff --git a/dlls/mountmgr.sys/Makefile.in b/dlls/mountmgr.sys/Makefile.in index f73e4d4..1d504c8 100644 --- a/dlls/mountmgr.sys/Makefile.in +++ b/dlls/mountmgr.sys/Makefile.in @@ -3,10 +3,15 @@ TOPOBJDIR = ../.. SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = mountmgr.sys -IMPORTS = advapi32 ntoskrnl.exe kernel32 ntdll +IMPORTS = advapi32 ntoskrnl.exe kernel32 ntdll user32 EXTRADLLFLAGS = -Wb,--subsystem,native +EXTRADEFS = @HALINCL@ +EXTRALIBS = @DISKARBITRATIONLIB@ -luuid C_SRCS = \ + diskarb.c \ + hal.c \ + ioctl.c \ mountmgr.c @MAKE_DLL_RULES@ diff --git a/dlls/mountmgr.sys/diskarb.c b/dlls/mountmgr.sys/diskarb.c new file mode 100644 index 0000000..bcac5eb --- /dev/null +++ b/dlls/mountmgr.sys/diskarb.c @@ -0,0 +1,145 @@ +/* + * Devices support using the MacOS Disk Arbitration library. + * + * Copyright 2006 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winuser.h" + +#include "wine/debug.h" +#include "mountmgr.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mountmgr); + +#ifdef HAVE_DISKARBITRATION_DISKARBITRATION_H + +#include + +static void appeared_callback( DADiskRef disk, void *context ) +{ + CFDictionaryRef dict = DADiskCopyDescription( disk ); + const void *ref; + char device[64]; + char mount_point[PATH_MAX]; + const char *type = NULL; + + if (!dict) return; + + /* ignore non-removable devices */ + if (!(ref = CFDictionaryGetValue( dict, CFSTR("DAMediaRemovable") )) || + !CFBooleanGetValue( ref )) goto done; + + /* get device name */ + if (!(ref = CFDictionaryGetValue( dict, CFSTR("DAMediaBSDName") ))) goto done; + strcpy( device, "/dev/r" ); + CFStringGetCString( ref, device + 6, sizeof(device) - 6, kCFStringEncodingASCII ); + + if ((ref = CFDictionaryGetValue( dict, CFSTR("DAVolumePath") ))) + CFURLGetFileSystemRepresentation( ref, true, (UInt8 *)mount_point, sizeof(mount_point) ); + else + mount_point[0] = 0; + + if ((ref = CFDictionaryGetValue( dict, CFSTR("DAVolumeKind") ))) + { + if (!CFStringCompare( ref, CFSTR("cd9660"), 0 ) || + !CFStringCompare( ref, CFSTR("udf"), 0 )) + type = "cdrom"; + } + + TRACE( "got mount notification for '%s' on '%s'\n", device, mount_point ); + + add_dos_device( device, NULL, device, mount_point, type ); +done: + CFRelease( dict ); +} + +static void changed_callback( DADiskRef disk, CFArrayRef keys, void *context ) +{ + appeared_callback( disk, context ); +} + +static void disappeared_callback( DADiskRef disk, void *context ) +{ + CFDictionaryRef dict = DADiskCopyDescription( disk ); + const void *ref; + char device[100]; + + if (!dict) return; + + /* ignore non-removable devices */ + if (!(ref = CFDictionaryGetValue( dict, CFSTR("DAMediaRemovable") )) || + !CFBooleanGetValue( ref )) goto done; + + /* get device name */ + if (!(ref = CFDictionaryGetValue( dict, CFSTR("DAMediaBSDName") ))) goto done; + strcpy( device, "/dev/r" ); + CFStringGetCString( ref, device + 6, sizeof(device) - 6, kCFStringEncodingASCII ); + + TRACE( "got unmount notification for '%s'\n", device ); + + remove_dos_device( device ); +done: + CFRelease( dict ); +} + +static DWORD WINAPI runloop_thread( void *arg ) +{ + DASessionRef session = DASessionCreate( NULL ); + + if (!session) return 1; + + DASessionScheduleWithRunLoop( session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode ); + DARegisterDiskAppearedCallback( session, kDADiskDescriptionMatchVolumeMountable, + appeared_callback, NULL ); + DARegisterDiskDisappearedCallback( session, kDADiskDescriptionMatchVolumeMountable, + disappeared_callback, NULL ); + DARegisterDiskDescriptionChangedCallback( session, kDADiskDescriptionMatchVolumeMountable, + kDADiskDescriptionWatchVolumePath, changed_callback, NULL ); + CFRunLoopRun(); + DASessionUnscheduleFromRunLoop( session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode ); + CFRelease( session ); + return 0; +} + +void initialize_diskarbitration(void) +{ + HANDLE handle; + + if (!(handle = CreateThread( NULL, 0, runloop_thread, NULL, 0, NULL ))) return; + CloseHandle( handle ); +} + +#else /* HAVE_DISKARBITRATION_DISKARBITRATION_H */ + +void initialize_diskarbitration(void) +{ + TRACE( "Skipping, Disk Arbitration support not compiled in\n" ); +} + +#endif /* HAVE_DISKARBITRATION_DISKARBITRATION_H */ diff --git a/dlls/mountmgr.sys/hal.c b/dlls/mountmgr.sys/hal.c new file mode 100644 index 0000000..2ea3048 --- /dev/null +++ b/dlls/mountmgr.sys/hal.c @@ -0,0 +1,348 @@ +/* + * HAL devices support + * + * Copyright 2006 Alexandre Julliard + * Copyright 2008 Maarten Lankhorst + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#define NONAMELESSUNION +#define NONAMELESSSTRUCT +#include "wine/port.h" + +#include +#include +#include +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "winioctl.h" +#include "winreg.h" +#include "winuser.h" +#include "setupapi.h" +#include "ddk/wdm.h" +#include "ddk/mountmgr.h" +#include "initguid.h" +#include "ntddstor.h" +#include "ntddcdrm.h" + +#include "wine/library.h" +#include "wine/exception.h" +#include "wine/debug.h" +#include "mountmgr.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mountmgr); + +#ifdef SONAME_LIBHAL + +#include +#include + +#define DBUS_FUNCS \ + DO_FUNC(dbus_bus_get); \ + DO_FUNC(dbus_connection_close); \ + DO_FUNC(dbus_connection_read_write_dispatch); \ + DO_FUNC(dbus_error_init); \ + DO_FUNC(dbus_error_free); \ + DO_FUNC(dbus_error_is_set) + +#define HAL_FUNCS \ + DO_FUNC(libhal_ctx_free); \ + DO_FUNC(libhal_ctx_init); \ + DO_FUNC(libhal_ctx_new); \ + DO_FUNC(libhal_ctx_set_dbus_connection); \ + DO_FUNC(libhal_ctx_set_device_added); \ + DO_FUNC(libhal_ctx_set_device_property_modified); \ + DO_FUNC(libhal_ctx_set_device_removed); \ + DO_FUNC(libhal_ctx_shutdown); \ + DO_FUNC(libhal_device_get_property_bool); \ + DO_FUNC(libhal_device_get_property_string); \ + DO_FUNC(libhal_device_add_property_watch); \ + DO_FUNC(libhal_device_remove_property_watch); \ + DO_FUNC(libhal_free_string); \ + DO_FUNC(libhal_free_string_array); \ + DO_FUNC(libhal_get_all_devices) + +#define DO_FUNC(f) static typeof(f) * p_##f +DBUS_FUNCS; +HAL_FUNCS; +#undef DO_FUNC + +static BOOL load_functions(void) +{ + void *hal_handle; + char error[128]; + + /* Load libhal with RTLD_GLOBAL so that the dbus symbols are available. + * We can't load libdbus directly since libhal may have been built against a + * different version but with the same soname. Binary compatibility is for wimps. */ + + if (!(hal_handle = wine_dlopen(SONAME_LIBHAL, RTLD_NOW|RTLD_GLOBAL, error, sizeof(error)))) + goto failed; + +#define DO_FUNC(f) if (!(p_##f = wine_dlsym( RTLD_DEFAULT, #f, error, sizeof(error) ))) goto failed + DBUS_FUNCS; +#undef DO_FUNC + +#define DO_FUNC(f) if (!(p_##f = wine_dlsym( hal_handle, #f, error, sizeof(error) ))) goto failed + HAL_FUNCS; +#undef DO_FUNC + + return TRUE; + +failed: + WARN( "failed to load HAL support: %s\n", error ); + return FALSE; +} + +static LONG WINAPI assert_fault(EXCEPTION_POINTERS *eptr) +{ + if (eptr->ExceptionRecord->ExceptionCode == EXCEPTION_WINE_ASSERTION) + return EXCEPTION_EXECUTE_HANDLER; + return EXCEPTION_CONTINUE_SEARCH; +} + +/* HAL callback for new device */ +static void new_device( LibHalContext *ctx, const char *udi ) +{ + DBusError error; + char *parent = NULL, *mount_point = NULL, *device = NULL, *type = NULL, *filesystem = NULL, *product = NULL; + char *parent_block = NULL, *serial = NULL, *vendor = NULL, *grandparent = NULL, *fw_version = NULL; + const char *settype; + + BOOL got_extended = 0; + + p_dbus_error_init( &error ); + + if (!(device = p_libhal_device_get_property_string( ctx, udi, "block.device", &error ))) + goto done; + + if (!(mount_point = p_libhal_device_get_property_string( ctx, udi, "volume.mount_point", &error ))) + goto done; + + if (!(parent = p_libhal_device_get_property_string( ctx, udi, "info.parent", &error ))) + goto done; + + if (!p_libhal_device_get_property_bool( ctx, parent, "storage.removable", &error )) + goto done; + + if (!(type = p_libhal_device_get_property_string( ctx, parent, "storage.drive_type", &error ))) + p_dbus_error_free( &error ); /* ignore error */ + + filesystem = p_libhal_device_get_property_string( ctx, udi, "volume.fstype", NULL ); + if ((!type || strcasecmp(type, "cdrom")) && (!filesystem || !filesystem[0])) + goto done; + + if (!type || !strcmp(type, "disk")) + settype = "hd"; + else settype = type; + + /* Check if not mounted */ + if (!mount_point[0]) + goto out; + + TRACE("Mount point: %s\n", mount_point); + + if (!(parent_block = p_libhal_device_get_property_string( ctx, parent, "block.device", NULL ))) + goto out; + TRACE("Parent block device: %s\n", parent_block); + +#ifdef linux + if (!strncmp(parent_block, "/dev/ub", 7)) + FIXME("You're using the ub driver instead of usb-storage, adding extended info won't work\n"); +#endif + + /* Try the extended version, which adds usbstor keys! (removable = 1 at this point) */ + if (!(vendor = p_libhal_device_get_property_string( ctx, parent, "info.vendor", NULL ))) + goto out; + + TRACE("Vendor: %s\n", vendor); + + if (!(product = p_libhal_device_get_property_string( ctx, parent, "info.product", NULL ))) + goto out; + + TRACE("Product: %s\n", product); + + if (!(fw_version = p_libhal_device_get_property_string( ctx, parent, "storage.firmware_version", NULL ))) + goto out; + + TRACE("Firmware version: %s\n", fw_version); + + grandparent = p_libhal_device_get_property_string( ctx, parent, "info.parent", NULL ); + if (!grandparent) + goto out; + TRACE("Grandparent: %s\n", grandparent); + + while (!serial) { + char *ancestor = p_libhal_device_get_property_string( ctx, grandparent, "info.parent", NULL ); + if (ancestor) + { + p_libhal_free_string( grandparent ); + grandparent = ancestor; + serial = p_libhal_device_get_property_string( ctx, grandparent, "usb_device.serial", NULL ); + } + else break; + } + if (!serial) + goto out; + TRACE("Serial: %s\n", serial); + + got_extended = 1; + add_extended_dos_device( udi, device, parent_block, mount_point, settype, vendor, product, fw_version, serial ); + +out: + if (mount_point[0]) + { + if (!got_extended) + { + TRACE("Extended usbstor failed, back to normal mode\n"); + add_dos_device( udi, parent_block, device, mount_point, settype ); + } + } + else + remove_dos_device( udi ); + + /* add property watch for mount point */ + p_libhal_device_add_property_watch( ctx, udi, &error ); + + if (serial) p_libhal_free_string( serial ); + if (grandparent) p_libhal_free_string( grandparent ); + if (fw_version) p_libhal_free_string( fw_version ); + if (product) p_libhal_free_string( product ); + if (vendor) p_libhal_free_string( vendor ); + if (parent_block) p_libhal_free_string( parent_block ); + +done: + if (filesystem) p_libhal_free_string( filesystem ); + if (type) p_libhal_free_string( type ); + if (parent) p_libhal_free_string( parent ); + if (mount_point) p_libhal_free_string( mount_point ); + if (device) p_libhal_free_string( device ); + p_dbus_error_free( &error ); +} + +/* HAL callback for removed device */ +static void removed_device( LibHalContext *ctx, const char *udi ) +{ + DBusError error; + + TRACE( "removed %s\n", wine_dbgstr_a(udi) ); + + if (remove_dos_device( udi )) + { + p_dbus_error_init( &error ); + p_libhal_device_remove_property_watch( ctx, udi, &error ); + p_dbus_error_free( &error ); + } +} + +/* HAL callback for property changes */ +static void property_modified (LibHalContext *ctx, const char *udi, + const char *key, dbus_bool_t is_removed, dbus_bool_t is_added) +{ + TRACE( "udi %s key %s %s\n", wine_dbgstr_a(udi), wine_dbgstr_a(key), + is_added ? "added" : is_removed ? "removed" : "modified" ); + + if (!strcmp( key, "volume.mount_point" )) new_device( ctx, udi ); +} + +static DBusError error; +static DBusConnection *dbc; +static LibHalContext *ctx; +static int i, num; +static char **list; + +static void hal_cleanup(void) +{ + p_libhal_ctx_shutdown( ctx, &error ); + p_dbus_error_free( &error ); /* just in case */ + p_dbus_connection_close( dbc ); + p_libhal_ctx_free( ctx ); +} + +static DWORD WINAPI hal_thread( void *arg ) +{ + __TRY + { + while (p_dbus_connection_read_write_dispatch( dbc, -1 )) /* nothing */ ; + } + __EXCEPT( assert_fault ) + { + WARN( "dbus assertion failure, disabling HAL support\n" ); + return 1; + } + __ENDTRY; + + hal_cleanup(); + return 0; +} + +void initialize_hal(void) +{ + HANDLE handle; + + if (!load_functions()) return; + + if (!(ctx = p_libhal_ctx_new())) return; + + p_dbus_error_init( &error ); + if (!(dbc = p_dbus_bus_get( DBUS_BUS_SYSTEM, &error ))) + { + WARN( "failed to get system dbus connection: %s\n", error.message ); + p_dbus_error_free( &error ); + return; + } + + p_libhal_ctx_set_dbus_connection( ctx, dbc ); + p_libhal_ctx_set_device_added( ctx, new_device ); + p_libhal_ctx_set_device_removed( ctx, removed_device ); + p_libhal_ctx_set_device_property_modified( ctx, property_modified ); + + if (!p_libhal_ctx_init( ctx, &error )) + { + WARN( "HAL context init failed: %s\n", error.message ); + p_dbus_error_free( &error ); + return; + } + + /* retrieve all existing devices, we really need to have completed this prior to returning */ + if (!(list = p_libhal_get_all_devices( ctx, &num, &error ))) p_dbus_error_free( &error ); + else + { + for (i = 0; i < num; i++) new_device( ctx, list[i] ); + p_libhal_free_string_array( list ); + } + + if ((handle = CreateThread( NULL, 0, hal_thread, NULL, 0, NULL ))) + CloseHandle( handle ); + else /* Clean up from failing */ + hal_cleanup(); +} + +#else /* SONAME_LIBHAL */ + +void initialize_hal(void) +{ + TRACE( "Skipping, HAL support not compiled in\n" ); +} + +#endif /* SONAME_LIBHAL */ diff --git a/dlls/mountmgr.sys/ioctl.c b/dlls/mountmgr.sys/ioctl.c new file mode 100644 index 0000000..70b625e --- /dev/null +++ b/dlls/mountmgr.sys/ioctl.c @@ -0,0 +1,384 @@ +/* -*- tab-width: 8; c-basic-offset: 4 -*- */ +/* Main file for CD-ROM support + * + * Copyright 1994 Martin Ayotte + * Copyright 1999, 2001, 2003 Eric Pouech + * Copyright 2000 Andreas Mohr + * Copyright 2005 Ivan Leo Puoti + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" +#include "wine/port.h" + +#include +#include +#include +#include +#ifdef HAVE_IO_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#include +#ifdef HAVE_SYS_STAT_H +# include +#endif +#include + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SCSI_SG_H +# include +#endif +#ifdef HAVE_SCSI_SCSI_H +# include +# undef REASSIGN_BLOCKS /* avoid conflict with winioctl.h */ +# undef FAILED /* avoid conflict with winerror.h */ +#endif +#ifdef HAVE_SCSI_SCSI_IOCTL_H +# include +#endif +#ifdef HAVE_LINUX_MAJOR_H +# include +#endif +#ifdef HAVE_LINUX_HDREG_H +# include +#endif +#ifdef HAVE_LINUX_PARAM_H +# include +#endif +#ifdef HAVE_LINUX_CDROM_H +# include +#endif +#ifdef HAVE_LINUX_UCDROM_H +# include +#endif +#ifdef HAVE_SYS_CDIO_H +# include +#endif +#ifdef HAVE_SYS_SCSIIO_H +# include +#endif + +#ifdef HAVE_IOKIT_IOKITLIB_H +# include +# include +# include +# include +# include +# define SENSEBUFLEN kSenseDefaultSize +#endif + +#define NONAMELESSUNION +#define NONAMELESSSTRUCT +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winternl.h" +#include "winioctl.h" +#include "ntddstor.h" +#include "ntddcdrm.h" +#include "ddk/ntddcdvd.h" +#include "ntddscsi.h" +#include "wine/server.h" +#include "wine/library.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(cdrom); + + +/*********************************************************************** + * FILE_GetNtStatus(void) + * + * Retrieve the Nt Status code from errno. + * Try to be consistent with FILE_SetDosError(). + */ +static NTSTATUS FILE_GetNtStatus(void) +{ + int err = errno; + + TRACE( "errno = %d\n", errno ); + switch (err) + { + case EAGAIN: return STATUS_SHARING_VIOLATION; + case EBADF: return STATUS_INVALID_HANDLE; + case EBUSY: return STATUS_DEVICE_BUSY; + case ENOSPC: return STATUS_DISK_FULL; + case EPERM: + case EROFS: + case EACCES: return STATUS_ACCESS_DENIED; + case ENOTDIR: return STATUS_OBJECT_PATH_NOT_FOUND; + case ENOENT: return STATUS_OBJECT_NAME_NOT_FOUND; + case EISDIR: return STATUS_FILE_IS_A_DIRECTORY; + case EMFILE: + case ENFILE: return STATUS_TOO_MANY_OPENED_FILES; + case EINVAL: return STATUS_INVALID_PARAMETER; + case ENOTEMPTY: return STATUS_DIRECTORY_NOT_EMPTY; + case EPIPE: return STATUS_PIPE_DISCONNECTED; + case EIO: return STATUS_DEVICE_NOT_READY; +#ifdef ENOMEDIUM + case ENOMEDIUM: return STATUS_NO_MEDIA_IN_DEVICE; +#endif + case ENXIO: return STATUS_NO_SUCH_DEVICE; + case ENOTTY: + case EOPNOTSUPP:return STATUS_NOT_SUPPORTED; + case ECONNRESET:return STATUS_PIPE_DISCONNECTED; + case EFAULT: return STATUS_ACCESS_VIOLATION; + case ESPIPE: return STATUS_ILLEGAL_FUNCTION; + case ENOEXEC: /* ?? */ + case EEXIST: /* ?? */ + default: + FIXME( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err ); + return STATUS_UNSUCCESSFUL; + } +} + +/****************************************************************** + * CDROM_GetStatusCode + * + * + */ +static NTSTATUS CDROM_GetStatusCode(int io) +{ + if (io == 0) return STATUS_SUCCESS; + return FILE_GetNtStatus(); +} + + +/****************************************************************** + * CDROM_ScsiPassThroughDirect + * Implements IOCTL_SCSI_PASS_THROUGH_DIRECT + * + */ +NTSTATUS MOUNTMGR_ScsiPassThroughDirect(int fd, PSCSI_PASS_THROUGH_DIRECT pPacket) +{ + int ret = STATUS_NOT_SUPPORTED; +#ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID + sg_io_hdr_t cmd; + int io; +#elif defined HAVE_SCSIREQ_T_CMD + scsireq_t cmd; + int io; +#endif + DWORD SenseInfoOffset = sizeof(*pPacket); + LPVOID DataBuffer = (char *)pPacket + SenseInfoOffset + pPacket->SenseInfoLength; + +#ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID + RtlZeroMemory(&cmd, sizeof(cmd)); + + cmd.interface_id = 'S'; + cmd.cmd_len = pPacket->CdbLength; + cmd.mx_sb_len = pPacket->SenseInfoLength; + cmd.dxfer_len = pPacket->DataTransferLength; + cmd.dxferp = DataBuffer; + cmd.cmdp = pPacket->Cdb; + cmd.sbp = (unsigned char*)pPacket + SenseInfoOffset; + cmd.timeout = pPacket->TimeOutValue*1000; + + switch (pPacket->DataIn) + { + case SCSI_IOCTL_DATA_IN: + cmd.dxfer_direction = SG_DXFER_FROM_DEV; + break; + case SCSI_IOCTL_DATA_OUT: + cmd.dxfer_direction = SG_DXFER_TO_DEV; + break; + case SCSI_IOCTL_DATA_UNSPECIFIED: + cmd.dxfer_direction = SG_DXFER_NONE; + break; + default: + return STATUS_INVALID_PARAMETER; + } + + io = ioctl(fd, SG_IO, &cmd); + + pPacket->ScsiStatus = cmd.status; + pPacket->DataTransferLength = cmd.resid; + pPacket->SenseInfoLength = cmd.sb_len_wr; + + ret = CDROM_GetStatusCode(io); + +#elif defined HAVE_SCSIREQ_T_CMD + + memset(&cmd, 0, sizeof(cmd)); + memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength); + + cmd.cmdlen = pPacket->CdbLength; + cmd.databuf = DataBuffer; + cmd.datalen = pPacket->DataTransferLength; + cmd.senselen = pPacket->SenseInfoLength; + cmd.timeout = pPacket->TimeOutValue*1000; /* in milliseconds */ + + switch (pPacket->DataIn) + { + case SCSI_IOCTL_DATA_OUT: + cmd.flags |= SCCMD_WRITE; + break; + case SCSI_IOCTL_DATA_IN: + cmd.flags |= SCCMD_READ; + break; + case SCSI_IOCTL_DATA_UNSPECIFIED: + cmd.flags = 0; + break; + default: + return STATUS_INVALID_PARAMETER; + } + + io = ioctl(fd, SCIOCCOMMAND, &cmd); + + switch (cmd.retsts) + { + case SCCMD_OK: break; + case SCCMD_TIMEOUT: return STATUS_TIMEOUT; + break; + case SCCMD_BUSY: return STATUS_DEVICE_BUSY; + break; + case SCCMD_SENSE: break; + case SCCMD_UNKNOWN: return STATUS_UNSUCCESSFUL; + break; + } + + if (pPacket->SenseInfoLength != 0) + { + memcpy((char*)pPacket + SenseInfoOffset, + cmd.sense, pPacket->SenseInfoLength); + } + + pPacket->ScsiStatus = cmd.status; + + ret = CDROM_GetStatusCode(io); +#endif + return ret; +} + +/****************************************************************** + * CDROM_ScsiPassThrough + * Implements IOCTL_SCSI_PASS_THROUGH + * + */ +NTSTATUS MOUNTMGR_ScsiPassThrough(int fd, PSCSI_PASS_THROUGH pPacket) +{ + int ret = STATUS_NOT_SUPPORTED; +#ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID + sg_io_hdr_t cmd; + int io; +#elif defined HAVE_SCSIREQ_T_CMD + scsireq_t cmd; + int io; +#endif + DWORD SenseInfoOffset = sizeof(*pPacket); + DWORD DataBufferOffset = SenseInfoOffset + pPacket->SenseInfoLength; + +#ifdef HAVE_SG_IO_HDR_T_INTERFACE_ID + RtlZeroMemory(&cmd, sizeof(cmd)); + + cmd.interface_id = 'S'; + cmd.dxfer_len = pPacket->DataTransferLength; + cmd.dxferp = (char*)pPacket + DataBufferOffset; + cmd.cmd_len = pPacket->CdbLength; + cmd.cmdp = pPacket->Cdb; + cmd.mx_sb_len = pPacket->SenseInfoLength; + cmd.timeout = pPacket->TimeOutValue*1000; + + if(cmd.mx_sb_len > 0) + cmd.sbp = (unsigned char*)pPacket + SenseInfoOffset; + + switch (pPacket->DataIn) + { + case SCSI_IOCTL_DATA_IN: + cmd.dxfer_direction = SG_DXFER_FROM_DEV; + break; + case SCSI_IOCTL_DATA_OUT: + cmd.dxfer_direction = SG_DXFER_TO_DEV; + break; + case SCSI_IOCTL_DATA_UNSPECIFIED: + cmd.dxfer_direction = SG_DXFER_NONE; + break; + default: + return STATUS_INVALID_PARAMETER; + } + + io = ioctl(fd, SG_IO, &cmd); + + pPacket->ScsiStatus = cmd.status; + pPacket->DataTransferLength = cmd.resid; + pPacket->SenseInfoLength = cmd.sb_len_wr; + + ret = CDROM_GetStatusCode(io); + +#elif defined HAVE_SCSIREQ_T_CMD + + memset(&cmd, 0, sizeof(cmd)); + memcpy(&(cmd.cmd), &(pPacket->Cdb), pPacket->CdbLength); + + if ( DataBufferOffset > 0x1000 ) + { + cmd.databuf = (void*)DataBufferOffset; + } + else + { + cmd.databuf = (char*)pPacket + DataBufferOffset; + } + + cmd.cmdlen = pPacket->CdbLength; + cmd.datalen = pPacket->DataTransferLength; + cmd.senselen = pPacket->SenseInfoLength; + cmd.timeout = pPacket->TimeOutValue*1000; /* in milliseconds */ + + switch (pPacket->DataIn) + { + case SCSI_IOCTL_DATA_OUT: + cmd.flags |= SCCMD_WRITE; + break; + case SCSI_IOCTL_DATA_IN: + cmd.flags |= SCCMD_READ; + break; + case SCSI_IOCTL_DATA_UNSPECIFIED: + cmd.flags = 0; + break; + default: + return STATUS_INVALID_PARAMETER; + } + + io = ioctl(fd, SCIOCCOMMAND, &cmd); + + switch (cmd.retsts) + { + case SCCMD_OK: break; + case SCCMD_TIMEOUT: return STATUS_TIMEOUT; + break; + case SCCMD_BUSY: return STATUS_DEVICE_BUSY; + break; + case SCCMD_SENSE: break; + case SCCMD_UNKNOWN: return STATUS_UNSUCCESSFUL; + break; + } + + if (pPacket->SenseInfoLength != 0) + { + memcpy((char*)pPacket + SenseInfoOffset, + cmd.sense, pPacket->SenseInfoLength); + } + + pPacket->ScsiStatus = cmd.status; + + ret = CDROM_GetStatusCode(io); +#endif + return ret; +} diff --git a/dlls/mountmgr.sys/mountmgr.c b/dlls/mountmgr.sys/mountmgr.c index ee819a5..6210e32 100644 --- a/dlls/mountmgr.sys/mountmgr.c +++ b/dlls/mountmgr.sys/mountmgr.c @@ -2,6 +2,7 @@ * Mount manager service implementation * * Copyright 2008 Alexandre Julliard + * Copyright 2008 Maarten Lankhorst * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,8 +19,14 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include "config.h" +#include "wine/port.h" + +#include +#include #include -#include +#include +#include #define NONAMELESSUNION #define NONAMELESSSTRUCT @@ -31,24 +38,32 @@ #include "winternl.h" #include "winioctl.h" #include "winreg.h" +#include "winuser.h" +#include "dbt.h" + #include "ntddstor.h" #include "ntddcdrm.h" +#include "ntddscsi.h" #include "ddk/wdm.h" #include "ddk/mountmgr.h" #include "wine/library.h" #include "wine/unicode.h" +#include "wine/list.h" #include "wine/debug.h" +#include "mountmgr.h" WINE_DEFAULT_DEBUG_CHANNEL(mountmgr); #define MAX_DOS_DRIVES 26 -#define MAX_MOUNT_POINTS (2 * MAX_DOS_DRIVES) +#define MAX_MOUNT_POINTS 2*MAX_DOS_DRIVES /* extra info for disk devices, stored in DeviceExtension */ struct disk_device_info { UNICODE_STRING name; /* device name */ STORAGE_DEVICE_NUMBER devnum; /* device number info */ + ULONG partitioncount; /* extension for primary harddisk: Keep track of partition count */ + char *dev_node; }; struct mount_point @@ -57,10 +72,29 @@ struct mount_point UNICODE_STRING link; /* DOS device symlink */ void *id; /* device unique id */ unsigned int id_len; + DEVICE_OBJECT *parent; }; +extern NTSTATUS MOUNTMGR_ScsiPassThroughDirect(int fd, PSCSI_PASS_THROUGH_DIRECT pPacket); +extern NTSTATUS MOUNTMGR_ScsiPassThrough(int, PSCSI_PASS_THROUGH pPacket); + static struct mount_point mount_points[MAX_MOUNT_POINTS]; -static HKEY mount_key; +static HKEY mount_key, drive_key, usbenum_key, devclass_key; +static ULONG globaldevicenumber; +static unsigned initializing; +static DEVICE_OBJECT *harddisk0; +static DRIVER_OBJECT *hddriver; + +static const WCHAR drive_types[][8] = +{ + { 0 }, /* DRIVE_UNKNOWN */ + { 0 }, /* DRIVE_NO_ROOT_DIR */ + {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */ + {'h','d',0}, /* DRIVE_FIXED */ + {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */ + {'c','d','r','o','m',0}, /* DRIVE_CDROM */ + {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */ +}; static inline UNICODE_STRING *get_device_name( DEVICE_OBJECT *dev ) { @@ -96,14 +130,14 @@ static char *read_symlink( const char *path ) } } -static NTSTATUS create_disk_device( DRIVER_OBJECT *driver, DWORD type, DEVICE_OBJECT **dev_obj ) +static NTSTATUS create_disk_device( DRIVER_OBJECT *driver, DWORD type, DEVICE_OBJECT **dev_obj, ULONG devicenumber, ULONG disknumber ) { static const WCHAR harddiskW[] = {'\\','D','e','v','i','c','e', '\\','H','a','r','d','d','i','s','k','V','o','l','u','m','e','%','u',0}; static const WCHAR cdromW[] = {'\\','D','e','v','i','c','e','\\','C','d','R','o','m','%','u',0}; static const WCHAR floppyW[] = {'\\','D','e','v','i','c','e','\\','F','l','o','p','p','y','%','u',0}; - UINT i, first = 0; + UINT i; NTSTATUS status = 0; const WCHAR *format; UNICODE_STRING name; @@ -120,13 +154,12 @@ static NTSTATUS create_disk_device( DRIVER_OBJECT *driver, DWORD type, DEVICE_OB case DRIVE_FIXED: default: /* FIXME */ format = harddiskW; - first = 1; /* harddisk volumes start counting from 1 */ break; } name.MaximumLength = (strlenW(format) + 10) * sizeof(WCHAR); name.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, name.MaximumLength ); - for (i = first; i < 32; i++) + for (i = 0; i < 32; i++) { sprintfW( name.Buffer, format, i ); name.Length = strlenW(name.Buffer) * sizeof(WCHAR); @@ -141,21 +174,18 @@ static NTSTATUS create_disk_device( DRIVER_OBJECT *driver, DWORD type, DEVICE_OB { case DRIVE_REMOVABLE: info->devnum.DeviceType = FILE_DEVICE_DISK; - info->devnum.DeviceNumber = i; - info->devnum.PartitionNumber = ~0u; break; case DRIVE_CDROM: info->devnum.DeviceType = FILE_DEVICE_CD_ROM; - info->devnum.DeviceNumber = i; - info->devnum.PartitionNumber = ~0u; break; case DRIVE_FIXED: default: /* FIXME */ info->devnum.DeviceType = FILE_DEVICE_DISK; - info->devnum.DeviceNumber = 0; - info->devnum.PartitionNumber = i; break; } + info->devnum.DeviceNumber = devicenumber; + info->devnum.PartitionNumber = disknumber; + info->dev_node = NULL; } else { @@ -166,7 +196,7 @@ static NTSTATUS create_disk_device( DRIVER_OBJECT *driver, DWORD type, DEVICE_OB } -static NTSTATUS add_mount_point( DRIVER_OBJECT *driver, DWORD type, int drive, +static NTSTATUS add_mount_point( DEVICE_OBJECT *device, DRIVER_OBJECT *driver, int drive, const void *id, unsigned int id_len ) { static const WCHAR driveW[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','%','c',':',0}; @@ -174,27 +204,29 @@ static NTSTATUS add_mount_point( DRIVER_OBJECT *driver, DWORD type, int drive, '%','0','8','x','-','%','0','4','x','-','%','0','4','x','-', '%','0','2','x','%','0','2','x','-','%','0','2','x','%','0','2','x', '%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','}',0}; + WCHAR disk[4] = {'A',':','\\',0}; WCHAR *drive_link, *volume_link; + WCHAR strtype[8]; NTSTATUS status; GUID guid; - UINT i; struct mount_point *mount_drive = NULL, *mount_volume = NULL; + struct disk_device_info *info = device->DeviceExtension; + LONG devnumberused; + LONG partnumberused = ~0u; + DWORD type; + int i; + LONG size = sizeof(strtype); /* find two free mount points */ - for (i = 0; i < MAX_MOUNT_POINTS; i++) + mount_drive = mount_points + 2*drive; + mount_volume = mount_drive + 1; + + if (mount_drive->device || mount_volume->device) { - if (mount_points[i].device) continue; - if (!mount_drive) - { - mount_drive = &mount_points[i]; - continue; - } - mount_volume = &mount_points[i]; - break; + ERR("Apparantly this device (%c:) is already taken!\n", 'A'+drive); + return STATUS_NO_MEMORY; } - if (!mount_volume) return STATUS_NO_MEMORY; - /* create the volume */ memset( &guid, 0, sizeof(guid) ); /* FIXME */ @@ -209,7 +241,28 @@ static NTSTATUS add_mount_point( DRIVER_OBJECT *driver, DWORD type, int drive, RtlInitUnicodeString( &mount_drive->link, drive_link ); RtlInitUnicodeString( &mount_volume->link, volume_link ); - status = create_disk_device( driver, type, &mount_drive->device ); + disk[0] += drive; + + + RegQueryValueW(drive_key, disk, strtype, &size); + type = DRIVE_FIXED; + for (i = 0; i < sizeof(drive_types)/sizeof(drive_types[0]); i++) + { + if (!strcmpiW( strtype, drive_types[i] )) + { + type = i; + break; + } + } + if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM) + devnumberused = globaldevicenumber++; + else + { + devnumberused = info->devnum.DeviceNumber; + partnumberused = ++(info->partitioncount); + } + + status = create_disk_device( driver, type, &mount_drive->device, devnumberused, partnumberused ); if (status) { RtlFreeUnicodeString( &mount_drive->link ); @@ -227,6 +280,7 @@ static NTSTATUS add_mount_point( DRIVER_OBJECT *driver, DWORD type, int drive, IoCreateSymbolicLink( &mount_drive->link, get_device_name(mount_drive->device) ); IoCreateSymbolicLink( &mount_volume->link, get_device_name(mount_volume->device) ); + mount_drive->parent = mount_volume->parent = device; TRACE( "created device %s symlinks %s %s\n", debugstr_w(get_device_name(mount_drive->device)->Buffer), debugstr_w(mount_drive->link.Buffer), debugstr_w(mount_volume->link.Buffer) ); @@ -396,6 +450,32 @@ static NTSTATUS WINAPI harddisk_ioctl( DEVICE_OBJECT *device, IRP *irp ) case IOCTL_CDROM_READ_TOC: irp->IoStatus.u.Status = STATUS_INVALID_DEVICE_REQUEST; break; + case IOCTL_SCSI_PASS_THROUGH: + { + int fd = open(disk_info->dev_node, 777); + memcpy(irp->UserBuffer, irp->AssociatedIrp.SystemBuffer, irpsp->Parameters.DeviceIoControl.OutputBufferLength); + if (fd >= 0) + { + irp->IoStatus.u.Status = MOUNTMGR_ScsiPassThrough(fd, irp->UserBuffer); + if (SUCCEEDED(irp->IoStatus.u.Status)) + irp->IoStatus.Information = irpsp->Parameters.DeviceIoControl.OutputBufferLength; + close(fd); + } + } + break; + case IOCTL_SCSI_PASS_THROUGH_DIRECT: + { + int fd = open(disk_info->dev_node, 777); + memcpy(irp->UserBuffer, irp->AssociatedIrp.SystemBuffer, irpsp->Parameters.DeviceIoControl.OutputBufferLength); + if (fd >= 0) + { + irp->IoStatus.u.Status = MOUNTMGR_ScsiPassThroughDirect(fd, irp->UserBuffer); + if (SUCCEEDED(irp->IoStatus.u.Status)) + irp->IoStatus.Information = irpsp->Parameters.DeviceIoControl.OutputBufferLength; + close(fd); + } + } + break; default: FIXME( "unsupported ioctl %x\n", irpsp->Parameters.DeviceIoControl.IoControlCode ); irp->IoStatus.u.Status = STATUS_NOT_SUPPORTED; @@ -404,29 +484,94 @@ static NTSTATUS WINAPI harddisk_ioctl( DEVICE_OBJECT *device, IRP *irp ) return irp->IoStatus.u.Status; } -/* create mount points for mapped drives */ -static void create_drive_mount_points( DRIVER_OBJECT *driver ) +static char *get_dosdevices_path(void) { const char *config_dir = wine_get_config_dir(); + size_t len = strlen(config_dir) + sizeof("/dosdevices/a::"); + char *path = RtlAllocateHeap( GetProcessHeap(), 0, len ); + if (path) + { + strcpy( path, config_dir ); + strcat( path, "/dosdevices/a::" ); + } + return path; +} + +/* create mount points for mapped drives */ +static void create_drive_mount_points( DEVICE_OBJECT *device, DRIVER_OBJECT *driver ) +{ char *buffer, *p, *link; unsigned int i; - if ((buffer = RtlAllocateHeap( GetProcessHeap(), 0, - strlen(config_dir) + sizeof("/dosdevices/a:") ))) - { - strcpy( buffer, config_dir ); - strcat( buffer, "/dosdevices/a:" ); - p = buffer + strlen(buffer) - 2; + buffer = get_dosdevices_path(); + if (!buffer) + return; + /* Remove the extra : */ + buffer[strlen(buffer)-1] = 0; - for (i = 0; i < MAX_DOS_DRIVES; i++) + p = buffer + strlen(buffer) - 2; + + for (i = 0; i < MAX_DOS_DRIVES; i++) + { + if (mount_points[i*2].device) { - *p = 'a' + i; - if (!(link = read_symlink( buffer ))) continue; - add_mount_point( driver, DRIVE_FIXED, i, link, strlen(link) ); - RtlFreeHeap( GetProcessHeap(), 0, link ); + TRACE("Drive %c: already exists, skipping\n", 'a'+i); + continue; } - RtlFreeHeap( GetProcessHeap(), 0, buffer ); + + *p = 'a' + i; + if (!(link = read_symlink( buffer ))) continue; + add_mount_point( device, driver, i, link, strlen(link) ); + RtlFreeHeap( GetProcessHeap(), 0, link ); + } + RtlFreeHeap( GetProcessHeap(), 0, buffer ); +} + +static NTSTATUS WINAPI create_harddisk( DRIVER_OBJECT *driver, DEVICE_OBJECT **dev_obj, const char *parent_device ) +{ + static const WCHAR harddiskW[] = {'\\','D','e','v','i','c','e','\\','H','a','r','d','d','i','s','k','%','u',0}; + static const WCHAR physdriveW[] = {'\\','?','?','\\','P','h','y','s','i','c','a','l','D','r','i','v','e','%','u',0}; + + UINT i; + NTSTATUS status = 0; + UNICODE_STRING harddiskname, physdrivename; + struct disk_device_info *info; + + harddiskname.MaximumLength = (strlenW(harddiskW) + 10) * sizeof(WCHAR); + harddiskname.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, harddiskname.MaximumLength ); + physdrivename.MaximumLength = (strlenW(physdriveW) + 10) * sizeof(WCHAR); + physdrivename.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, physdrivename.MaximumLength ); + for (i = 0; i < 32; i++) + { + sprintfW( harddiskname.Buffer, harddiskW, i ); + harddiskname.Length = strlenW(harddiskname.Buffer) * sizeof(WCHAR); + status = IoCreateDevice( driver, sizeof(*info), &harddiskname, 0, 0, FALSE, dev_obj ); + if (status != STATUS_OBJECT_NAME_COLLISION) break; + } + if (!status) + { + sprintfW( physdrivename.Buffer, physdriveW, i ); + physdrivename.Length = strlenW(physdrivename.Buffer) * sizeof(WCHAR); + status = IoCreateSymbolicLink( &physdrivename, &harddiskname ); + } + RtlFreeUnicodeString( &physdrivename ); + if (!status) + { + info = (*dev_obj)->DeviceExtension; + info->name = harddiskname; + info->devnum.DeviceType = FILE_DEVICE_DISK; + info->devnum.DeviceNumber = globaldevicenumber++; + info->devnum.PartitionNumber = 0; + info->partitioncount = 0; + info->dev_node = RtlAllocateHeap(GetProcessHeap(), 0, strlen(parent_device)+1); + strcpy(info->dev_node, parent_device); + } + else + { + FIXME( "IoCreateDevice %s got %x\n", debugstr_w(harddiskname.Buffer), status ); + RtlFreeUnicodeString( &harddiskname ); } + return status; } /* driver entry point for the harddisk driver */ @@ -434,37 +579,42 @@ static NTSTATUS WINAPI harddisk_driver_entry( DRIVER_OBJECT *driver, UNICODE_STR { static const WCHAR mounted_devicesW[] = {'S','y','s','t','e','m','\\', 'M','o','u','n','t','e','d','D','e','v','i','c','e','s',0}; - static const WCHAR harddisk0W[] = {'\\','D','e','v','i','c','e', - '\\','H','a','r','d','d','i','s','k','0',0}; - static const WCHAR physdrive0W[] = {'\\','?','?','\\','P','h','y','s','i','c','a','l','D','r','i','v','e','0',0}; - - UNICODE_STRING nameW, linkW; - DEVICE_OBJECT *device; + static const WCHAR wine_drivesW[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\', + 'D','r','i','v','e','s',0}; + static const WCHAR devclassW[] = {'S','y','s','t','e','m','\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', + 'C','o','n','t','r','o','l','\\','D','e','v','i','c','e','C','l','a','s','s','e','s','\\', + '{','5','3','f','5','6','3','0','7','-','b','6','b','f','-','1','1','d','0','-', + '9','4','f','2','-','0','0','a','0','c','9','1','e','f','b','8','b','}',0 }; + static const WCHAR enumusbW[] = {'S','y','s','t','e','m','\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', + 'E','n','u','m','\\','U','S','B','S','T','O','R',0}; NTSTATUS status; - struct disk_device_info *info; driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = harddisk_ioctl; RegCreateKeyExW( HKEY_LOCAL_MACHINE, mounted_devicesW, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &mount_key, NULL ); - - RtlInitUnicodeString( &nameW, harddisk0W ); - RtlInitUnicodeString( &linkW, physdrive0W ); - if (!(status = IoCreateDevice( driver, sizeof(*info), &nameW, 0, 0, FALSE, &device ))) - status = IoCreateSymbolicLink( &linkW, &nameW ); + RegCreateKeyExW( HKEY_LOCAL_MACHINE, wine_drivesW, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &drive_key, NULL ); + RegCreateKeyExW( HKEY_LOCAL_MACHINE, devclassW, 0, NULL, + REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &devclass_key, NULL ); + RegCreateKeyExW( HKEY_LOCAL_MACHINE, enumusbW, 0, NULL, + 0, KEY_ALL_ACCESS, NULL, &usbenum_key, NULL ); + status = create_harddisk( driver, &harddisk0, "/dev/null" ); if (status) { FIXME( "failed to create device error %x\n", status ); return status; } - info = device->DeviceExtension; - info->name = nameW; - info->devnum.DeviceType = FILE_DEVICE_DISK; - info->devnum.DeviceNumber = 0; - info->devnum.PartitionNumber = 0; + hddriver = driver; - create_drive_mount_points( driver ); + /* Suppress device notifications during initialization */ + initializing = 1; + initialize_hal(); + initialize_diskarbitration(); + + initializing = 0; + + create_drive_mount_points( harddisk0, driver ); return status; } @@ -495,6 +645,408 @@ NTSTATUS WINAPI DriverEntry( DRIVER_OBJECT *driver, UNICODE_STRING *path ) RtlInitUnicodeString( &nameW, harddiskW ); status = IoCreateDriver( &nameW, harddisk_driver_entry ); - return status; } + +/* send notification about a change to a given drive */ +static void send_notify( int drive, int code ) +{ + DWORD_PTR result; + DEV_BROADCAST_VOLUME info; + + if (initializing) return; + + info.dbcv_size = sizeof(info); + info.dbcv_devicetype = DBT_DEVTYP_VOLUME; + info.dbcv_reserved = 0; + info.dbcv_unitmask = 1 << drive; + info.dbcv_flags = DBTF_MEDIA; + result = BroadcastSystemMessageW( BSF_FORCEIFHUNG|BSF_POSTMESSAGE, NULL, + WM_DEVICECHANGE, code, (LPARAM)&info ); +} + +static inline int is_valid_device( struct stat *st ) +{ +#if defined(linux) || defined(__sun__) + return S_ISBLK( st->st_mode ); +#else + /* disks are char devices on *BSD */ + return S_ISCHR( st->st_mode ); +#endif +} + +/* find or create a DOS drive for the corresponding device */ +static int add_drive( const char *device, const char *type ) +{ + char *path, *p; + struct stat dev_st, drive_st; + int drive, first, last, avail = -1; + + if (stat( device, &dev_st ) == -1 || !is_valid_device( &dev_st )) return -1; + + if (!(path = get_dosdevices_path())) return -1; + p = path + strlen(path) - 3; + + first = 2; + last = 26; + if (type && !strcmp( type, "floppy" )) + { + first = 0; + last = 2; + } + + for (drive = first; drive < last; ++drive) + { + *p = drive + 'a'; + + if (stat( path, &drive_st) == -1) + { + if (avail == -1 && errno == ENOENT && !mount_points[drive*2].device) + { + p[2] = 0; + if (lstat( path, &drive_st ) == -1 && errno == ENOENT) + avail = drive; + p[2] = ':'; + } + } + else if (is_valid_device( &drive_st ) && dev_st.st_rdev == drive_st.st_rdev) + break; + } + if (drive == last) + drive = avail; + + if (drive != -1 && drive == avail) + { + *p = 'a'+avail; + symlink(device, path); + } + + RtlFreeHeap( GetProcessHeap(), 0, path ); + return drive; +} + +static BOOL set_mount_point( int drive, const char *mount_point ) +{ + char *path, *p; + struct stat path_st, mnt_st; + BOOL modified = FALSE; + + if (!(path = get_dosdevices_path())) + return FALSE; + + p = path + strlen(path) - 3; + *p = 'a' + drive; + p[2] = 0; + + if (mount_point[0]) + { + /* try to avoid unlinking if already set correctly */ + if (stat( path, &path_st ) == -1 || stat( mount_point, &mnt_st ) == -1 || + path_st.st_dev != mnt_st.st_dev || path_st.st_ino != mnt_st.st_ino) + { + unlink( path ); + symlink( mount_point, path ); + modified = TRUE; + } + } + else + { + if (unlink( path ) != -1) modified = TRUE; + } + + RtlFreeHeap( GetProcessHeap(), 0, path ); + return modified; +} + +static DEVICE_OBJECT *find_parent_device(const char *parent_device) +{ + struct disk_device_info *info; + DEVICE_OBJECT *devobj; + NTSTATUS status; + + int i; + if (!parent_device) + return harddisk0; + + for (i = 0; i < MAX_MOUNT_POINTS; ++i) + { + if (!mount_points[i].parent) + continue; + info = mount_points[i].parent->DeviceExtension; + if (!strcmp(info->dev_node, parent_device)) + { + TRACE("Found existing hard disk for %s\n", parent_device); + return mount_points[i].parent; + } + } + status = create_harddisk( hddriver, &devobj, parent_device ); + if (status) + { + ERR("Creating harddisk failed: %08x\n", status); + return harddisk0; + } + TRACE("Created parent device for %s\n", parent_device); + return devobj; +} + +/* Convert all non-alphanumeric characters except hash to underscore */ +static void make_valid(char *string) +{ + for (;*string;string++) + { + if (*string >= '0' && *string <= '9') continue; + if (*string >= 'a' && *string <= 'z') continue; + if (*string >= 'A' && *string <= 'Z') continue; + if (*string == '#' || *string == '-') continue; + if (*string == '{' || *string == '}') continue; + if (*string == '&') continue; + *string = '_'; + } +} + +const char enumstor1printf[] = "Disk&Ven_%s&Prod_%s&Rev_%s"; +const char enumstor2printf[] = "%s&0"; +const char devinstlinkprintf[] = "USBSTOR\\%s\\%s"; +const char devclassprintf[] = "##?#USBSTOR#%s#%s#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}"; +const char hardwareidprintf[] = + "USBSTOR\\Disk%s%s%s\n" /* ven, prod, rev */ + "USBSTOR\\Disk%s%s\n" /* ven, prod */ + "USBSTOR\\Disk%s\n" /* ven */ + "USBSTOR\\%s%s%s\n" /* ven, prod, rev */ + "%s%s%s\n" /* ven, prod, rev */ + "USBSTOR\\GenDisk\n" + "GenDisk\n\n"; + +BOOL add_extended_dos_device( const char *udi, const char *device, const char *parent_device, + const char *mount_point, const char *type, const char *vendor, + const char *product, const char *fw_version, const char *serial ) +{ + char msvendor[] = "________", msproduct[] = "________________"; + char *enumstor1, *enumstor2, *devinstlink, *devclass, *hardwareid, *versiondot, *newline; + int enumstor1len, enumstor2len, devinstlinklen, devclasslen, hardwareidlen; + HKEY dev1 = 0, dev2 = 0, enum1 = 0, enum2 = 0; + BOOL ret; + + if (!add_dos_device( udi, parent_device, device, mount_point, type )) + { + WARN("Could not add device %s, or device already added, skipping creationism\n", udi); + return FALSE; + } + if (!type || strcmp(type, "hd")) + { + TRACE("Only adding extra information for hard disk devices\n"); + return TRUE; + } + + /* This allocation is optimistic: the length is bigger then the actual usage */ + enumstor1len = sizeof(enumstor1printf) + strlen(vendor) + strlen(product) + strlen(fw_version); + enumstor2len = sizeof(enumstor2printf) + strlen(serial); + devinstlinklen = sizeof(devinstlinkprintf) + enumstor1len + enumstor2len; + devclasslen = sizeof(devclassprintf) + enumstor1len + enumstor2len; + hardwareidlen = sizeof(hardwareidprintf) + 5 * sizeof(msvendor) + 4 * sizeof(msproduct) - 9 + 3 * strlen(fw_version); + + enumstor1 = RtlAllocateHeap(GetProcessHeap(), 0, enumstor1len); + enumstor2 = RtlAllocateHeap(GetProcessHeap(), 0, enumstor2len); + devinstlink = RtlAllocateHeap(GetProcessHeap(), 0, devinstlinklen); + devclass = RtlAllocateHeap(GetProcessHeap(), 0, devclasslen); + hardwareid = RtlAllocateHeap(GetProcessHeap(), 0, hardwareidlen); + + if (!enumstor1 || !enumstor2 || !devinstlink || !devclass || !hardwareid) + goto out_heap; + + sprintf(enumstor1, enumstor1printf, vendor, product, fw_version); + sprintf(enumstor2, enumstor2printf, serial); + sprintf(devinstlink, devinstlinkprintf, enumstor1, enumstor2); + sprintf(devclass, devclassprintf, enumstor1, enumstor2); + + versiondot = strchr(fw_version, '.'); + if (versiondot) + *versiondot = 0; + + lstrcpynA(msvendor, vendor, sizeof(msvendor)-1); + make_valid(msvendor); + if (strlen(msvendor) < sizeof(msvendor)-1) + memset(msvendor + strlen(msvendor), '_', sizeof(msvendor)-1-strlen(msvendor)); + + lstrcpynA(msproduct, product, sizeof(msproduct)-1); + make_valid(msproduct); + if (strlen(msproduct) < sizeof(msproduct)-1) + memset(msproduct + strlen(msproduct), '_', sizeof(msproduct)-1-strlen(msproduct)); + + sprintf(hardwareid, hardwareidprintf, msvendor, msproduct, fw_version, msvendor, msproduct, + msvendor, msvendor, msproduct, fw_version, msvendor, msproduct, fw_version); + hardwareidlen = strlen(hardwareid)+1; + + if (versiondot) + *versiondot = '.'; + + /* These keys can not be created by setupapi, because that would require an installation using a .inf (as far as I can see) */ + ret = !RegCreateKeyExA(usbenum_key, enumstor1, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &enum1, NULL); + if (ret) + ret = !RegCreateKeyExA(enum1, enumstor2, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &enum2, NULL); + if (ret) + ret = !RegCreateKeyExA(devclass_key, devclass, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &dev1, NULL); + if (ret) + ret = !RegCreateKeyExA(dev1, "#", 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &dev2, NULL); + if (ret) + { + static const WCHAR caps[] = {'C','a','p','a','b','i','l','i','t','i','e','s',0}; + static const WCHAR class[] = {'C','l','a','s','s',0}; + static const WCHAR disk[] = {'D','i','s','k','D','r','i','v','e',0}; + static const WCHAR cguid[] = {'C','l','a','s','s','G','u','i','d',0}; + static const WCHAR diskguid[] = {'{','4','D','3','6','E','9','6','7','-','E','3','2','5','-','1','1','C','E','-', + 'B','F','C','1','-','0','8','0','0','2','B','E','1','0','3','1','8','}',0}; + DEVICE_OBJECT *parent; + UNICODE_STRING symlink; + NTSTATUS res; + DWORD capsvalue = 0x10; + + RegSetValueExW(enum2, caps, 0, REG_DWORD, (BYTE*)&capsvalue, sizeof(capsvalue)); + RegSetValueExW(enum2, class, 0, REG_SZ, (BYTE*)&disk, sizeof(disk)); + RegSetValueExW(enum2, cguid, 0, REG_SZ, (BYTE*)&diskguid, sizeof(diskguid)); + /* Convert all newlines to \0's, for the REG_SZ_MULTI type */ + while ((newline = strrchr(hardwareid, '\n'))) + *newline = 0; + RegSetValueExA(enum2, "HardwareId", 0, REG_MULTI_SZ, (BYTE*)hardwareid, hardwareidlen); + RegSetValueExA(dev1, "DeviceInstance", 0, REG_SZ, (BYTE*)devinstlink, strlen(devinstlink)+1); + devclass[0] = devclass[1] = devclass[3] = '\\'; + RegSetValueExA(dev2, "SymbolicLink", 0, REG_SZ, (BYTE*)devclass, strlen(devclass)+1); + devclass[1] = '?'; + symlink.Length = symlink.MaximumLength = (strlen(devclass)+1)*sizeof(WCHAR); + symlink.Buffer = RtlAllocateHeap(GetProcessHeap(), 0, symlink.Length); + MultiByteToWideChar(CP_ACP, 0, devclass, -1, symlink.Buffer, symlink.Length); + symlink.Length -= sizeof(WCHAR); + parent = find_parent_device( parent_device ); + res = IoCreateSymbolicLink(&symlink, get_device_name(parent)); + TRACE("RES: %08x (%s->%s)\n", res, debugstr_wn(symlink.Buffer, symlink.Length/sizeof(WCHAR)), debugstr_w(get_device_name(parent)->Buffer)); + RtlFreeHeap(GetProcessHeap(), 0, symlink.Buffer); + } + + RegCloseKey(dev2); + RegCloseKey(dev1); + RegCloseKey(enum2); + RegCloseKey(enum1); + +out_heap: + RtlFreeHeap(GetProcessHeap(), 0, enumstor1); + RtlFreeHeap(GetProcessHeap(), 0, enumstor2); + RtlFreeHeap(GetProcessHeap(), 0, devinstlink); + RtlFreeHeap(GetProcessHeap(), 0, devclass); + RtlFreeHeap(GetProcessHeap(), 0, hardwareid); + + return TRUE; +} + +BOOL add_dos_device( const char *udi, const char *parent_device, const char *device, + const char *mount_point, const char *type ) +{ + DEVICE_OBJECT *parent; + int drive; + char name[3] = "a:"; + struct disk_device_info *info; + + TRACE("add_dos_device(%s, %s, %s, %s, %s)\n", udi, parent_device, device, mount_point, type); + + drive = add_drive( device, type ); + if (drive == -1) + return FALSE; + + if (mount_points[drive * 2].device) + { + TRACE("Already added %s!\n", device); + return FALSE; + } + + set_mount_point( drive, mount_point ); + + name[0] += drive; + if (!type || strcmp( type, "cdrom" )) type = "hd"; + RegSetValueExA( drive_key, name, 0, REG_SZ, (const BYTE *)type, strlen(type) + 1 ); + + parent = find_parent_device( parent_device ); + add_mount_point( parent, hddriver, drive, udi, strlen(udi) ); + + info = mount_points[drive * 2].device->DeviceExtension; + info->dev_node = RtlAllocateHeap(GetProcessHeap(), 0, strlen(device)+1); + strcpy(info->dev_node, device); + + /* Check to see if there already is a parent with parent_device */ + + TRACE( "added device %c: udi %s for %s on %s type %s\n", + 'a' + drive, wine_dbgstr_a(udi), wine_dbgstr_a(device), + wine_dbgstr_a(mount_point), wine_dbgstr_a(type) ); + + send_notify( drive, DBT_DEVICEARRIVAL ); + return TRUE; +} + +static void delete_disk_device(DEVICE_OBJECT *device) +{ + struct disk_device_info info; + info = *(struct disk_device_info*)device->DeviceExtension; + + IoDeleteDevice(device); + RtlFreeHeap(GetProcessHeap(), 0, info.dev_node); + RtlFreeHeap(GetProcessHeap(), 0, info.name.Buffer); +} + +static void free_mount_point(struct mount_point *drive) +{ + RegDeleteValueW( mount_key, drive->link.Buffer ); + RtlFreeUnicodeString(&drive->link); + RtlFreeHeap(GetProcessHeap(), 0, drive->id); + memset(drive, 0, sizeof(*drive)); +} + +static void delete_mount_point(struct mount_point *drive) +{ + int i; + DEVICE_OBJECT *parent = drive->parent; + + delete_disk_device(drive->device); + free_mount_point(drive); + free_mount_point(drive+1); + + if (parent != harddisk0) + { + /* Scan for drives that still have the parent */ + for (i = 0; i < MAX_MOUNT_POINTS; ++i) + { + if (mount_points[i].parent == parent) + break; + } + if (i == MAX_MOUNT_POINTS) + { + TRACE("Deleting parent device for %s\n", ((struct disk_device_info*)parent->DeviceExtension)->dev_node); + delete_disk_device(parent); + } + } +} + +BOOL remove_dos_device( const char *udi ) +{ + int i; + TRACE("remove_dos_device(%s)\n", udi); + + for (i = 0; i < MAX_DOS_DRIVES; ++i) + if (mount_points[i*2].device) + { + struct mount_point *drive = mount_points + (i*2); + if (!strcmp(udi, drive->id)) + { + /* clear the registry key too */ + WCHAR name[3] = {'a',':',0}; + if (!set_mount_point( i, "" )) break; + + delete_mount_point(drive); + + name[0] += i; + TRACE("Deleting %s\n", debugstr_w(name)); + RegDeleteValueW( drive_key, name ); + + send_notify( i, DBT_DEVICEREMOVECOMPLETE ); + } + } + + return FALSE; +} diff --git a/dlls/mountmgr.sys/mountmgr.h b/dlls/mountmgr.sys/mountmgr.h new file mode 100644 index 0000000..88ab9b2 --- /dev/null +++ b/dlls/mountmgr.sys/mountmgr.h @@ -0,0 +1,29 @@ +/* + * Mountmanager private header + * + * Copyright 2008 Maarten Lankhorst + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +void initialize_hal(void); +void initialize_diskarbitration(void); +BOOL add_dos_device( const char *udi, const char *device, const char *parent_device, + const char *mount_point, const char *type ); +BOOL add_extended_dos_device( const char *udi, const char *device, const char *parent_device, + const char *mount_point, const char *type, const char *vendor, + const char *product, const char *fw_version, const char *serial ); +BOOL remove_dos_device( const char *udi ); + diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c index ce94917..f498d22 100644 --- a/dlls/ntdll/file.c +++ b/dlls/ntdll/file.c @@ -79,6 +79,7 @@ #include "winternl.h" #include "winioctl.h" #include "ddk/ntddser.h" +#include "ntddscsi.h" WINE_DEFAULT_DEBUG_CHANNEL(ntdll); @@ -1222,6 +1223,168 @@ static NTSTATUS server_ioctl_file( HANDLE handle, HANDLE event, } +static NTSTATUS SERVER_ScsiPassThroughDirect(HANDLE handle, HANDLE event, + PIO_APC_ROUTINE apc, PVOID apc_context, + PIO_STATUS_BLOCK io, ULONG code, + char *in_buffer, ULONG in_size, + char *out_buffer, ULONG out_size) +{ + PSCSI_PASS_THROUGH_DIRECT pPacket = (PSCSI_PASS_THROUGH_DIRECT)in_buffer; + PSCSI_PASS_THROUGH_DIRECT sentpacket; + NTSTATUS status; + DWORD size; + + /* Can't put this in the structure, it needs to stay unmodified */ + DWORD SenseInfoOffset, DataBufferOffset; + + if (!in_buffer || !out_buffer) + return STATUS_INVALID_PARAMETER; + + if (in_size < sizeof(SCSI_PASS_THROUGH_DIRECT) || out_size < sizeof(SCSI_PASS_THROUGH_DIRECT)) + return STATUS_BUFFER_TOO_SMALL; + + if (pPacket->Length < sizeof(SCSI_PASS_THROUGH_DIRECT)) + return STATUS_BUFFER_TOO_SMALL; + + if (pPacket->CdbLength > 16) + return STATUS_INVALID_PARAMETER; + + if (pPacket->SenseInfoLength > 48) + return STATUS_INVALID_PARAMETER; + + if (pPacket->DataTransferLength > 0 && !pPacket->DataBuffer) + return STATUS_INVALID_PARAMETER; + + if (pPacket->DataIn != SCSI_IOCTL_DATA_IN && + pPacket->DataIn != SCSI_IOCTL_DATA_OUT && + pPacket->DataIn != SCSI_IOCTL_DATA_UNSPECIFIED) + return STATUS_INVALID_PARAMETER; + + size = sizeof(SCSI_PASS_THROUGH_DIRECT); + size += pPacket->SenseInfoLength; + if (pPacket->DataIn != SCSI_IOCTL_DATA_UNSPECIFIED) + size += pPacket->DataTransferLength; + + in_buffer = RtlAllocateHeap(GetProcessHeap(), 0, size); + sentpacket = (PSCSI_PASS_THROUGH_DIRECT)in_buffer; + if (!in_buffer) + return STATUS_NO_MEMORY; + + memcpy(in_buffer, pPacket, sizeof(SCSI_PASS_THROUGH_DIRECT)); + SenseInfoOffset = sizeof(*pPacket); + DataBufferOffset = SenseInfoOffset + sentpacket->SenseInfoLength; + + /* Append sense data */ + memcpy(in_buffer + SenseInfoOffset, (char*)pPacket + pPacket->SenseInfoOffset, pPacket->SenseInfoLength); + + /* Append memory */ + if (pPacket->DataIn == SCSI_IOCTL_DATA_OUT) + { + memcpy(in_buffer + DataBufferOffset, pPacket->DataBuffer, pPacket->DataTransferLength); + } + + /* Do ioctl, except locally */ + status = server_ioctl_file( handle, event, apc, apc_context, io, code, + in_buffer, size, in_buffer, size ); + + if (FAILED(status)) + { + WARN("Failed with result %08x\n", status); + goto out; + } + if (pPacket->DataIn == SCSI_IOCTL_DATA_IN) + { + memcpy(pPacket->DataBuffer, in_buffer + DataBufferOffset, sentpacket->DataTransferLength); + } + + memcpy((char *)pPacket + pPacket->SenseInfoOffset, in_buffer + SenseInfoOffset, sentpacket->SenseInfoLength); + memcpy(out_buffer, sentpacket, sizeof(SCSI_PASS_THROUGH_DIRECT)); + +out: + RtlFreeHeap(GetProcessHeap(), 0, in_buffer); + return status; +} + +static NTSTATUS SERVER_ScsiPassThrough(HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, PVOID apc_context, PIO_STATUS_BLOCK io, + ULONG code, char *in_buffer, ULONG in_size, char *out_buffer, ULONG out_size) +{ + PSCSI_PASS_THROUGH pPacket = (PSCSI_PASS_THROUGH)in_buffer; + PSCSI_PASS_THROUGH sentpacket; + NTSTATUS status; + DWORD size; + + /* Can't put this in the structure, it needs to stay unmodified */ + DWORD SenseInfoOffset, DataBufferOffset; + + if (!in_buffer || !out_buffer) + return STATUS_INVALID_PARAMETER; + + if (in_size < sizeof(SCSI_PASS_THROUGH) || out_size < sizeof(SCSI_PASS_THROUGH)) + return STATUS_BUFFER_TOO_SMALL; + + if (pPacket->Length < sizeof(SCSI_PASS_THROUGH)) + return STATUS_BUFFER_TOO_SMALL; + + if (pPacket->CdbLength > 16) + return STATUS_INVALID_PARAMETER; + + if (pPacket->SenseInfoLength > 48) + return STATUS_INVALID_PARAMETER; + + if (pPacket->DataTransferLength > 0 && !pPacket->DataBufferOffset) + return STATUS_INVALID_PARAMETER; + + if (pPacket->DataIn != SCSI_IOCTL_DATA_IN && + pPacket->DataIn != SCSI_IOCTL_DATA_OUT && + pPacket->DataIn != SCSI_IOCTL_DATA_UNSPECIFIED) + return STATUS_INVALID_PARAMETER; + + size = sizeof(SCSI_PASS_THROUGH); + size += pPacket->SenseInfoLength; + if (pPacket->DataIn != SCSI_IOCTL_DATA_UNSPECIFIED) + size += pPacket->DataTransferLength; + + in_buffer = RtlAllocateHeap(GetProcessHeap(), 0, size); + sentpacket = (PSCSI_PASS_THROUGH)in_buffer; + if (!in_buffer) + return STATUS_NO_MEMORY; + + memcpy(in_buffer, pPacket, sizeof(SCSI_PASS_THROUGH)); + SenseInfoOffset = sizeof(*pPacket); + DataBufferOffset = SenseInfoOffset + sentpacket->SenseInfoLength; + + /* Append sense data */ + memcpy(in_buffer + SenseInfoOffset, (char*)pPacket + pPacket->SenseInfoOffset, pPacket->SenseInfoLength); + + /* Append memory */ + if (pPacket->DataIn == SCSI_IOCTL_DATA_OUT) + { + memcpy(in_buffer + DataBufferOffset, (char *)pPacket + pPacket->DataBufferOffset, pPacket->DataTransferLength); + } + + /* Do ioctl, except locally */ + status = server_ioctl_file( handle, event, apc, apc_context, io, code, + in_buffer, size, in_buffer, size ); + + if (FAILED(status)) + { + WARN("Failed with result %08x\n", status); + goto out; + } + if (pPacket->DataIn == SCSI_IOCTL_DATA_IN) + { + memcpy((char *)pPacket + pPacket->DataBufferOffset, in_buffer + DataBufferOffset, sentpacket->DataTransferLength); + } + + memcpy((char *)pPacket + pPacket->SenseInfoOffset, in_buffer + SenseInfoOffset, sentpacket->SenseInfoLength); + memcpy(out_buffer, sentpacket, sizeof(SCSI_PASS_THROUGH)); + +out: + RtlFreeHeap(GetProcessHeap(), 0, in_buffer); + return status; +} + + /************************************************************************** * NtDeviceIoControlFile [NTDLL.@] * ZwDeviceIoControlFile [NTDLL.@] @@ -1278,8 +1441,21 @@ NTSTATUS WINAPI NtDeviceIoControlFile(HANDLE handle, HANDLE event, } if (status == STATUS_NOT_SUPPORTED || status == STATUS_BAD_DEVICE_TYPE) - status = server_ioctl_file( handle, event, apc, apc_context, io, code, - in_buffer, in_size, out_buffer, out_size ); + { + if (code == IOCTL_SCSI_PASS_THROUGH_DIRECT) + { + status = SERVER_ScsiPassThroughDirect( handle, event, apc, apc_context, io, code, + in_buffer, in_size, out_buffer, out_size ); + } + else if (code == IOCTL_SCSI_PASS_THROUGH) + { + status = SERVER_ScsiPassThrough( handle, event, apc, apc_context, io, code, + in_buffer, in_size, out_buffer, out_size ); + } + else + status = server_ioctl_file( handle, event, apc, apc_context, io, code, + in_buffer, in_size, out_buffer, out_size ); + } if (status != STATUS_PENDING) io->u.Status = status; return status; diff --git a/programs/explorer/Makefile.in b/programs/explorer/Makefile.in index 9498a0f..4854eda 100644 --- a/programs/explorer/Makefile.in +++ b/programs/explorer/Makefile.in @@ -6,15 +6,10 @@ MODULE = explorer.exe APPMODE = -mwindows IMPORTS = rpcrt4 user32 gdi32 advapi32 kernel32 ntdll DELAYIMPORTS = comctl32 -EXTRADEFS = @HALINCL@ -EXTRALIBS = @DISKARBITRATIONLIB@ C_SRCS = \ desktop.c \ - device.c \ - diskarb.c \ explorer.c \ - hal.c \ systray.c @MAKE_PROG_RULES@ diff --git a/programs/explorer/desktop.c b/programs/explorer/desktop.c index ddca914..40b2f8e 100644 --- a/programs/explorer/desktop.c +++ b/programs/explorer/desktop.c @@ -234,8 +234,6 @@ void manage_desktop( char *arg ) SystemParametersInfoA( SPI_SETDESKPATTERN, -1, NULL, FALSE ); SetDeskWallPaper( (LPSTR)-1 ); initialize_display_settings( hwnd ); - initialize_diskarbitration(); - initialize_hal(); initialize_systray(); } else diff --git a/programs/explorer/device.c b/programs/explorer/device.c deleted file mode 100644 index 922f4d0..0000000 --- a/programs/explorer/device.c +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Dynamic devices support - * - * Copyright 2006 Alexandre Julliard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include "config.h" -#include "wine/port.h" - -#include -#include -#include -#include -#include - -#include "windef.h" -#include "winbase.h" -#include "winreg.h" -#include "winuser.h" -#include "dbt.h" - -#include "wine/library.h" -#include "wine/list.h" -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(explorer); - -struct dos_drive -{ - struct list entry; - char *udi; - int drive; -}; - -static struct list drives_list = LIST_INIT(drives_list); - -static char *get_dosdevices_path(void) -{ - const char *config_dir = wine_get_config_dir(); - size_t len = strlen(config_dir) + sizeof("/dosdevices/a::"); - char *path = HeapAlloc( GetProcessHeap(), 0, len ); - if (path) - { - strcpy( path, config_dir ); - strcat( path, "/dosdevices/a::" ); - } - return path; -} - -/* send notification about a change to a given drive */ -static void send_notify( int drive, int code ) -{ - DWORD_PTR result; - DEV_BROADCAST_VOLUME info; - - info.dbcv_size = sizeof(info); - info.dbcv_devicetype = DBT_DEVTYP_VOLUME; - info.dbcv_reserved = 0; - info.dbcv_unitmask = 1 << drive; - info.dbcv_flags = DBTF_MEDIA; - SendMessageTimeoutW( HWND_BROADCAST, WM_DEVICECHANGE, code, (LPARAM)&info, - SMTO_ABORTIFHUNG, 0, &result ); -} - -static inline int is_valid_device( struct stat *st ) -{ -#if defined(linux) || defined(__sun__) - return S_ISBLK( st->st_mode ); -#else - /* disks are char devices on *BSD */ - return S_ISCHR( st->st_mode ); -#endif -} - -/* find or create a DOS drive for the corresponding device */ -static int add_drive( const char *device, const char *type ) -{ - char *path, *p; - char in_use[26]; - struct stat dev_st, drive_st; - int drive, first, last, avail = 0; - - if (stat( device, &dev_st ) == -1 || !is_valid_device( &dev_st )) return -1; - - if (!(path = get_dosdevices_path())) return -1; - p = path + strlen(path) - 3; - - memset( in_use, 0, sizeof(in_use) ); - - first = 2; - last = 26; - if (type && !strcmp( type, "floppy" )) - { - first = 0; - last = 2; - } - - while (avail != -1) - { - avail = -1; - for (drive = first; drive < last; drive++) - { - if (in_use[drive]) continue; /* already checked */ - *p = 'a' + drive; - if (stat( path, &drive_st ) == -1) - { - if (lstat( path, &drive_st ) == -1 && errno == ENOENT) /* this is a candidate */ - { - if (avail == -1) - { - p[2] = 0; - /* if mount point symlink doesn't exist either, it's available */ - if (lstat( path, &drive_st ) == -1 && errno == ENOENT) avail = drive; - p[2] = ':'; - } - } - else in_use[drive] = 1; - } - else - { - in_use[drive] = 1; - if (!is_valid_device( &drive_st )) continue; - if (dev_st.st_rdev == drive_st.st_rdev) goto done; - } - } - if (avail != -1) - { - /* try to use the one we found */ - drive = avail; - *p = 'a' + drive; - if (symlink( device, path ) != -1) goto done; - /* failed, retry the search */ - } - } - drive = -1; - -done: - HeapFree( GetProcessHeap(), 0, path ); - return drive; -} - -static BOOL set_mount_point( struct dos_drive *drive, const char *mount_point ) -{ - char *path, *p; - struct stat path_st, mnt_st; - BOOL modified = FALSE; - - if (drive->drive == -1) return FALSE; - if (!(path = get_dosdevices_path())) return FALSE; - p = path + strlen(path) - 3; - *p = 'a' + drive->drive; - p[2] = 0; - - if (mount_point[0]) - { - /* try to avoid unlinking if already set correctly */ - if (stat( path, &path_st ) == -1 || stat( mount_point, &mnt_st ) == -1 || - path_st.st_dev != mnt_st.st_dev || path_st.st_ino != mnt_st.st_ino) - { - unlink( path ); - symlink( mount_point, path ); - modified = TRUE; - } - } - else - { - if (unlink( path ) != -1) modified = TRUE; - } - - HeapFree( GetProcessHeap(), 0, path ); - return modified; -} - -BOOL add_dos_device( const char *udi, const char *device, - const char *mount_point, const char *type ) -{ - struct dos_drive *drive; - - /* first check if it already exists */ - LIST_FOR_EACH_ENTRY( drive, &drives_list, struct dos_drive, entry ) - { - if (!strcmp( udi, drive->udi )) goto found; - } - - if (!(drive = HeapAlloc( GetProcessHeap(), 0, sizeof(*drive) ))) return FALSE; - if (!(drive->udi = HeapAlloc( GetProcessHeap(), 0, strlen(udi)+1 ))) - { - HeapFree( GetProcessHeap(), 0, drive ); - return FALSE; - } - strcpy( drive->udi, udi ); - list_add_tail( &drives_list, &drive->entry ); - -found: - drive->drive = add_drive( device, type ); - if (drive->drive != -1) - { - HKEY hkey; - - set_mount_point( drive, mount_point ); - - WINE_TRACE( "added device %c: udi %s for %s on %s type %s\n", - 'a' + drive->drive, wine_dbgstr_a(udi), wine_dbgstr_a(device), - wine_dbgstr_a(mount_point), wine_dbgstr_a(type) ); - - /* hack: force the drive type in the registry */ - if (!RegCreateKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Drives", &hkey )) - { - char name[3] = "a:"; - name[0] += drive->drive; - if (!type || strcmp( type, "cdrom" )) type = "floppy"; /* FIXME: default to floppy */ - RegSetValueExA( hkey, name, 0, REG_SZ, (const BYTE *)type, strlen(type) + 1 ); - RegCloseKey( hkey ); - } - - send_notify( drive->drive, DBT_DEVICEARRIVAL ); - } - return TRUE; -} - -BOOL remove_dos_device( const char *udi ) -{ - HKEY hkey; - struct dos_drive *drive; - - LIST_FOR_EACH_ENTRY( drive, &drives_list, struct dos_drive, entry ) - { - if (strcmp( udi, drive->udi )) continue; - - if (drive->drive != -1) - { - BOOL modified = set_mount_point( drive, "" ); - - /* clear the registry key too */ - if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Drives", &hkey )) - { - char name[3] = "a:"; - name[0] += drive->drive; - RegDeleteValueA( hkey, name ); - RegCloseKey( hkey ); - } - - if (modified) send_notify( drive->drive, DBT_DEVICEREMOVECOMPLETE ); - } - - list_remove( &drive->entry ); - HeapFree( GetProcessHeap(), 0, drive->udi ); - HeapFree( GetProcessHeap(), 0, drive ); - return TRUE; - } - return FALSE; -} diff --git a/programs/explorer/diskarb.c b/programs/explorer/diskarb.c deleted file mode 100644 index 08a8097..0000000 --- a/programs/explorer/diskarb.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Devices support using the MacOS Disk Arbitration library. - * - * Copyright 2006 Alexandre Julliard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include "config.h" -#include "wine/port.h" - -#include -#include -#include -#include -#include - -#include "windef.h" -#include "winbase.h" -#include "winreg.h" -#include "winuser.h" - -#include "wine/debug.h" -#include "explorer_private.h" - -WINE_DEFAULT_DEBUG_CHANNEL(explorer); - -#ifdef HAVE_DISKARBITRATION_DISKARBITRATION_H - -#include - -static void appeared_callback( DADiskRef disk, void *context ) -{ - CFDictionaryRef dict = DADiskCopyDescription( disk ); - const void *ref; - char device[64]; - char mount_point[PATH_MAX]; - const char *type = NULL; - - if (!dict) return; - - /* ignore non-removable devices */ - if (!(ref = CFDictionaryGetValue( dict, CFSTR("DAMediaRemovable") )) || - !CFBooleanGetValue( ref )) goto done; - - /* get device name */ - if (!(ref = CFDictionaryGetValue( dict, CFSTR("DAMediaBSDName") ))) goto done; - strcpy( device, "/dev/r" ); - CFStringGetCString( ref, device + 6, sizeof(device) - 6, kCFStringEncodingASCII ); - - if ((ref = CFDictionaryGetValue( dict, CFSTR("DAVolumePath") ))) - CFURLGetFileSystemRepresentation( ref, true, (UInt8 *)mount_point, sizeof(mount_point) ); - else - mount_point[0] = 0; - - if ((ref = CFDictionaryGetValue( dict, CFSTR("DAVolumeKind") ))) - { - if (!CFStringCompare( ref, CFSTR("cd9660"), 0 ) || - !CFStringCompare( ref, CFSTR("udf"), 0 )) - type = "cdrom"; - } - - WINE_TRACE( "got mount notification for '%s' on '%s'\n", device, mount_point ); - - add_dos_device( device, device, mount_point, type ); -done: - CFRelease( dict ); -} - -static void changed_callback( DADiskRef disk, CFArrayRef keys, void *context ) -{ - appeared_callback( disk, context ); -} - -static void disappeared_callback( DADiskRef disk, void *context ) -{ - CFDictionaryRef dict = DADiskCopyDescription( disk ); - const void *ref; - char device[100]; - - if (!dict) return; - - /* ignore non-removable devices */ - if (!(ref = CFDictionaryGetValue( dict, CFSTR("DAMediaRemovable") )) || - !CFBooleanGetValue( ref )) goto done; - - /* get device name */ - if (!(ref = CFDictionaryGetValue( dict, CFSTR("DAMediaBSDName") ))) goto done; - strcpy( device, "/dev/r" ); - CFStringGetCString( ref, device + 6, sizeof(device) - 6, kCFStringEncodingASCII ); - - WINE_TRACE( "got unmount notification for '%s'\n", device ); - - remove_dos_device( device ); -done: - CFRelease( dict ); -} - -static DWORD WINAPI runloop_thread( void *arg ) -{ - DASessionRef session = DASessionCreate( NULL ); - - if (!session) return 1; - - DASessionScheduleWithRunLoop( session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode ); - DARegisterDiskAppearedCallback( session, kDADiskDescriptionMatchVolumeMountable, - appeared_callback, NULL ); - DARegisterDiskDisappearedCallback( session, kDADiskDescriptionMatchVolumeMountable, - disappeared_callback, NULL ); - DARegisterDiskDescriptionChangedCallback( session, kDADiskDescriptionMatchVolumeMountable, - kDADiskDescriptionWatchVolumePath, changed_callback, NULL ); - CFRunLoopRun(); - DASessionUnscheduleFromRunLoop( session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode ); - CFRelease( session ); - return 0; -} - -void initialize_diskarbitration(void) -{ - HANDLE handle; - - if (!(handle = CreateThread( NULL, 0, runloop_thread, NULL, 0, NULL ))) return; - CloseHandle( handle ); -} - -#else /* HAVE_DISKARBITRATION_DISKARBITRATION_H */ - -void initialize_diskarbitration(void) -{ - WINE_TRACE( "Skipping, Disk Arbitration support not compiled in\n" ); -} - -#endif /* HAVE_DISKARBITRATION_DISKARBITRATION_H */ diff --git a/programs/explorer/hal.c b/programs/explorer/hal.c deleted file mode 100644 index df31c6b..0000000 --- a/programs/explorer/hal.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * HAL devices support - * - * Copyright 2006 Alexandre Julliard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include "config.h" -#include "wine/port.h" - -#include -#include -#include -#include -#include - -#include "windef.h" -#include "winbase.h" -#include "winreg.h" -#include "winuser.h" -#include "excpt.h" - -#include "wine/library.h" -#include "wine/exception.h" -#include "wine/debug.h" -#include "explorer_private.h" - -WINE_DEFAULT_DEBUG_CHANNEL(explorer); - -#ifdef SONAME_LIBHAL - -#include -#include - -#define DBUS_FUNCS \ - DO_FUNC(dbus_bus_get); \ - DO_FUNC(dbus_connection_close); \ - DO_FUNC(dbus_connection_read_write_dispatch); \ - DO_FUNC(dbus_error_init); \ - DO_FUNC(dbus_error_free); \ - DO_FUNC(dbus_error_is_set) - -#define HAL_FUNCS \ - DO_FUNC(libhal_ctx_free); \ - DO_FUNC(libhal_ctx_init); \ - DO_FUNC(libhal_ctx_new); \ - DO_FUNC(libhal_ctx_set_dbus_connection); \ - DO_FUNC(libhal_ctx_set_device_added); \ - DO_FUNC(libhal_ctx_set_device_property_modified); \ - DO_FUNC(libhal_ctx_set_device_removed); \ - DO_FUNC(libhal_ctx_shutdown); \ - DO_FUNC(libhal_device_get_property_bool); \ - DO_FUNC(libhal_device_get_property_string); \ - DO_FUNC(libhal_device_add_property_watch); \ - DO_FUNC(libhal_device_remove_property_watch); \ - DO_FUNC(libhal_free_string); \ - DO_FUNC(libhal_free_string_array); \ - DO_FUNC(libhal_get_all_devices) - -#define DO_FUNC(f) static typeof(f) * p_##f -DBUS_FUNCS; -HAL_FUNCS; -#undef DO_FUNC - -static BOOL load_functions(void) -{ - void *hal_handle; - char error[128]; - - /* Load libhal with RTLD_GLOBAL so that the dbus symbols are available. - * We can't load libdbus directly since libhal may have been built against a - * different version but with the same soname. Binary compatibility is for wimps. */ - - if (!(hal_handle = wine_dlopen(SONAME_LIBHAL, RTLD_NOW|RTLD_GLOBAL, error, sizeof(error)))) - goto failed; - -#define DO_FUNC(f) if (!(p_##f = wine_dlsym( RTLD_DEFAULT, #f, error, sizeof(error) ))) goto failed - DBUS_FUNCS; -#undef DO_FUNC - -#define DO_FUNC(f) if (!(p_##f = wine_dlsym( hal_handle, #f, error, sizeof(error) ))) goto failed - HAL_FUNCS; -#undef DO_FUNC - - return TRUE; - -failed: - WINE_WARN( "failed to load HAL support: %s\n", error ); - return FALSE; -} - -static LONG WINAPI assert_fault(EXCEPTION_POINTERS *eptr) -{ - if (eptr->ExceptionRecord->ExceptionCode == EXCEPTION_WINE_ASSERTION) - return EXCEPTION_EXECUTE_HANDLER; - return EXCEPTION_CONTINUE_SEARCH; -} - -/* HAL callback for new device */ -static void new_device( LibHalContext *ctx, const char *udi ) -{ - DBusError error; - char *parent = NULL; - char *mount_point = NULL; - char *device = NULL; - char *type = NULL; - - p_dbus_error_init( &error ); - - if (!(device = p_libhal_device_get_property_string( ctx, udi, "block.device", &error ))) - goto done; - - if (!(mount_point = p_libhal_device_get_property_string( ctx, udi, "volume.mount_point", &error ))) - goto done; - - if (!(parent = p_libhal_device_get_property_string( ctx, udi, "info.parent", &error ))) - goto done; - - if (!p_libhal_device_get_property_bool( ctx, parent, "storage.removable", &error )) - goto done; - - if (!(type = p_libhal_device_get_property_string( ctx, parent, "storage.drive_type", &error ))) - p_dbus_error_free( &error ); /* ignore error */ - - add_dos_device( udi, device, mount_point, type ); - - /* add property watch for mount point */ - p_libhal_device_add_property_watch( ctx, udi, &error ); - -done: - if (type) p_libhal_free_string( type ); - if (parent) p_libhal_free_string( parent ); - if (device) p_libhal_free_string( device ); - if (mount_point) p_libhal_free_string( mount_point ); - p_dbus_error_free( &error ); -} - -/* HAL callback for removed device */ -static void removed_device( LibHalContext *ctx, const char *udi ) -{ - DBusError error; - - WINE_TRACE( "removed %s\n", wine_dbgstr_a(udi) ); - - if (remove_dos_device( udi )) - { - p_dbus_error_init( &error ); - p_libhal_device_remove_property_watch( ctx, udi, &error ); - p_dbus_error_free( &error ); - } -} - -/* HAL callback for property changes */ -static void property_modified (LibHalContext *ctx, const char *udi, - const char *key, dbus_bool_t is_removed, dbus_bool_t is_added) -{ - WINE_TRACE( "udi %s key %s %s\n", wine_dbgstr_a(udi), wine_dbgstr_a(key), - is_added ? "added" : is_removed ? "removed" : "modified" ); - - if (!strcmp( key, "volume.mount_point" )) new_device( ctx, udi ); -} - - -static DWORD WINAPI hal_thread( void *arg ) -{ - DBusError error; - DBusConnection *dbc; - LibHalContext *ctx; - int i, num; - char **list; - - if (!(ctx = p_libhal_ctx_new())) return 1; - - p_dbus_error_init( &error ); - if (!(dbc = p_dbus_bus_get( DBUS_BUS_SYSTEM, &error ))) - { - WINE_WARN( "failed to get system dbus connection: %s\n", error.message ); - p_dbus_error_free( &error ); - return 1; - } - - p_libhal_ctx_set_dbus_connection( ctx, dbc ); - p_libhal_ctx_set_device_added( ctx, new_device ); - p_libhal_ctx_set_device_removed( ctx, removed_device ); - p_libhal_ctx_set_device_property_modified( ctx, property_modified ); - - if (!p_libhal_ctx_init( ctx, &error )) - { - WINE_WARN( "HAL context init failed: %s\n", error.message ); - p_dbus_error_free( &error ); - return 1; - } - - /* retrieve all existing devices */ - if (!(list = p_libhal_get_all_devices( ctx, &num, &error ))) p_dbus_error_free( &error ); - else - { - for (i = 0; i < num; i++) new_device( ctx, list[i] ); - p_libhal_free_string_array( list ); - } - - __TRY - { - while (p_dbus_connection_read_write_dispatch( dbc, -1 )) /* nothing */ ; - } - __EXCEPT( assert_fault ) - { - WINE_WARN( "dbus assertion failure, disabling HAL support\n" ); - return 1; - } - __ENDTRY; - - p_libhal_ctx_shutdown( ctx, &error ); - p_dbus_error_free( &error ); /* just in case */ - p_dbus_connection_close( dbc ); - p_libhal_ctx_free( ctx ); - return 0; -} - -void initialize_hal(void) -{ - HANDLE handle; - - if (!load_functions()) return; - if (!(handle = CreateThread( NULL, 0, hal_thread, NULL, 0, NULL ))) return; - CloseHandle( handle ); -} - -#else /* SONAME_LIBHAL */ - -void initialize_hal(void) -{ - WINE_TRACE( "Skipping, HAL support not compiled in\n" ); -} - -#endif /* SONAME_LIBHAL */