/*
 * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <direct.h>
#include <malloc.h>
#include <io.h>
#include <windows.h>
#include <aclapi.h>
#include <winioctl.h>
#include <Sddl.h>

#include "jni.h"
#include "jni_util.h"
#include "jlong.h"

#include "sun_nio_fs_WindowsNativeDispatcher.h"

/**
 * jfieldIDs
 */
static jfieldID findFirst_handle;
static jfieldID findFirst_name;
static jfieldID findFirst_attributes;

static jfieldID findStream_handle;
static jfieldID findStream_name;

static jfieldID volumeInfo_fsName;
static jfieldID volumeInfo_volName;
static jfieldID volumeInfo_volSN;
static jfieldID volumeInfo_flags;

static jfieldID diskSpace_bytesAvailable;
static jfieldID diskSpace_totalBytes;
static jfieldID diskSpace_totalFree;

static jfieldID account_domain;
static jfieldID account_name;
static jfieldID account_use;

static jfieldID aclInfo_aceCount;

static jfieldID completionStatus_error;
static jfieldID completionStatus_bytesTransferred;
static jfieldID completionStatus_completionKey;

static jfieldID backupResult_bytesTransferred;
static jfieldID backupResult_context;


/**
 * Win32 APIs not available in Windows XP
 */
typedef HANDLE (WINAPI* FindFirstStream_Proc)(LPCWSTR, STREAM_INFO_LEVELS, LPVOID, DWORD);
typedef BOOL (WINAPI* FindNextStream_Proc)(HANDLE, LPVOID);

typedef BOOLEAN (WINAPI* CreateSymbolicLinkProc) (LPCWSTR, LPCWSTR, DWORD);
typedef BOOL (WINAPI* GetFinalPathNameByHandleProc) (HANDLE, LPWSTR, DWORD, DWORD);

static FindFirstStream_Proc FindFirstStream_func;
static FindNextStream_Proc FindNextStream_func;

static CreateSymbolicLinkProc CreateSymbolicLink_func;
static GetFinalPathNameByHandleProc GetFinalPathNameByHandle_func;

static void throwWindowsException(JNIEnv* env, DWORD lastError) {
    jobject x = JNU_NewObjectByName(env, "sun/nio/fs/WindowsException",
        "(I)V", lastError);
    if (x != NULL) {
        (*env)->Throw(env, x);
    }
}

/**
 * Initializes jfieldIDs and get address of Win32 calls that are located
 * at runtime.
 */
JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_initIDs(JNIEnv* env, jclass this)
{
    jclass clazz;
    HMODULE h;

    clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$FirstFile");
    CHECK_NULL(clazz);
    findFirst_handle = (*env)->GetFieldID(env, clazz, "handle", "J");
    CHECK_NULL(findFirst_handle);
    findFirst_name = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;");
    CHECK_NULL(findFirst_name);
    findFirst_attributes = (*env)->GetFieldID(env, clazz, "attributes", "I");
    CHECK_NULL(findFirst_attributes);

    clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$FirstStream");
    CHECK_NULL(clazz);
    findStream_handle = (*env)->GetFieldID(env, clazz, "handle", "J");
    CHECK_NULL(findStream_handle);
    findStream_name = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;");
    CHECK_NULL(findStream_name);

    clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$VolumeInformation");
    CHECK_NULL(clazz);
    volumeInfo_fsName = (*env)->GetFieldID(env, clazz, "fileSystemName", "Ljava/lang/String;");
    CHECK_NULL(volumeInfo_fsName);
    volumeInfo_volName = (*env)->GetFieldID(env, clazz, "volumeName", "Ljava/lang/String;");
    CHECK_NULL(volumeInfo_volName);
    volumeInfo_volSN = (*env)->GetFieldID(env, clazz, "volumeSerialNumber", "I");
    CHECK_NULL(volumeInfo_volSN);
    volumeInfo_flags = (*env)->GetFieldID(env, clazz, "flags", "I");
    CHECK_NULL(volumeInfo_flags);

    clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$DiskFreeSpace");
    CHECK_NULL(clazz);
    diskSpace_bytesAvailable = (*env)->GetFieldID(env, clazz, "freeBytesAvailable", "J");
    CHECK_NULL(diskSpace_bytesAvailable);
    diskSpace_totalBytes = (*env)->GetFieldID(env, clazz, "totalNumberOfBytes", "J");
    CHECK_NULL(diskSpace_totalBytes);
    diskSpace_totalFree = (*env)->GetFieldID(env, clazz, "totalNumberOfFreeBytes", "J");
    CHECK_NULL(diskSpace_totalFree);

    clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$Account");
    CHECK_NULL(clazz);
    account_domain = (*env)->GetFieldID(env, clazz, "domain", "Ljava/lang/String;");
    CHECK_NULL(account_domain);
    account_name = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;");
    CHECK_NULL(account_name);
    account_use = (*env)->GetFieldID(env, clazz, "use", "I");
    CHECK_NULL(account_use);

    clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$AclInformation");
    CHECK_NULL(clazz);
    aclInfo_aceCount = (*env)->GetFieldID(env, clazz, "aceCount", "I");
    CHECK_NULL(aclInfo_aceCount);

    clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$CompletionStatus");
    CHECK_NULL(clazz);
    completionStatus_error = (*env)->GetFieldID(env, clazz, "error", "I");
    CHECK_NULL(completionStatus_error);
    completionStatus_bytesTransferred = (*env)->GetFieldID(env, clazz, "bytesTransferred", "I");
    CHECK_NULL(completionStatus_bytesTransferred);
    completionStatus_completionKey = (*env)->GetFieldID(env, clazz, "completionKey", "J");
    CHECK_NULL(completionStatus_completionKey);

    clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$BackupResult");
    CHECK_NULL(clazz);
    backupResult_bytesTransferred = (*env)->GetFieldID(env, clazz, "bytesTransferred", "I");
    CHECK_NULL(backupResult_bytesTransferred);
    backupResult_context = (*env)->GetFieldID(env, clazz, "context", "J");
    CHECK_NULL(backupResult_context);

    // get handle to kernel32
    if (GetModuleHandleExW((GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
                            GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT),
                           (LPCWSTR)&CreateFileW, &h) != 0)
    {
        // requires Windows Server 2003 or newer
        FindFirstStream_func =
            (FindFirstStream_Proc)GetProcAddress(h, "FindFirstStreamW");
        FindNextStream_func =
            (FindNextStream_Proc)GetProcAddress(h, "FindNextStreamW");

        // requires Windows Vista or newer
        CreateSymbolicLink_func =
            (CreateSymbolicLinkProc)GetProcAddress(h, "CreateSymbolicLinkW");
        GetFinalPathNameByHandle_func =
            (GetFinalPathNameByHandleProc)GetProcAddress(h, "GetFinalPathNameByHandleW");
    }
}

