From 7546b4a63d437c2f7f8673cae9341d358f84f1a5 Mon Sep 17 00:00:00 2001 From: Joel Holdsworth Date: Thu, 25 Aug 2022 12:58:31 +0100 Subject: [PATCH] mountmgr.sys: Implemented FileFsSizeInformation and FileFsFullSizeInformation volume queries. This patch fixes GetDiskFreeSpaceA/W when an NT-style GUID volume path is provided e.g. "\\?\Volume{00000000-0000-0000-0000-000000000043}\" as might be retrieved by FindFirstVolumeA/W and FindNextVolumeA/W. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53547 Signed-off-by: Joel Holdsworth --- dlls/kernel32/tests/drive.c | 2 - dlls/mountmgr.sys/device.c | 50 ++++++++++++++ dlls/mountmgr.sys/unixlib.c | 131 ++++++++++++++++++++++++++++++++++++ dlls/mountmgr.sys/unixlib.h | 16 +++++ 4 files changed, 197 insertions(+), 2 deletions(-) diff --git a/dlls/kernel32/tests/drive.c b/dlls/kernel32/tests/drive.c index 4cde08ffbee..db9f626aaab 100644 --- a/dlls/kernel32/tests/drive.c +++ b/dlls/kernel32/tests/drive.c @@ -164,7 +164,6 @@ static void test_GetDiskFreeSpaceA(void) ok(ret, "GetVolumeNameForVolumeMountPointA error %ld\n", GetLastError()); ret = GetDiskFreeSpaceA(volume_guid_path, §ors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters); - todo_wine ok(ret, "GetDiskFreeSpaceA error %ld\n", GetLastError()); logical_drives = GetLogicalDrives(); @@ -257,7 +256,6 @@ static void test_GetDiskFreeSpaceW(void) ok(ret, "GetVolumeNameForVolumeMountPointW error %ld\n", GetLastError()); ret = GetDiskFreeSpaceW(volume_guid_path, §ors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters); - todo_wine ok(ret, "GetDiskFreeSpaceW error %ld\n", GetLastError()); logical_drives = GetLogicalDrives(); diff --git a/dlls/mountmgr.sys/device.c b/dlls/mountmgr.sys/device.c index 50b2f38bbd8..328b0b2f344 100644 --- a/dlls/mountmgr.sys/device.c +++ b/dlls/mountmgr.sys/device.c @@ -1656,6 +1656,30 @@ static NTSTATUS WINAPI harddisk_query_volume( DEVICE_OBJECT *device, IRP *irp ) status = STATUS_SUCCESS; break; } + case FileFsSizeInformation: + { + FILE_FS_SIZE_INFORMATION *info = irp->AssociatedIrp.SystemBuffer; + struct size_info size_info = { 0, 0, 0, 0, 0 }; + struct get_volume_size_info_params params = { dev->unix_mount, &size_info }; + + if (length < sizeof(FILE_FS_SIZE_INFORMATION)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + if ((status = MOUNTMGR_CALL( get_volume_size_info, ¶ms )) == STATUS_SUCCESS) + { + info->TotalAllocationUnits.QuadPart = size_info.total_allocation_units; + info->AvailableAllocationUnits.QuadPart = size_info.caller_available_allocation_units; + info->SectorsPerAllocationUnit = size_info.sectors_per_allocation_unit; + info->BytesPerSector = size_info.bytes_per_sector; + io->Information = sizeof(*info); + status = STATUS_SUCCESS; + } + + break; + } case FileFsAttributeInformation: { FILE_FS_ATTRIBUTE_INFORMATION *info = irp->AssociatedIrp.SystemBuffer; @@ -1702,6 +1726,32 @@ static NTSTATUS WINAPI harddisk_query_volume( DEVICE_OBJECT *device, IRP *irp ) status = STATUS_SUCCESS; break; } + case FileFsFullSizeInformation: + { + FILE_FS_FULL_SIZE_INFORMATION *info = irp->AssociatedIrp.SystemBuffer; + struct size_info size_info = { 0, 0, 0, 0, 0 }; + struct get_volume_size_info_params params = { dev->unix_mount, &size_info }; + + if (length < sizeof(FILE_FS_FULL_SIZE_INFORMATION)) + { + status = STATUS_BUFFER_TOO_SMALL; + break; + } + + if ((status = MOUNTMGR_CALL( get_volume_size_info, ¶ms )) == STATUS_SUCCESS) + { + info->TotalAllocationUnits.QuadPart = size_info.total_allocation_units; + info->CallerAvailableAllocationUnits.QuadPart = size_info.caller_available_allocation_units; + info->ActualAvailableAllocationUnits.QuadPart = size_info.actual_available_allocation_units; + info->SectorsPerAllocationUnit = size_info.sectors_per_allocation_unit; + info->BytesPerSector = size_info.bytes_per_sector; + io->Information = sizeof(*info); + status = STATUS_SUCCESS; + } + + break; + } + default: FIXME("Unsupported volume query %x\n", irpsp->Parameters.QueryVolume.FsInformationClass); status = STATUS_NOT_SUPPORTED; diff --git a/dlls/mountmgr.sys/unixlib.c b/dlls/mountmgr.sys/unixlib.c index 52a3fce66d6..13f6fbecf09 100644 --- a/dlls/mountmgr.sys/unixlib.c +++ b/dlls/mountmgr.sys/unixlib.c @@ -30,12 +30,63 @@ #include #include #include +#ifdef HAVE_SYS_STATFS_H +#include +#endif +#ifdef HAVE_SYS_STATVFS_H +# include +#endif #include #include "unixlib.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mountmgr); static struct run_loop_params run_loop_params; +static NTSTATUS errno_to_status( int err ) +{ + TRACE( "errno = %d\n", err ); + 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_INVALID_DEVICE_REQUEST; + 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 ELOOP: return STATUS_REPARSE_POINT_NOT_RESOLVED; +#ifdef ETIME /* Missing on FreeBSD */ + case ETIME: return STATUS_IO_TIMEOUT; +#endif + case ENOEXEC: /* ?? */ + case EEXIST: /* ?? */ + default: + FIXME( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err ); + return STATUS_UNSUCCESSFUL; + } +} + static char *get_dosdevices_path( const char *dev ) { const char *home = getenv( "HOME" ); @@ -266,6 +317,85 @@ static NTSTATUS set_dosdev_symlink( void *args ) return status; } +static NTSTATUS get_volume_size_info( void *args ) +{ + const struct get_volume_size_info_params *params = args; + const char *unix_mount = params->unix_mount; + struct size_info *info = params->info; + + struct stat st; + ULONGLONG bsize; + NTSTATUS status; + int fd = -1; + +#if !defined(linux) || !defined(HAVE_FSTATFS) + struct statvfs stfs; +#else + struct statfs stfs; +#endif + + if (!unix_mount) return STATUS_NO_SUCH_DEVICE; + + if (unix_mount[0] != '/') + { + char *path = get_dosdevices_path( unix_mount ); + if (path) fd = open( path, O_RDONLY ); + free( path ); + } + else fd = open( unix_mount, O_RDONLY ); + + if (fstat( fd, &st ) < 0) + { + status = errno_to_status( errno ); + goto done; + } + if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode)) + { + status = STATUS_INVALID_DEVICE_REQUEST; + goto done; + } + + /* Linux's fstatvfs is buggy */ +#if !defined(linux) || !defined(HAVE_FSTATFS) + if (fstatvfs( fd, &stfs ) < 0) + { + status = errno_to_status( errno ); + goto done; + } + bsize = stfs.f_frsize; +#else + if (fstatfs( fd, &stfs ) < 0) + { + status = errno_to_status( errno ); + goto done; + } + bsize = stfs.f_bsize; +#endif + if (bsize == 2048) /* assume CD-ROM */ + { + info->bytes_per_sector = 2048; + info->sectors_per_allocation_unit = 1; + } + else + { + info->bytes_per_sector = 512; + info->sectors_per_allocation_unit = 8; + } + + info->total_allocation_units = + bsize * stfs.f_blocks / (info->bytes_per_sector * info->sectors_per_allocation_unit); + info->caller_available_allocation_units = + bsize * stfs.f_bavail / (info->bytes_per_sector * info->sectors_per_allocation_unit); + info->actual_available_allocation_units = + bsize * stfs.f_bfree / (info->bytes_per_sector * info->sectors_per_allocation_unit); + + status = STATUS_SUCCESS; + +done: + close( fd ); + return status; +} + static NTSTATUS get_volume_dos_devices( void *args ) { const struct get_volume_dos_devices_params *params = args; @@ -440,6 +570,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = add_drive, get_dosdev_symlink, set_dosdev_symlink, + get_volume_size_info, get_volume_dos_devices, read_volume_file, match_unixdev, diff --git a/dlls/mountmgr.sys/unixlib.h b/dlls/mountmgr.sys/unixlib.h index e7846a764da..d70371876fa 100644 --- a/dlls/mountmgr.sys/unixlib.h +++ b/dlls/mountmgr.sys/unixlib.h @@ -64,6 +64,21 @@ struct add_drive_params int *letter; }; +struct size_info +{ + LONGLONG total_allocation_units; + LONGLONG caller_available_allocation_units; + LONGLONG actual_available_allocation_units; + ULONG sectors_per_allocation_unit; + ULONG bytes_per_sector; +}; + +struct get_volume_size_info_params +{ + const char *unix_mount; + struct size_info *info; +}; + struct get_dosdev_symlink_params { const char *dev; @@ -142,6 +157,7 @@ enum mountmgr_funcs unix_add_drive, unix_get_dosdev_symlink, unix_set_dosdev_symlink, + unix_get_volume_size_info, unix_get_volume_dos_devices, unix_read_volume_file, unix_match_unixdev,