stream_file: Check the handle for network streams

Use NtQueryVolumeInformationFile instead of GetDriveType for detecting
remote filesystems on Windows. This has the advantage of working
directly on the file handle instead of needing a path and it works
unmodified in Cygwin where the previous code wouldn't understand Cygwin
paths or symlinks.

There is some risk in using NtQueryVolumeInformationFile, since it's an
internal function and its behaviour could change at any time or it could
be removed in a future version of Windows, however it's documented[1] in
the WDK and it's used successfully by Cygwin, so it should be fine. If
it's removed, the code should fail gracefully by treating all files as
local.

[1]: http://msdn.microsoft.com/en-us/library/windows/hardware/ff567070.aspx

Signed-off-by: wm4 <wm4@nowhere>
This commit is contained in:
James Ross-Gowan 2014-04-09 18:14:18 +10:00 committed by wm4
parent 217008be4a
commit 17d0609d1e
1 changed files with 34 additions and 9 deletions

View File

@ -43,8 +43,14 @@
#include <sys/vfs.h>
#endif
#ifdef __MINGW32__
#ifdef _WIN32
#include <windows.h>
#include <winternl.h>
#include <io.h>
#ifndef FILE_REMOTE_DEVICE
#define FILE_REMOTE_DEVICE (0x10)
#endif
#endif
struct priv {
@ -158,18 +164,37 @@ static bool check_stream_network(stream_t *stream)
return false;
}
#elif defined(__MINGW32__)
#elif defined(_WIN32)
static bool check_stream_network(stream_t *stream)
{
wchar_t volume[MAX_PATH];
wchar_t *path = mp_from_utf8(NULL, stream->path);
bool remote = false;
NTSTATUS (NTAPI *pNtQueryVolumeInformationFile)(HANDLE,
PIO_STATUS_BLOCK, PVOID, ULONG, FS_INFORMATION_CLASS) = NULL;
if (GetVolumePathNameW(path, volume, MAX_PATH))
remote = GetDriveTypeW(volume) == DRIVE_REMOTE;
// NtQueryVolumeInformationFile is an internal Windows function. It has
// been present since Windows XP, however this code should fail gracefully
// if it's removed from a future version of Windows.
HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
pNtQueryVolumeInformationFile = (NTSTATUS (NTAPI*)(HANDLE,
PIO_STATUS_BLOCK, PVOID, ULONG, FS_INFORMATION_CLASS))
GetProcAddress(ntdll, "NtQueryVolumeInformationFile");
talloc_free(path);
return remote;
if (!pNtQueryVolumeInformationFile)
return false;
struct priv *priv = stream->priv;
HANDLE h = (HANDLE)_get_osfhandle(priv->fd);
if (h == INVALID_HANDLE_VALUE)
return false;
FILE_FS_DEVICE_INFORMATION info = { 0 };
IO_STATUS_BLOCK io;
NTSTATUS status = pNtQueryVolumeInformationFile(h, &io, &info,
sizeof(info), FileFsDeviceInformation);
if (!NT_SUCCESS(status))
return false;
return info.DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM ||
(info.Characteristics & FILE_REMOTE_DEVICE);
}
#else
static bool check_stream_network(stream_t *stream)