JNIEXPORT jlong JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_CreateEvent(JNIEnv* env, jclass this,
    jboolean bManualReset, jboolean bInitialState)
{
    HANDLE hEvent = CreateEventW(NULL, bManualReset, bInitialState, NULL);
    if (hEvent == NULL) {
        throwWindowsException(env, GetLastError());
    }
    return ptr_to_jlong(hEvent);
}

JNIEXPORT jstring JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_FormatMessage(JNIEnv* env, jclass this, jint errorCode) {
    WCHAR message[255];

    DWORD len = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM,
                               NULL,
                               (DWORD)errorCode,
                               0,
                               &message[0],
                               255,
                               NULL);


    if (len == 0) {
        return NULL;
    } else {
        return (*env)->NewString(env, (const jchar *)message, (jsize)wcslen(message));
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_LocalFree(JNIEnv* env, jclass this, jlong address)
{
    HLOCAL hMem = (HLOCAL)jlong_to_ptr(address);
    LocalFree(hMem);
}

JNIEXPORT jlong JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_CreateFile0(JNIEnv* env, jclass this,
    jlong address, jint dwDesiredAccess, jint dwShareMode, jlong sdAddress,
    jint dwCreationDisposition, jint dwFlagsAndAttributes)
{
    HANDLE handle;
    LPCWSTR lpFileName = jlong_to_ptr(address);

    SECURITY_ATTRIBUTES securityAttributes;
    LPSECURITY_ATTRIBUTES lpSecurityAttributes;
    PSECURITY_DESCRIPTOR lpSecurityDescriptor = jlong_to_ptr(sdAddress);


    if (lpSecurityDescriptor == NULL) {
        lpSecurityAttributes = NULL;
    } else {
        securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
        securityAttributes.lpSecurityDescriptor = lpSecurityDescriptor;
        securityAttributes.bInheritHandle = FALSE;
        lpSecurityAttributes = &securityAttributes;
    }

    handle = CreateFileW(lpFileName,
                        (DWORD)dwDesiredAccess,
                        (DWORD)dwShareMode,
                        lpSecurityAttributes,
                        (DWORD)dwCreationDisposition,
                        (DWORD)dwFlagsAndAttributes,
                        NULL);
    if (handle == INVALID_HANDLE_VALUE) {
        throwWindowsException(env, GetLastError());
    }
    return ptr_to_jlong(handle);
}


JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_DeviceIoControlSetSparse(JNIEnv* env, jclass this,
    jlong handle)
{
    DWORD bytesReturned;
    HANDLE h = (HANDLE)jlong_to_ptr(handle);
    if (DeviceIoControl(h, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &bytesReturned, NULL) == 0) {
        throwWindowsException(env, GetLastError());
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_DeviceIoControlGetReparsePoint(JNIEnv* env, jclass this,
    jlong handle, jlong bufferAddress, jint bufferSize)
{
    DWORD bytesReturned;
    HANDLE h = (HANDLE)jlong_to_ptr(handle);
    LPVOID outBuffer = (LPVOID)jlong_to_ptr(bufferAddress);

    if (DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, outBuffer, (DWORD)bufferSize,
                        &bytesReturned, NULL) == 0)
    {
        throwWindowsException(env, GetLastError());
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_DeleteFile0(JNIEnv* env, jclass this, jlong address)
{
    LPCWSTR lpFileName = jlong_to_ptr(address);
    if (DeleteFileW(lpFileName) == 0) {
        throwWindowsException(env, GetLastError());
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_CreateDirectory0(JNIEnv* env, jclass this,
    jlong address, jlong sdAddress)
{
    LPCWSTR lpFileName = jlong_to_ptr(address);

    SECURITY_ATTRIBUTES securityAttributes;
    LPSECURITY_ATTRIBUTES lpSecurityAttributes;
    PSECURITY_DESCRIPTOR lpSecurityDescriptor = jlong_to_ptr(sdAddress);


    if (lpSecurityDescriptor == NULL) {
        lpSecurityAttributes = NULL;
    } else {
        securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
        securityAttributes.lpSecurityDescriptor = lpSecurityDescriptor;
        securityAttributes.bInheritHandle = FALSE;
        lpSecurityAttributes = &securityAttributes;
    }

    if (CreateDirectoryW(lpFileName, lpSecurityAttributes) == 0) {
        throwWindowsException(env, GetLastError());
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_RemoveDirectory0(JNIEnv* env, jclass this, jlong address)
{
    LPCWSTR lpFileName = jlong_to_ptr(address);
    if (RemoveDirectoryW(lpFileName) == 0) {
        throwWindowsException(env, GetLastError());
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_CloseHandle(JNIEnv* env, jclass this,
    jlong handle)
{
    HANDLE h = (HANDLE)jlong_to_ptr(handle);
    CloseHandle(h);
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_FindFirstFile0(JNIEnv* env, jclass this,
    jlong address, jobject obj)
{
    WIN32_FIND_DATAW data;
    LPCWSTR lpFileName = jlong_to_ptr(address);

    HANDLE handle = FindFirstFileW(lpFileName, &data);
    if (handle != INVALID_HANDLE_VALUE) {
        jstring name = (*env)->NewString(env, data.cFileName, (jsize)wcslen(data.cFileName));
        if (name == NULL)
            return;
        (*env)->SetLongField(env, obj, findFirst_handle, ptr_to_jlong(handle));
        (*env)->SetObjectField(env, obj, findFirst_name, name);
        (*env)->SetIntField(env, obj, findFirst_attributes, data.dwFileAttributes);
    } else {
        throwWindowsException(env, GetLastError());
    }
}

JNIEXPORT jlong JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_FindFirstFile1(JNIEnv* env, jclass this,
    jlong pathAddress, jlong dataAddress)
{
    LPCWSTR lpFileName = jlong_to_ptr(pathAddress);
    WIN32_FIND_DATAW* data = (WIN32_FIND_DATAW*)jlong_to_ptr(dataAddress);

    HANDLE handle = FindFirstFileW(lpFileName, data);
    if (handle == INVALID_HANDLE_VALUE) {
        throwWindowsException(env, GetLastError());
    }
    return ptr_to_jlong(handle);
}

JNIEXPORT jstring JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_FindNextFile(JNIEnv* env, jclass this,
    jlong handle, jlong dataAddress)
{
    HANDLE h = (HANDLE)jlong_to_ptr(handle);
    WIN32_FIND_DATAW* data = (WIN32_FIND_DATAW*)jlong_to_ptr(dataAddress);

    if (FindNextFileW(h, data) != 0) {
        return (*env)->NewString(env, data->cFileName, (jsize)wcslen(data->cFileName));
    } else {
    if (GetLastError() != ERROR_NO_MORE_FILES)
        throwWindowsException(env, GetLastError());
        return NULL;
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_FindFirstStream0(JNIEnv* env, jclass this,
    jlong address, jobject obj)
{
    WIN32_FIND_STREAM_DATA data;
    LPCWSTR lpFileName = jlong_to_ptr(address);
    HANDLE handle;

    if (FindFirstStream_func == NULL) {
        JNU_ThrowInternalError(env, "Should not get here");
        return;
    }

    handle = (*FindFirstStream_func)(lpFileName, FindStreamInfoStandard, &data, 0);
    if (handle != INVALID_HANDLE_VALUE) {
        jstring name = (*env)->NewString(env, data.cStreamName, (jsize)wcslen(data.cStreamName));
        if (name == NULL)
            return;
        (*env)->SetLongField(env, obj, findStream_handle, ptr_to_jlong(handle));
        (*env)->SetObjectField(env, obj, findStream_name, name);
    } else {
        if (GetLastError() == ERROR_HANDLE_EOF) {
             (*env)->SetLongField(env, obj, findStream_handle, ptr_to_jlong(handle));
        } else {
            throwWindowsException(env, GetLastError());
        }
    }

}

JNIEXPORT jstring JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_FindNextStream(JNIEnv* env, jclass this,
    jlong handle)
{
    WIN32_FIND_STREAM_DATA data;
    HANDLE h = (HANDLE)jlong_to_ptr(handle);

    if (FindNextStream_func == NULL) {
        JNU_ThrowInternalError(env, "Should not get here");
        return NULL;
    }

    if ((*FindNextStream_func)(h, &data) != 0) {
        return (*env)->NewString(env, data.cStreamName, (jsize)wcslen(data.cStreamName));
    } else {
        if (GetLastError() != ERROR_HANDLE_EOF)
            throwWindowsException(env, GetLastError());
        return NULL;
    }
}


JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_FindClose(JNIEnv* env, jclass this,
    jlong handle)
{
    HANDLE h = (HANDLE)jlong_to_ptr(handle);
    if (FindClose(h) == 0) {
        throwWindowsException(env, GetLastError());
    }
}


JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetFileInformationByHandle(JNIEnv* env, jclass this,
    jlong handle, jlong address)
{
    HANDLE h = (HANDLE)jlong_to_ptr(handle);
    BY_HANDLE_FILE_INFORMATION* info =
        (BY_HANDLE_FILE_INFORMATION*)jlong_to_ptr(address);
    if (GetFileInformationByHandle(h, info) == 0) {
        throwWindowsException(env, GetLastError());
    }
}


JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_CopyFileEx0(JNIEnv* env, jclass this,
    jlong existingAddress, jlong newAddress, jint flags, jlong cancelAddress)
{
    LPCWSTR lpExistingFileName = jlong_to_ptr(existingAddress);
    LPCWSTR lpNewFileName = jlong_to_ptr(newAddress);
    LPBOOL cancel = (LPBOOL)jlong_to_ptr(cancelAddress);
    if (CopyFileExW(lpExistingFileName, lpNewFileName, NULL, NULL, cancel,
                    (DWORD)flags) == 0)
    {
        throwWindowsException(env, GetLastError());
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_MoveFileEx0(JNIEnv* env, jclass this,
    jlong existingAddress, jlong newAddress, jint flags)
{
    LPCWSTR lpExistingFileName = jlong_to_ptr(existingAddress);
    LPCWSTR lpNewFileName = jlong_to_ptr(newAddress);
    if (MoveFileExW(lpExistingFileName, lpNewFileName, (DWORD)flags) == 0) {
        throwWindowsException(env, GetLastError());
    }
}

JNIEXPORT jint JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetLogicalDrives(JNIEnv* env, jclass this)
{
    DWORD res = GetLogicalDrives();
    if (res == 0) {
        throwWindowsException(env, GetLastError());
    }
    return (jint)res;
}

JNIEXPORT jint JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetFileAttributes0(JNIEnv* env, jclass this,
    jlong address)
{
    LPCWSTR lpFileName = jlong_to_ptr(address);
    DWORD value = GetFileAttributesW(lpFileName);

    if (value == INVALID_FILE_ATTRIBUTES) {
        throwWindowsException(env, GetLastError());
    }
    return (jint)value;
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_SetFileAttributes0(JNIEnv* env, jclass this,
    jlong address, jint value)
{
    LPCWSTR lpFileName = jlong_to_ptr(address);
    if (SetFileAttributesW(lpFileName, (DWORD)value) == 0) {
        throwWindowsException(env, GetLastError());
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetFileAttributesEx0(JNIEnv* env, jclass this,
    jlong pathAddress, jlong dataAddress)
{
    LPCWSTR lpFileName = jlong_to_ptr(pathAddress);
    WIN32_FILE_ATTRIBUTE_DATA* data = (WIN32_FILE_ATTRIBUTE_DATA*)jlong_to_ptr(dataAddress);

    BOOL res = GetFileAttributesExW(lpFileName, GetFileExInfoStandard, (LPVOID)data);
    if (res == 0)
        throwWindowsException(env, GetLastError());
}


JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_SetFileTime(JNIEnv* env, jclass this,
    jlong handle, jlong createTime, jlong lastAccessTime, jlong lastWriteTime)
{
    HANDLE h = (HANDLE)jlong_to_ptr(handle);

    if (SetFileTime(h,
        (createTime == (jlong)-1) ? NULL : (CONST FILETIME *)&createTime,
        (lastAccessTime == (jlong)-1) ? NULL : (CONST FILETIME *)&lastAccessTime,
        (lastWriteTime == (jlong)-1) ? NULL : (CONST FILETIME *)&lastWriteTime) == 0)
    {
        throwWindowsException(env, GetLastError());
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_SetEndOfFile(JNIEnv* env, jclass this,
    jlong handle)
{
    HANDLE h = (HANDLE)jlong_to_ptr(handle);

    if (SetEndOfFile(h) == 0)
        throwWindowsException(env, GetLastError());
}


JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetVolumeInformation0(JNIEnv* env, jclass this,
    jlong address, jobject obj)
{
    WCHAR volumeName[MAX_PATH+1];
    DWORD volumeSerialNumber;
    DWORD maxComponentLength;
    DWORD flags;
    WCHAR fileSystemName[MAX_PATH+1];
    LPCWSTR lpFileName = jlong_to_ptr(address);
    jstring str;

    BOOL res = GetVolumeInformationW(lpFileName,
                                     &volumeName[0],
                                     MAX_PATH+1,
                                     &volumeSerialNumber,
                                     &maxComponentLength,
                                     &flags,
                                     &fileSystemName[0],
                                     MAX_PATH+1);
    if (res == 0) {
        throwWindowsException(env, GetLastError());
        return;
    }

    str = (*env)->NewString(env, (const jchar *)fileSystemName, (jsize)wcslen(fileSystemName));
    if (str == NULL) return;
    (*env)->SetObjectField(env, obj, volumeInfo_fsName, str);

    str = (*env)->NewString(env, (const jchar *)volumeName, (jsize)wcslen(volumeName));
    if (str == NULL) return;
    (*env)->SetObjectField(env, obj, volumeInfo_volName, str);

    (*env)->SetIntField(env, obj, volumeInfo_volSN, (jint)volumeSerialNumber);
    (*env)->SetIntField(env, obj, volumeInfo_flags, (jint)flags);
}


JNIEXPORT jint JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetDriveType0(JNIEnv* env, jclass this, jlong address) {
    LPCWSTR lpRootPathName = jlong_to_ptr(address);
    return (jint)GetDriveTypeW(lpRootPathName);
}


JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetDiskFreeSpaceEx0(JNIEnv* env, jclass this,
    jlong address, jobject obj)
{
    ULARGE_INTEGER freeBytesAvailable;
    ULARGE_INTEGER totalNumberOfBytes;
    ULARGE_INTEGER totalNumberOfFreeBytes;
    LPCWSTR lpDirName = jlong_to_ptr(address);


    BOOL res = GetDiskFreeSpaceExW(lpDirName,
                                   &freeBytesAvailable,
                                   &totalNumberOfBytes,
                                   &totalNumberOfFreeBytes);
    if (res == 0) {
        throwWindowsException(env, GetLastError());
        return;
    }

    (*env)->SetLongField(env, obj, diskSpace_bytesAvailable,
        long_to_jlong(freeBytesAvailable.QuadPart));
    (*env)->SetLongField(env, obj, diskSpace_totalBytes,
        long_to_jlong(totalNumberOfBytes.QuadPart));
    (*env)->SetLongField(env, obj, diskSpace_totalFree,
        long_to_jlong(totalNumberOfFreeBytes.QuadPart));
}


JNIEXPORT jstring JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetVolumePathName0(JNIEnv* env, jclass this,
    jlong address)
{
    WCHAR volumeName[MAX_PATH+1];
    LPCWSTR lpFileName = jlong_to_ptr(address);


    BOOL res = GetVolumePathNameW(lpFileName,
                                  &volumeName[0],
                                  MAX_PATH+1);
    if (res == 0) {
        throwWindowsException(env, GetLastError());
        return NULL;
    } else {
        return (*env)->NewString(env, (const jchar *)volumeName, (jsize)wcslen(volumeName));
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_InitializeSecurityDescriptor(JNIEnv* env, jclass this,
    jlong address)
{
    PSECURITY_DESCRIPTOR pSecurityDescriptor =
        (PSECURITY_DESCRIPTOR)jlong_to_ptr(address);

    if (InitializeSecurityDescriptor(pSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION) == 0) {
        throwWindowsException(env, GetLastError());
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_InitializeAcl(JNIEnv* env, jclass this,
    jlong address, jint size)
{
    PACL pAcl = (PACL)jlong_to_ptr(address);

    if (InitializeAcl(pAcl, (DWORD)size, ACL_REVISION) == 0) {
        throwWindowsException(env, GetLastError());
    }
}


JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_SetFileSecurity0(JNIEnv* env, jclass this,
    jlong pathAddress, jint requestedInformation, jlong descAddress)
{
    LPCWSTR lpFileName = jlong_to_ptr(pathAddress);
    PSECURITY_DESCRIPTOR pSecurityDescriptor = jlong_to_ptr(descAddress);
    DWORD lengthNeeded = 0;

    BOOL res = SetFileSecurityW(lpFileName,
                                (SECURITY_INFORMATION)requestedInformation,
                                pSecurityDescriptor);

    if (res == 0) {
        throwWindowsException(env, GetLastError());
    }
}

JNIEXPORT jint JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetFileSecurity0(JNIEnv* env, jclass this,
    jlong pathAddress, jint requestedInformation, jlong descAddress, jint nLength)
{
    LPCWSTR lpFileName = jlong_to_ptr(pathAddress);
    PSECURITY_DESCRIPTOR pSecurityDescriptor = jlong_to_ptr(descAddress);
    DWORD lengthNeeded = 0;

    BOOL res = GetFileSecurityW(lpFileName,
                                (SECURITY_INFORMATION)requestedInformation,
                                pSecurityDescriptor,
                                (DWORD)nLength,
                                &lengthNeeded);

    if (res == 0) {
        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
            return (jint)lengthNeeded;
        } else {
            throwWindowsException(env, GetLastError());
            return 0;
        }
    } else {
        return (jint)nLength;
    }
}

JNIEXPORT jlong JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetSecurityDescriptorOwner(JNIEnv* env,
    jclass this, jlong address)
{
    PSECURITY_DESCRIPTOR pSecurityDescriptor = jlong_to_ptr(address);
    PSID pOwner;
    BOOL bOwnerDefaulted;


    if (GetSecurityDescriptorOwner(pSecurityDescriptor, &pOwner, &bOwnerDefaulted) == 0) {
        throwWindowsException(env, GetLastError());
    }
    return ptr_to_jlong(pOwner);
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_SetSecurityDescriptorOwner(JNIEnv* env,
    jclass this, jlong descAddress, jlong ownerAddress)
{
    PSECURITY_DESCRIPTOR pSecurityDescriptor = jlong_to_ptr(descAddress);
    PSID pOwner = jlong_to_ptr(ownerAddress);

    if (SetSecurityDescriptorOwner(pSecurityDescriptor, pOwner, FALSE) == 0) {
        throwWindowsException(env, GetLastError());
    }
}


JNIEXPORT jlong JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetSecurityDescriptorDacl(JNIEnv* env,
    jclass this, jlong address)
{
    PSECURITY_DESCRIPTOR pSecurityDescriptor = jlong_to_ptr(address);
    BOOL bDaclPresent;
    PACL pDacl;
    BOOL bDaclDefaulted;

    if (GetSecurityDescriptorDacl(pSecurityDescriptor, &bDaclPresent, &pDacl, &bDaclDefaulted) == 0) {
        throwWindowsException(env, GetLastError());
        return (jlong)0;
    } else {
        return (bDaclPresent) ? ptr_to_jlong(pDacl) : (jlong)0;
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_SetSecurityDescriptorDacl(JNIEnv* env,
    jclass this, jlong descAddress, jlong aclAddress)
{
    PSECURITY_DESCRIPTOR pSecurityDescriptor = (PSECURITY_DESCRIPTOR)jlong_to_ptr(descAddress);
    PACL pAcl = (PACL)jlong_to_ptr(aclAddress);

    if (SetSecurityDescriptorDacl(pSecurityDescriptor, TRUE, pAcl, FALSE) == 0) {
        throwWindowsException(env, GetLastError());
    }
}


JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetAclInformation0(JNIEnv* env,
    jclass this, jlong address, jobject obj)
{
    PACL pAcl = (PACL)jlong_to_ptr(address);
    ACL_SIZE_INFORMATION acl_size_info;

    if (GetAclInformation(pAcl, (void *) &acl_size_info, sizeof(acl_size_info), AclSizeInformation) == 0) {
        throwWindowsException(env, GetLastError());
    } else {
        (*env)->SetIntField(env, obj, aclInfo_aceCount, (jint)acl_size_info.AceCount);
    }
}

JNIEXPORT jlong JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetAce(JNIEnv* env, jclass this, jlong address,
    jint aceIndex)
{
    PACL pAcl = (PACL)jlong_to_ptr(address);
    LPVOID pAce;

    if (GetAce(pAcl, (DWORD)aceIndex, &pAce) == 0) {
        throwWindowsException(env, GetLastError());
        return (jlong)0;
    } else {
        return ptr_to_jlong(pAce);
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_AddAccessAllowedAceEx(JNIEnv* env,
    jclass this, jlong aclAddress, jint flags, jint mask, jlong sidAddress)
{
    PACL pAcl = (PACL)jlong_to_ptr(aclAddress);
    PSID pSid = (PSID)jlong_to_ptr(sidAddress);

    if (AddAccessAllowedAceEx(pAcl, ACL_REVISION, (DWORD)flags, (DWORD)mask, pSid) == 0) {
        throwWindowsException(env, GetLastError());
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_AddAccessDeniedAceEx(JNIEnv* env,
    jclass this, jlong aclAddress, jint flags, jint mask, jlong sidAddress)
{
    PACL pAcl = (PACL)jlong_to_ptr(aclAddress);
    PSID pSid = (PSID)jlong_to_ptr(sidAddress);

    if (AddAccessDeniedAceEx(pAcl, ACL_REVISION, (DWORD)flags, (DWORD)mask, pSid) == 0) {
        throwWindowsException(env, GetLastError());
    }
}


JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_LookupAccountSid0(JNIEnv* env,
    jclass this, jlong address, jobject obj)
{
    WCHAR domain[255];
    WCHAR name[255];
    DWORD domainLen = sizeof(domain);
    DWORD nameLen = sizeof(name);
    SID_NAME_USE use;
    PSID sid = jlong_to_ptr(address);
    jstring s;

    if (LookupAccountSidW(NULL, sid, &name[0], &nameLen, &domain[0], &domainLen, &use) == 0) {
        throwWindowsException(env, GetLastError());
        return;
    }

    s = (*env)->NewString(env, (const jchar *)domain, (jsize)wcslen(domain));
    if (s == NULL)
        return;
    (*env)->SetObjectField(env, obj, account_domain, s);

    s = (*env)->NewString(env, (const jchar *)name, (jsize)wcslen(name));
    if (s == NULL)
        return;
    (*env)->SetObjectField(env, obj, account_name, s);
    (*env)->SetIntField(env, obj, account_use, (jint)use);
}

JNIEXPORT jint JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_LookupAccountName0(JNIEnv* env,
    jclass this, jlong nameAddress, jlong sidAddress, jint cbSid)
{

    LPCWSTR accountName = jlong_to_ptr(nameAddress);
    PSID sid = jlong_to_ptr(sidAddress);
    WCHAR domain[255];
    DWORD domainLen = sizeof(domain);
    SID_NAME_USE use;

    if (LookupAccountNameW(NULL, accountName, sid, (LPDWORD)&cbSid,
                           &domain[0], &domainLen, &use) == 0)
    {
        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
            throwWindowsException(env, GetLastError());
        }
    }

    return cbSid;
}

JNIEXPORT jint JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetLengthSid(JNIEnv* env,
    jclass this, jlong address)
{
    PSID sid = jlong_to_ptr(address);
    return (jint)GetLengthSid(sid);
}


JNIEXPORT jstring JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_ConvertSidToStringSid(JNIEnv* env,
    jclass this, jlong address)
{
    PSID sid = jlong_to_ptr(address);
    LPWSTR string;
    if (ConvertSidToStringSidW(sid, &string) == 0) {
        throwWindowsException(env, GetLastError());
        return NULL;
    } else {
        jstring s = (*env)->NewString(env, (const jchar *)string,
            (jsize)wcslen(string));
        LocalFree(string);
        return s;
    }
}

JNIEXPORT jlong JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_ConvertStringSidToSid0(JNIEnv* env,
    jclass this, jlong address)
{
    LPWSTR lpStringSid = jlong_to_ptr(address);
    PSID pSid;
    if (ConvertStringSidToSidW(lpStringSid, &pSid) == 0)
        throwWindowsException(env, GetLastError());
    return ptr_to_jlong(pSid);
}

JNIEXPORT jlong JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetCurrentProcess(JNIEnv* env, jclass this) {
    HANDLE hProcess = GetCurrentProcess();
    return ptr_to_jlong(hProcess);
}

JNIEXPORT jlong JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetCurrentThread(JNIEnv* env, jclass this) {
    HANDLE hThread = GetCurrentThread();
    return ptr_to_jlong(hThread);
}

JNIEXPORT jlong JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_OpenProcessToken(JNIEnv* env,
    jclass this, jlong process, jint desiredAccess)
{
    HANDLE hProcess = (HANDLE)jlong_to_ptr(process);
    HANDLE hToken;

    if (OpenProcessToken(hProcess, (DWORD)desiredAccess, &hToken) == 0)
        throwWindowsException(env, GetLastError());
    return ptr_to_jlong(hToken);
}

JNIEXPORT jlong JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_OpenThreadToken(JNIEnv* env,
    jclass this, jlong thread, jint desiredAccess, jboolean openAsSelf)
{
    HANDLE hThread = (HANDLE)jlong_to_ptr(thread);
    HANDLE hToken;
    BOOL bOpenAsSelf = (openAsSelf == JNI_TRUE) ? TRUE : FALSE;

    if (OpenThreadToken(hThread, (DWORD)desiredAccess, bOpenAsSelf, &hToken) == 0) {
        if (GetLastError() == ERROR_NO_TOKEN)
            return (jlong)0;
        throwWindowsException(env, GetLastError());
    }
    return ptr_to_jlong(hToken);
}

JNIEXPORT jlong JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_DuplicateTokenEx(JNIEnv* env,
    jclass this, jlong token, jint desiredAccess)
{
    HANDLE hToken = (HANDLE)jlong_to_ptr(token);
    HANDLE resultToken;
    BOOL res;

    res = DuplicateTokenEx(hToken,
                           (DWORD)desiredAccess,
                           NULL,
                           SecurityImpersonation,
                           TokenImpersonation,
                           &resultToken);
    if (res == 0)
        throwWindowsException(env, GetLastError());
    return ptr_to_jlong(resultToken);
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_SetThreadToken(JNIEnv* env,
    jclass this, jlong thread, jlong token)
{
    HANDLE hThread = (HANDLE)jlong_to_ptr(thread);
    HANDLE hToken = (HANDLE)jlong_to_ptr(token);

    if (SetThreadToken(hThread, hToken) == 0)
        throwWindowsException(env, GetLastError());
}

JNIEXPORT jint JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetTokenInformation(JNIEnv* env,
    jclass this, jlong token, jint tokenInfoClass, jlong tokenInfo, jint tokenInfoLength)
{
    BOOL res;
    DWORD lengthNeeded;
    HANDLE hToken = (HANDLE)jlong_to_ptr(token);
    LPVOID result = (LPVOID)jlong_to_ptr(tokenInfo);

    res = GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)tokenInfoClass, (LPVOID)result,
                              tokenInfoLength, &lengthNeeded);
    if (res == 0) {
        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
            return (jint)lengthNeeded;
        } else {
            throwWindowsException(env, GetLastError());
            return 0;
        }
    } else {
        return tokenInfoLength;
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_AdjustTokenPrivileges(JNIEnv* env,
    jclass this, jlong token, jlong luid, jint attributes)
{
    TOKEN_PRIVILEGES privs[1];
    HANDLE hToken = (HANDLE)jlong_to_ptr(token);
    PLUID pLuid = (PLUID)jlong_to_ptr(luid);

    privs[0].PrivilegeCount = 1;
    privs[0].Privileges[0].Luid = *pLuid;
    privs[0].Privileges[0].Attributes = (DWORD)attributes;

    if (AdjustTokenPrivileges(hToken, FALSE, &privs[0], 1, NULL, NULL) == 0)
        throwWindowsException(env, GetLastError());
}

JNIEXPORT jboolean JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_AccessCheck(JNIEnv* env,
    jclass this, jlong token, jlong securityInfo, jint accessMask,
    jint genericRead, jint genericWrite, jint genericExecute, jint genericAll)
{
    HANDLE hImpersonatedToken = (HANDLE)jlong_to_ptr(token);
    PSECURITY_DESCRIPTOR security = (PSECURITY_DESCRIPTOR)jlong_to_ptr(securityInfo);
    DWORD checkAccessRights = (DWORD)accessMask;
    GENERIC_MAPPING mapping = {
        genericRead,
        genericWrite,
        genericExecute,
        genericAll};
    PRIVILEGE_SET privileges = {0};
    DWORD privilegesLength = sizeof(privileges);
    DWORD grantedAccess = 0;
    BOOL result = FALSE;

    /* checkAccessRights is in-out parameter */
    MapGenericMask(&checkAccessRights, &mapping);
    if (AccessCheck(security, hImpersonatedToken, checkAccessRights,
            &mapping, &privileges, &privilegesLength, &grantedAccess, &result) == 0)
        throwWindowsException(env, GetLastError());

    return (result == FALSE) ? JNI_FALSE : JNI_TRUE;
}

JNIEXPORT jlong JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_LookupPrivilegeValue0(JNIEnv* env,
    jclass this, jlong name)
{
    LPCWSTR lpName = (LPCWSTR)jlong_to_ptr(name);
    PLUID pLuid = LocalAlloc(0, sizeof(LUID));

    if (pLuid == NULL) {
        JNU_ThrowInternalError(env, "Unable to allocate LUID structure");
    } else {
        if (LookupPrivilegeValueW(NULL, lpName, pLuid) == 0) {
            LocalFree(pLuid);
            throwWindowsException(env, GetLastError());
            return (jlong)0;
        }
    }
    return ptr_to_jlong(pLuid);
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_CreateSymbolicLink0(JNIEnv* env,
    jclass this, jlong linkAddress, jlong targetAddress, jint flags)
{
    LPCWSTR link = jlong_to_ptr(linkAddress);
    LPCWSTR target = jlong_to_ptr(targetAddress);

    if (CreateSymbolicLink_func == NULL) {
        JNU_ThrowInternalError(env, "Should not get here");
        return;
    }

    /* On Windows 64-bit this appears to succeed even when there is insufficient privileges */
    if ((*CreateSymbolicLink_func)(link, target, (DWORD)flags) == 0)
        throwWindowsException(env, GetLastError());
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_CreateHardLink0(JNIEnv* env,
    jclass this, jlong newFileAddress, jlong existingFileAddress)
{
    LPCWSTR newFile = jlong_to_ptr(newFileAddress);
    LPCWSTR existingFile = jlong_to_ptr(existingFileAddress);

    if (CreateHardLinkW(newFile, existingFile, NULL) == 0)
        throwWindowsException(env, GetLastError());
}

JNIEXPORT jstring JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetFullPathName0(JNIEnv *env,
                                                         jclass clz,
                                                         jlong pathAddress)
{
    jstring rv = NULL;
    WCHAR *lpBuf = NULL;
    WCHAR buf[MAX_PATH];
    DWORD len;
    LPCWSTR lpFileName = jlong_to_ptr(pathAddress);

    len = GetFullPathNameW(lpFileName, MAX_PATH, buf, NULL);
    if (len > 0) {
        if (len < MAX_PATH) {
            rv = (*env)->NewString(env, buf, len);
        } else {
            len += 1;  /* return length does not include terminator */
            lpBuf = (WCHAR*)malloc(len * sizeof(WCHAR));
            if (lpBuf != NULL) {
                len = GetFullPathNameW(lpFileName, len, lpBuf, NULL);
                if (len > 0) {
                    rv = (*env)->NewString(env, lpBuf, len);
                } else {
                    JNU_ThrowInternalError(env, "GetFullPathNameW failed");
                }
                free(lpBuf);
            } else {
                JNU_ThrowOutOfMemoryError(env, "native memory allocation failure");
            }
        }
    } else {
        throwWindowsException(env, GetLastError());
    }

    return rv;
}

JNIEXPORT jstring JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetFinalPathNameByHandle(JNIEnv* env,
    jclass this, jlong handle)
{
    jstring rv = NULL;
    WCHAR *lpBuf = NULL;
    WCHAR path[MAX_PATH];
    HANDLE h = (HANDLE)jlong_to_ptr(handle);
    DWORD len;

    if (GetFinalPathNameByHandle_func == NULL) {
        JNU_ThrowInternalError(env, "Should not get here");
        return NULL;
    }

    len = (*GetFinalPathNameByHandle_func)(h, path, MAX_PATH, 0);
    if (len > 0) {
        if (len < MAX_PATH) {
            rv = (*env)->NewString(env, (const jchar *)path, (jsize)len);
        } else {
            len += 1;  /* return length does not include terminator */
            lpBuf = (WCHAR*)malloc(len * sizeof(WCHAR));
            if (lpBuf != NULL) {
                len = (*GetFinalPathNameByHandle_func)(h, lpBuf, len, 0);
                if (len > 0)  {
                    rv = (*env)->NewString(env, (const jchar *)lpBuf, (jsize)len);
                } else {
                    JNU_ThrowInternalError(env, "GetFinalPathNameByHandleW failed");
                }
                free(lpBuf);
            } else {
                JNU_ThrowOutOfMemoryError(env, "native memory allocation failure");
            }
        }
    } else {
        throwWindowsException(env, GetLastError());
    }
    return rv;
}

JNIEXPORT jlong JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_CreateIoCompletionPort(JNIEnv* env, jclass this,
    jlong fileHandle, jlong existingPort, jlong completionKey)
{
    HANDLE port = CreateIoCompletionPort((HANDLE)jlong_to_ptr(fileHandle),
                                         (HANDLE)jlong_to_ptr(existingPort),
                                         (ULONG_PTR)completionKey,
                                         0);
    if (port == NULL) {
        throwWindowsException(env, GetLastError());
    }
    return ptr_to_jlong(port);
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetQueuedCompletionStatus0(JNIEnv* env, jclass this,
    jlong completionPort, jobject obj)
{
    DWORD bytesTransferred;
    ULONG_PTR completionKey;
    OVERLAPPED *lpOverlapped;
    BOOL res;

    res = GetQueuedCompletionStatus((HANDLE)jlong_to_ptr(completionPort),
                                  &bytesTransferred,
                                  &completionKey,
                                  &lpOverlapped,
                                  INFINITE);
    if (res == 0 && lpOverlapped == NULL) {
        throwWindowsException(env, GetLastError());
    } else {
        DWORD ioResult = (res == 0) ? GetLastError() : 0;
        (*env)->SetIntField(env, obj, completionStatus_error, ioResult);
        (*env)->SetIntField(env, obj, completionStatus_bytesTransferred,
            (jint)bytesTransferred);
        (*env)->SetLongField(env, obj, completionStatus_completionKey,
            (jlong)completionKey);
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_PostQueuedCompletionStatus(JNIEnv* env, jclass this,
    jlong completionPort, jlong completionKey)
{
    BOOL res;

    res = PostQueuedCompletionStatus((HANDLE)jlong_to_ptr(completionPort),
                                     (DWORD)0,  /* dwNumberOfBytesTransferred */
                                     (ULONG_PTR)completionKey,
                                     NULL);  /* lpOverlapped */
    if (res == 0) {
        throwWindowsException(env, GetLastError());
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_CancelIo(JNIEnv* env, jclass this, jlong hFile) {
    if (CancelIo((HANDLE)jlong_to_ptr(hFile)) == 0) {
        throwWindowsException(env, GetLastError());
    }
}

JNIEXPORT jint JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_GetOverlappedResult(JNIEnv *env, jclass this,
    jlong hFile, jlong lpOverlapped)
{
    BOOL res;
    DWORD bytesTransferred = -1;

    res = GetOverlappedResult((HANDLE)jlong_to_ptr(hFile),
                              (LPOVERLAPPED)jlong_to_ptr(lpOverlapped),
                              &bytesTransferred,
                              TRUE);
    if (res == 0) {
        throwWindowsException(env, GetLastError());
    }

    return (jint)bytesTransferred;
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_ReadDirectoryChangesW(JNIEnv* env, jclass this,
    jlong hDirectory, jlong bufferAddress, jint bufferLength, jboolean watchSubTree, jint filter,
    jlong bytesReturnedAddress, jlong pOverlapped)
{
    BOOL res;
    BOOL subtree = (watchSubTree == JNI_TRUE) ? TRUE : FALSE;
    LPOVERLAPPED ov = (LPOVERLAPPED)jlong_to_ptr(pOverlapped);

    res = ReadDirectoryChangesW((HANDLE)jlong_to_ptr(hDirectory),
                                (LPVOID)jlong_to_ptr(bufferAddress),
                                (DWORD)bufferLength,
                                subtree,
                                (DWORD)filter,
                                (LPDWORD)jlong_to_ptr(bytesReturnedAddress),
                                (LPOVERLAPPED)jlong_to_ptr(pOverlapped),
                                NULL);
    if (res == 0) {
        throwWindowsException(env, GetLastError());
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_BackupRead0(JNIEnv* env, jclass this,
    jlong hFile, jlong bufferAddress, jint bufferSize, jboolean abort,
    jlong context, jobject obj)
{
    BOOL res;
    DWORD bytesTransferred;
    BOOL a = (abort == JNI_TRUE) ? TRUE : FALSE;
    VOID* pContext = (VOID*)jlong_to_ptr(context);

    res = BackupRead((HANDLE)jlong_to_ptr(hFile),
                     (LPBYTE)jlong_to_ptr(bufferAddress),
                     (DWORD)bufferSize,
                     &bytesTransferred,
                     a,
                     FALSE,
                     &pContext);
    if (res == 0) {
        throwWindowsException(env, GetLastError());
    } else {
        (*env)->SetIntField(env, obj, backupResult_bytesTransferred,
            bytesTransferred);
        (*env)->SetLongField(env, obj, backupResult_context,
            ptr_to_jlong(pContext));
    }
}

JNIEXPORT void JNICALL
Java_sun_nio_fs_WindowsNativeDispatcher_BackupSeek(JNIEnv* env, jclass this,
    jlong hFile, jlong bytesToSeek, jlong context)
{
    BOOL res;
    jint lowBytesToSeek = (jint)bytesToSeek;
    jint highBytesToSeek = (jint)(bytesToSeek >> 32);
    DWORD lowBytesSeeked;
    DWORD highBytesSeeked;
    VOID* pContext = jlong_to_ptr(context);

    res = BackupSeek((HANDLE)jlong_to_ptr(hFile),
                     (DWORD)lowBytesToSeek,
                     (DWORD)highBytesToSeek,
                     &lowBytesSeeked,
                     &highBytesSeeked,
                     &pContext);
    if (res == 0) {
        throwWindowsException(env, GetLastError());
    }
}
