We may call CopyFile() and thus GetFileInformationByHandle(), which retrieves the volume serial number, many times when setting up a prefix, before mountmgr has been initialized.
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/ntdll/file.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c index 1adc1e094ec..dac8538add8 100644 --- a/dlls/ntdll/file.c +++ b/dlls/ntdll/file.c @@ -3458,7 +3458,6 @@ NTSTATUS WINAPI NtQueryVolumeInformationFile( HANDLE handle, PIO_STATUS_BLOCK io
if (!(drive = get_mountmgr_fs_info( handle, fd ))) { - ERR_(winediag)("Failed to query volume information from mountmgr.\n"); io->u.Status = STATUS_NOT_IMPLEMENTED; break; }
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- v3: Don't depend on success from NtQueryVolumeInformationFile().
dlls/kernelbase/file.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 18b86820367..9ca184a5d04 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -2201,6 +2201,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH FlushFileBuffers( HANDLE file ) */ BOOL WINAPI DECLSPEC_HOTPATCH GetFileInformationByHandle( HANDLE file, BY_HANDLE_FILE_INFORMATION *info ) { + FILE_FS_VOLUME_INFORMATION volume_info; FILE_ALL_INFORMATION all_info; IO_STATUS_BLOCK io; NTSTATUS status; @@ -2216,12 +2217,17 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetFileInformationByHandle( HANDLE file, BY_HANDLE info->ftLastAccessTime.dwLowDateTime = all_info.BasicInformation.LastAccessTime.u.LowPart; info->ftLastWriteTime.dwHighDateTime = all_info.BasicInformation.LastWriteTime.u.HighPart; info->ftLastWriteTime.dwLowDateTime = all_info.BasicInformation.LastWriteTime.u.LowPart; - info->dwVolumeSerialNumber = 0; /* FIXME */ + info->dwVolumeSerialNumber = 0; info->nFileSizeHigh = all_info.StandardInformation.EndOfFile.u.HighPart; info->nFileSizeLow = all_info.StandardInformation.EndOfFile.u.LowPart; info->nNumberOfLinks = all_info.StandardInformation.NumberOfLinks; info->nFileIndexHigh = all_info.InternalInformation.IndexNumber.u.HighPart; info->nFileIndexLow = all_info.InternalInformation.IndexNumber.u.LowPart; + + status = NtQueryVolumeInformationFile( file, &io, &volume_info, sizeof(volume_info), FileFsVolumeInformation ); + if (status == STATUS_SUCCESS || status == STATUS_BUFFER_OVERFLOW) + info->dwVolumeSerialNumber = volume_info.VolumeSerialNumber; + return TRUE; }
April 8, 2020 4:06 PM, "Zebediah Figura" z.figura12@gmail.com wrote:
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 18b86820367..9ca184a5d04 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -2216,12 +2217,17 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetFileInformationByHandle( HANDLE file, BY_HANDLE info->ftLastAccessTime.dwLowDateTime = all_info.BasicInformation.LastAccessTime.u.LowPart; info->ftLastWriteTime.dwHighDateTime = all_info.BasicInformation.LastWriteTime.u.HighPart; info->ftLastWriteTime.dwLowDateTime = all_info.BasicInformation.LastWriteTime.u.LowPart;
- info->dwVolumeSerialNumber = 0; /* FIXME */
- info->dwVolumeSerialNumber = 0; info->nFileSizeHigh = all_info.StandardInformation.EndOfFile.u.HighPart; info->nFileSizeLow = all_info.StandardInformation.EndOfFile.u.LowPart; info->nNumberOfLinks = all_info.StandardInformation.NumberOfLinks; info->nFileIndexHigh = all_info.InternalInformation.IndexNumber.u.HighPart; info->nFileIndexLow = all_info.InternalInformation.IndexNumber.u.LowPart;
- status = NtQueryVolumeInformationFile( file, &io, &volume_info, sizeof(volume_info), FileFsVolumeInformation );
Why aren't you using NtQueryInformationFile(FileIdInformation) instead of NtQueryVolumeInformationFile(FileFsVolumeInformation)?
Chip
On 4/9/20 1:37 AM, Chip Davis wrote:
April 8, 2020 4:06 PM, "Zebediah Figura" z.figura12@gmail.com wrote:
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 18b86820367..9ca184a5d04 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -2216,12 +2217,17 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetFileInformationByHandle( HANDLE file, BY_HANDLE info->ftLastAccessTime.dwLowDateTime = all_info.BasicInformation.LastAccessTime.u.LowPart; info->ftLastWriteTime.dwHighDateTime = all_info.BasicInformation.LastWriteTime.u.HighPart; info->ftLastWriteTime.dwLowDateTime = all_info.BasicInformation.LastWriteTime.u.LowPart;
- info->dwVolumeSerialNumber = 0; /* FIXME */
- info->dwVolumeSerialNumber = 0; info->nFileSizeHigh = all_info.StandardInformation.EndOfFile.u.HighPart; info->nFileSizeLow = all_info.StandardInformation.EndOfFile.u.LowPart; info->nNumberOfLinks = all_info.StandardInformation.NumberOfLinks; info->nFileIndexHigh = all_info.InternalInformation.IndexNumber.u.HighPart; info->nFileIndexLow = all_info.InternalInformation.IndexNumber.u.LowPart;
- status = NtQueryVolumeInformationFile( file, &io, &volume_info, sizeof(volume_info), FileFsVolumeInformation );
Why aren't you using NtQueryInformationFile(FileIdInformation) instead of NtQueryVolumeInformationFile(FileFsVolumeInformation)?
I could, but does it matter?
Chip
April 9, 2020 10:03 AM, "Zebediah Figura" zfigura@codeweavers.com wrote:
On 4/9/20 1:37 AM, Chip Davis wrote:
April 8, 2020 4:06 PM, "Zebediah Figura" z.figura12@gmail.com wrote:
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c index 18b86820367..9ca184a5d04 100644 --- a/dlls/kernelbase/file.c +++ b/dlls/kernelbase/file.c @@ -2216,12 +2217,17 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetFileInformationByHandle( HANDLE file, BY_HANDLE info->ftLastAccessTime.dwLowDateTime = all_info.BasicInformation.LastAccessTime.u.LowPart; info->ftLastWriteTime.dwHighDateTime = all_info.BasicInformation.LastWriteTime.u.HighPart; info->ftLastWriteTime.dwLowDateTime = all_info.BasicInformation.LastWriteTime.u.LowPart;
- info->dwVolumeSerialNumber = 0; /* FIXME */
- info->dwVolumeSerialNumber = 0;
info->nFileSizeHigh = all_info.StandardInformation.EndOfFile.u.HighPart; info->nFileSizeLow = all_info.StandardInformation.EndOfFile.u.LowPart; info->nNumberOfLinks = all_info.StandardInformation.NumberOfLinks; info->nFileIndexHigh = all_info.InternalInformation.IndexNumber.u.HighPart; info->nFileIndexLow = all_info.InternalInformation.IndexNumber.u.LowPart;
- status = NtQueryVolumeInformationFile( file, &io, &volume_info, sizeof(volume_info),
FileFsVolumeInformation );
Why aren't you using NtQueryInformationFile(FileIdInformation) instead of NtQueryVolumeInformationFile(FileFsVolumeInformation)?
I could, but does it matter?
Not really.
Chip
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- My apologies for forgetting to move those functions upwards in the first place...
dlls/ntdll/file.c | 246 ++++++++++++++++++++++++---------------------- 1 file changed, 126 insertions(+), 120 deletions(-)
diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c index dac8538add8..a9770290e0c 100644 --- a/dlls/ntdll/file.c +++ b/dlls/ntdll/file.c @@ -2237,6 +2237,124 @@ static NTSTATUS server_get_file_info( HANDLE handle, IO_STATUS_BLOCK *io, void *
}
+/* Find a DOS device which can act as the root of "path". + * Similar to find_drive_root(), but returns -1 instead of crossing volumes. */ +static int find_dos_device( const char *path ) +{ + int len = strlen(path); + int drive; + char *buffer; + struct stat st; + struct drive_info info[MAX_DOS_DRIVES]; + dev_t dev_id; + + if (!DIR_get_drives_info( info )) return -1; + + if (stat( path, &st ) < 0) return -1; + dev_id = st.st_dev; + + /* strip off trailing slashes */ + while (len > 1 && path[len - 1] == '/') len--; + + /* make a copy of the path */ + if (!(buffer = RtlAllocateHeap( GetProcessHeap(), 0, len + 1 ))) return -1; + memcpy( buffer, path, len ); + buffer[len] = 0; + + for (;;) + { + if (!stat( buffer, &st ) && S_ISDIR( st.st_mode )) + { + if (st.st_dev != dev_id) break; + + for (drive = 0; drive < MAX_DOS_DRIVES; drive++) + { + if ((info[drive].dev == st.st_dev) && (info[drive].ino == st.st_ino)) + { + if (len == 1) len = 0; /* preserve root slash in returned path */ + TRACE( "%s -> drive %c:, root=%s, name=%s\n", + debugstr_a(path), 'A' + drive, debugstr_a(buffer), debugstr_a(path + len)); + RtlFreeHeap( GetProcessHeap(), 0, buffer ); + return drive; + } + } + } + if (len <= 1) break; /* reached root */ + while (path[len - 1] != '/') len--; + while (path[len - 1] == '/') len--; + buffer[len] = 0; + } + RtlFreeHeap( GetProcessHeap(), 0, buffer ); + return -1; +} + +static struct mountmgr_unix_drive *get_mountmgr_fs_info( HANDLE handle, int fd ) +{ + struct mountmgr_unix_drive *drive; + OBJECT_ATTRIBUTES attr; + UNICODE_STRING string; + ANSI_STRING unix_name; + IO_STATUS_BLOCK io; + HANDLE mountmgr; + NTSTATUS status; + int letter; + + if (server_get_unix_name( handle, &unix_name )) + return NULL; + + letter = find_dos_device( unix_name.Buffer ); + RtlFreeAnsiString( &unix_name ); + + if (!(drive = RtlAllocateHeap( GetProcessHeap(), 0, 1024 ))) + return NULL; + + if (letter == -1) + { + struct stat st; + + if (fstat( fd, &st ) == -1) + { + RtlFreeHeap( GetProcessHeap(), 0, drive ); + return NULL; + } + + drive->unix_dev = st.st_dev; + drive->letter = 0; + } + else + drive->letter = 'a' + letter; + + RtlInitUnicodeString( &string, MOUNTMGR_DEVICE_NAME ); + InitializeObjectAttributes( &attr, &string, 0, NULL, NULL ); + if (NtOpenFile( &mountmgr, GENERIC_READ | SYNCHRONIZE, &attr, &io, + FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT )) + return NULL; + + status = NtDeviceIoControlFile( mountmgr, NULL, NULL, NULL, &io, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE, + drive, sizeof(*drive), drive, 1024 ); + if (status == STATUS_BUFFER_OVERFLOW) + { + if (!(drive = RtlReAllocateHeap( GetProcessHeap(), 0, drive, drive->size ))) + { + RtlFreeHeap( GetProcessHeap(), 0, drive ); + NtClose( mountmgr ); + return NULL; + } + status = NtDeviceIoControlFile( mountmgr, NULL, NULL, NULL, &io, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE, + drive, sizeof(*drive), drive, drive->size ); + } + NtClose( mountmgr ); + + if (status) + { + WARN("failed to retrieve filesystem type from mountmgr, status %#x\n", status); + RtlFreeHeap( GetProcessHeap(), 0, drive ); + return NULL; + } + + return drive; +} + /****************************************************************************** * NtQueryInformationFile [NTDLL.@] * ZwQueryInformationFile [NTDLL.@] @@ -2507,8 +2625,15 @@ NTSTATUS WINAPI NtQueryInformationFile( HANDLE hFile, PIO_STATUS_BLOCK io, if (fd_get_file_info( fd, options, &st, &attr ) == -1) io->u.Status = FILE_GetNtStatus(); else { + struct mountmgr_unix_drive *drive; FILE_ID_INFORMATION *info = ptr; - info->VolumeSerialNumber = 0; /* FIXME */ + + info->VolumeSerialNumber = 0; + if ((drive = get_mountmgr_fs_info( hFile, fd ))) + { + info->VolumeSerialNumber = drive->serial; + RtlFreeHeap( GetProcessHeap(), 0, drive ); + } memset( &info->FileId, 0, sizeof(info->FileId) ); *(ULONGLONG *)&info->FileId = st.st_ino; } @@ -3120,125 +3245,6 @@ static NTSTATUS get_device_info( int fd, FILE_FS_DEVICE_INFORMATION *info ) return STATUS_SUCCESS; }
-/* Find a DOS device which can act as the root of "path". - * Similar to find_drive_root(), but returns -1 instead of crossing volumes. */ -static int find_dos_device( const char *path ) -{ - int len = strlen(path); - int drive; - char *buffer; - struct stat st; - struct drive_info info[MAX_DOS_DRIVES]; - dev_t dev_id; - - if (!DIR_get_drives_info( info )) return -1; - - if (stat( path, &st ) < 0) return -1; - dev_id = st.st_dev; - - /* strip off trailing slashes */ - while (len > 1 && path[len - 1] == '/') len--; - - /* make a copy of the path */ - if (!(buffer = RtlAllocateHeap( GetProcessHeap(), 0, len + 1 ))) return -1; - memcpy( buffer, path, len ); - buffer[len] = 0; - - for (;;) - { - if (!stat( buffer, &st ) && S_ISDIR( st.st_mode )) - { - if (st.st_dev != dev_id) break; - - for (drive = 0; drive < MAX_DOS_DRIVES; drive++) - { - if ((info[drive].dev == st.st_dev) && (info[drive].ino == st.st_ino)) - { - if (len == 1) len = 0; /* preserve root slash in returned path */ - TRACE( "%s -> drive %c:, root=%s, name=%s\n", - debugstr_a(path), 'A' + drive, debugstr_a(buffer), debugstr_a(path + len)); - RtlFreeHeap( GetProcessHeap(), 0, buffer ); - return drive; - } - } - } - if (len <= 1) break; /* reached root */ - while (path[len - 1] != '/') len--; - while (path[len - 1] == '/') len--; - buffer[len] = 0; - } - RtlFreeHeap( GetProcessHeap(), 0, buffer ); - return -1; -} - -static struct mountmgr_unix_drive *get_mountmgr_fs_info( HANDLE handle, int fd ) -{ - struct mountmgr_unix_drive *drive; - OBJECT_ATTRIBUTES attr; - UNICODE_STRING string; - ANSI_STRING unix_name; - IO_STATUS_BLOCK io; - HANDLE mountmgr; - NTSTATUS status; - int letter; - - if (server_get_unix_name( handle, &unix_name )) - return NULL; - - letter = find_dos_device( unix_name.Buffer ); - RtlFreeAnsiString( &unix_name ); - - if (!(drive = RtlAllocateHeap( GetProcessHeap(), 0, 1024 ))) - return NULL; - - if (letter == -1) - { - struct stat st; - - if (fstat( fd, &st ) == -1) - { - RtlFreeHeap( GetProcessHeap(), 0, drive ); - return NULL; - } - - drive->unix_dev = st.st_dev; - drive->letter = 0; - } - else - drive->letter = 'a' + letter; - - RtlInitUnicodeString( &string, MOUNTMGR_DEVICE_NAME ); - InitializeObjectAttributes( &attr, &string, 0, NULL, NULL ); - if (NtOpenFile( &mountmgr, GENERIC_READ | SYNCHRONIZE, &attr, &io, - FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT )) - return NULL; - - status = NtDeviceIoControlFile( mountmgr, NULL, NULL, NULL, &io, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE, - drive, sizeof(*drive), drive, 1024 ); - if (status == STATUS_BUFFER_OVERFLOW) - { - if (!(drive = RtlReAllocateHeap( GetProcessHeap(), 0, drive, drive->size ))) - { - RtlFreeHeap( GetProcessHeap(), 0, drive ); - NtClose( mountmgr ); - return NULL; - } - status = NtDeviceIoControlFile( mountmgr, NULL, NULL, NULL, &io, IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE, - drive, sizeof(*drive), drive, drive->size ); - } - NtClose( mountmgr ); - - if (status) - { - WARN("failed to retrieve filesystem type from mountmgr, status %#x\n", status); - RtlFreeHeap( GetProcessHeap(), 0, drive ); - return NULL; - } - - return drive; -} - - /****************************************************************************** * NtQueryVolumeInformationFile [NTDLL.@] * ZwQueryVolumeInformationFile [NTDLL.@]
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- v3: Reorder patches to fix test failures.
There might be a better way of deriving a serial from a UUID, but by my understanding, Linux uses version 4 UUIDs, and taking the last 4 bytes is as random as it can be. I don't know about Mac.
For folders such as drive_c which are not separate host systems, this returns a serial with the high byte containing the DOS drive letter, and the other three bytes zero, so it would seem to be exactly as unique and stable as the volume GUID itself.
dlls/mountmgr.sys/device.c | 3 +++ dlls/ntdll/tests/file.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/dlls/mountmgr.sys/device.c b/dlls/mountmgr.sys/device.c index 8fb984579e3..f68ab4de9b4 100644 --- a/dlls/mountmgr.sys/device.c +++ b/dlls/mountmgr.sys/device.c @@ -1123,6 +1123,9 @@ static NTSTATUS set_volume_info( struct volume *volume, struct dos_drive *drive, } }
+ if (!volume->serial) + memcpy(&volume->serial, &volume->guid.Data4[4], sizeof(DWORD)); + if (!volume->mount) volume->mount = add_volume_mount_point( disk_device->dev_obj, &disk_device->name, &volume->guid ); if (drive && !drive->mount) diff --git a/dlls/ntdll/tests/file.c b/dlls/ntdll/tests/file.c index 33b8ead5528..68c6a79e795 100644 --- a/dlls/ntdll/tests/file.c +++ b/dlls/ntdll/tests/file.c @@ -3908,7 +3908,7 @@ static void test_query_volume_information_file(void) io.Information);
todo_wine ok(ffvi->VolumeCreationTime.QuadPart != 0, "Missing VolumeCreationTime\n"); - todo_wine ok(ffvi->VolumeSerialNumber != 0, "Missing VolumeSerialNumber\n"); + ok(ffvi->VolumeSerialNumber != 0, "Missing VolumeSerialNumber\n"); ok(ffvi->SupportsObjects == 1,"expected 1, got %d\n", ffvi->SupportsObjects); ok(ffvi->VolumeLabelLength == lstrlenW(ffvi->VolumeLabel) * sizeof(WCHAR), "got %d\n", ffvi->VolumeLabelLength);
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- dlls/kernel32/path.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-)
diff --git a/dlls/kernel32/path.c b/dlls/kernel32/path.c index 31652d3164b..334fc25e873 100644 --- a/dlls/kernel32/path.c +++ b/dlls/kernel32/path.c @@ -257,21 +257,12 @@ DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen
static BOOL is_same_file(HANDLE h1, HANDLE h2) { - int fd1; - BOOL ret = FALSE; - if (wine_server_handle_to_fd(h1, 0, &fd1, NULL) == STATUS_SUCCESS) - { - int fd2; - if (wine_server_handle_to_fd(h2, 0, &fd2, NULL) == STATUS_SUCCESS) - { - struct stat stat1, stat2; - if (fstat(fd1, &stat1) == 0 && fstat(fd2, &stat2) == 0) - ret = (stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino); - wine_server_release_fd(h2, fd2); - } - wine_server_release_fd(h1, fd1); - } - return ret; + FILE_ID_INFORMATION id1, id2; + IO_STATUS_BLOCK io; + + return !NtQueryInformationFile( h1, &io, &id1, sizeof(id1), FileIdInformation ) + && !NtQueryInformationFile( h2, &io, &id2, sizeof(id2), FileIdInformation ) + && !memcmp( &id1, &id2, sizeof(FILE_ID_INFORMATION) ); }
/**************************************************************************
Signed-off-by: Zebediah Figura zfigura@codeweavers.com --- This paves the way for an implementation of GetVolumeInformation() on top of ntdll. Unfortunately, I ran into a snag while doing so, which is that one of the tests attempts to open a file by a device path other than the DOS one (specifically, \?\Volume{...}). mountmgr allows that, which was enough for kernel32 to fall back to assuming NTFS. Unless we implement a similar fallback (which feels much like papering over the problem), that test would start failing.
I don't know how to resolve this. One way is to hook up the missing IRPs in mountmgr, but this seems like more work than its worth. It'd be nice for ntdll to be able to recognize volume paths such that they resolve to the same Unix file object as the equivalent DOS path, but there are many ways to achieve this goal and I can't even guess at what would be the right one.
dlls/kernel32/tests/volume.c | 68 ++++++++++++++++++++++++++++++++++++ dlls/kernel32/volume.c | 67 +++++++++++++++++++++++++++-------- 2 files changed, 120 insertions(+), 15 deletions(-)
diff --git a/dlls/kernel32/tests/volume.c b/dlls/kernel32/tests/volume.c index 37d15fd3f8c..126df5b8a0f 100644 --- a/dlls/kernel32/tests/volume.c +++ b/dlls/kernel32/tests/volume.c @@ -60,6 +60,7 @@ static BOOL (WINAPI *pGetVolumePathNameA)(LPCSTR, LPSTR, DWORD); static BOOL (WINAPI *pGetVolumePathNamesForVolumeNameA)(LPCSTR, LPSTR, DWORD, LPDWORD); static BOOL (WINAPI *pGetVolumePathNamesForVolumeNameW)(LPCWSTR, LPWSTR, DWORD, LPDWORD); static BOOL (WINAPI *pCreateSymbolicLinkA)(const char *, const char *, DWORD); +static BOOL (WINAPI *pGetVolumeInformationByHandleW)(HANDLE, WCHAR *, DWORD, DWORD *, DWORD *, DWORD *, WCHAR *, DWORD);
/* ############################### */
@@ -1520,6 +1521,71 @@ static void test_mounted_folder(void) ok(ret, "got error %u\n", GetLastError()); }
+static void test_GetVolumeInformationByHandle(void) +{ + char buffer[50] DECLSPEC_ALIGN(8); + FILE_FS_ATTRIBUTE_INFORMATION *attr_info = (void *)buffer; + FILE_FS_VOLUME_INFORMATION *volume_info = (void *)buffer; + DWORD serial, filename_len, flags; + WCHAR label[20], fsname[20]; + IO_STATUS_BLOCK io; + HANDLE file; + NTSTATUS status; + BOOL ret; + + if (!pGetVolumeInformationByHandleW) + { + win_skip("GetVolumeInformationByHandleW is not present.\n"); + return; + } + + file = CreateFileA( "C:/windows", 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); + ok(file != INVALID_HANDLE_VALUE, "failed to open file, error %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = pGetVolumeInformationByHandleW( INVALID_HANDLE_VALUE, label, ARRAY_SIZE(label), &serial, + &filename_len, &flags, fsname, ARRAY_SIZE(fsname) ); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_INVALID_HANDLE, "got error %u\n", GetLastError()); + + ret = pGetVolumeInformationByHandleW( file, NULL, 0, NULL, NULL, NULL, NULL, 0 ); + ok(ret, "got error %u\n", GetLastError()); + + ret = pGetVolumeInformationByHandleW( file, label, ARRAY_SIZE(label), &serial, + &filename_len, &flags, fsname, ARRAY_SIZE(fsname) ); + ok(ret, "got error %u\n", GetLastError()); + + memset(buffer, 0, sizeof(buffer)); + status = NtQueryVolumeInformationFile( file, &io, buffer, sizeof(buffer), FileFsAttributeInformation ); + ok(!status, "got status %#x\n", status); + ok(flags == attr_info->FileSystemAttributes, "expected flags %#x, got %#x\n", + attr_info->FileSystemAttributes, flags); + ok(filename_len == attr_info->MaximumComponentNameLength, "expected filename_len %u, got %u\n", + attr_info->MaximumComponentNameLength, filename_len); + ok(!wcscmp( fsname, attr_info->FileSystemName ), "expected fsname %s, got %s\n", + debugstr_w( attr_info->FileSystemName ), debugstr_w( fsname )); + ok(wcslen( fsname ) == attr_info->FileSystemNameLength / sizeof(WCHAR), + "expected fsname length %u, got %u\n", attr_info->FileSystemNameLength / sizeof(WCHAR), wcslen( fsname )); + + SetLastError(0xdeadbeef); + ret = pGetVolumeInformationByHandleW( file, NULL, 0, NULL, &filename_len, &flags, fsname, 2 ); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_BAD_LENGTH, "got error %u\n", GetLastError()); + + memset(buffer, 0, sizeof(buffer)); + status = NtQueryVolumeInformationFile( file, &io, buffer, sizeof(buffer), FileFsVolumeInformation ); + ok(!status, "got status %#x\n", status); + ok(serial == volume_info->VolumeSerialNumber, "expected serial %08x, got %08x\n", + volume_info->VolumeSerialNumber, serial); + ok(!wcscmp( label, volume_info->VolumeLabel ), "expected label %s, got %s\n", + debugstr_w( volume_info->VolumeLabel ), debugstr_w( label )); + ok(wcslen( label ) == volume_info->VolumeLabelLength / sizeof(WCHAR), + "expected label length %u, got %u\n", volume_info->VolumeLabelLength / sizeof(WCHAR), wcslen( label )); + + CloseHandle( file ); +} + START_TEST(volume) { hdll = GetModuleHandleA("kernel32.dll"); @@ -1535,6 +1601,7 @@ START_TEST(volume) pGetVolumePathNamesForVolumeNameA = (void *) GetProcAddress(hdll, "GetVolumePathNamesForVolumeNameA"); pGetVolumePathNamesForVolumeNameW = (void *) GetProcAddress(hdll, "GetVolumePathNamesForVolumeNameW"); pCreateSymbolicLinkA = (void *) GetProcAddress(hdll, "CreateSymbolicLinkA"); + pGetVolumeInformationByHandleW = (void *) GetProcAddress(hdll, "GetVolumeInformationByHandleW");
test_query_dos_deviceA(); test_dos_devices(); @@ -1553,4 +1620,5 @@ START_TEST(volume) test_GetVolumePathNamesForVolumeNameW(); test_cdrom_ioctl(); test_mounted_folder(); + test_GetVolumeInformationByHandle(); } diff --git a/dlls/kernel32/volume.c b/dlls/kernel32/volume.c index 4f0f4a0a0fc..7555dd9e95d 100644 --- a/dlls/kernel32/volume.c +++ b/dlls/kernel32/volume.c @@ -2117,21 +2117,58 @@ BOOL WINAPI SetVolumeMountPointW(LPCWSTR path, LPCWSTR volume) /*********************************************************************** * GetVolumeInformationByHandleW (KERNEL32.@) */ -BOOL WINAPI GetVolumeInformationByHandleW(HANDLE handle, WCHAR *volnamebuf, DWORD volnamesize, DWORD *volserial, DWORD *maxlength, DWORD *flags, WCHAR *fsnamebuf, DWORD fsnamesize) +BOOL WINAPI GetVolumeInformationByHandleW( HANDLE handle, WCHAR *label, DWORD label_len, + DWORD *serial, DWORD *filename_len, DWORD *flags, + WCHAR *fsname, DWORD fsname_len ) { - FIXME("%p %p %d %p %p %p %p %d\n", handle, volnamebuf, volnamesize, volserial, maxlength, flags, fsnamebuf, fsnamesize); - - if(volnamebuf && volnamesize) - *volnamebuf = 0; - if(volserial) - *volserial = 0; - if(maxlength) - *maxlength = 0; - if(flags) - *flags = 0; - if(fsnamebuf && fsnamesize) - *fsnamebuf = 0; + IO_STATUS_BLOCK io;
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + TRACE( "%p\n", handle ); + + if (label || serial) + { + char buffer[sizeof(FILE_FS_VOLUME_INFORMATION) + MAX_PATH * sizeof(WCHAR)]; + FILE_FS_VOLUME_INFORMATION *info = (FILE_FS_VOLUME_INFORMATION *)buffer; + + if (!set_ntstatus( NtQueryVolumeInformationFile( handle, &io, info, sizeof(buffer), + FileFsVolumeInformation ) )) + return FALSE; + + if (label) + { + if (label_len < info->VolumeLabelLength / sizeof(WCHAR) + 1) + { + SetLastError( ERROR_BAD_LENGTH ); + return FALSE; + } + memcpy( label, info->VolumeLabel, info->VolumeLabelLength ); + label[info->VolumeLabelLength / sizeof(WCHAR)] = 0; + } + if (serial) *serial = info->VolumeSerialNumber; + } + + if (filename_len || flags || fsname) + { + char buffer[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + MAX_PATH * sizeof(WCHAR)]; + FILE_FS_ATTRIBUTE_INFORMATION *info = (FILE_FS_ATTRIBUTE_INFORMATION *)buffer; + + if (!set_ntstatus( NtQueryVolumeInformationFile( handle, &io, info, sizeof(buffer), + FileFsAttributeInformation ) )) + return FALSE; + + if (fsname) + { + if (fsname_len < info->FileSystemNameLength / sizeof(WCHAR) + 1) + { + SetLastError( ERROR_BAD_LENGTH ); + return FALSE; + } + memcpy( fsname, info->FileSystemName, info->FileSystemNameLength ); + fsname[info->FileSystemNameLength / sizeof(WCHAR)] = 0; + } + if (filename_len) *filename_len = info->MaximumComponentNameLength; + if (flags) *flags = info->FileSystemAttributes; + } + + return TRUE; }