Allows programs to query if disk has a seek penalty.
Signed-off-by: David Koolhoven david@koolhoven-home.net --- v3: Linux side/sysfs logic is contained file. --- dlls/mountmgr.sys/Makefile.in | 1 + dlls/mountmgr.sys/device.c | 24 +++ dlls/mountmgr.sys/unix/device_seek_penalty.c | 145 +++++++++++++++++++ include/ntddstor.h | 9 +- 4 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 dlls/mountmgr.sys/unix/device_seek_penalty.c
diff --git a/dlls/mountmgr.sys/Makefile.in b/dlls/mountmgr.sys/Makefile.in index 83204e66504..61b462ddd7b 100644 --- a/dlls/mountmgr.sys/Makefile.in +++ b/dlls/mountmgr.sys/Makefile.in @@ -7,6 +7,7 @@ EXTRALIBS = $(DISKARBITRATION_LIBS) $(SYSTEMCONFIGURATION_LIBS) $(CORESERVICES_L
C_SRCS = \ dbus.c \ + unix/device_seek_penalty.c \ device.c \ diskarb.c \ mountmgr.c diff --git a/dlls/mountmgr.sys/device.c b/dlls/mountmgr.sys/device.c index 04e8fe3c0f5..90c73b13139 100644 --- a/dlls/mountmgr.sys/device.c +++ b/dlls/mountmgr.sys/device.c @@ -1894,6 +1894,30 @@ static NTSTATUS query_property( struct disk_device *device, IRP *irp )
break; } + case StorageDeviceSeekPenaltyProperty: + { + DEVICE_SEEK_PENALTY_DESCRIPTOR *descriptor; + int ret; + + memset( irp->AssociatedIrp.SystemBuffer, 0, irpsp->Parameters.DeviceIoControl.OutputBufferLength ); + descriptor = irp->AssociatedIrp.SystemBuffer; + descriptor->Version = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR); + descriptor->Size = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR); + irp->IoStatus.Information = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR); + + ret = device_seek_penalty(device->unix_mount); + if (ret < 0) { + FIXME( "Unsupported property StorageDeviceSeekPenalty\n" ); + status = STATUS_NOT_SUPPORTED; + } else if (ret == 0) { + descriptor->IncursSeekPenalty = FALSE; + status = STATUS_SUCCESS; + } else if (ret == 1) { + descriptor->IncursSeekPenalty = TRUE; + status = STATUS_SUCCESS; + } + break; + } default: FIXME( "Unsupported property %#x\n", query->PropertyId ); status = STATUS_NOT_SUPPORTED; diff --git a/dlls/mountmgr.sys/unix/device_seek_penalty.c b/dlls/mountmgr.sys/unix/device_seek_penalty.c new file mode 100644 index 00000000000..eefb4d67e65 --- /dev/null +++ b/dlls/mountmgr.sys/unix/device_seek_penalty.c @@ -0,0 +1,145 @@ +/* + * System information APIs + * + * Copyright 1996-1998 Marcus Meissner + * + * 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" + +#ifdef linux +#include <assert.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> + +#define NONAMELESSUNION +#include "ntstatus.h" +#include "mountmgr.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winternl.h" +#endif +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mountmgr); + +int device_seek_penalty (char *unix_mount) +{ +#ifdef linux + FILE *fp; + char isrotapathstr[260]; + char evpath[260]; + char ueventbufstr[260]; + char isrotastrbuf[2]; + char *fgetsret = NULL; + int ret = 0; + char *rptr = NULL; + char *sptr = NULL; + char *devstr_edited = NULL; + char mountpath[260]; + char *part_dev = NULL; + int c = 0; + struct stat statbuf; + int seekpen; + + const char *home = getenv( "HOME" ); + const char *prefix = getenv( "WINEPREFIX" ); + size_t len = (prefix ? strlen(prefix) : strlen(home) + strlen("/.wine")) + sizeof("/dosdevices/com256"); + char *path = HeapAlloc( GetProcessHeap(), 0, len ); + + if (path) + { + if (prefix) strcpy( path, prefix ); + else + { + strcpy( path, home ); + strcat( path, "/.wine" ); + } + strcat( path, "/dosdevices/" ); + } + + if (!unix_mount) { + return -1; + } + + snprintf (mountpath, 260, "%s%s", path, unix_mount); + ret = stat (mountpath, &statbuf); + if (ret == -1) { + return -1; + } + HeapFree( GetProcessHeap(), 0, path ); + sprintf (evpath, "/sys/dev/block/%d:%d/uevent", major(statbuf.st_dev), minor(statbuf.st_dev)); + fp = fopen(evpath, "r"); + if (!fp) { + return -1; + } + while ((rptr = fgets (ueventbufstr, 260, fp))) { + sptr = strstr (rptr, "DEVNAME="); + if (sptr) { + sptr += strlen ("DEVNAME="); + break; + } + } + fclose (fp); + devstr_edited = sptr; + if (!devstr_edited) { + return -1; + } + /* Find first character after forwardslash '/' */ + part_dev = strrchr (devstr_edited, '/'); + if (!part_dev || (part_dev == devstr_edited)) part_dev = devstr_edited; + else part_dev++; + /* Trim off trailing digits and whitespace */ + c = strlen (devstr_edited); + c--; + while ((devstr_edited[c] >= '0' && devstr_edited[c] <= '9') + || (devstr_edited[c] == '\n' || devstr_edited[c] == '\r')) + devstr_edited[c--] = '\0'; + + ret = snprintf (isrotapathstr, 260, "/sys/block/%s/queue/rotational", part_dev); + if (ret < 1 || ret == 260) { + return -1; + } + + fp = fopen(isrotapathstr, "r"); + if (!fp) { + return -1; + } + + fgetsret = fgets(isrotastrbuf, 2, fp); + if (!fgetsret) { + return -1; + } + + fclose (fp); + + if (isrotastrbuf[0] == '1') { + seekpen = 1; + } else if (isrotastrbuf[0] == '0') { + seekpen = 0; + } else { + return -1; + } + + return seekpen; +#else + return -1; +#endif +} diff --git a/include/ntddstor.h b/include/ntddstor.h index b8c4bb73b0d..836def413fe 100644 --- a/include/ntddstor.h +++ b/include/ntddstor.h @@ -214,7 +214,8 @@ typedef enum _STORAGE_QUERY_TYPE {
typedef enum _STORAGE_PROPERTY_ID { StorageDeviceProperty = 0, - StorageAdapterProperty + StorageAdapterProperty = 1, + StorageDeviceSeekPenaltyProperty = 7, } STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID;
typedef struct _STORAGE_PROPERTY_QUERY { @@ -272,6 +273,12 @@ typedef struct _STORAGE_ADAPTER_DESCRIPTOR { USHORT BusMinorVersion; } STORAGE_ADAPTER_DESCRIPTOR, *PSTORAGE_ADAPTER_DESCRIPTOR;
+typedef struct _DEVICE_SEEK_PENALTY_DESCRIPTOR { + ULONG Version; + ULONG Size; + BOOLEAN IncursSeekPenalty; +} DEVICE_SEEK_PENALTY_DESCRIPTOR, *PDEVICE_SEEK_PENALTY_DESCRIPTOR; + #ifdef __cplusplus } #endif
I almost forgot, this clears the following Wine-Bug: Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51065
Please reject this patch. I'm getting reports it doesn't work with managed volumes. I'll work toward a v4 that covers both.
Thanks!
Hello David, thanks for the patch!
I'm curious how the application is using this information, and if it makes sense to just fake it instead, for simplicity?
I have some further comments inlined below.
On 4/27/21 4:31 PM, David Koolhoven wrote:
Allows programs to query if disk has a seek penalty.
Signed-off-by: David Koolhoven david@koolhoven-home.net
You could add a Wine-Bug tag for this; see e.g. https://source.winehq.org/patches/data/204879 for an example.
v3: Linux side/sysfs logic is contained file.
dlls/mountmgr.sys/Makefile.in | 1 + dlls/mountmgr.sys/device.c | 24 +++ dlls/mountmgr.sys/unix/device_seek_penalty.c | 145 +++++++++++++++++++ include/ntddstor.h | 9 +- 4 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 dlls/mountmgr.sys/unix/device_seek_penalty.c
diff --git a/dlls/mountmgr.sys/Makefile.in b/dlls/mountmgr.sys/Makefile.in index 83204e66504..61b462ddd7b 100644 --- a/dlls/mountmgr.sys/Makefile.in +++ b/dlls/mountmgr.sys/Makefile.in @@ -7,6 +7,7 @@ EXTRALIBS = $(DISKARBITRATION_LIBS) $(SYSTEMCONFIGURATION_LIBS) $(CORESERVICES_L
C_SRCS = \ dbus.c \
- unix/device_seek_penalty.c \ device.c \ diskarb.c \ mountmgr.c
I don't think there's a good enough reason to do this; leaving it in device.c would be better.
diff --git a/dlls/mountmgr.sys/device.c b/dlls/mountmgr.sys/device.c index 04e8fe3c0f5..90c73b13139 100644 --- a/dlls/mountmgr.sys/device.c +++ b/dlls/mountmgr.sys/device.c @@ -1894,6 +1894,30 @@ static NTSTATUS query_property( struct disk_device *device, IRP *irp )
break; }
- case StorageDeviceSeekPenaltyProperty:
- {
DEVICE_SEEK_PENALTY_DESCRIPTOR *descriptor;
- int ret;
Please use consistent four-space indentation, and avoid tabs.
memset( irp->AssociatedIrp.SystemBuffer, 0, irpsp->Parameters.DeviceIoControl.OutputBufferLength );
descriptor = irp->AssociatedIrp.SystemBuffer;
descriptor->Version = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
That seems wrong.
descriptor->Size = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
irp->IoStatus.Information = sizeof(DEVICE_SEEK_PENALTY_DESCRIPTOR);
ret = device_seek_penalty(device->unix_mount);
This function is badly named, especially considering that it's a predicate.
if (ret < 0) {
FIXME( "Unsupported property StorageDeviceSeekPenalty\n" );
status = STATUS_NOT_SUPPORTED;
} else if (ret == 0) {
descriptor->IncursSeekPenalty = FALSE;
status = STATUS_SUCCESS;
} else if (ret == 1) {
descriptor->IncursSeekPenalty = TRUE;
status = STATUS_SUCCESS;
- }
break;
- } default: FIXME( "Unsupported property %#x\n", query->PropertyId ); status = STATUS_NOT_SUPPORTED;
diff --git a/dlls/mountmgr.sys/unix/device_seek_penalty.c b/dlls/mountmgr.sys/unix/device_seek_penalty.c new file mode 100644 index 00000000000..eefb4d67e65 --- /dev/null +++ b/dlls/mountmgr.sys/unix/device_seek_penalty.c @@ -0,0 +1,145 @@ +/*
- System information APIs
- Copyright 1996-1998 Marcus Meissner
- 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"
+#ifdef linux +#include <assert.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/sysmacros.h>
+#define NONAMELESSUNION +#include "ntstatus.h" +#include "mountmgr.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winternl.h" +#endif +#include "wine/debug.h"
+WINE_DEFAULT_DEBUG_CHANNEL(mountmgr);
+int device_seek_penalty (char *unix_mount) +{ +#ifdef linux
- FILE *fp;
- char isrotapathstr[260];
- char evpath[260];
- char ueventbufstr[260];
- char isrotastrbuf[2];
- char *fgetsret = NULL;
- int ret = 0;
- char *rptr = NULL;
- char *sptr = NULL;
- char *devstr_edited = NULL;
- char mountpath[260];
- char *part_dev = NULL;
- int c = 0;
- struct stat statbuf;
- int seekpen;
- const char *home = getenv( "HOME" );
This function is full of inconsistent spacing; please try to match the surrounding code style.
- const char *prefix = getenv( "WINEPREFIX" );
- size_t len = (prefix ? strlen(prefix) : strlen(home) + strlen("/.wine")) + sizeof("/dosdevices/com256");
- char *path = HeapAlloc( GetProcessHeap(), 0, len );
- if (path)
- {
if (prefix) strcpy( path, prefix );
else
{
strcpy( path, home );
strcat( path, "/.wine" );
}
strcat( path, "/dosdevices/" );
- }
- if (!unix_mount) {
return -1;
- }
- snprintf (mountpath, 260, "%s%s", path, unix_mount);
- ret = stat (mountpath, &statbuf);
This doesn't make any sense; unix_mount is the absolute Unix mount point. Did you test this code?
- if (ret == -1) {
return -1;
- }
- HeapFree( GetProcessHeap(), 0, path );
- sprintf (evpath, "/sys/dev/block/%d:%d/uevent", major(statbuf.st_dev), minor(statbuf.st_dev));
- fp = fopen(evpath, "r");
- if (!fp) {
return -1;
- }
- while ((rptr = fgets (ueventbufstr, 260, fp))) {
sptr = strstr (rptr, "DEVNAME=");
if (sptr) {
sptr += strlen ("DEVNAME=");
break;
}
- }
- fclose (fp);
- devstr_edited = sptr;
- if (!devstr_edited) {
return -1;
- }
- /* Find first character after forwardslash '/' */
- part_dev = strrchr (devstr_edited, '/');
- if (!part_dev || (part_dev == devstr_edited)) part_dev = devstr_edited;
- else part_dev++;
- /* Trim off trailing digits and whitespace */
- c = strlen (devstr_edited);
- c--;
- while ((devstr_edited[c] >= '0' && devstr_edited[c] <= '9')
|| (devstr_edited[c] == '\n' || devstr_edited[c] == '\r'))
devstr_edited[c--] = '\0';
This isn't reliable; there's no guarantee that partition devices are named in that scheme (and I've seen partitions that aren't in practice).
- ret = snprintf (isrotapathstr, 260, "/sys/block/%s/queue/rotational", part_dev);
- if (ret < 1 || ret == 260) {
return -1;
- }
"ret >= 260"
- fp = fopen(isrotapathstr, "r");
- if (!fp) {
return -1;
- }
- fgetsret = fgets(isrotastrbuf, 2, fp);
- if (!fgetsret) {
return -1;
- }
fgetc() seems even easier.
- fclose (fp);
- if (isrotastrbuf[0] == '1') {
seekpen = 1;
- } else if (isrotastrbuf[0] == '0') {
seekpen = 0;
- } else {
return -1;
- }
- return seekpen;
+#else
- return -1;
+#endif +} diff --git a/include/ntddstor.h b/include/ntddstor.h index b8c4bb73b0d..836def413fe 100644 --- a/include/ntddstor.h +++ b/include/ntddstor.h @@ -214,7 +214,8 @@ typedef enum _STORAGE_QUERY_TYPE {
typedef enum _STORAGE_PROPERTY_ID { StorageDeviceProperty = 0,
- StorageAdapterProperty
- StorageAdapterProperty = 1,
- StorageDeviceSeekPenaltyProperty = 7,
} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID;
typedef struct _STORAGE_PROPERTY_QUERY { @@ -272,6 +273,12 @@ typedef struct _STORAGE_ADAPTER_DESCRIPTOR { USHORT BusMinorVersion; } STORAGE_ADAPTER_DESCRIPTOR, *PSTORAGE_ADAPTER_DESCRIPTOR;
+typedef struct _DEVICE_SEEK_PENALTY_DESCRIPTOR {
- ULONG Version;
- ULONG Size;
- BOOLEAN IncursSeekPenalty;
+} DEVICE_SEEK_PENALTY_DESCRIPTOR, *PDEVICE_SEEK_PENALTY_DESCRIPTOR;
#ifdef __cplusplus } #endif