[NFS]
authorPierre Schweitzer <pierre@reactos.org>
Sun, 18 Jun 2017 20:49:46 +0000 (20:49 +0000)
committerPierre Schweitzer <pierre@reactos.org>
Sun, 18 Jun 2017 20:49:46 +0000 (20:49 +0000)
Import the NFS file system mini-redirector and the associated network provider.
Not all the pieces are in place to make it working.

CORE-8204

svn path=/trunk/; revision=75105

17 files changed:
reactos/dll/CMakeLists.txt
reactos/dll/np/CMakeLists.txt [new file with mode: 0644]
reactos/dll/np/nfs/CMakeLists.txt [new file with mode: 0644]
reactos/dll/np/nfs/dllmain.c [new file with mode: 0644]
reactos/dll/np/nfs/nfs41_np.c [new file with mode: 0644]
reactos/dll/np/nfs/nfs41_np.h [new file with mode: 0644]
reactos/dll/np/nfs/nfs41_np.spec [new file with mode: 0644]
reactos/dll/np/nfs/nfsnp.rc [new file with mode: 0644]
reactos/dll/np/nfs/options.c [new file with mode: 0644]
reactos/dll/np/nfs/options.h [new file with mode: 0644]
reactos/drivers/filesystems/CMakeLists.txt
reactos/drivers/filesystems/nfs/CMakeLists.txt [new file with mode: 0644]
reactos/drivers/filesystems/nfs/nfs.rc [new file with mode: 0644]
reactos/drivers/filesystems/nfs/nfs41_debug.c [new file with mode: 0644]
reactos/drivers/filesystems/nfs/nfs41_debug.h [new file with mode: 0644]
reactos/drivers/filesystems/nfs/nfs41_driver.c [new file with mode: 0644]
reactos/drivers/filesystems/nfs/nfs41_driver.h [new file with mode: 0644]

index 3037cc6..2e913af 100644 (file)
@@ -5,6 +5,7 @@ add_subdirectory(cpl)
 add_subdirectory(directx)
 add_subdirectory(keyboard)
 add_subdirectory(nls)
+add_subdirectory(np)
 add_subdirectory(ntdll)
 add_subdirectory(opengl)
 add_subdirectory(shellext)
diff --git a/reactos/dll/np/CMakeLists.txt b/reactos/dll/np/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6a90722
--- /dev/null
@@ -0,0 +1 @@
+add_subdirectory(nfs)
diff --git a/reactos/dll/np/nfs/CMakeLists.txt b/reactos/dll/np/nfs/CMakeLists.txt
new file mode 100644 (file)
index 0000000..756be2e
--- /dev/null
@@ -0,0 +1,17 @@
+spec2def(nfs41_np.dll nfs41_np.spec ADD_IMPORTLIB)
+
+list(APPEND SOURCE
+    dllmain.c
+    nfs41_np.c
+    nfs41_np.h
+    options.c
+    options.h)
+
+include_directories(
+    ${REACTOS_SOURCE_DIR}/drivers/filesystems/nfs)
+
+add_library(nfs41_np SHARED ${SOURCE} nfsnp.rc ${CMAKE_CURRENT_BINARY_DIR}/nfs41_np.def)
+set_module_type(nfs41_np win32dll UNICODE)
+target_link_libraries(nfs41_np ${PSEH_LIB})
+add_importlibs(nfs41_np msvcrt kernel32)
+add_cd_file(TARGET nfs41_np DESTINATION reactos/system32 FOR all)
diff --git a/reactos/dll/np/nfs/dllmain.c b/reactos/dll/np/nfs/dllmain.c
new file mode 100644 (file)
index 0000000..928f44d
--- /dev/null
@@ -0,0 +1,74 @@
+/* NFSv4.1 client for Windows
+ * Copyright © 2012 The Regents of the University of Michigan
+ *
+ * Olga Kornievskaia <aglo@umich.edu>
+ * Casey Bodley <cbodley@umich.edu>
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ */
+
+/*++
+
+Copyright (c) 1989-1999  Microsoft Corporation
+
+Module Name:
+
+    dllmain.c
+
+Abstract:
+
+    This module implements the initialization routines for network
+    provider interface
+
+Notes:
+
+    This module has been built and tested only in UNICODE environment
+
+--*/
+
+#include <windows.h>
+#include <process.h>
+
+
+// NOTE:
+//
+// Function:`   DllMain
+//
+// Return:  TRUE  => Success
+//      FALSE => Failure
+
+BOOL WINAPI DllMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved)
+{
+    BOOL    bStatus = TRUE;
+
+    switch (fdwReason) {
+    case DLL_PROCESS_ATTACH:
+        break;
+
+    case DLL_PROCESS_DETACH:
+        break;
+
+    case DLL_THREAD_ATTACH:
+        break;
+
+    case DLL_THREAD_DETACH:
+        break;
+
+    default:
+        break;
+    }
+
+    return(bStatus);
+}
diff --git a/reactos/dll/np/nfs/nfs41_np.c b/reactos/dll/np/nfs/nfs41_np.c
new file mode 100644 (file)
index 0000000..d25f808
--- /dev/null
@@ -0,0 +1,910 @@
+/* NFSv4.1 client for Windows
+ * Copyright © 2012 The Regents of the University of Michigan
+ *
+ * Olga Kornievskaia <aglo@umich.edu>
+ * Casey Bodley <cbodley@umich.edu>
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ */
+
+#include <windows.h>
+#include <npapi.h>
+#include <devioctl.h>
+#include <strsafe.h>
+
+#include "nfs41_driver.h"
+#include "nfs41_np.h"
+#include "options.h"
+
+#ifdef DBG
+#define DbgP(_x_) NFS41DbgPrint _x_
+#else
+#define DbgP(_x_)
+#endif
+#define TRACE_TAG   L"[NFS41_NP]"
+#define WNNC_DRIVER( major, minor ) ( major * 0x00010000 + minor )
+
+
+ULONG _cdecl NFS41DbgPrint( __in LPTSTR Format, ... )
+{
+    ULONG rc = 0;
+    TCHAR szbuffer[256];
+
+    va_list marker;
+    va_start( marker, Format );
+    {
+
+        //StringCchVPrintfW( szbuffer, 127, Format, marker );
+        StringCchVPrintfW( szbuffer, 256, Format, marker );
+        szbuffer[255] = (TCHAR)0;
+        OutputDebugString( TRACE_TAG );
+        OutputDebugString( szbuffer );
+    }
+
+    return rc;
+}
+
+int filter(unsigned int code)
+{
+    DbgP((L"####Got exception %u\n", code));
+    return EXCEPTION_CONTINUE_SEARCH;
+}
+
+DWORD
+OpenSharedMemory(
+    PHANDLE phMutex,
+    PHANDLE phMemory,
+    PVOID   *pMemory)
+/*++
+
+Routine Description:
+
+    This routine opens the shared memory for exclusive manipulation
+
+Arguments:
+
+    phMutex - the mutex handle
+
+    phMemory - the memory handle
+
+    pMemory - a ptr. to the shared memory which is set if successful
+
+Return Value:
+
+    WN_SUCCESS -- if successful
+
+--*/
+{
+    DWORD dwStatus;
+
+    *phMutex = 0;
+    *phMemory = 0;
+    *pMemory = NULL;
+
+    *phMutex = CreateMutex(NULL, FALSE, TEXT(NFS41NP_MUTEX_NAME));
+    if (*phMutex == NULL)
+    {
+        dwStatus = GetLastError();
+        DbgP((TEXT("OpenSharedMemory:  OpenMutex failed\n")));
+        goto OpenSharedMemoryAbort1;
+    }
+
+    WaitForSingleObject(*phMutex, INFINITE);
+
+    *phMemory = OpenFileMapping(FILE_MAP_WRITE,
+                                FALSE,
+                                TEXT(NFS41_USER_SHARED_MEMORY_NAME));
+    if (*phMemory == NULL)
+    {
+        dwStatus = GetLastError();
+        DbgP((TEXT("OpenSharedMemory:  OpenFileMapping failed\n")));
+        goto OpenSharedMemoryAbort2;
+    }
+
+    *pMemory = MapViewOfFile(*phMemory, FILE_MAP_WRITE, 0, 0, 0);
+    if (*pMemory == NULL)
+    {
+        dwStatus = GetLastError();
+        DbgP((TEXT("OpenSharedMemory:  MapViewOfFile failed\n")));
+        goto OpenSharedMemoryAbort3;
+    }
+
+    return ERROR_SUCCESS;
+
+OpenSharedMemoryAbort3:
+    CloseHandle(*phMemory);
+
+OpenSharedMemoryAbort2:
+    ReleaseMutex(*phMutex);
+    CloseHandle(*phMutex);
+    *phMutex = NULL;
+
+OpenSharedMemoryAbort1:
+    DbgP((TEXT("OpenSharedMemory: return dwStatus: %d\n"), dwStatus));
+
+    return dwStatus;
+}
+
+VOID
+CloseSharedMemory(
+    PHANDLE hMutex,
+    PHANDLE hMemory,
+    PVOID   *pMemory)
+/*++
+
+Routine Description:
+
+    This routine relinquishes control of the shared memory after exclusive
+    manipulation
+
+Arguments:
+
+    hMutex - the mutex handle
+
+    hMemory  - the memory handle
+
+    pMemory - a ptr. to the shared memory which is set if successful
+
+Return Value:
+
+--*/
+{
+    if (*pMemory)
+    {
+        UnmapViewOfFile(*pMemory);
+        *pMemory = NULL;
+    }
+    if (*hMemory)
+    {
+        CloseHandle(*hMemory);
+        *hMemory = 0;
+    }
+    if (*hMutex)
+    {
+        if (ReleaseMutex(*hMutex) == FALSE)
+        {
+            DbgP((TEXT("CloseSharedMemory: ReleaseMutex error: %d\n"), GetLastError()));
+        }
+        CloseHandle(*hMutex);
+        *hMutex = 0;
+    }
+}
+
+static DWORD StoreConnectionInfo(
+    IN LPCWSTR LocalName,
+    IN LPCWSTR ConnectionName,
+    IN USHORT ConnectionNameLength,
+    IN LPNETRESOURCE lpNetResource)
+{
+    DWORD status;
+    HANDLE hMutex, hMemory;
+    PNFS41NP_SHARED_MEMORY pSharedMemory;
+    PNFS41NP_NETRESOURCE pNfs41NetResource;
+    INT Index;
+    BOOLEAN FreeEntryFound = FALSE;
+
+#ifdef __REACTOS__
+    status = OpenSharedMemory(&hMutex, &hMemory, (PVOID *)&pSharedMemory);
+#else
+    status = OpenSharedMemory(&hMutex, &hMemory, &(PVOID)pSharedMemory);
+#endif
+    if (status)
+        goto out;
+
+    DbgP((TEXT("StoreConnectionInfo: NextIndex %d, NumResources %d\n"),
+        pSharedMemory->NextAvailableIndex,
+        pSharedMemory->NumberOfResourcesInUse));
+
+    for (Index = 0; Index < pSharedMemory->NextAvailableIndex; Index++)
+    {
+        if (!pSharedMemory->NetResources[Index].InUse)
+        {
+            FreeEntryFound = TRUE;
+            DbgP((TEXT("Reusing existing index %d\n"), Index));
+            break;
+        }
+    }
+
+    if (!FreeEntryFound)
+    {
+        if (pSharedMemory->NextAvailableIndex >= NFS41NP_MAX_DEVICES) {
+            status = WN_NO_MORE_DEVICES;
+            goto out_close;
+        }
+        Index = pSharedMemory->NextAvailableIndex++;
+        DbgP((TEXT("Using new index %d\n"), Index));
+    }
+
+    pSharedMemory->NumberOfResourcesInUse += 1;
+
+    pNfs41NetResource = &pSharedMemory->NetResources[Index];
+
+    pNfs41NetResource->InUse                = TRUE;
+    pNfs41NetResource->dwScope              = lpNetResource->dwScope;
+    pNfs41NetResource->dwType               = lpNetResource->dwType;
+    pNfs41NetResource->dwDisplayType        = lpNetResource->dwDisplayType;
+    pNfs41NetResource->dwUsage              = RESOURCEUSAGE_CONNECTABLE;
+    pNfs41NetResource->LocalNameLength      = (USHORT)(wcslen(LocalName) + 1) * sizeof(WCHAR);
+    pNfs41NetResource->RemoteNameLength     = (USHORT)(wcslen(lpNetResource->lpRemoteName) + 1) * sizeof(WCHAR);
+    pNfs41NetResource->ConnectionNameLength = ConnectionNameLength;
+
+    StringCchCopy(pNfs41NetResource->LocalName,
+        pNfs41NetResource->LocalNameLength,
+        LocalName);
+    StringCchCopy(pNfs41NetResource->RemoteName,
+        pNfs41NetResource->RemoteNameLength,
+        lpNetResource->lpRemoteName);
+    StringCchCopy(pNfs41NetResource->ConnectionName,
+        pNfs41NetResource->ConnectionNameLength,
+        ConnectionName);
+
+    // TODO: copy mount options -cbodley
+
+out_close:
+#ifdef __REACTOS__
+    CloseSharedMemory(&hMutex, &hMemory, (PVOID *)&pSharedMemory);
+#else
+    CloseSharedMemory(&hMutex, &hMemory, &(PVOID)pSharedMemory);
+#endif
+out:
+    return status;
+}
+
+ULONG
+SendTo_NFS41Driver(
+    IN ULONG            IoctlCode,
+    IN PVOID            InputDataBuf,
+    IN ULONG            InputDataLen,
+    IN PVOID            OutputDataBuf,
+    IN PULONG           pOutputDataLen)
+{
+    HANDLE  DeviceHandle;       // The mini rdr device handle
+    BOOL    rc = FALSE;
+    ULONG   Status;
+
+    Status = WN_SUCCESS;
+    DbgP((L"[aglo] calling CreateFile\n"));
+    DeviceHandle = CreateFile(
+        NFS41_USER_DEVICE_NAME,
+        GENERIC_READ | GENERIC_WRITE,
+        FILE_SHARE_READ | FILE_SHARE_WRITE,
+        (LPSECURITY_ATTRIBUTES)NULL,
+        OPEN_EXISTING,
+        0,
+        (HANDLE) NULL );
+
+    DbgP((L"[aglo] after CreateFile Device Handle\n"));
+    if ( INVALID_HANDLE_VALUE != DeviceHandle )
+    {
+        _SEH2_TRY {
+        DbgP((L"[aglo] calling DeviceIoControl\n"));
+        rc = DeviceIoControl(
+            DeviceHandle,
+            IoctlCode,
+            InputDataBuf,
+            InputDataLen,
+            OutputDataBuf,
+            *pOutputDataLen,
+            pOutputDataLen,
+            NULL );
+        } _SEH2_EXCEPT(_SEH2_GetExceptionCode()) {
+            DbgP((L"#### In except\n"));
+        } _SEH2_END;
+        DbgP((L"[aglo] returned from DeviceIoControl %08lx\n", rc));
+            if ( !rc )
+            {
+                DbgP((L"[aglo] SendTo_NFS41Driver: returning error from DeviceIoctl\n"));
+                Status = GetLastError( );
+            }
+            else
+            {
+                DbgP((L"[aglo] SendTo_NFS41Driver: The DeviceIoctl call succeded\n"));
+            }
+            CloseHandle(DeviceHandle);
+    }
+    else
+    {
+        Status = GetLastError( );
+        DbgP((L"[aglo] SendTo_NFS41Driver: error %08lx opening device \n", Status));
+    }
+    DbgP((L"[aglo] returned from SendTo_NFS41Driver %08lx\n", Status));
+    return Status;
+}
+
+DWORD APIENTRY
+NPGetCaps(
+    DWORD nIndex )
+{
+   DWORD rc = 0;
+
+    DbgP(( L"[aglo] GetNetCaps %d\n", nIndex ));
+    switch ( nIndex )
+    {
+        case WNNC_SPEC_VERSION:
+            rc = WNNC_SPEC_VERSION51;
+            break;
+
+        case WNNC_NET_TYPE:
+            rc = WNNC_NET_RDR2SAMPLE;
+            break;
+
+        case WNNC_DRIVER_VERSION:
+            rc = WNNC_DRIVER(1, 0);
+            break;
+
+        case WNNC_CONNECTION:
+            rc = WNNC_CON_GETCONNECTIONS |
+                 WNNC_CON_CANCELCONNECTION |
+                 WNNC_CON_ADDCONNECTION |
+                 WNNC_CON_ADDCONNECTION3;
+            break;
+
+        case WNNC_ENUMERATION:
+            rc = WNNC_ENUM_LOCAL;
+            break;
+
+        case WNNC_START:
+            rc = 1;
+            break;
+
+        case WNNC_USER:
+        case WNNC_DIALOG:
+        case WNNC_ADMIN:
+        default:
+            rc = 0;
+            break;
+    }
+
+    return rc;
+}
+
+DWORD APIENTRY
+NPLogonNotify(
+    __in PLUID   lpLogonId,
+    __in PCWSTR lpAuthentInfoType,
+    __in PVOID  lpAuthentInfo,
+    __in PCWSTR lpPreviousAuthentInfoType,
+    __in PVOID  lpPreviousAuthentInfo,
+    __in PWSTR  lpStationName,
+    __in PVOID  StationHandle,
+    __out PWSTR  *lpLogonScript)
+{
+    *lpLogonScript = NULL;
+    DbgP(( L"[aglo] NPLogonNotify: returning WN_SUCCESS\n" ));
+    return WN_SUCCESS;
+}
+
+DWORD APIENTRY
+NPPasswordChangeNotify (
+    __in LPCWSTR lpAuthentInfoType,
+    __in LPVOID  lpAuthentInfo,
+    __in LPCWSTR lpPreviousAuthentInfoType,
+    __in LPVOID  lpPreviousAuthentInfo,
+    __in LPWSTR  lpStationName,
+    LPVOID  StationHandle,
+    DWORD   dwChangeInfo )
+{
+    DbgP(( L"[aglo] NPPasswordChangeNotify: WN_NOT_SUPPORTED\n" ));
+    SetLastError( WN_NOT_SUPPORTED );
+    return WN_NOT_SUPPORTED;
+}
+
+#ifdef __REACTOS__
+DWORD APIENTRY
+NPAddConnection3(
+    __in HWND           hwndOwner,
+    __in LPNETRESOURCE  lpNetResource,
+    __in_opt LPWSTR     lpPassword,
+    __in_opt LPWSTR     lpUserName,
+    __in DWORD          dwFlags);
+#endif
+
+DWORD APIENTRY
+NPAddConnection(
+    __in LPNETRESOURCE   lpNetResource,
+    __in_opt LPWSTR      lpPassword,
+    __in_opt LPWSTR      lpUserName )
+{
+    return NPAddConnection3( NULL, lpNetResource, lpPassword, lpUserName, 0 );
+}
+
+DWORD APIENTRY
+NPAddConnection3(
+    __in HWND           hwndOwner,
+    __in LPNETRESOURCE  lpNetResource,
+    __in_opt LPWSTR     lpPassword,
+    __in_opt LPWSTR     lpUserName,
+    __in DWORD          dwFlags)
+{
+    DWORD   Status;
+    WCHAR   wszScratch[128];
+    WCHAR   LocalName[3];
+    DWORD   CopyBytes = 0;
+    CONNECTION_INFO Connection;
+    LPWSTR  ConnectionName;
+    WCHAR ServerName[MAX_PATH];
+    PWCHAR p;
+    DWORD i;
+
+    DbgP(( L"[aglo] NPAddConnection3('%s', '%s')\n",
+        lpNetResource->lpLocalName, lpNetResource->lpRemoteName ));
+    DbgP(( L"[aglo] username = '%s', passwd = '%s'\n", lpUserName, lpPassword));
+
+    Status = InitializeConnectionInfo(&Connection,
+        (PMOUNT_OPTION_BUFFER)lpNetResource->lpComment,
+        &ConnectionName);
+    if (Status)  {
+        DbgP(( L"InitializeConnectionInfo failed with %d\n", Status ));
+        goto out;
+    }
+
+    //  \device\miniredirector\;<DriveLetter>:\Server\Share
+
+    // local name, must start with "X:"
+    if (lstrlen(lpNetResource->lpLocalName) < 2 ||
+        lpNetResource->lpLocalName[1] != L':') {
+        Status = WN_BAD_LOCALNAME;
+        goto out;
+    }
+
+    LocalName[0] = (WCHAR) toupper(lpNetResource->lpLocalName[0]);
+    LocalName[1] = L':';
+    LocalName[2] = L'\0';
+    StringCchCopyW( ConnectionName, MAX_PATH, NFS41_DEVICE_NAME );
+    StringCchCatW( ConnectionName, MAX_PATH, L"\\;" );
+    StringCchCatW( ConnectionName, MAX_PATH, LocalName );
+
+    // remote name, must start with "\\"
+    if (lpNetResource->lpRemoteName[0] == L'\0' ||
+        lpNetResource->lpRemoteName[0] != L'\\' ||
+        lpNetResource->lpRemoteName[1] != L'\\') {
+        Status = WN_BAD_NETNAME;
+        goto out;
+    }
+
+    /* note: remotename comes as \\server but we need to add \server thus +1 pointer */
+    p = lpNetResource->lpRemoteName + 1;
+    ServerName[0] = L'\\';
+    i = 1;
+    for(;;) {
+        /* convert servername ending unix slash to windows slash */
+        if (p[i] == L'/')
+            p[i] = L'\\';
+        /* deal with servername ending with any slash */
+        if (p[i] == L'\0')
+            p[i] = L'\\';
+        ServerName[i] = p[i];
+        if (p[i] == L'\\') break;
+        i++;
+    }
+    ServerName[i] = L'\0';
+    StringCchCatW( ConnectionName, MAX_PATH, ServerName);
+    /* insert the "nfs4" in between the server name and the path,
+     * just to make sure all calls to our driver come thru this */
+    StringCchCatW( ConnectionName, MAX_PATH, L"\\nfs4" );
+
+#ifdef CONVERT_2_UNIX_SLASHES
+    /* convert all windows slashes to unix slashes */
+    {
+        PWCHAR q = p;
+        DWORD j = 0;
+        for(;;) {
+            if(q[j] == L'\0') break;
+            if (q[j] == L'\\') q[j] = L'/';
+            j++;
+        }
+    }
+#else
+    /* convert all unix slashes to windows slashes */
+    {
+        PWCHAR q = p;
+        DWORD j = 0;
+        for(;;) {
+            if(q[j] == L'\0') break;
+            if (q[j] == L'/') q[j] = L'\\';
+            j++;
+        }
+    }
+#endif
+    StringCchCatW( ConnectionName, MAX_PATH, &p[i]);
+    DbgP(( L"[aglo] Full Connect Name: %s\n", ConnectionName ));
+    DbgP(( L"[aglo] Full Connect Name Length: %d %d\n",
+        (wcslen(ConnectionName) + 1) * sizeof(WCHAR),
+        (lstrlen(ConnectionName) + 1) * sizeof(WCHAR)));
+
+    if ( QueryDosDevice( LocalName, wszScratch, 128 )
+        || GetLastError() != ERROR_FILE_NOT_FOUND) {
+        Status = WN_ALREADY_CONNECTED;
+        goto out;
+    }
+
+    MarshalConnectionInfo(&Connection);
+
+    Status = SendTo_NFS41Driver( IOCTL_NFS41_ADDCONN,
+        Connection.Buffer, Connection.BufferSize,
+        NULL, &CopyBytes );
+    if (Status) {
+        DbgP(( L"[aglo] SendTo_NFS41Driver failed with %d\n", Status));
+        goto out;
+    }
+
+    DbgP(( L"[aglo] calling DefineDosDevice\n"));
+    if ( !DefineDosDevice( DDD_RAW_TARGET_PATH |
+                           DDD_NO_BROADCAST_SYSTEM,
+                           lpNetResource->lpLocalName,
+                           ConnectionName ) ) {
+        Status = GetLastError();
+        DbgP(( L"[aglo] DefineDosDevice failed with %d\n", Status));
+        goto out_delconn;
+    }
+
+    // The connection was established and the local device mapping
+    // added. Include this in the list of mapped devices.
+    Status = StoreConnectionInfo(LocalName, ConnectionName,
+        Connection.Buffer->NameLength, lpNetResource);
+    if (Status) {
+        DbgP(( L"[aglo] StoreConnectionInfo failed with %d\n", Status));
+        goto out_undefine;
+    }
+
+out:
+    FreeConnectionInfo(&Connection);
+    DbgP(( L"[aglo] NPAddConnection3: status %08X\n", Status));
+    return Status;
+out_undefine:
+    DefineDosDevice(DDD_REMOVE_DEFINITION | DDD_RAW_TARGET_PATH |
+        DDD_EXACT_MATCH_ON_REMOVE, LocalName, ConnectionName);
+out_delconn:
+    SendTo_NFS41Driver(IOCTL_NFS41_DELCONN, ConnectionName,
+        Connection.Buffer->NameLength, NULL, &CopyBytes);
+    goto out;
+}
+
+DWORD APIENTRY
+NPCancelConnection(
+    __in LPWSTR  lpName,
+    __in BOOL    fForce )
+{
+    DWORD   Status = 0;
+
+    HANDLE  hMutex, hMemory;
+    PNFS41NP_SHARED_MEMORY  pSharedMemory;
+
+    DbgP((TEXT("NPCancelConnection\n")));
+    DbgP((TEXT("NPCancelConnection: ConnectionName: %S\n"), lpName));
+
+    Status = OpenSharedMemory( &hMutex,
+                               &hMemory,
+                               (PVOID)&pSharedMemory);
+
+    if (Status == WN_SUCCESS)
+    {
+        INT  Index;
+        PNFS41NP_NETRESOURCE pNetResource;
+        Status = WN_NOT_CONNECTED;
+
+        DbgP((TEXT("NPCancelConnection: NextIndex %d, NumResources %d\n"),
+                    pSharedMemory->NextAvailableIndex,
+                    pSharedMemory->NumberOfResourcesInUse));
+
+        for (Index = 0; Index < pSharedMemory->NextAvailableIndex; Index++)
+        {
+            pNetResource = &pSharedMemory->NetResources[Index];
+
+            if (pNetResource->InUse)
+            {
+                if ( ( (wcslen(lpName) + 1) * sizeof(WCHAR) ==
+                        pNetResource->LocalNameLength)
+                        && ( !wcscmp(lpName, pNetResource->LocalName) ))
+                {
+                    ULONG CopyBytes;
+
+                    DbgP((TEXT("NPCancelConnection: Connection Found:\n")));
+
+                    CopyBytes = 0;
+
+                    Status = SendTo_NFS41Driver( IOCTL_NFS41_DELCONN,
+                                pNetResource->ConnectionName,
+                                pNetResource->ConnectionNameLength,
+                                NULL,
+                                &CopyBytes );
+
+                    if (Status != WN_SUCCESS)
+                    {
+                        DbgP((TEXT("NPCancelConnection: SendToMiniRdr returned Status %lx\n"),Status));
+                        break;
+                    }
+
+                    if (DefineDosDevice(DDD_REMOVE_DEFINITION | DDD_RAW_TARGET_PATH | DDD_EXACT_MATCH_ON_REMOVE,
+                            lpName,
+                            pNetResource->ConnectionName) == FALSE)
+                    {
+                        DbgP((TEXT("RemoveDosDevice:  DefineDosDevice error: %d\n"), GetLastError()));
+                        Status = GetLastError();
+                    }
+                    else
+                    {
+                        pNetResource->InUse = FALSE;
+                        pSharedMemory->NumberOfResourcesInUse--;
+
+                        if (Index+1 == pSharedMemory->NextAvailableIndex)
+                            pSharedMemory->NextAvailableIndex--;
+                    }
+                    break;
+                }
+
+                DbgP((TEXT("NPCancelConnection: Name %S EntryName %S\n"),
+                            lpName,pNetResource->LocalName));
+                DbgP((TEXT("NPCancelConnection: Name Length %d Entry Name Length %d\n"),
+                           pNetResource->LocalNameLength,pNetResource->LocalName));
+
+            }
+        }
+
+        CloseSharedMemory( &hMutex,
+                           &hMemory,
+                          (PVOID)&pSharedMemory);
+    }
+
+    return Status;
+}
+
+DWORD APIENTRY
+NPGetConnection(
+    __in LPWSTR  lpLocalName,
+    __out_bcount(*lpBufferSize) LPWSTR  lpRemoteName,
+    __inout LPDWORD lpBufferSize )
+{
+    DWORD   Status = 0;
+
+    HANDLE  hMutex, hMemory;
+    PNFS41NP_SHARED_MEMORY  pSharedMemory;
+
+    Status = OpenSharedMemory( &hMutex,
+                               &hMemory,
+                               (PVOID)&pSharedMemory);
+
+    if (Status == WN_SUCCESS)
+    {
+        INT  Index;
+        PNFS41NP_NETRESOURCE pNetResource;
+        Status = WN_NOT_CONNECTED;
+
+        for (Index = 0; Index < pSharedMemory->NextAvailableIndex; Index++)
+        {
+            pNetResource = &pSharedMemory->NetResources[Index];
+
+            if (pNetResource->InUse)
+            {
+                if ( ( (wcslen(lpLocalName) + 1) * sizeof(WCHAR) ==
+                        pNetResource->LocalNameLength)
+                        && ( !wcscmp(lpLocalName, pNetResource->LocalName) ))
+                {
+                    if (*lpBufferSize < pNetResource->RemoteNameLength)
+                    {
+                        *lpBufferSize = pNetResource->RemoteNameLength;
+                        Status = WN_MORE_DATA;
+                    }
+                    else
+                    {
+                        *lpBufferSize = pNetResource->RemoteNameLength;
+                        CopyMemory( lpRemoteName,
+                                    pNetResource->RemoteName,
+                                    pNetResource->RemoteNameLength);
+                        Status = WN_SUCCESS;
+                    }
+                    break;
+                }
+            }
+        }
+
+        CloseSharedMemory( &hMutex, &hMemory, (PVOID)&pSharedMemory);
+    }
+
+    return Status;
+}
+
+DWORD APIENTRY
+NPOpenEnum(
+    DWORD          dwScope,
+    DWORD          dwType,
+    DWORD          dwUsage,
+    LPNETRESOURCE  lpNetResource,
+    LPHANDLE       lphEnum )
+{
+    DWORD   Status;
+
+    DbgP((L"[aglo] NPOpenEnum\n"));
+
+    *lphEnum = NULL;
+
+    switch ( dwScope )
+    {
+        case RESOURCE_CONNECTED:
+        {
+            *lphEnum = HeapAlloc( GetProcessHeap( ), HEAP_ZERO_MEMORY, sizeof( ULONG ) );
+
+            if (*lphEnum )
+            {
+                Status = WN_SUCCESS;
+            }
+            else
+            {
+                Status = WN_OUT_OF_MEMORY;
+            }
+            break;
+        }
+        break;
+
+        case RESOURCE_CONTEXT:
+        default:
+            Status  = WN_NOT_SUPPORTED;
+            break;
+    }
+
+
+    DbgP((L"[aglo] NPOpenEnum returning Status %lx\n",Status));
+
+    return(Status);
+}
+
+DWORD APIENTRY
+NPEnumResource(
+    HANDLE  hEnum,
+    LPDWORD lpcCount,
+    LPVOID  lpBuffer,
+    LPDWORD lpBufferSize)
+{
+    DWORD           Status = WN_SUCCESS;
+    ULONG           EntriesCopied;
+    LPNETRESOURCE   pNetResource;
+    ULONG           SpaceNeeded = 0;
+    ULONG           SpaceAvailable;
+    PWCHAR          StringZone;
+    HANDLE  hMutex, hMemory;
+    PNFS41NP_SHARED_MEMORY  pSharedMemory;
+    PNFS41NP_NETRESOURCE pNfsNetResource;
+    INT  Index = *(PULONG)hEnum;
+
+
+    DbgP((L"[aglo] NPEnumResource\n"));
+
+    DbgP((L"[aglo] NPEnumResource Count Requested %d\n", *lpcCount));
+
+    pNetResource = (LPNETRESOURCE) lpBuffer;
+    SpaceAvailable = *lpBufferSize;
+    EntriesCopied = 0;
+    StringZone = (PWCHAR) ((PBYTE)lpBuffer + *lpBufferSize);
+
+    Status = OpenSharedMemory( &hMutex,
+                               &hMemory,
+                               (PVOID)&pSharedMemory);
+
+    if ( Status == WN_SUCCESS)
+    {
+        Status = WN_NO_MORE_ENTRIES;
+        for (Index = *(PULONG)hEnum; EntriesCopied < *lpcCount &&
+                Index < pSharedMemory->NextAvailableIndex; Index++)
+        {
+            pNfsNetResource = &pSharedMemory->NetResources[Index];
+
+            if (pNfsNetResource->InUse)
+            {
+                SpaceNeeded  = sizeof( NETRESOURCE );
+                SpaceNeeded += pNfsNetResource->LocalNameLength;
+                SpaceNeeded += pNfsNetResource->RemoteNameLength;
+                SpaceNeeded += 5 * sizeof(WCHAR);               // comment
+                SpaceNeeded += sizeof(NFS41_PROVIDER_NAME_U);  // provider name
+                if ( SpaceNeeded > SpaceAvailable )
+                {
+                    Status = WN_MORE_DATA;
+                    DbgP((L"[aglo] NPEnumResource More Data Needed - %d\n", SpaceNeeded));
+                    *lpBufferSize = SpaceNeeded;
+                    break;
+                }
+                else
+                {
+                    SpaceAvailable -= SpaceNeeded;
+
+                    pNetResource->dwScope       = pNfsNetResource->dwScope;
+                    pNetResource->dwType        = pNfsNetResource->dwType;
+                    pNetResource->dwDisplayType = pNfsNetResource->dwDisplayType;
+                    pNetResource->dwUsage       = pNfsNetResource->dwUsage;
+
+                    // setup string area at opposite end of buffer
+                    SpaceNeeded -= sizeof( NETRESOURCE );
+                    StringZone = (PWCHAR)( (PBYTE) StringZone - SpaceNeeded );
+                    // copy local name
+                    StringCchCopy( StringZone,
+                        pNfsNetResource->LocalNameLength,
+                        pNfsNetResource->LocalName );
+                    pNetResource->lpLocalName = StringZone;
+                    StringZone += pNfsNetResource->LocalNameLength/sizeof(WCHAR);
+                    // copy remote name
+                    StringCchCopy( StringZone,
+                        pNfsNetResource->RemoteNameLength,
+                        pNfsNetResource->RemoteName );
+                    pNetResource->lpRemoteName = StringZone;
+                    StringZone += pNfsNetResource->RemoteNameLength/sizeof(WCHAR);
+                    // copy comment
+                    pNetResource->lpComment = StringZone;
+                    *StringZone++ = L'A';
+                    *StringZone++ = L'_';
+                    *StringZone++ = L'O';
+                    *StringZone++ = L'K';
+                    *StringZone++ = L'\0';
+                    // copy provider name
+                    pNetResource->lpProvider = StringZone;
+                    StringCbCopyW( StringZone, sizeof(NFS41_PROVIDER_NAME_U), NFS41_PROVIDER_NAME_U );
+                    StringZone += sizeof(NFS41_PROVIDER_NAME_U)/sizeof(WCHAR);
+                    EntriesCopied++;
+                    // set new bottom of string zone
+                    StringZone = (PWCHAR)( (PBYTE) StringZone - SpaceNeeded );
+                    Status = WN_SUCCESS;
+                }
+                pNetResource++;
+            }
+        }
+        CloseSharedMemory( &hMutex, &hMemory, (PVOID*)&pSharedMemory);
+    }
+
+    *lpcCount = EntriesCopied;
+    *(PULONG) hEnum = Index;
+
+    DbgP((L"[aglo] NPEnumResource entries returned: %d\n", EntriesCopied));
+
+    return Status;
+}
+
+DWORD APIENTRY
+NPCloseEnum(
+    HANDLE hEnum )
+{
+    DbgP((L"[aglo] NPCloseEnum\n"));
+    HeapFree( GetProcessHeap( ), 0, (PVOID) hEnum );
+    return WN_SUCCESS;
+}
+
+DWORD APIENTRY
+NPGetResourceParent(
+    LPNETRESOURCE   lpNetResource,
+    LPVOID  lpBuffer,
+    LPDWORD lpBufferSize )
+{
+    DbgP(( L"[aglo] NPGetResourceParent: WN_NOT_SUPPORTED\n" ));
+    return WN_NOT_SUPPORTED;
+}
+
+DWORD APIENTRY
+NPGetResourceInformation(
+    __in LPNETRESOURCE   lpNetResource,
+    __out_bcount(*lpBufferSize) LPVOID  lpBuffer,
+    __inout LPDWORD lpBufferSize,
+    __deref_out LPWSTR *lplpSystem )
+{
+    DbgP(( L"[aglo] NPGetResourceInformation: WN_NOT_SUPPORTED\n" ));
+    return WN_NOT_SUPPORTED;
+}
+
+DWORD APIENTRY
+NPGetUniversalName(
+    LPCWSTR lpLocalPath,
+    DWORD   dwInfoLevel,
+    LPVOID  lpBuffer,
+    LPDWORD lpBufferSize )
+{
+    DbgP(( L"[aglo] NPGetUniversalName: WN_NOT_SUPPORTED\n" ));
+    return WN_NOT_SUPPORTED;
+}
diff --git a/reactos/dll/np/nfs/nfs41_np.h b/reactos/dll/np/nfs/nfs41_np.h
new file mode 100644 (file)
index 0000000..710246e
--- /dev/null
@@ -0,0 +1,50 @@
+/* NFSv4.1 client for Windows
+ * Copyright © 2012 The Regents of the University of Michigan
+ *
+ * Olga Kornievskaia <aglo@umich.edu>
+ * Casey Bodley <cbodley@umich.edu>
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ */
+
+#ifndef __NFS41_NP_H__
+#define __NFS41_NP_H__
+
+#define NFS41NP_MUTEX_NAME  "NFS41NPMUTEX"
+
+#define NFS41NP_MAX_DEVICES 26
+
+typedef struct __NFS41NP_NETRESOURCE {
+    BOOL    InUse;
+    USHORT  LocalNameLength;
+    USHORT  RemoteNameLength;
+    USHORT  ConnectionNameLength;
+    DWORD   dwScope;
+    DWORD   dwType;
+    DWORD   dwDisplayType;
+    DWORD   dwUsage;
+    WCHAR   LocalName[MAX_PATH];
+    WCHAR   RemoteName[MAX_PATH];
+    WCHAR   ConnectionName[MAX_PATH];
+    WCHAR   Options[MAX_PATH];
+} NFS41NP_NETRESOURCE, *PNFS41NP_NETRESOURCE;
+
+typedef struct __NFS41NP_SHARED_MEMORY {
+    INT                 NextAvailableIndex;
+    INT                 NumberOfResourcesInUse;
+    NFS41NP_NETRESOURCE NetResources[NFS41NP_MAX_DEVICES];
+} NFS41NP_SHARED_MEMORY, *PNFS41NP_SHARED_MEMORY;
+
+#endif /* !__NFS41_NP_H__ */
diff --git a/reactos/dll/np/nfs/nfs41_np.spec b/reactos/dll/np/nfs/nfs41_np.spec
new file mode 100644 (file)
index 0000000..8c04a7b
--- /dev/null
@@ -0,0 +1,13 @@
+@ stdcall NPGetCaps(long)
+@ stdcall NPLogonNotify(ptr wstr ptr wstr ptr wstr ptr wstr)
+@ stdcall NPPasswordChangeNotify(wstr ptr wstr ptr wstr ptr long)
+@ stdcall NPAddConnection(ptr wstr wstr)
+@ stdcall NPAddConnection3(long ptr wstr wstr long)
+@ stdcall NPCancelConnection(wstr long)
+@ stdcall NPGetConnection(wstr ptr ptr)
+@ stdcall NPOpenEnum(long long long ptr ptr)
+@ stdcall NPEnumResource(long ptr ptr ptr)
+@ stdcall NPCloseEnum(long)
+@ stdcall NPGetResourceParent(ptr ptr ptr)
+@ stdcall NPGetResourceInformation(ptr ptr ptr ptr)
+@ stdcall NPGetUniversalName(ptr long ptr ptr)
diff --git a/reactos/dll/np/nfs/nfsnp.rc b/reactos/dll/np/nfs/nfsnp.rc
new file mode 100644 (file)
index 0000000..10d170f
--- /dev/null
@@ -0,0 +1,5 @@
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION  "NFS Network Provider"
+#define REACTOS_STR_INTERNAL_NAME     "nfsnp"
+#define REACTOS_STR_ORIGINAL_FILENAME "nfsnp.dll"
+#include <reactos/version.rc>
diff --git a/reactos/dll/np/nfs/options.c b/reactos/dll/np/nfs/options.c
new file mode 100644 (file)
index 0000000..f32b1fe
--- /dev/null
@@ -0,0 +1,102 @@
+/* NFSv4.1 client for Windows
+ * Copyright © 2012 The Regents of the University of Michigan
+ *
+ * Olga Kornievskaia <aglo@umich.edu>
+ * Casey Bodley <cbodley@umich.edu>
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ */
+
+#include <windows.h>
+#include "options.h"
+
+
+DWORD InitializeConnectionInfo(
+    IN OUT PCONNECTION_INFO Connection,
+    IN PMOUNT_OPTION_BUFFER Options,
+    OUT LPWSTR *ConnectionName)
+{
+    DWORD result = WN_SUCCESS;
+    SIZE_T size;
+
+    /* verify that this is a mount options buffer */
+    if (Options &&
+        Options->Zero == 0 &&
+        Options->Secret == MOUNT_OPTION_BUFFER_SECRET)
+    {
+        Connection->Options = Options;
+        size = MAX_CONNECTION_BUFFER_SIZE(Options->Length);
+    }
+    else
+    {
+        Connection->Options = NULL;
+        size = MAX_CONNECTION_BUFFER_SIZE(0);
+    }
+
+    Connection->Buffer = LocalAlloc(LMEM_ZEROINIT, size);
+    if (Connection->Buffer)
+        *ConnectionName = (LPWSTR)Connection->Buffer->Buffer;
+    else
+        result = WN_OUT_OF_MEMORY;
+
+    return result;
+}
+
+#ifdef __REACTOS__
+FORCEINLINE SIZE_T ConnectionBufferSize(
+#else
+static FORCEINLINE SIZE_T ConnectionBufferSize(
+#endif
+    IN PCONNECTION_BUFFER Buffer)
+{
+    return sizeof(USHORT) + sizeof(USHORT) + sizeof(ULONG) +
+        Buffer->NameLength + Buffer->EaPadding + Buffer->EaLength;
+}
+
+void MarshalConnectionInfo(
+    IN OUT PCONNECTION_INFO Connection)
+{
+    PCONNECTION_BUFFER Buffer = Connection->Buffer;
+    LPWSTR ConnectionName = (LPWSTR)Buffer->Buffer;
+
+    Buffer->NameLength = (USHORT)(wcslen(ConnectionName) + 1) * sizeof(WCHAR);
+
+    /* copy the EaBuffer after the end of ConnectionName */
+    if (Connection->Options && Connection->Options->Length)
+    {
+        PBYTE ptr = Buffer->Buffer + Buffer->NameLength;
+        /* add padding so EaBuffer starts on a ULONG boundary */
+        Buffer->EaPadding = (USHORT)
+            (sizeof(ULONG) - (SIZE_T)ptr % sizeof(ULONG)) % sizeof(ULONG);
+        Buffer->EaLength = Connection->Options->Length;
+        ptr += Buffer->EaPadding;
+
+        RtlCopyMemory(ptr, Connection->Options->Buffer, Buffer->EaLength);
+    }
+
+    Connection->BufferSize = (ULONG)ConnectionBufferSize(Buffer);
+}
+
+void FreeConnectionInfo(
+    IN PCONNECTION_INFO Connection)
+{
+    if (Connection->Buffer)
+    {
+        LocalFree(Connection->Buffer);
+        Connection->Buffer = NULL;
+    }
+    Connection->Options = NULL;
+    Connection->BufferSize = 0;
+}
diff --git a/reactos/dll/np/nfs/options.h b/reactos/dll/np/nfs/options.h
new file mode 100644 (file)
index 0000000..5cd057c
--- /dev/null
@@ -0,0 +1,82 @@
+/* NFSv4.1 client for Windows
+ * Copyright © 2012 The Regents of the University of Michigan
+ *
+ * Olga Kornievskaia <aglo@umich.edu>
+ * Casey Bodley <cbodley@umich.edu>
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ */
+
+#ifndef __NFS41_NP_OPTIONS_H__
+#define __NFS41_NP_OPTIONS_H__
+
+
+#define MOUNT_OPTION_BUFFER_SECRET ('n4')
+
+/* MOUNT_OPTION_BUFFER
+ *   The mount options buffer received by NPAddConnection3
+ * via NETRESOURCE.lpComment. To avoid interpreting a normal
+ * comment string as mount options, a NULL and secret number
+ * are expected at the front. */
+typedef struct _MOUNT_OPTION_BUFFER {
+       USHORT  Zero;   /* = 0 */
+       USHORT  Secret; /* = 'n4' */
+       ULONG   Length;
+       BYTE    Buffer[1];
+} MOUNT_OPTION_BUFFER, *PMOUNT_OPTION_BUFFER;
+
+/* CONNECTION_BUFFER
+ *   The connection information as sent to the driver via
+ * IOCTL_NFS41_ADDCONN. The buffer contains the connection name
+ * followed by any extended attributes for mount options. */
+typedef struct _CONNECTION_BUFFER {
+       USHORT  NameLength;     /* length of connection filename */
+       USHORT  EaPadding;      /* 0-3 bytes of padding to put EaBuffer
+                                                * on a ULONG boundary */
+       ULONG   EaLength;       /* length of EaBuffer */
+       BYTE    Buffer[1];
+} CONNECTION_BUFFER, *PCONNECTION_BUFFER;
+
+/* CONNECTION_INFO
+ *   Used in NPAddConnection3 to encapsulate the formation of
+ * the connection buffer. */
+typedef struct _CONNECTION_INFO {
+       PMOUNT_OPTION_BUFFER    Options;
+       ULONG                                   BufferSize;
+       PCONNECTION_BUFFER              Buffer;
+} CONNECTION_INFO, *PCONNECTION_INFO;
+
+#define MAX_CONNECTION_BUFFER_SIZE(EaSize) ( \
+       sizeof(CONNECTION_BUFFER) + MAX_PATH + (EaSize) )
+
+
+/* options.c */
+DWORD InitializeConnectionInfo(
+       IN OUT PCONNECTION_INFO Connection,
+       IN PMOUNT_OPTION_BUFFER Options,
+       OUT LPWSTR *ConnectionName);
+
+void FreeConnectionInfo(
+       IN OUT PCONNECTION_INFO Connection);
+
+/* MarshallConnectionInfo
+ *   Prepares the CONNECTION_BUFFER for transmission to the driver
+ * by copying the extended attributes into place and updating the
+ * lengths accordingly. */
+void MarshalConnectionInfo(
+       IN OUT PCONNECTION_INFO Connection);
+
+
+#endif /* !__NFS41_NP_OPTIONS_H__ */
index ef6e644..4cb3c5a 100644 (file)
@@ -8,6 +8,7 @@ add_subdirectory(ffs)
 add_subdirectory(fs_rec)
 add_subdirectory(msfs)
 add_subdirectory(mup)
+add_subdirectory(nfs)
 add_subdirectory(npfs)
 add_subdirectory(ntfs)
 add_subdirectory(reiserfs)
diff --git a/reactos/drivers/filesystems/nfs/CMakeLists.txt b/reactos/drivers/filesystems/nfs/CMakeLists.txt
new file mode 100644 (file)
index 0000000..4f44cf4
--- /dev/null
@@ -0,0 +1,21 @@
+list(APPEND SOURCE
+    nfs41_driver.c
+    nfs41_debug.c
+    nfs41_driver.h
+    nfs41_debug.h)
+
+include_directories(
+    ${REACTOS_SOURCE_DIR}/dll/np/nfs)
+
+add_definitions(-DRDBSS_TRACKER)
+
+add_library(nfs41_driver SHARED ${SOURCE} nfs.rc)
+set_module_type(nfs41_driver kernelmodedriver)
+target_link_libraries(nfs41_driver ntoskrnl_vista rdbsslib ${PSEH_LIB})
+add_importlibs(nfs41_driver ntoskrnl hal)
+
+if((NOT MSVC) AND (NOT CMAKE_C_COMPILER_ID STREQUAL "Clang"))
+    add_target_compile_flags(nfs41_driver "-Wno-switch")
+endif()
+
+add_cd_file(TARGET nfs41_driver DESTINATION reactos/system32/drivers FOR all)
diff --git a/reactos/drivers/filesystems/nfs/nfs.rc b/reactos/drivers/filesystems/nfs/nfs.rc
new file mode 100644 (file)
index 0000000..c07c700
--- /dev/null
@@ -0,0 +1,5 @@
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION  "NFS Driver"
+#define REACTOS_STR_INTERNAL_NAME     "nfs"
+#define REACTOS_STR_ORIGINAL_FILENAME "nfs.sys"
+#include <reactos/version.rc>
diff --git a/reactos/drivers/filesystems/nfs/nfs41_debug.c b/reactos/drivers/filesystems/nfs/nfs41_debug.c
new file mode 100644 (file)
index 0000000..29b8dd7
--- /dev/null
@@ -0,0 +1,749 @@
+/* NFSv4.1 client for Windows
+ * Copyright © 2012 The Regents of the University of Michigan
+ *
+ * Olga Kornievskaia <aglo@umich.edu>
+ * Casey Bodley <cbodley@umich.edu>
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ */
+
+#define MINIRDR__NAME "Value is ignored, only fact of definition"
+#include <rx.h>
+
+#include "nfs41_driver.h"
+#include "nfs41_debug.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <ntstrsafe.h>
+#include <winerror.h>
+
+#if defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7)
+NTSTATUS NTAPI RtlUnicodeToUTF8N(CHAR *utf8_dest, ULONG utf8_bytes_max,
+                                 ULONG *utf8_bytes_written,
+                                 const WCHAR *uni_src, ULONG uni_bytes);
+NTSTATUS NTAPI RtlUTF8ToUnicodeN(WCHAR *uni_dest, ULONG uni_bytes_max,
+                                 ULONG *uni_bytes_written,
+                                 const CHAR *utf8_src, ULONG utf8_bytes);
+#endif /* defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7) */
+
+//#define INCLUDE_TIMESTAMPS
+
+ULONG __cdecl DbgP(IN PCCH fmt, ...)
+{
+    CHAR msg[512];
+    va_list args;
+    NTSTATUS status;
+
+    va_start(args, fmt);
+    ASSERT(fmt != NULL);
+    status = RtlStringCbVPrintfA(msg, sizeof(msg), fmt, args);
+    if (NT_SUCCESS(status)) {
+#ifdef INCLUDE_TIMESTAMPS
+        LARGE_INTEGER timestamp, local_time;
+        TIME_FIELDS time_fields;
+
+        KeQuerySystemTime(&timestamp);
+        ExSystemTimeToLocalTime(&timestamp,&local_time);
+        RtlTimeToTimeFields(&local_time, &time_fields);
+
+        DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, 
+            "[%ld].[%02u:%02u:%02u.%u] %s", IoGetCurrentProcess(), 
+            time_fields.Hour, time_fields.Minute, time_fields.Second, 
+            time_fields.Milliseconds, msg);
+#else
+        DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, 
+            "[%04x] %s", PsGetCurrentProcessId(), msg);
+#endif
+    }
+    va_end(args);
+
+    return 0;
+}
+
+ULONG __cdecl print_error(IN PCCH fmt, ...)
+{
+    CHAR msg[512];
+    va_list args;
+    NTSTATUS status;
+
+    va_start(args, fmt);
+    ASSERT(fmt != NULL);
+    status = RtlStringCbVPrintfA(msg, sizeof(msg), fmt, args);
+    if (NT_SUCCESS(status)) {
+#ifdef INCLUDE_TIMESTAMPS
+        LARGE_INTEGER timestamp, local_time;
+        TIME_FIELDS time_fields;
+
+        KeQuerySystemTime(&timestamp);
+        ExSystemTimeToLocalTime(&timestamp,&local_time);
+        RtlTimeToTimeFields(&local_time, &time_fields);
+
+        DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, 
+            "[%ld].[%02u:%02u:%02u.%u] %s", IoGetCurrentProcess(), 
+            time_fields.Hour, time_fields.Minute, time_fields.Second, 
+            time_fields.Milliseconds, msg);
+#else
+        DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, 
+            "[%04x] %s", PsGetCurrentProcessId(), msg);
+#endif
+    }
+    va_end(args);
+
+    return 0;
+}
+
+void print_hexbuf(int on, unsigned char *title, unsigned char *buf, int len) 
+{
+    int j, k;
+    LARGE_INTEGER timestamp, local_time;
+    TIME_FIELDS time_fields;
+
+    if (!on) return;
+
+    KeQuerySystemTime(&timestamp);
+    ExSystemTimeToLocalTime(&timestamp,&local_time);
+    RtlTimeToTimeFields(&local_time, &time_fields);
+
+    DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, 
+        "[%ld].[%02u:%02u:%02u.%u] %s\n", IoGetCurrentProcess(), 
+        time_fields.Hour, time_fields.Minute, time_fields.Second, 
+        time_fields.Milliseconds, title);
+    for(j = 0, k = 0; j < len; j++, k++) {
+        DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL,
+            "%02x ", buf[j]);
+        if (((k+1) % 30 == 0 && k > 0))
+            DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, "\n");
+    }
+    DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, "\n");
+}
+
+void print_ioctl(int on, int op)
+{
+    if(!on) return;
+    switch(op) {
+        case IRP_MJ_FILE_SYSTEM_CONTROL:
+            DbgP("IRP_MJ_FILE_SYSTEM_CONTROL\n");
+            break;
+        case IRP_MJ_DEVICE_CONTROL:
+            DbgP("IRP_MJ_DEVICE_CONTROL\n");
+            break;
+        case IRP_MJ_INTERNAL_DEVICE_CONTROL:
+            DbgP("IRP_MJ_INTERNAL_DEVICE_CONTROL\n");
+            break;
+        default:
+            DbgP("UNKNOWN MJ IRP %d\n", op);
+    };
+}
+
+void print_fs_ioctl(int on, int op)
+{
+    if(!on) return;
+    switch(op) {
+        case IOCTL_NFS41_INVALCACHE:
+            DbgP("IOCTL_NFS41_INVALCACHE\n");
+            break;
+        case IOCTL_NFS41_READ:
+            DbgP("IOCTL_NFS41_UPCALL\n");
+            break;
+        case IOCTL_NFS41_WRITE:
+            DbgP("IOCTL_NFS41_DOWNCALL\n");
+            break;
+        case IOCTL_NFS41_ADDCONN:
+            DbgP("IOCTL_NFS41_ADDCONN\n");
+            break;
+        case IOCTL_NFS41_DELCONN:
+            DbgP("IOCTL_NFS41_DELCONN\n");
+            break;
+        case IOCTL_NFS41_GETSTATE:
+            DbgP("IOCTL_NFS41_GETSTATE\n");
+            break;
+        case IOCTL_NFS41_START:
+            DbgP("IOCTL_NFS41_START\n");
+            break;
+        case IOCTL_NFS41_STOP:
+            DbgP("IOCTL_NFS41_STOP\n");
+            break;
+        default:
+            DbgP("UNKNOWN FS IOCTL %d\n", op);
+    };
+}
+
+void print_driver_state(int state)
+{
+    switch (state) {
+        case NFS41_START_DRIVER_STARTABLE:
+            DbgP("NFS41_START_DRIVER_STARTABLE\n");
+            break;
+        case NFS41_START_DRIVER_STOPPED:
+            DbgP("NFS41_START_DRIVER_STOPPED\n");
+            break;
+        case NFS41_START_DRIVER_START_IN_PROGRESS:
+            DbgP("NFS41_START_DRIVER_START_IN_PROGRESS\n");
+            break;
+        case NFS41_START_DRIVER_STARTED:
+            DbgP("NFS41_START_DRIVER_STARTED\n");
+            break;
+        default:
+            DbgP("UNKNOWN DRIVER STATE %d\n", state);
+    };
+
+}
+
+void print_basic_info(int on, PFILE_BASIC_INFORMATION info)
+{
+    if (!on) return;
+    DbgP("BASIC_INFO: Create=%lx Access=%lx Write=%lx Change=%lx Attr=%x\n",
+        info->CreationTime.QuadPart, info->LastAccessTime.QuadPart,
+        info->LastWriteTime.QuadPart, info->ChangeTime.QuadPart, 
+        info->FileAttributes);
+}
+void print_std_info(int on, PFILE_STANDARD_INFORMATION info)
+{
+    if (!on) return;
+    DbgP("STD_INFO: Type=%s #Links=%d Alloc=%lx EOF=%lx Delete=%d\n",
+        info->Directory?"DIR":"FILE", info->NumberOfLinks, 
+        info->AllocationSize.QuadPart, info->EndOfFile.QuadPart, 
+        info->DeletePending);
+}
+
+void print_ea_info(int on, PFILE_FULL_EA_INFORMATION info)
+{
+    if (!on) return;
+    DbgP("FULL_EA_INFO: NextOffset=%d Flags=%x EaNameLength=%d "
+        "ExValueLength=%x EaName=%s\n", info->NextEntryOffset, info->Flags,
+        info->EaNameLength, info->EaValueLength, info->EaName);
+    if (info->EaValueLength) 
+        print_hexbuf(0, (unsigned char *)"eavalue", 
+            (unsigned char *)info->EaName + info->EaNameLength + 1, 
+            info->EaValueLength);
+}
+
+void print_get_ea(int on, PFILE_GET_EA_INFORMATION info)
+{
+    if (!on || !info) return;
+    DbgP("GET_EA_INFO: NextOffset=%d EaNameLength=%d EaName=%s\n", 
+        info->NextEntryOffset, info->EaNameLength, info->EaName);
+}
+
+VOID print_srv_call(int on, IN PMRX_SRV_CALL p)
+{
+    if (!on) return;
+    DbgP("PMRX_SRV_CALL %p\n", p);
+#if 0
+    DbgP("\tNodeReferenceCount %ld\n", p->NodeReferenceCount);
+    //DbgP("Context %p\n", p->Context);
+    //DbgP("Context2 %p\n", p->Context2);
+    //DbgP("pSrvCallName %wZ\n", p->pSrvCallName);
+    //DbgP("pPrincipalName %wZ\n", p->pPrincipalName);
+    //DbgP("PDomainName %wZ\n", p->pDomainName);
+    //DbgP("Flags %08lx\n", p->Flags);
+    //DbgP("MaximumNumberOfCloseDelayedFiles %ld\n", p->MaximumNumberOfCloseDelayedFiles);
+    //DbgP("Status %ld\n", p->Status);
+    DbgP("*****************\n");
+#endif
+}
+
+VOID print_net_root(int on, IN PMRX_NET_ROOT p)
+{
+    if (!on) return;
+    DbgP("PMRX_NET_ROOT %p\n", p);
+#if 0
+    DbgP("\tNodeReferenceCount %ld\n", p->NodeReferenceCount);
+    DbgP("\tpSrvCall %p\n", p->pSrvCall);
+    //DbgP("Context %p\n", p->Context);
+    //DbgP("Context2 %p\n", p->Context2);
+    //DbgP("Flags %08lx\n", p->Flags);
+    DbgP("\tNumberOfFcbs %ld\n", p->NumberOfFcbs);
+    DbgP("\tNumberofSrvOpens %ld\n", p->NumberOfSrvOpens);
+    //DbgP("MRxNetRootState %ld\n", p->MRxNetRootState);
+    //DbgP("Type %ld\n", p->Type);
+    //DbgP("DeviceType %ld\n", p->DeviceType);
+    //DbgP("pNetRootName %wZ\n", p->pNetRootName);
+    //DbgP("InnerNamePrefix %wZ\n", &p->InnerNamePrefix);
+    DbgP("*****************\n");
+#endif
+}
+
+VOID print_v_net_root(int on, IN PMRX_V_NET_ROOT p)
+{
+    if (!on) return;
+    DbgP("PMRX_V_NET_ROOT %p\n", p);
+#if 0
+    DbgP("\tNodeReferenceCount %ld\n", p->NodeReferenceCount);
+    DbgP("\tpNetRoot %p\n", p->pNetRoot);
+    //DbgP("Context %p\n", p->Context);
+    //DbgP("Context2 %p\n", p->Context2);
+    //DbgP("Flags %08lx\n", p->Flags);
+    DbgP("\tNumberofOpens %ld\n", p->NumberOfOpens);
+    DbgP("\tNumberofFobxs %ld\n", p->NumberOfFobxs);
+    //DbgP("LogonId\n");
+    //DbgP("pUserDomainName %wZ\n", p->pUserDomainName);
+    //DbgP("pUserName %wZ\n", p->pUserName);
+    //DbgP("pPassword %wZ\n", p->pPassword);
+    //DbgP("SessionId %ld\n", p->SessionId);
+    //DbgP("ConstructionStatus %08lx\n", p->ConstructionStatus);
+    //DbgP("IsExplicitConnection %d\n", p->IsExplicitConnection);
+    DbgP("*****************\n");
+#endif
+}
+
+void print_file_object(int on, PFILE_OBJECT file)
+{
+    if (!on) return;   
+    DbgP("FsContext %p FsContext2 %p\n", file->FsContext, file->FsContext2);
+    DbgP("DeletePending %d ReadAccess %d WriteAccess %d DeleteAccess %d\n",
+        file->DeletePending, file->WriteAccess, file->DeleteAccess);
+    DbgP("SharedRead %d SharedWrite %d SharedDelete %d Flags %x\n",
+        file->SharedRead, file->SharedWrite, file->SharedDelete, file->Flags);
+}
+
+void print_fo_all(int on, PRX_CONTEXT c)
+{
+    if (!on) return;
+    if (c->pFcb && c->pRelevantSrvOpen)
+        DbgP("OpenCount %d FCB %p SRV %p FOBX %p VNET %p NET %p\n", 
+            c->pFcb->OpenCount, c->pFcb, c->pRelevantSrvOpen, c->pFobx,
+            c->pRelevantSrvOpen->pVNetRoot, c->pFcb->pNetRoot);
+}
+
+VOID print_fcb(int on, IN PMRX_FCB p)
+{
+    if (!on) return;
+    DbgP("PMRX_FCB %p OpenCount %d\n", p, p->OpenCount);
+#if 0
+    DbgP("\tNodeReferenceCount %ld\n", p->NodeReferenceCount);
+    DbgP("\tpNetRoot %p\n", p->pNetRoot);
+    //DbgP("Context %p\n", p->Context);
+    //DbgP("Context2 %p\n", p->Context2);
+    //DbgP("FcbState %ld\n", p->FcbState);
+    //DbgP("UncleanCount %ld\n", p->UncleanCount);
+    //DbgP("UncachedUncleanCount %ld\n", p->UncachedUncleanCount);
+    DbgP("\tOpenCount %ld\n", p->OpenCount);
+    //DbgP("OutstandingLockOperationsCount %ld\n", p->OutstandingLockOperationsCount);
+    //DbgP("ActualAllocationLength %ull\n", p->ActualAllocationLength);
+    //DbgP("Attributes %ld\n", p->Attributes);
+    //DbgP("IsFileWritten %d\n", p->IsFileWritten);
+    //DbgP("fShouldBeOrphaned %d\n", p->fShouldBeOrphaned);
+    //DbgP("fMiniInited %ld\n", p->fMiniInited);
+    //DbgP("CachedNetRootType %c\n", p->CachedNetRootType);
+    //DbgP("SrvOpenList\n");
+    //DbgP("SrvOpenListVersion %ld\n", p->SrvOpenListVersion);
+    DbgP("*****************\n");
+#endif
+}
+
+VOID print_srv_open(int on, IN PMRX_SRV_OPEN p)
+{
+    if (!on) return;
+    DbgP("PMRX_SRV_OPEN %p\n", p);
+#if 0
+    DbgP("\tNodeReferenceCount %ld\n", p->NodeReferenceCount);
+    DbgP("\tpFcb %p\n", p->pFcb);
+    DbgP("\tpVNetRoot %p\n", p->pVNetRoot);
+    //DbgP("Context %p\n", p->Context);
+    //DbgP("Context2 %p\n", p->Context2);
+    //DbgP("Flags %08lx\n", p->Flags);
+    //DbgP("pAlreadyPrefixedName %wZ\n", p->pAlreadyPrefixedName);
+    //DbgP("UncleanFobxCount %ld\n", p->UncleanFobxCount);
+    DbgP("\tOpenCount %ld\n", p->OpenCount);
+    //DbgP("Key %p\n", p->Key);
+    //DbgP("DesiredAccess\n");
+    //DbgP("ShareAccess %ld\n", p->ShareAccess);
+    //DbgP("CreateOptions %ld\n", p->CreateOptions);
+    //DbgP("BufferingFlags %ld\n", p->BufferingFlags);
+    //DbgP("ulFileSizeVersion %ld\n", p->ulFileSizeVersion);
+    //DbgP("SrvOpenQLinks\n");
+    DbgP("*****************\n");
+#endif
+}
+
+VOID print_fobx(int on, IN PMRX_FOBX p)
+{
+    if (!on) return;
+    DbgP("PMRX_FOBX %p\n", p);
+#if 0
+    DbgP("\tNodeReferenceCount %ld\n", p->NodeReferenceCount);
+    DbgP("\tpSrvOpen %p\n", p->pSrvOpen);
+    DbgP("\tAssociatedFileObject %p\n", p->AssociatedFileObject);
+    //DbgP("Context %p\n", p->Context);
+    //DbgP("Context2 %p\n", p->Context2);
+    //DbgP("Flags %08lx\n", p->Flags);
+    DbgP("*****************\n");
+#endif
+}
+
+VOID print_irp_flags(int on, PIRP irp) 
+{
+    if (!on) return;
+    if (irp->Flags)
+        DbgP("IRP FLAGS: 0x%x %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n", 
+            irp->Flags,
+            (irp->Flags & IRP_NOCACHE)?"NOCACHE":"",
+            (irp->Flags & IRP_PAGING_IO)?"PAGING_IO":"",
+            (irp->Flags & IRP_MOUNT_COMPLETION)?"MOUNT":"",
+            (irp->Flags & IRP_SYNCHRONOUS_API)?"SYNC":"",
+            (irp->Flags & IRP_ASSOCIATED_IRP)?"ASSOC_IPR":"",
+            (irp->Flags & IRP_BUFFERED_IO)?"BUFFERED":"",
+            (irp->Flags & IRP_DEALLOCATE_BUFFER)?"DEALLOC_BUF":"",
+            (irp->Flags & IRP_INPUT_OPERATION)?"INPUT_OP":"",
+            (irp->Flags & IRP_SYNCHRONOUS_PAGING_IO)?"SYNC_PAGIN_IO":"",
+            (irp->Flags & IRP_CREATE_OPERATION)?"CREATE_OP":"",
+            (irp->Flags & IRP_READ_OPERATION)?"READ_OP":"",
+            (irp->Flags & IRP_WRITE_OPERATION)?"WRITE_OP":"",
+            (irp->Flags & IRP_CLOSE_OPERATION)?"CLOSE_OP":"",
+            (irp->Flags & IRP_DEFER_IO_COMPLETION)?"DEFER_IO":"");
+}
+
+void print_irps_flags(int on, PIO_STACK_LOCATION irps)
+{
+    if (!on) return;
+    if (irps->Flags)
+        DbgP("IRPSP FLAGS 0x%x %s %s %s %s\n", irps->Flags,
+            (irps->Flags & SL_CASE_SENSITIVE)?"CASE_SENSITIVE":"",
+            (irps->Flags & SL_OPEN_PAGING_FILE)?"PAGING_FILE":"",
+            (irps->Flags & SL_FORCE_ACCESS_CHECK)?"ACCESS_CHECK":"",
+            (irps->Flags & SL_OPEN_TARGET_DIRECTORY)?"TARGET_DIR":"");
+}
+void print_nt_create_params(int on, NT_CREATE_PARAMETERS params)
+{
+    if (!on) return;
+    if (params.FileAttributes)
+        DbgP("File attributes %x: %s %s %s %s %s %s %s %s %s %s %s %s %s %s\n", 
+            params.FileAttributes,
+            (params.FileAttributes & FILE_ATTRIBUTE_TEMPORARY)?"TEMPFILE ":"",
+            (params.FileAttributes & FILE_ATTRIBUTE_READONLY)?"READONLY ":"",
+            (params.FileAttributes & FILE_ATTRIBUTE_HIDDEN)?"HIDDEN ":"",
+            (params.FileAttributes & FILE_ATTRIBUTE_SYSTEM)?"SYSTEM ":"",
+            (params.FileAttributes & FILE_ATTRIBUTE_ARCHIVE)?"ARCHIVE ":"",
+            (params.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)?"DIR ":"",
+            (params.FileAttributes & FILE_ATTRIBUTE_DEVICE)?"DEVICE ":"",
+            (params.FileAttributes & FILE_ATTRIBUTE_NORMAL)?"NORMAL ":"",
+            (params.FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE)?"SPARSE_FILE ":"",
+            (params.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)?"REPARSE_POINT ":"",
+            (params.FileAttributes & FILE_ATTRIBUTE_COMPRESSED)?"COMPRESSED ":"",
+            (params.FileAttributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)?"NOT INDEXED ":"",
+            (params.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED)?"ENCRYPTED ":"",
+            (params.FileAttributes & FILE_ATTRIBUTE_VIRTUAL)?"VIRTUAL":"");
+    if (params.Disposition  == FILE_SUPERSEDE)
+        DbgP("Create Dispositions: FILE_SUPERSEDE\n");
+    if (params.Disposition == FILE_CREATE)
+        DbgP("Create Dispositions: FILE_CREATE\n");
+    if (params.Disposition == FILE_OPEN)
+        DbgP("Create Dispositions: FILE_OPEN\n");
+    if (params.Disposition == FILE_OPEN_IF)
+        DbgP("Create Dispositions: FILE_OPEN_IF\n");
+    if (params.Disposition == FILE_OVERWRITE)
+        DbgP("Create Dispositions: FILE_OVERWRITE\n");
+    if (params.Disposition == FILE_OVERWRITE_IF)
+        DbgP("Create Dispositions: FILE_OVERWRITE_IF\n");
+
+    DbgP("Create Attributes: 0x%x %s %s %s %s %s %s %s %s %s %s %s %s %s %s "
+        "%s %s\n", params.CreateOptions, 
+        (params.CreateOptions & FILE_DIRECTORY_FILE)?"DIRFILE":"",
+        (params.CreateOptions & FILE_NON_DIRECTORY_FILE)?"FILE":"",
+        (params.CreateOptions & FILE_DELETE_ON_CLOSE)?"DELETE_ON_CLOSE":"",
+        (params.CreateOptions & FILE_WRITE_THROUGH)?"WRITE_THROUGH":"",
+        (params.CreateOptions & FILE_SEQUENTIAL_ONLY)?"SEQUENTIAL":"",
+        (params.CreateOptions & FILE_RANDOM_ACCESS)?"RANDOM":"",
+        (params.CreateOptions & FILE_NO_INTERMEDIATE_BUFFERING)?"NO_BUFFERING":"",
+        (params.CreateOptions & FILE_SYNCHRONOUS_IO_ALERT)?"SYNC_ALERT":"",
+        (params.CreateOptions & FILE_SYNCHRONOUS_IO_NONALERT)?"SYNC_NOALERT":"",
+        (params.CreateOptions & FILE_CREATE_TREE_CONNECTION)?"CREATE_TREE_CONN":"",
+        (params.CreateOptions & FILE_COMPLETE_IF_OPLOCKED)?"OPLOCKED":"",
+        (params.CreateOptions & FILE_NO_EA_KNOWLEDGE)?"NO_EA":"",
+        (params.CreateOptions & FILE_OPEN_REPARSE_POINT)?"OPEN_REPARSE":"",
+        (params.CreateOptions & FILE_OPEN_BY_FILE_ID)?"BY_ID":"",
+        (params.CreateOptions & FILE_OPEN_FOR_BACKUP_INTENT)?"4_BACKUP":"",
+        (params.CreateOptions & FILE_RESERVE_OPFILTER)?"OPFILTER":"");
+
+    DbgP("Share Access: %s %s %s\n", 
+        (params.ShareAccess & FILE_SHARE_READ)?"READ":"",
+        (params.ShareAccess & FILE_SHARE_WRITE)?"WRITE":"",
+        (params.ShareAccess & FILE_SHARE_DELETE)?"DELETE":"");
+
+    DbgP("Desired Access: 0x%x %s %s %s %s %s %s %s %s %s %s %s\n", 
+        params.DesiredAccess,
+        (params.DesiredAccess & FILE_READ_DATA)?"READ":"",
+        (params.DesiredAccess & STANDARD_RIGHTS_READ)?"READ_ACL":"",
+        (params.DesiredAccess & FILE_READ_ATTRIBUTES)?"GETATTR":"",
+        (params.DesiredAccess & FILE_READ_EA)?"READ_EA":"",
+        (params.DesiredAccess & FILE_WRITE_DATA)?"WRITE":"",
+        (params.DesiredAccess & FILE_WRITE_ATTRIBUTES)?"SETATTR":"",
+        (params.DesiredAccess & FILE_WRITE_EA)?"WRITE_EA":"",
+        (params.DesiredAccess & FILE_APPEND_DATA)?"APPEND":"",
+        (params.DesiredAccess & FILE_EXECUTE)?"EXEC":"",
+        (params.DesiredAccess & FILE_LIST_DIRECTORY)?"LSDIR":"",
+        (params.DesiredAccess & FILE_TRAVERSE)?"TRAVERSE":"",
+        (params.DesiredAccess & FILE_LIST_DIRECTORY)?"LSDIR":"",
+        (params.DesiredAccess & DELETE)?"DELETE":"",
+        (params.DesiredAccess & READ_CONTROL)?"READ_CONTROL":"",
+        (params.DesiredAccess & WRITE_DAC)?"WRITE_DAC":"",
+        (params.DesiredAccess & WRITE_OWNER)?"WRITE_OWNER":"",
+        (params.DesiredAccess & SYNCHRONIZE)?"SYNCHRONIZE":"");
+}
+
+unsigned char * print_file_information_class(int InfoClass) 
+{
+    switch(InfoClass) {
+        case FileBothDirectoryInformation:
+            return (unsigned char *)"FileBothDirectoryInformation";
+        case FileDirectoryInformation:
+            return (unsigned char *)"FileDirectoryInformation";
+        case FileFullDirectoryInformation:
+            return (unsigned char *)"FileFullDirectoryInformation";
+        case FileIdBothDirectoryInformation:
+            return (unsigned char *)"FileIdBothDirectoryInformation";
+        case FileIdFullDirectoryInformation:
+            return (unsigned char *)"FileIdFullDirectoryInformation";
+        case FileNamesInformation:
+            return (unsigned char *)"FileNamesInformation";
+        case FileObjectIdInformation:
+            return (unsigned char *)"FileObjectIdInformation";
+        case FileQuotaInformation:
+            return (unsigned char *)"FileQuotaInformation";
+        case FileReparsePointInformation:
+            return (unsigned char *)"FileReparsePointInformation";
+        case FileAllInformation:
+            return (unsigned char *)"FileAllInformation";
+        case FileAttributeTagInformation:
+            return (unsigned char *)"FileAttributeTagInformation";
+        case FileBasicInformation:
+            return (unsigned char *)"FileBasicInformation";
+        case FileCompressionInformation:
+            return (unsigned char *)"FileCompressionInformation";
+        case FileEaInformation:
+            return (unsigned char *)"FileEaInformation";
+        case FileInternalInformation:
+            return (unsigned char *)"FileInternalInformation";
+        case FileNameInformation:
+            return (unsigned char *)"FileNameInformation";
+        case FileNetworkOpenInformation:
+            return (unsigned char *)"FileNetworkOpenInformation";
+        case FilePositionInformation:
+            return (unsigned char *)"FilePositionInformation";
+        case FileStandardInformation:
+            return (unsigned char *)"FileStandardInformation";
+        case FileStreamInformation:
+            return (unsigned char *)"FileStreamInformation";
+        case FileAllocationInformation:
+            return (unsigned char *)"FileAllocationInformation";
+        case FileDispositionInformation:
+            return (unsigned char *)"FileDispositionInformation";
+        case FileEndOfFileInformation:
+            return (unsigned char *)"FileEndOfFileInformation";
+        case FileLinkInformation:
+            return (unsigned char *)"FileLinkInformation";
+        case FileRenameInformation:
+            return (unsigned char *)"FileRenameInformation";
+        case FileValidDataLengthInformation:
+            return (unsigned char *)"FileValidDataLengthInformation";
+        default:
+            return (unsigned char *)"UNKNOWN";
+    }
+}
+
+unsigned char *print_fs_information_class(int InfoClass)
+{
+    switch (InfoClass) {
+        case FileFsAttributeInformation:
+            return (unsigned char *)"FileFsAttributeInformation";
+        case FileFsControlInformation:
+            return (unsigned char *)"FileFsControlInformation";
+        case FileFsDeviceInformation:
+            return (unsigned char *)"FileFsDeviceInformation";
+        case FileFsDriverPathInformation:
+            return (unsigned char *)"FileFsDriverPathInformation";
+        case FileFsFullSizeInformation:
+            return (unsigned char *)"FileFsFullSizeInformation";
+        case FileFsObjectIdInformation:
+            return (unsigned char *)"FileFsObjectIdInformation";
+        case FileFsSizeInformation:
+            return (unsigned char *)"FileFsSizeInformation";
+        case FileFsVolumeInformation:
+            return (unsigned char *)"FileFsVolumeInformation";
+        default:
+            return (unsigned char *)"UNKNOWN";
+    }
+}
+
+void print_caching_level(int on, ULONG flag, PUNICODE_STRING name)
+{
+    if (!on) return;
+    switch(flag) {
+        case 0: 
+            DbgP("enable_caching: DISABLE_CACHING %wZ\n", name);
+            break;
+        case 1:
+            DbgP("enable_caching: ENABLE_READ_CACHING %wZ\n", name);
+            break;
+        case 2:
+            DbgP("enable_caching: ENABLE_WRITE_CACHING %wZ\n", name);
+            break;
+        case 3:
+            DbgP("enable_caching: ENABLE_READWRITE_CACHING %wZ\n", name);
+            break;   
+    }
+}
+
+const char *opcode2string(int opcode)
+{
+    switch(opcode) {
+    case NFS41_SHUTDOWN: return "NFS41_SHUTDOWN";
+    case NFS41_MOUNT: return "NFS41_MOUNT";
+    case NFS41_UNMOUNT: return "NFS41_UNMOUNT";
+    case NFS41_OPEN: return "NFS41_OPEN";
+    case NFS41_CLOSE: return "NFS41_CLOSE";
+    case NFS41_READ: return "NFS41_READ";
+    case NFS41_WRITE: return "NFS41_WRITE";
+    case NFS41_LOCK: return "NFS41_LOCK";
+    case NFS41_UNLOCK: return "NFS41_UNLOCK";
+    case NFS41_DIR_QUERY: return "NFS41_DIR_QUERY";
+    case NFS41_FILE_QUERY: return "NFS41_FILE_QUERY";
+    case NFS41_FILE_SET: return "NFS41_FILE_SET";
+    case NFS41_EA_SET: return "NFS41_EA_SET";
+    case NFS41_EA_GET: return "NFS41_EA_GET";
+    case NFS41_SYMLINK: return "NFS41_SYMLINK";
+    case NFS41_VOLUME_QUERY: return "NFS41_VOLUME_QUERY";
+    case NFS41_ACL_QUERY: return "NFS41_ACL_QUERY";
+    case NFS41_ACL_SET: return "NFS41_ACL_SET";
+    default: return "UNKNOWN";
+    }
+}
+
+void print_acl_args(
+    SECURITY_INFORMATION info)
+{
+    DbgP("Security query: %s %s %s\n",
+        (info & OWNER_SECURITY_INFORMATION)?"OWNER":"",
+        (info & GROUP_SECURITY_INFORMATION)?"GROUP":"",
+        (info & DACL_SECURITY_INFORMATION)?"DACL":"",
+        (info & SACL_SECURITY_INFORMATION)?"SACL":"");
+}
+
+void print_open_error(int on, int status)
+{
+    if (!on) return;
+    switch (status) {
+    case STATUS_ACCESS_DENIED:
+        DbgP("[ERROR] nfs41_Create: STATUS_ACCESS_DENIED\n");
+        break;
+    case STATUS_NETWORK_ACCESS_DENIED:
+        DbgP("[ERROR] nfs41_Create: STATUS_NETWORK_ACCESS_DENIED\n");
+        break;
+    case STATUS_OBJECT_NAME_INVALID:
+        DbgP("[ERROR] nfs41_Create: STATUS_OBJECT_NAME_INVALID\n");
+        break;
+    case STATUS_OBJECT_NAME_COLLISION:
+        DbgP("[ERROR] nfs41_Create: STATUS_OBJECT_NAME_COLLISION\n");
+        break;
+    case STATUS_FILE_INVALID:
+        DbgP("[ERROR] nfs41_Create: STATUS_FILE_INVALID\n");
+        break;
+    case STATUS_OBJECT_NAME_NOT_FOUND:
+        DbgP("[ERROR] nfs41_Create: STATUS_OBJECT_NAME_NOT_FOUND\n");
+        break;
+    case STATUS_NAME_TOO_LONG:
+        DbgP("[ERROR] nfs41_Create: STATUS_NAME_TOO_LONG\n");
+        break;
+    case STATUS_OBJECT_PATH_NOT_FOUND:
+        DbgP("[ERROR] nfs41_Create: STATUS_OBJECT_PATH_NOT_FOUND\n");
+        break;
+    case STATUS_BAD_NETWORK_PATH:
+        DbgP("[ERROR] nfs41_Create: STATUS_BAD_NETWORK_PATH\n");
+        break;
+    case STATUS_SHARING_VIOLATION:
+        DbgP("[ERROR] nfs41_Create: STATUS_SHARING_VIOLATION\n");
+        break;
+    case ERROR_REPARSE:
+        DbgP("[ERROR] nfs41_Create: STATUS_REPARSE\n");
+        break;
+    case ERROR_TOO_MANY_LINKS:
+        DbgP("[ERROR] nfs41_Create: STATUS_TOO_MANY_LINKS\n");
+        break;
+    case ERROR_DIRECTORY:
+        DbgP("[ERROR] nfs41_Create: STATUS_FILE_IS_A_DIRECTORY\n");
+        break;
+    case ERROR_BAD_FILE_TYPE:
+        DbgP("[ERROR] nfs41_Create: STATUS_NOT_A_DIRECTORY\n");
+        break;
+    default:
+        DbgP("[ERROR] nfs41_Create: STATUS_INSUFFICIENT_RESOURCES\n");
+        break;
+    }
+}
+
+void print_wait_status(int on, const char *prefix, NTSTATUS status, 
+                       const char *opcode, PVOID entry, LONGLONG xid)
+{
+    if (!on) return;
+    switch (status) {
+    case STATUS_SUCCESS:
+        if (opcode)
+            DbgP("%s Got a wakeup call, finishing %s entry=%p xid=%lld\n", 
+                prefix, opcode, entry, xid);
+        else
+            DbgP("%s Got a wakeup call\n", prefix);
+        break;
+    case STATUS_USER_APC:
+        DbgP("%s KeWaitForSingleObject returned STATUS_USER_APC\n", prefix);
+        break;
+    case STATUS_ALERTED:
+        DbgP("%s KeWaitForSingleObject returned STATUS_ALERTED\n", prefix);
+        break;
+    default:
+        DbgP("%s KeWaitForSingleObject returned %d\n", prefix, status);
+    }
+}
+/* This is taken from toaster/func.  Rumor says this should be replaced
+ * with a WMI interface???
+ */
+ULONG
+dprintk(
+    IN PCHAR func,
+    IN ULONG flags,
+    IN PCHAR format,
+    ...)
+{
+    #define     TEMP_BUFFER_SIZE        1024
+    va_list    list;
+    CHAR      debugMessageBuffer[TEMP_BUFFER_SIZE];
+    NTSTATUS status, rv = STATUS_SUCCESS;
+
+    va_start(list, format);
+
+    if (format)
+    {
+        //
+        // Use the safe string function, RtlStringCbVPrintfA, instead of _vsnprintf.
+        // RtlStringCbVPrintfA NULL terminates the output buffer even if the message
+        // is longer than the buffer. This prevents malicious code from compromising
+        // the security of the system.
+        //
+        status = RtlStringCbVPrintfA(debugMessageBuffer, sizeof(debugMessageBuffer),
+                                    format, list);
+
+        if (!NT_SUCCESS(status))
+            rv = DbgPrintEx(PNFS_FLTR_ID, DPFLTR_MASK | flags,
+                            "RtlStringCbVPrintfA failed %x \n", status);
+        else
+            rv = DbgPrintEx(PNFS_FLTR_ID, DPFLTR_MASK | flags, "%s    %s: %s\n",
+                    PNFS_TRACE_TAG, func, debugMessageBuffer);
+    }
+    va_end(list);
+
+    return rv;
+}
+
diff --git a/reactos/drivers/filesystems/nfs/nfs41_debug.h b/reactos/drivers/filesystems/nfs/nfs41_debug.h
new file mode 100644 (file)
index 0000000..4bc4e9a
--- /dev/null
@@ -0,0 +1,102 @@
+/* NFSv4.1 client for Windows
+ * Copyright © 2012 The Regents of the University of Michigan
+ *
+ * Olga Kornievskaia <aglo@umich.edu>
+ * Casey Bodley <cbodley@umich.edu>
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ */
+
+#ifndef _NFS41_DEBUG_
+#define _NFS41_DEBUG_
+
+#define _DRIVER_NAME_ "NFS4.1 Driver"
+
+ULONG __cdecl DbgP(IN PCCH fmt, ...);
+ULONG __cdecl print_error(IN PCCH fmt, ...);
+VOID print_fo_all(int on, IN PRX_CONTEXT c);
+VOID print_srv_call(int on, IN PMRX_SRV_CALL p);
+VOID print_net_root(int on, IN PMRX_NET_ROOT p);
+VOID print_v_net_root(int on, IN PMRX_V_NET_ROOT p);
+VOID print_fcb(int on, IN PMRX_FCB p);
+VOID print_srv_open(int on, IN PMRX_SRV_OPEN p);
+VOID print_fobx(int on, IN PMRX_FOBX p);
+VOID print_irp_flags(int on, PIRP irp);
+VOID print_irps_flags(int on, PIO_STACK_LOCATION irps);
+void print_nt_create_params(int on, NT_CREATE_PARAMETERS params);
+unsigned char *print_file_information_class(int InfoClass);
+unsigned char *print_fs_information_class(int InfoClass);
+void print_hexbuf(int on, unsigned char *title, unsigned char *buf, int len);
+void print_ioctl(int on, int op);
+void print_fs_ioctl(int on, int op);
+void print_driver_state(int state);
+void print_file_object(int on, PFILE_OBJECT file);
+void print_basic_info(int on, PFILE_BASIC_INFORMATION info);
+void print_std_info(int on, PFILE_STANDARD_INFORMATION info);
+void print_ea_info(int on, PFILE_FULL_EA_INFORMATION info);
+void print_get_ea(int on, PFILE_GET_EA_INFORMATION info);
+void print_caching_level(int on, ULONG flag, PUNICODE_STRING s);
+const char *opcode2string(int opcode);
+void print_open_error(int on, int status);
+void print_wait_status(int on, const char *str, NTSTATUS status, 
+                       const char *opcode, PVOID entry, LONGLONG xid);
+void print_acl_args(SECURITY_INFORMATION info);
+
+#define DbgEn() DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, \
+        "--> [%s] [%04x] %s\n", _DRIVER_NAME_, PsGetCurrentProcessId(), \
+        __FUNCTION__); _SEH2_TRY {
+        
+#define DbgEx() DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, \
+        "<-- [%s] [%04x] %s status = %08lx\n", _DRIVER_NAME_, PsGetCurrentProcessId(), \
+        __FUNCTION__, status); \
+        } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { \
+            status = _SEH2_GetExceptionCode() ; \
+            DbgP("Exception encountered with value = Ox%x\n", status); \
+        } _SEH2_END;
+#define DbgR() DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_ERROR_LEVEL, \
+        "<-- [%s] [%04x] %s\n", _DRIVER_NAME_, PsGetCurrentProcessId(), __FUNCTION__); \
+        } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { \
+            NTSTATUS status; \
+            status = _SEH2_GetExceptionCode() ; \
+            DbgP("Exception encountered with value = Ox%x\n", status); \
+        } _SEH2_END;
+
+/* These are for ToasterDebugPrint */
+
+#define     DBG_ERROR       0x00000001
+#define     DBG_WARN        0x00000002
+#define     DBG_TRACE       0x00000004
+#define     DBG_INFO        0x00000008
+#define     DBG_DISP_IN     0x00000010 /* Marks entry into dispatch functions */
+#define     DBG_DISP_OUT    0x00000020 /* Marks exit from dispatch functions */
+
+/* I want to do:
+ * #define dprintk(flags, args...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_MASK | flags, ## args)
+ * but the ... is gcc specific, can't seem to do it here.
+ */
+#define PNFS_TRACE_TAG      "PNFSMRX: "
+#define PNFS_FLTR_ID        DPFLTR_IHVDRIVER_ID
+
+#define DbgEnter()      DbgPrintEx(PNFS_FLTR_ID, DPFLTR_MASK | DBG_DISP_IN, "%s*** %s ***\n", \
+                                PNFS_TRACE_TAG, __FUNCTION__);
+#define DbgExit(status) DbgPrintEx(PNFS_FLTR_ID, DPFLTR_MASK | DBG_DISP_OUT, "%s<-- %s <-- 0x%08lx\n", \
+                                PNFS_TRACE_TAG, __FUNCTION__, status);
+ULONG
+dprintk(
+    IN PCHAR func,
+    IN ULONG flags,
+    IN PCHAR format,
+    ...);
+#endif
diff --git a/reactos/drivers/filesystems/nfs/nfs41_driver.c b/reactos/drivers/filesystems/nfs/nfs41_driver.c
new file mode 100644 (file)
index 0000000..31ab6b6
--- /dev/null
@@ -0,0 +1,7140 @@
+/* NFSv4.1 client for Windows
+ * Copyright © 2012 The Regents of the University of Michigan
+ *
+ * Olga Kornievskaia <aglo@umich.edu>
+ * Casey Bodley <cbodley@umich.edu>
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ */
+
+#define MINIRDR__NAME "Value is ignored, only fact of definition"
+#include <rx.h>
+#include <windef.h>
+#include <winerror.h>
+
+#include <ntstrsafe.h>
+
+#ifdef __REACTOS__
+#include <pseh/pseh2.h>
+#endif
+
+#include "nfs41_driver.h"
+#include "nfs41_np.h"
+#include "nfs41_debug.h"
+
+#if defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7)
+NTSTATUS NTAPI RtlUnicodeToUTF8N(CHAR *utf8_dest, ULONG utf8_bytes_max,
+                                 ULONG *utf8_bytes_written,
+                                 const WCHAR *uni_src, ULONG uni_bytes);
+NTSTATUS NTAPI RtlUTF8ToUnicodeN(WCHAR *uni_dest, ULONG uni_bytes_max,
+                                 ULONG *uni_bytes_written,
+                                 const CHAR *utf8_src, ULONG utf8_bytes);
+#endif /* defined(__REACTOS__) && (NTDDI_VERSION < NTDDI_WIN7) */
+
+#define USE_MOUNT_SEC_CONTEXT
+
+/* debugging printout defines */
+#define DEBUG_MARSHAL_HEADER
+#define DEBUG_MARSHAL_DETAIL
+//#define DEBUG_OPEN
+//#define DEBUG_CLOSE
+//#define DEBUG_CACHE
+#define DEBUG_INVALIDATE_CACHE
+//#define DEBUG_READ
+//#define DEBUG_WRITE
+//#define DEBUG_DIR_QUERY
+//#define DEBUG_FILE_QUERY
+//#define DEBUG_FILE_SET
+//#define DEBUG_ACL_QUERY
+//#define DEBUG_ACL_SET
+//#define DEBUG_EA_QUERY
+//#define DEBUG_EA_SET
+//#define DEBUG_LOCK
+//#define DEBUG_MISC
+#define DEBUG_TIME_BASED_COHERENCY
+#define DEBUG_MOUNT
+//#define DEBUG_VOLUME_QUERY
+
+//#define ENABLE_TIMINGS
+//#define ENABLE_INDV_TIMINGS
+#ifdef ENABLE_TIMINGS
+typedef struct __nfs41_timings {
+    LONG tops, sops;
+    LONGLONG ticks, size;
+} nfs41_timings;
+
+nfs41_timings lookup, readdir, open, close, getattr, setattr, getacl, setacl, volume,
+    read, write, lock, unlock, setexattr, getexattr;
+#endif
+DRIVER_INITIALIZE DriverEntry;
+DRIVER_UNLOAD nfs41_driver_unload;
+DRIVER_DISPATCH ( nfs41_FsdDispatch );
+
+struct _MINIRDR_DISPATCH nfs41_ops;
+PRDBSS_DEVICE_OBJECT nfs41_dev;
+
+#define DISABLE_CACHING 0
+#define ENABLE_READ_CACHING 1
+#define ENABLE_WRITE_CACHING 2
+#define ENABLE_READWRITE_CACHING 3
+
+#define NFS41_MM_POOLTAG        ('nfs4')
+#define NFS41_MM_POOLTAG_ACL    ('acls')
+#define NFS41_MM_POOLTAG_MOUNT  ('mnts')
+#define NFS41_MM_POOLTAG_OPEN   ('open')
+#define NFS41_MM_POOLTAG_UP     ('upca')
+#define NFS41_MM_POOLTAG_DOWN   ('down')
+
+KEVENT upcallEvent;
+FAST_MUTEX upcallLock, downcallLock, fcblistLock;
+FAST_MUTEX xidLock;
+FAST_MUTEX openOwnerLock;
+
+LONGLONG xid = 0;
+LONG open_owner_id = 1;
+
+#define DECLARE_CONST_ANSI_STRING(_var, _string) \
+    const CHAR _var ## _buffer[] = _string; \
+    const ANSI_STRING _var = { sizeof(_string) - sizeof(CHAR), \
+        sizeof(_string), (PCH) _var ## _buffer }
+#define RELATIVE(wait) (-(wait))
+#define NANOSECONDS(nanos) (((signed __int64)(nanos)) / 100L)
+#define MICROSECONDS(micros) (((signed __int64)(micros)) * NANOSECONDS(1000L))
+#define MILLISECONDS(milli) (((signed __int64)(milli)) * MICROSECONDS(1000L))
+#define SECONDS(seconds) (((signed __int64)(seconds)) * MILLISECONDS(1000L))
+
+DECLARE_CONST_ANSI_STRING(NfsV3Attributes, "NfsV3Attributes");
+DECLARE_CONST_ANSI_STRING(NfsSymlinkTargetName, "NfsSymlinkTargetName");
+DECLARE_CONST_ANSI_STRING(NfsActOnLink, "NfsActOnLink");
+
+INLINE BOOL AnsiStrEq(
+    IN const ANSI_STRING *lhs,
+    IN const CHAR *rhs,
+    IN const UCHAR rhs_len)
+{
+    return lhs->Length == rhs_len &&
+        RtlCompareMemory(lhs->Buffer, rhs, rhs_len) == rhs_len;
+}
+
+typedef struct _nfs3_attrs {
+    DWORD type, mode, nlink, uid, gid, filler1;
+    LARGE_INTEGER size, used;
+    struct {
+        DWORD specdata1;
+        DWORD specdata2;
+    } rdev;
+    LONGLONG fsid, fileid;
+    LONGLONG atime, mtime, ctime;
+} nfs3_attrs;
+LARGE_INTEGER unix_time_diff; //needed to convert windows time to unix
+
+enum ftype3 {
+    NF3REG = 1,
+    NF3DIR,
+    NF3BLK,
+    NF3CHR,
+    NF3LNK,
+    NF3SOCK,
+    NF3FIFO
+};
+
+typedef enum _nfs41_updowncall_state {
+    NFS41_WAITING_FOR_UPCALL,
+    NFS41_WAITING_FOR_DOWNCALL,
+    NFS41_DONE_PROCESSING,
+    NFS41_NOT_WAITING
+} nfs41_updowncall_state;
+
+#ifdef __REACTOS__
+#undef _errno
+#undef errno
+#endif
+
+typedef struct _updowncall_entry {
+    DWORD version;
+    LONGLONG xid;
+    DWORD opcode;
+    NTSTATUS status;
+    nfs41_updowncall_state state;
+    FAST_MUTEX lock;
+    LIST_ENTRY next;
+    KEVENT cond;
+    DWORD errno;
+    BOOLEAN async_op;
+    SECURITY_CLIENT_CONTEXT sec_ctx;
+    PSECURITY_CLIENT_CONTEXT psec_ctx;
+    HANDLE open_state;
+    HANDLE session;
+    PUNICODE_STRING filename;
+    PVOID buf;
+    ULONG buf_len;
+    ULONGLONG ChangeTime;
+    union {
+        struct {
+            PUNICODE_STRING srv_name;
+            PUNICODE_STRING root;
+            PFILE_FS_ATTRIBUTE_INFORMATION FsAttrs;
+            DWORD sec_flavor;
+            DWORD rsize;
+            DWORD wsize;
+            DWORD lease_time;
+        } Mount;
+        struct {
+            PMDL MdlAddress;
+            ULONGLONG offset;
+            PRX_CONTEXT rxcontext;
+        } ReadWrite;
+        struct {
+            LONGLONG offset;
+            LONGLONG length;
+            BOOLEAN exclusive;
+            BOOLEAN blocking;
+        } Lock;
+        struct {
+            ULONG count;
+            LOWIO_LOCK_LIST locks;
+        } Unlock;
+        struct {
+            FILE_BASIC_INFORMATION binfo;
+            FILE_STANDARD_INFORMATION sinfo;
+            UNICODE_STRING symlink;
+            ULONG access_mask;
+            ULONG access_mode;
+            ULONG attrs;
+            ULONG copts;
+            ULONG disp;
+            ULONG cattrs;
+            LONG open_owner_id;
+            DWORD mode;
+            HANDLE srv_open;
+            DWORD deleg_type;
+            BOOLEAN symlink_embedded;
+            PMDL EaMdl;
+            PVOID EaBuffer;
+        } Open;
+        struct {
+            HANDLE srv_open;
+            BOOLEAN remove;
+            BOOLEAN renamed;
+        } Close;
+        struct {
+            PUNICODE_STRING filter;
+            FILE_INFORMATION_CLASS InfoClass;
+            BOOLEAN restart_scan;
+            BOOLEAN return_single;
+            BOOLEAN initial_query;
+            PMDL mdl;
+            PVOID mdl_buf;
+        } QueryFile;
+        struct {
+            FILE_INFORMATION_CLASS InfoClass;
+        } SetFile;
+        struct {
+            DWORD mode;
+        } SetEa;
+        struct {
+            PVOID EaList;
+            ULONG EaListLength;
+            ULONG Overflow;
+            ULONG EaIndex;
+            BOOLEAN ReturnSingleEntry;
+            BOOLEAN RestartScan;
+        } QueryEa;
+        struct {
+            PUNICODE_STRING target;
+            BOOLEAN set;
+        } Symlink;
+        struct {
+            FS_INFORMATION_CLASS query;
+        } Volume;
+        struct {
+            SECURITY_INFORMATION query;
+        } Acl;
+    } u;
+
+} nfs41_updowncall_entry;
+
+typedef struct _updowncall_list {
+    LIST_ENTRY head;
+} nfs41_updowncall_list;
+nfs41_updowncall_list upcall, downcall;
+
+typedef struct _nfs41_mount_entry {
+    LIST_ENTRY next;
+    LUID login_id;
+    HANDLE authsys_session;
+    HANDLE gss_session;
+    HANDLE gssi_session;
+    HANDLE gssp_session;
+} nfs41_mount_entry;
+
+typedef struct _nfs41_mount_list {
+    LIST_ENTRY head;
+} nfs41_mount_list;
+
+#define nfs41_AddEntry(lock,list,pEntry)                    \
+            ExAcquireFastMutex(&lock);                      \
+            InsertTailList(&(list).head, &(pEntry)->next);  \
+            ExReleaseFastMutex(&lock);
+#define nfs41_RemoveFirst(lock,list,pEntry)                 \
+            ExAcquireFastMutex(&lock);                      \
+            pEntry = (IsListEmpty(&(list).head)             \
+            ? NULL                                          \
+            : RemoveHeadList(&(list).head));                \
+            ExReleaseFastMutex(&lock);
+#define nfs41_RemoveEntry(lock,pEntry)                      \
+            ExAcquireFastMutex(&lock);                      \
+            RemoveEntryList(&pEntry->next);                 \
+            ExReleaseFastMutex(&lock);
+#define nfs41_IsListEmpty(lock,list,flag)                   \
+            ExAcquireFastMutex(&lock);                      \
+            flag = IsListEmpty(&(list).head);               \
+            ExReleaseFastMutex(&lock);
+#define nfs41_GetFirstEntry(lock,list,pEntry)               \
+            ExAcquireFastMutex(&lock);                      \
+            pEntry = (IsListEmpty(&(list).head)             \
+             ? NULL                                         \
+             : (nfs41_updowncall_entry *)                   \
+               (CONTAINING_RECORD((list).head.Flink,        \
+                                  nfs41_updowncall_entry,   \
+                                  next)));                  \
+            ExReleaseFastMutex(&lock);
+#define nfs41_GetFirstMountEntry(lock,list,pEntry)          \
+            ExAcquireFastMutex(&lock);                      \
+            pEntry = (IsListEmpty(&(list).head)             \
+             ? NULL                                         \
+             : (nfs41_mount_entry *)                        \
+               (CONTAINING_RECORD((list).head.Flink,        \
+                                  nfs41_mount_entry,        \
+                                  next)));                  \
+            ExReleaseFastMutex(&lock);
+
+/* In order to cooperate with other network providers,
+ * we only claim paths of the format '\\server\nfs4\path' */
+DECLARE_CONST_UNICODE_STRING(NfsPrefix, L"\\nfs4");
+DECLARE_CONST_UNICODE_STRING(AUTH_SYS_NAME, L"sys");
+DECLARE_CONST_UNICODE_STRING(AUTHGSS_KRB5_NAME, L"krb5");
+DECLARE_CONST_UNICODE_STRING(AUTHGSS_KRB5I_NAME, L"krb5i");
+DECLARE_CONST_UNICODE_STRING(AUTHGSS_KRB5P_NAME, L"krb5p");
+DECLARE_CONST_UNICODE_STRING(SLASH, L"\\");
+DECLARE_CONST_UNICODE_STRING(EMPTY_STRING, L"");
+
+#define SERVER_NAME_BUFFER_SIZE         1024
+#define MOUNT_CONFIG_RW_SIZE_MIN        1024
+#define MOUNT_CONFIG_RW_SIZE_DEFAULT    1048576
+#define MOUNT_CONFIG_RW_SIZE_MAX        1048576
+#define MAX_SEC_FLAVOR_LEN              12
+#define UPCALL_TIMEOUT_DEFAULT          50  /* in seconds */
+
+typedef struct _NFS41_MOUNT_CONFIG {
+    DWORD ReadSize;
+    DWORD WriteSize;
+    BOOLEAN ReadOnly;
+    BOOLEAN write_thru;
+    BOOLEAN nocache;
+    WCHAR srv_buffer[SERVER_NAME_BUFFER_SIZE];
+    UNICODE_STRING SrvName;
+    WCHAR mntpt_buffer[MAX_PATH];
+    UNICODE_STRING MntPt;
+    WCHAR sec_flavor[MAX_SEC_FLAVOR_LEN];
+    UNICODE_STRING SecFlavor;
+    DWORD timeout;
+} NFS41_MOUNT_CONFIG, *PNFS41_MOUNT_CONFIG;
+
+typedef struct _NFS41_NETROOT_EXTENSION {
+    NODE_TYPE_CODE          NodeTypeCode;
+    NODE_BYTE_SIZE          NodeByteSize;
+    DWORD                   nfs41d_version;
+    BOOLEAN                 mounts_init;
+    FAST_MUTEX              mountLock;
+    nfs41_mount_list        mounts;
+} NFS41_NETROOT_EXTENSION, *PNFS41_NETROOT_EXTENSION;
+#define NFS41GetNetRootExtension(pNetRoot)      \
+        (((pNetRoot) == NULL) ? NULL :          \
+        (PNFS41_NETROOT_EXTENSION)((pNetRoot)->Context))
+
+/* FileSystemName as reported by FileFsAttributeInfo query */
+#define FS_NAME     L"NFS"
+#define FS_NAME_LEN (sizeof(FS_NAME) - sizeof(WCHAR))
+#define FS_ATTR_LEN (sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + FS_NAME_LEN)
+
+/* FileSystemName as reported by FileFsAttributeInfo query */
+#define VOL_NAME     L"PnfsVolume"
+#define VOL_NAME_LEN (sizeof(VOL_NAME) - sizeof(WCHAR))
+#define VOL_ATTR_LEN (sizeof(FILE_FS_VOLUME_INFORMATION) + VOL_NAME_LEN)
+
+typedef struct _NFS41_V_NET_ROOT_EXTENSION {
+    NODE_TYPE_CODE          NodeTypeCode;
+    NODE_BYTE_SIZE          NodeByteSize;
+    HANDLE                  session;
+    FILE_FS_ATTRIBUTE_INFORMATION FsAttrs;
+    DWORD                   sec_flavor;
+    DWORD                   timeout;
+    USHORT                  MountPathLen;
+    BOOLEAN                 read_only;
+    BOOLEAN                 write_thru;
+    BOOLEAN                 nocache;
+#define STORE_MOUNT_SEC_CONTEXT
+#ifdef STORE_MOUNT_SEC_CONTEXT
+    SECURITY_CLIENT_CONTEXT mount_sec_ctx;
+#endif
+} NFS41_V_NET_ROOT_EXTENSION, *PNFS41_V_NET_ROOT_EXTENSION;
+#define NFS41GetVNetRootExtension(pVNetRoot)      \
+        (((pVNetRoot) == NULL) ? NULL :           \
+        (PNFS41_V_NET_ROOT_EXTENSION)((pVNetRoot)->Context))
+
+typedef struct _NFS41_FCB {
+    NODE_TYPE_CODE          NodeTypeCode;
+    NODE_BYTE_SIZE          NodeByteSize;
+    FILE_BASIC_INFORMATION  BasicInfo;
+    FILE_STANDARD_INFORMATION StandardInfo;
+    BOOLEAN                 Renamed;
+    BOOLEAN                 DeletePending;
+    DWORD                   mode;
+    ULONGLONG               changeattr;
+} NFS41_FCB, *PNFS41_FCB;
+#define NFS41GetFcbExtension(pFcb)      \
+        (((pFcb) == NULL) ? NULL : (PNFS41_FCB)((pFcb)->Context))
+
+typedef struct _NFS41_FOBX {
+    NODE_TYPE_CODE          NodeTypeCode;
+    NODE_BYTE_SIZE          NodeByteSize;
+
+    HANDLE nfs41_open_state;
+    SECURITY_CLIENT_CONTEXT sec_ctx;
+    PVOID acl;
+    DWORD acl_len;
+    LARGE_INTEGER time;
+    DWORD deleg_type;
+    BOOLEAN write_thru;
+    BOOLEAN nocache;
+} NFS41_FOBX, *PNFS41_FOBX;
+#define NFS41GetFobxExtension(pFobx)  \
+        (((pFobx) == NULL) ? NULL : (PNFS41_FOBX)((pFobx)->Context))
+
+typedef struct _NFS41_SERVER_ENTRY {
+    PMRX_SRV_CALL                 pRdbssSrvCall;
+    WCHAR                         NameBuffer[SERVER_NAME_BUFFER_SIZE];
+    UNICODE_STRING                Name;             // the server name.
+} NFS41_SERVER_ENTRY, *PNFS41_SERVER_ENTRY;
+
+typedef struct _NFS41_DEVICE_EXTENSION {
+    NODE_TYPE_CODE          NodeTypeCode;
+    NODE_BYTE_SIZE          NodeByteSize;
+    PRDBSS_DEVICE_OBJECT    DeviceObject;
+    ULONG                   ActiveNodes;
+    HANDLE                  SharedMemorySection;
+    DWORD                   nfs41d_version;
+    BYTE                    VolAttrs[VOL_ATTR_LEN];
+    DWORD                   VolAttrsLen;
+    HANDLE                  openlistHandle;
+} NFS41_DEVICE_EXTENSION, *PNFS41_DEVICE_EXTENSION;
+
+#define NFS41GetDeviceExtension(RxContext,pExt)        \
+        PNFS41_DEVICE_EXTENSION pExt = (PNFS41_DEVICE_EXTENSION) \
+        ((PBYTE)(RxContext->RxDeviceObject) + sizeof(RDBSS_DEVICE_OBJECT))
+
+typedef struct _nfs41_fcb_list_entry {
+    LIST_ENTRY next;
+    PMRX_FCB fcb;
+    HANDLE session;
+    PNFS41_FOBX nfs41_fobx;
+    ULONGLONG ChangeTime;
+    BOOLEAN skip;
+} nfs41_fcb_list_entry;
+
+typedef struct _nfs41_fcb_list {
+    LIST_ENTRY head;
+} nfs41_fcb_list;
+nfs41_fcb_list openlist;
+
+typedef enum _NULMRX_STORAGE_TYPE_CODES {
+    NTC_NFS41_DEVICE_EXTENSION      =   (NODE_TYPE_CODE)0xFC00,
+} NFS41_STORAGE_TYPE_CODES;
+#define RxDefineNode( node, type )          \
+        node->NodeTypeCode = NTC_##type;    \
+        node->NodeByteSize = sizeof(type);
+
+#define RDR_NULL_STATE  0
+#define RDR_UNLOADED    1
+#define RDR_UNLOADING   2
+#define RDR_LOADING     3
+#define RDR_LOADED      4
+#define RDR_STOPPED     5
+#define RDR_STOPPING    6
+#define RDR_STARTING    7
+#define RDR_STARTED     8
+
+nfs41_init_driver_state nfs41_init_state = NFS41_INIT_DRIVER_STARTABLE;
+nfs41_start_driver_state nfs41_start_state = NFS41_START_DRIVER_STARTABLE;
+
+NTSTATUS map_readwrite_errors(DWORD status);
+
+void print_debug_header(
+    PRX_CONTEXT RxContext)
+{
+
+    PIO_STACK_LOCATION IrpSp = RxContext->CurrentIrpSp;
+
+    if (IrpSp) {
+        DbgP("FileOject %p name %wZ access r=%d,w=%d,d=%d share r=%d,w=%d,d=%d\n",
+            IrpSp->FileObject, &IrpSp->FileObject->FileName,
+            IrpSp->FileObject->ReadAccess, IrpSp->FileObject->WriteAccess,
+            IrpSp->FileObject->DeleteAccess, IrpSp->FileObject->SharedRead,
+            IrpSp->FileObject->SharedWrite, IrpSp->FileObject->SharedDelete);
+        print_file_object(0, IrpSp->FileObject);
+        print_irps_flags(0, RxContext->CurrentIrpSp);
+    } else
+        DbgP("Couldn't print FileObject IrpSp is NULL\n");
+
+    print_fo_all(1, RxContext);
+    if (RxContext->CurrentIrp)
+        print_irp_flags(0, RxContext->CurrentIrp);
+}
+
+/* convert strings from unicode -> ansi during marshalling to
+ * save space in the upcall buffers and avoid extra copies */
+INLINE ULONG length_as_utf8(
+    PCUNICODE_STRING str)
+{
+    ULONG ActualCount = 0;
+    RtlUnicodeToUTF8N(NULL, 0xffff, &ActualCount, str->Buffer, str->Length);
+    return sizeof(str->MaximumLength) + ActualCount + sizeof(UNICODE_NULL);
+}
+
+NTSTATUS marshall_unicode_as_utf8(
+    IN OUT unsigned char **pos,
+    IN PCUNICODE_STRING str)
+{
+    ANSI_STRING ansi;
+    ULONG ActualCount;
+    NTSTATUS status;
+
+    if (str->Length == 0) {
+        status = STATUS_SUCCESS;
+        ActualCount = 0;
+        ansi.MaximumLength = 1;
+        goto out_copy;
+    }
+
+    /* query the number of bytes required for the utf8 encoding */
+    status = RtlUnicodeToUTF8N(NULL, 0xffff,
+        &ActualCount, str->Buffer, str->Length);
+    if (status) {
+        print_error("RtlUnicodeToUTF8N('%wZ') failed with 0x%08X\n",
+            str, status);
+        goto out;
+    }
+
+    /* convert the string directly into the upcall buffer */
+    ansi.Buffer = (PCHAR)*pos + sizeof(ansi.MaximumLength);
+    ansi.MaximumLength = (USHORT)ActualCount + sizeof(UNICODE_NULL);
+    status = RtlUnicodeToUTF8N(ansi.Buffer, ansi.MaximumLength,
+        &ActualCount, str->Buffer, str->Length);
+    if (status) {
+        print_error("RtlUnicodeToUTF8N(%hu, '%wZ', %hu) failed with 0x%08X\n",
+            ansi.MaximumLength, str, str->Length, status);
+        goto out;
+    }
+
+out_copy:
+    RtlCopyMemory(*pos, &ansi.MaximumLength, sizeof(ansi.MaximumLength));
+    *pos += sizeof(ansi.MaximumLength);
+    (*pos)[ActualCount] = '\0';
+    *pos += ansi.MaximumLength;
+out:
+    return status;
+}
+
+NTSTATUS marshal_nfs41_header(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG header_len = 0;
+    unsigned char *tmp = buf;
+
+    header_len = sizeof(entry->version) + sizeof(entry->xid) +
+        sizeof(entry->opcode) + 2 * sizeof(HANDLE);
+    if (header_len > buf_len) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+    else
+        *len = header_len;
+    RtlCopyMemory(tmp, &entry->version, sizeof(entry->version));
+    tmp += sizeof(entry->version);
+    RtlCopyMemory(tmp, &entry->xid, sizeof(entry->xid));
+    tmp += sizeof(entry->xid);
+    RtlCopyMemory(tmp, &entry->opcode, sizeof(entry->opcode));
+    tmp += sizeof(entry->opcode);
+    RtlCopyMemory(tmp, &entry->session, sizeof(HANDLE));
+    tmp += sizeof(HANDLE);
+    RtlCopyMemory(tmp, &entry->open_state, sizeof(HANDLE));
+    tmp += sizeof(HANDLE);
+
+#ifdef DEBUG_MARSHAL_HEADER
+    if (MmIsAddressValid(entry->filename))
+        DbgP("[upcall header] xid=%lld opcode=%s filename=%wZ version=%d "
+            "session=0x%x open_state=0x%x\n", entry->xid,
+            opcode2string(entry->opcode), entry->filename,
+            entry->version, entry->session, entry->open_state);
+    else
+        status = STATUS_INTERNAL_ERROR;
+#endif
+out:
+    return status;
+}
+
+const char* secflavorop2name(
+    DWORD sec_flavor)
+{
+    switch(sec_flavor) {
+    case RPCSEC_AUTH_SYS:      return "AUTH_SYS";
+    case RPCSEC_AUTHGSS_KRB5:  return "AUTHGSS_KRB5";
+    case RPCSEC_AUTHGSS_KRB5I: return "AUTHGSS_KRB5I";
+    case RPCSEC_AUTHGSS_KRB5P: return "AUTHGSS_KRB5P";
+    }
+
+    return "UNKNOWN FLAVOR";
+}
+NTSTATUS marshal_nfs41_mount(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG header_len = 0;
+    unsigned char *tmp = buf;
+
+    status = marshal_nfs41_header(entry, tmp, buf_len, len);
+    if (status) goto out;
+    else tmp += *len;
+
+    /* 03/25/2011: Kernel crash to nfsd not running but mount upcall cued up */
+    if (!MmIsAddressValid(entry->u.Mount.srv_name) ||
+            !MmIsAddressValid(entry->u.Mount.root)) {
+        status = STATUS_INTERNAL_ERROR;
+        goto out;
+    }
+    header_len = *len + length_as_utf8(entry->u.Mount.srv_name) +
+        length_as_utf8(entry->u.Mount.root) + 3 * sizeof(DWORD);
+    if (header_len > buf_len) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+    status = marshall_unicode_as_utf8(&tmp, entry->u.Mount.srv_name);
+    if (status) goto out;
+    status = marshall_unicode_as_utf8(&tmp, entry->u.Mount.root);
+    if (status) goto out;
+    RtlCopyMemory(tmp, &entry->u.Mount.sec_flavor, sizeof(DWORD));
+    tmp += sizeof(DWORD);
+    RtlCopyMemory(tmp, &entry->u.Mount.rsize, sizeof(DWORD));
+    tmp += sizeof(DWORD);
+    RtlCopyMemory(tmp, &entry->u.Mount.wsize, sizeof(DWORD));
+
+    *len = header_len;
+
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("marshal_nfs41_mount: server name=%wZ mount point=%wZ sec_flavor=%s "
+         "rsize=%d wsize=%d\n", entry->u.Mount.srv_name, entry->u.Mount.root,
+         secflavorop2name(entry->u.Mount.sec_flavor), entry->u.Mount.rsize,
+         entry->u.Mount.wsize);
+#endif
+out:
+    return status;
+}
+
+NTSTATUS marshal_nfs41_unmount(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    return marshal_nfs41_header(entry, buf, buf_len, len);
+}
+
+NTSTATUS marshal_nfs41_open(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG header_len = 0;
+    unsigned char *tmp = buf;
+
+    status = marshal_nfs41_header(entry, tmp, buf_len, len);
+    if (status) goto out;
+    else tmp += *len;
+
+    header_len = *len + length_as_utf8(entry->filename) +
+        7 * sizeof(ULONG) + 2 * sizeof(HANDLE) +
+        length_as_utf8(&entry->u.Open.symlink);
+    if (header_len > buf_len) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+    status = marshall_unicode_as_utf8(&tmp, entry->filename);
+    if (status) goto out;
+    RtlCopyMemory(tmp, &entry->u.Open.access_mask,
+        sizeof(entry->u.Open.access_mask));
+    tmp += sizeof(entry->u.Open.access_mask);
+    RtlCopyMemory(tmp, &entry->u.Open.access_mode,
+        sizeof(entry->u.Open.access_mode));
+    tmp += sizeof(entry->u.Open.access_mode);
+    RtlCopyMemory(tmp, &entry->u.Open.attrs, sizeof(entry->u.Open.attrs));
+    tmp += sizeof(entry->u.Open.attrs);
+    RtlCopyMemory(tmp, &entry->u.Open.copts, sizeof(entry->u.Open.copts));
+    tmp += sizeof(entry->u.Open.copts);
+    RtlCopyMemory(tmp, &entry->u.Open.disp, sizeof(entry->u.Open.disp));
+    tmp += sizeof(entry->u.Open.disp);
+    RtlCopyMemory(tmp, &entry->u.Open.open_owner_id,
+        sizeof(entry->u.Open.open_owner_id));
+    tmp += sizeof(entry->u.Open.open_owner_id);
+    RtlCopyMemory(tmp, &entry->u.Open.mode, sizeof(DWORD));
+    tmp += sizeof(DWORD);
+    RtlCopyMemory(tmp, &entry->u.Open.srv_open, sizeof(HANDLE));
+    tmp += sizeof(HANDLE);
+    status = marshall_unicode_as_utf8(&tmp, &entry->u.Open.symlink);
+    if (status) goto out;
+
+    _SEH2_TRY {
+        if (entry->u.Open.EaMdl) {
+            entry->u.Open.EaBuffer =
+                MmMapLockedPagesSpecifyCache(entry->u.Open.EaMdl,
+#ifndef __REACTOS__
+                    UserMode, MmNonCached, NULL, TRUE, NormalPagePriority);
+#else
+                    UserMode, MmCached, NULL, TRUE, NormalPagePriority);
+#endif
+            if (entry->u.Open.EaBuffer == NULL) {
+                print_error("MmMapLockedPagesSpecifyCache failed to map pages\n");
+                status = STATUS_INSUFFICIENT_RESOURCES;
+                goto out;
+            }
+        }
+    } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+        print_error("Call to MmMapLocked failed due to exception 0x%x\n", _SEH2_GetExceptionCode());
+        status = STATUS_ACCESS_DENIED;
+        goto out;
+    } _SEH2_END;
+    RtlCopyMemory(tmp, &entry->u.Open.EaBuffer, sizeof(HANDLE));
+    *len = header_len;
+
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("marshal_nfs41_open: name=%wZ mask=0x%x access=0x%x attrs=0x%x "
+         "opts=0x%x dispo=0x%x open_owner_id=0x%x mode=%o srv_open=%p ea=%p\n",
+         entry->filename, entry->u.Open.access_mask,
+         entry->u.Open.access_mode, entry->u.Open.attrs, entry->u.Open.copts,
+         entry->u.Open.disp, entry->u.Open.open_owner_id, entry->u.Open.mode,
+         entry->u.Open.srv_open, entry->u.Open.EaBuffer);
+#endif
+out:
+    return status;
+}
+
+NTSTATUS marshal_nfs41_rw(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG header_len = 0;
+    unsigned char *tmp = buf;
+
+    status = marshal_nfs41_header(entry, tmp, buf_len, len);
+    if (status) goto out;
+    else tmp += *len;
+
+    header_len = *len + sizeof(entry->buf_len) +
+        sizeof(entry->u.ReadWrite.offset) + sizeof(HANDLE);
+    if (header_len > buf_len) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+
+    RtlCopyMemory(tmp, &entry->buf_len, sizeof(entry->buf_len));
+    tmp += sizeof(entry->buf_len);
+    RtlCopyMemory(tmp, &entry->u.ReadWrite.offset,
+        sizeof(entry->u.ReadWrite.offset));
+    tmp += sizeof(entry->u.ReadWrite.offset);
+    _SEH2_TRY {
+        entry->u.ReadWrite.MdlAddress->MdlFlags |= MDL_MAPPING_CAN_FAIL;
+        entry->buf =
+            MmMapLockedPagesSpecifyCache(entry->u.ReadWrite.MdlAddress,
+#ifndef __REACTOS__
+                UserMode, MmNonCached, NULL, TRUE, NormalPagePriority);
+#else
+                UserMode, MmCached, NULL, TRUE, NormalPagePriority);
+#endif
+        if (entry->buf == NULL) {
+            print_error("MmMapLockedPagesSpecifyCache failed to map pages\n");
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            goto out;
+        }
+    } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+        NTSTATUS code;
+        code = _SEH2_GetExceptionCode();
+        print_error("Call to MmMapLocked failed due to exception 0x%x\n", code);
+        status = STATUS_ACCESS_DENIED;
+        goto out;
+    } _SEH2_END;
+    RtlCopyMemory(tmp, &entry->buf, sizeof(HANDLE));
+    *len = header_len;
+
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("marshal_nfs41_rw: len=%lu offset=%llu MdlAddress=%p Userspace=%p\n",
+         entry->buf_len, entry->u.ReadWrite.offset,
+         entry->u.ReadWrite.MdlAddress, entry->buf);
+#endif
+out:
+    return status;
+}
+
+NTSTATUS marshal_nfs41_lock(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG header_len = 0;
+    unsigned char *tmp = buf;
+
+    status = marshal_nfs41_header(entry, tmp, buf_len, len);
+    if (status) goto out;
+    else tmp += *len;
+
+    header_len = *len + 2 * sizeof(LONGLONG) + 2 * sizeof(BOOLEAN);
+    if (header_len > buf_len) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+    RtlCopyMemory(tmp, &entry->u.Lock.offset, sizeof(LONGLONG));
+    tmp += sizeof(LONGLONG);
+    RtlCopyMemory(tmp, &entry->u.Lock.length, sizeof(LONGLONG));
+    tmp += sizeof(LONGLONG);
+    RtlCopyMemory(tmp, &entry->u.Lock.exclusive, sizeof(BOOLEAN));
+    tmp += sizeof(BOOLEAN);
+    RtlCopyMemory(tmp, &entry->u.Lock.blocking, sizeof(BOOLEAN));
+    *len = header_len;
+
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("marshal_nfs41_lock: offset=%llx length=%llx exclusive=%u "
+         "blocking=%u\n", entry->u.Lock.offset, entry->u.Lock.length,
+         entry->u.Lock.exclusive, entry->u.Lock.blocking);
+#endif
+out:
+    return status;
+}
+
+NTSTATUS marshal_nfs41_unlock(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG header_len = 0;
+    unsigned char *tmp = buf;
+    PLOWIO_LOCK_LIST lock;
+
+    status = marshal_nfs41_header(entry, tmp, buf_len, len);
+    if (status) goto out;
+    else tmp += *len;
+
+    header_len = *len + sizeof(ULONG) +
+        entry->u.Unlock.count * 2 * sizeof(LONGLONG);
+    if (header_len > buf_len) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+    RtlCopyMemory(tmp, &entry->u.Unlock.count, sizeof(ULONG));
+    tmp += sizeof(ULONG);
+
+    lock = &entry->u.Unlock.locks;
+    while (lock) {
+        RtlCopyMemory(tmp, &lock->ByteOffset, sizeof(LONGLONG));
+        tmp += sizeof(LONGLONG);
+        RtlCopyMemory(tmp, &lock->Length, sizeof(LONGLONG));
+        tmp += sizeof(LONGLONG);
+        lock = lock->Next;
+    }
+    *len = header_len;
+
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("marshal_nfs41_unlock: count=%u\n", entry->u.Unlock.count);
+#endif
+out:
+    return status;
+}
+
+NTSTATUS marshal_nfs41_close(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG header_len = 0;
+    unsigned char *tmp = buf;
+
+    status = marshal_nfs41_header(entry, tmp, buf_len, len);
+    if (status) goto out;
+    else tmp += *len;
+
+    header_len = *len + sizeof(BOOLEAN) + sizeof(HANDLE);
+    if (entry->u.Close.remove)
+        header_len += length_as_utf8(entry->filename) +
+            sizeof(BOOLEAN);
+
+    if (header_len > buf_len) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+    RtlCopyMemory(tmp, &entry->u.Close.remove, sizeof(BOOLEAN));
+    tmp += sizeof(BOOLEAN);
+    RtlCopyMemory(tmp, &entry->u.Close.srv_open, sizeof(HANDLE));
+    if (entry->u.Close.remove) {
+        tmp += sizeof(HANDLE);
+        status = marshall_unicode_as_utf8(&tmp, entry->filename);
+        if (status) goto out;
+        RtlCopyMemory(tmp, &entry->u.Close.renamed, sizeof(BOOLEAN));
+    }
+    *len = header_len;
+
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("marshal_nfs41_close: name=%wZ remove=%d srv_open=%p renamed=%d\n",
+        entry->filename->Length?entry->filename:&SLASH,
+        entry->u.Close.remove, entry->u.Close.srv_open, entry->u.Close.renamed);
+#endif
+out:
+    return status;
+}
+
+NTSTATUS marshal_nfs41_dirquery(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG header_len = 0;
+    unsigned char *tmp = buf;
+
+    status = marshal_nfs41_header(entry, tmp, buf_len, len);
+    if (status) goto out;
+    else tmp += *len;
+
+    header_len = *len + 2 * sizeof(ULONG) + sizeof(HANDLE) +
+        length_as_utf8(entry->u.QueryFile.filter) + 3 * sizeof(BOOLEAN);
+    if (header_len > buf_len) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+
+    RtlCopyMemory(tmp, &entry->u.QueryFile.InfoClass, sizeof(ULONG));
+    tmp += sizeof(ULONG);
+    RtlCopyMemory(tmp, &entry->buf_len, sizeof(ULONG));
+    tmp += sizeof(ULONG);
+    status = marshall_unicode_as_utf8(&tmp, entry->u.QueryFile.filter);
+    if (status) goto out;
+    RtlCopyMemory(tmp, &entry->u.QueryFile.initial_query, sizeof(BOOLEAN));
+    tmp += sizeof(BOOLEAN);
+    RtlCopyMemory(tmp, &entry->u.QueryFile.restart_scan, sizeof(BOOLEAN));
+    tmp += sizeof(BOOLEAN);
+    RtlCopyMemory(tmp, &entry->u.QueryFile.return_single, sizeof(BOOLEAN));
+    tmp += sizeof(BOOLEAN);
+    _SEH2_TRY {
+        entry->u.QueryFile.mdl_buf =
+            MmMapLockedPagesSpecifyCache(entry->u.QueryFile.mdl,
+#ifndef __REACTOS__
+                UserMode, MmNonCached, NULL, TRUE, NormalPagePriority);
+#else
+                UserMode, MmCached, NULL, TRUE, NormalPagePriority);
+#endif
+        if (entry->u.QueryFile.mdl_buf == NULL) {
+            print_error("MmMapLockedPagesSpecifyCache failed to map pages\n");
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            goto out;
+        }
+    } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+        NTSTATUS code;
+        code = _SEH2_GetExceptionCode();
+        print_error("Call to MmMapLocked failed due to exception 0x%x\n", code);
+        status = STATUS_ACCESS_DENIED;
+        goto out;
+    } _SEH2_END;
+    RtlCopyMemory(tmp, &entry->u.QueryFile.mdl_buf, sizeof(HANDLE));
+    *len = header_len;
+
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("marshal_nfs41_dirquery: filter='%wZ'class=%d len=%d "
+         "1st\\restart\\single=%d\\%d\\%d\n", entry->u.QueryFile.filter,
+         entry->u.QueryFile.InfoClass, entry->buf_len,
+         entry->u.QueryFile.initial_query, entry->u.QueryFile.restart_scan,
+         entry->u.QueryFile.return_single);
+#endif
+out:
+    return status;
+}
+
+NTSTATUS marshal_nfs41_filequery(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG header_len = 0;
+    unsigned char *tmp = buf;
+
+    status = marshal_nfs41_header(entry, tmp, buf_len, len);
+    if (status) goto out;
+    else tmp += *len;
+
+    header_len = *len + 2 * sizeof(ULONG);
+    if (header_len > buf_len) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+    RtlCopyMemory(tmp, &entry->u.QueryFile.InfoClass, sizeof(ULONG));
+    tmp += sizeof(ULONG);
+    RtlCopyMemory(tmp, &entry->buf_len, sizeof(ULONG));
+    tmp += sizeof(ULONG);
+    RtlCopyMemory(tmp, &entry->session, sizeof(HANDLE));
+    tmp += sizeof(HANDLE);
+    RtlCopyMemory(tmp, &entry->open_state, sizeof(HANDLE));
+    *len = header_len;
+
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("marshal_nfs41_filequery: class=%d\n", entry->u.QueryFile.InfoClass);
+#endif
+out:
+    return status;
+}
+
+NTSTATUS marshal_nfs41_fileset(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG header_len = 0;
+    unsigned char *tmp = buf;
+
+    status = marshal_nfs41_header(entry, tmp, buf_len, len);
+    if (status) goto out;
+    else tmp += *len;
+
+    header_len = *len + length_as_utf8(entry->filename) +
+        2 * sizeof(ULONG) + entry->buf_len;
+    if (header_len > buf_len) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+    status = marshall_unicode_as_utf8(&tmp, entry->filename);
+    if (status) goto out;
+    RtlCopyMemory(tmp, &entry->u.SetFile.InfoClass, sizeof(ULONG));
+    tmp += sizeof(ULONG);
+    RtlCopyMemory(tmp, &entry->buf_len, sizeof(ULONG));
+    tmp += sizeof(ULONG);
+    RtlCopyMemory(tmp, entry->buf, entry->buf_len);
+    *len = header_len;
+
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("marshal_nfs41_fileset: filename='%wZ' class=%d\n",
+        entry->filename, entry->u.SetFile.InfoClass);
+#endif
+out:
+    return status;
+}
+
+NTSTATUS marshal_nfs41_easet(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG header_len = 0;
+    unsigned char *tmp = buf;
+
+    status = marshal_nfs41_header(entry, tmp, buf_len, len);
+    if (status) goto out;
+    else tmp += *len;
+
+    header_len = *len + length_as_utf8(entry->filename) +
+        sizeof(ULONG) + entry->buf_len  + sizeof(DWORD);
+    if (header_len > buf_len) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+
+    status = marshall_unicode_as_utf8(&tmp, entry->filename);
+    if (status) goto out;
+    RtlCopyMemory(tmp, &entry->u.SetEa.mode, sizeof(DWORD));
+    tmp += sizeof(DWORD);
+    RtlCopyMemory(tmp, &entry->buf_len, sizeof(ULONG));
+    tmp += sizeof(ULONG);
+    RtlCopyMemory(tmp, entry->buf, entry->buf_len);
+    *len = header_len;
+
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("marshal_nfs41_easet: filename=%wZ, buflen=%d mode=0x%x\n",
+        entry->filename, entry->buf_len, entry->u.SetEa.mode);
+#endif
+out:
+    return status;
+}
+
+NTSTATUS marshal_nfs41_eaget(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG header_len = 0;
+    unsigned char *tmp = buf;
+
+    status = marshal_nfs41_header(entry, tmp, buf_len, len);
+    if (status) goto out;
+    else tmp += *len;
+
+    header_len = *len + length_as_utf8(entry->filename) +
+        3 * sizeof(ULONG) + entry->u.QueryEa.EaListLength + 2 * sizeof(BOOLEAN);
+
+    if (header_len > buf_len) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+
+    status = marshall_unicode_as_utf8(&tmp, entry->filename);
+    if (status) goto out;
+    RtlCopyMemory(tmp, &entry->u.QueryEa.EaIndex, sizeof(ULONG));
+    tmp += sizeof(ULONG);
+    RtlCopyMemory(tmp, &entry->u.QueryEa.RestartScan, sizeof(BOOLEAN));
+    tmp += sizeof(BOOLEAN);
+    RtlCopyMemory(tmp, &entry->u.QueryEa.ReturnSingleEntry, sizeof(BOOLEAN));
+    tmp += sizeof(BOOLEAN);
+    RtlCopyMemory(tmp, &entry->buf_len, sizeof(ULONG));
+    tmp += sizeof(ULONG);
+    RtlCopyMemory(tmp, &entry->u.QueryEa.EaListLength, sizeof(ULONG));
+    tmp += sizeof(ULONG);
+    if (entry->u.QueryEa.EaList && entry->u.QueryEa.EaListLength)
+        RtlCopyMemory(tmp, entry->u.QueryEa.EaList,
+            entry->u.QueryEa.EaListLength);
+    *len = header_len;
+
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("marshal_nfs41_eaget: filename=%wZ, index=%d list_len=%d "
+        "rescan=%d single=%d\n", entry->filename,
+        entry->u.QueryEa.EaIndex, entry->u.QueryEa.EaListLength,
+        entry->u.QueryEa.RestartScan, entry->u.QueryEa.ReturnSingleEntry);
+#endif
+out:
+    return status;
+}
+
+NTSTATUS marshal_nfs41_symlink(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG header_len = 0;
+    unsigned char *tmp = buf;
+
+    status = marshal_nfs41_header(entry, tmp, buf_len, len);
+    if (status) goto out;
+    else tmp += *len;
+
+    header_len = *len + sizeof(BOOLEAN) + length_as_utf8(entry->filename);
+    if (entry->u.Symlink.set)
+        header_len += length_as_utf8(entry->u.Symlink.target);
+    if (header_len > buf_len) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+
+    status = marshall_unicode_as_utf8(&tmp, entry->filename);
+    if (status) goto out;
+    RtlCopyMemory(tmp, &entry->u.Symlink.set, sizeof(BOOLEAN));
+    tmp += sizeof(BOOLEAN);
+    if (entry->u.Symlink.set) {
+        status = marshall_unicode_as_utf8(&tmp, entry->u.Symlink.target);
+        if (status) goto out;
+    }
+    *len = header_len;
+
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("marshal_nfs41_symlink: name %wZ symlink target %wZ\n",
+         entry->filename,
+         entry->u.Symlink.set?entry->u.Symlink.target : NULL);
+#endif
+out:
+    return status;
+}
+
+NTSTATUS marshal_nfs41_volume(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG header_len = 0;
+    unsigned char *tmp = buf;
+
+    status = marshal_nfs41_header(entry, tmp, buf_len, len);
+    if (status) goto out;
+    else tmp += *len;
+
+    header_len = *len + sizeof(FS_INFORMATION_CLASS);
+    if (header_len > buf_len) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+
+    RtlCopyMemory(tmp, &entry->u.Volume.query, sizeof(FS_INFORMATION_CLASS));
+    *len = header_len;
+
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("marshal_nfs41_volume: class=%d\n", entry->u.Volume.query);
+#endif
+out:
+    return status;
+}
+
+NTSTATUS marshal_nfs41_getacl(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG header_len = 0;
+    unsigned char *tmp = buf;
+
+    status = marshal_nfs41_header(entry, tmp, buf_len, len);
+    if (status) goto out;
+    else tmp += *len;
+
+    header_len = *len + sizeof(SECURITY_INFORMATION);
+    if (header_len > buf_len) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+
+    RtlCopyMemory(tmp, &entry->u.Acl.query, sizeof(SECURITY_INFORMATION));
+    *len = header_len;
+
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("marshal_nfs41_getacl: class=0x%x\n", entry->u.Acl.query);
+#endif
+out:
+    return status;
+}
+
+NTSTATUS marshal_nfs41_setacl(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG header_len = 0;
+    unsigned char *tmp = buf;
+
+    status = marshal_nfs41_header(entry, tmp, buf_len, len);
+    if (status) goto out;
+    else tmp += *len;
+
+    header_len = *len + sizeof(SECURITY_INFORMATION) +
+        sizeof(ULONG) + entry->buf_len;
+    if (header_len > buf_len) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+
+    RtlCopyMemory(tmp, &entry->u.Acl.query, sizeof(SECURITY_INFORMATION));
+    tmp += sizeof(SECURITY_INFORMATION);
+    RtlCopyMemory(tmp, &entry->buf_len, sizeof(ULONG));
+    tmp += sizeof(ULONG);
+    RtlCopyMemory(tmp, entry->buf, entry->buf_len);
+    *len = header_len;
+
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("marshal_nfs41_setacl: class=0x%x sec_desc_len=%lu\n",
+         entry->u.Acl.query, entry->buf_len);
+#endif
+out:
+    return status;
+}
+
+NTSTATUS marshal_nfs41_shutdown(
+    nfs41_updowncall_entry *entry,
+    unsigned char *buf,
+    ULONG buf_len,
+    ULONG *len)
+{
+    return marshal_nfs41_header(entry, buf, buf_len, len);
+}
+
+void nfs41_invalidate_cache (
+    IN PRX_CONTEXT RxContext)
+{
+    PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
+    unsigned char *buf = LowIoContext->ParamsFor.IoCtl.pInputBuffer;
+    ULONG flag = DISABLE_CACHING;
+    PMRX_SRV_OPEN srv_open;
+
+    RtlCopyMemory(&srv_open, buf, sizeof(HANDLE));
+#ifdef DEBUG_INVALIDATE_CACHE
+    DbgP("nfs41_invalidate_cache: received srv_open=%p %wZ\n",
+        srv_open, srv_open->pAlreadyPrefixedName);
+#endif
+    if (MmIsAddressValid(srv_open))
+        RxIndicateChangeOfBufferingStateForSrvOpen(
+            srv_open->pFcb->pNetRoot->pSrvCall, srv_open,
+            srv_open->Key, ULongToPtr(flag));
+}
+
+NTSTATUS handle_upcall(
+    IN PRX_CONTEXT RxContext,
+    IN nfs41_updowncall_entry *entry,
+    OUT ULONG *len)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
+    ULONG cbOut = LowIoContext->ParamsFor.IoCtl.OutputBufferLength;
+    unsigned char *pbOut = LowIoContext->ParamsFor.IoCtl.pOutputBuffer;
+
+    status = SeImpersonateClientEx(entry->psec_ctx, NULL);
+    if (status != STATUS_SUCCESS) {
+        print_error("SeImpersonateClientEx failed %x\n", status);
+        goto out;
+    }
+
+    switch(entry->opcode) {
+    case NFS41_SHUTDOWN:
+        status = marshal_nfs41_shutdown(entry, pbOut, cbOut, len);
+        KeSetEvent(&entry->cond, 0, FALSE);
+        break;
+    case NFS41_MOUNT:
+        status = marshal_nfs41_mount(entry, pbOut, cbOut, len);
+        break;
+    case NFS41_UNMOUNT:
+        status = marshal_nfs41_unmount(entry, pbOut, cbOut, len);
+        break;
+    case NFS41_OPEN:
+        status = marshal_nfs41_open(entry, pbOut, cbOut, len);
+        break;
+    case NFS41_READ:
+        status = marshal_nfs41_rw(entry, pbOut, cbOut, len);
+        break;
+    case NFS41_WRITE:
+        status = marshal_nfs41_rw(entry, pbOut, cbOut, len);
+        break;
+    case NFS41_LOCK:
+        status = marshal_nfs41_lock(entry, pbOut, cbOut, len);
+        break;
+    case NFS41_UNLOCK:
+        status = marshal_nfs41_unlock(entry, pbOut, cbOut, len);
+        break;
+    case NFS41_CLOSE:
+        status = marshal_nfs41_close(entry, pbOut, cbOut, len);
+        break;
+    case NFS41_DIR_QUERY:
+        status = marshal_nfs41_dirquery(entry, pbOut, cbOut, len);
+        break;
+    case NFS41_FILE_QUERY:
+        status = marshal_nfs41_filequery(entry, pbOut, cbOut, len);
+        break;
+    case NFS41_FILE_SET:
+        status = marshal_nfs41_fileset(entry, pbOut, cbOut, len);
+        break;
+    case NFS41_EA_SET:
+        status = marshal_nfs41_easet(entry, pbOut, cbOut, len);
+        break;
+    case NFS41_EA_GET:
+        status = marshal_nfs41_eaget(entry, pbOut, cbOut, len);
+        break;
+    case NFS41_SYMLINK:
+        status = marshal_nfs41_symlink(entry, pbOut, cbOut, len);
+        break;
+    case NFS41_VOLUME_QUERY:
+        status = marshal_nfs41_volume(entry, pbOut, cbOut, len);
+        break;
+    case NFS41_ACL_QUERY:
+        status = marshal_nfs41_getacl(entry, pbOut, cbOut, len);
+        break;
+    case NFS41_ACL_SET:
+        status = marshal_nfs41_setacl(entry, pbOut, cbOut, len);
+        break;
+    default:
+        status = STATUS_INVALID_PARAMETER;
+        print_error("Unknown nfs41 ops %d\n", entry->opcode);
+    }
+
+    if (status == STATUS_SUCCESS)
+        print_hexbuf(0, (unsigned char *)"upcall buffer", pbOut, *len);
+
+out:
+    return status;
+}
+
+NTSTATUS nfs41_UpcallCreate(
+    IN DWORD opcode,
+    IN PSECURITY_CLIENT_CONTEXT clnt_sec_ctx,
+    IN HANDLE session,
+    IN HANDLE open_state,
+    IN DWORD version,
+    IN PUNICODE_STRING filename,
+    OUT nfs41_updowncall_entry **entry_out)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    nfs41_updowncall_entry *entry;
+    SECURITY_SUBJECT_CONTEXT sec_ctx;
+    SECURITY_QUALITY_OF_SERVICE sec_qos;
+
+    entry = RxAllocatePoolWithTag(NonPagedPool, sizeof(nfs41_updowncall_entry),
+                NFS41_MM_POOLTAG_UP);
+    if (entry == NULL) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+
+    RtlZeroMemory(entry, sizeof(nfs41_updowncall_entry));
+    entry->xid = InterlockedIncrement64(&xid);
+    entry->opcode = opcode;
+    entry->state = NFS41_WAITING_FOR_UPCALL;
+    entry->session = session;
+    entry->open_state = open_state;
+    entry->version = version;
+    if (filename && filename->Length) entry->filename = filename;
+    else if (filename && !filename->Length) entry->filename = (PUNICODE_STRING)&SLASH;
+    else entry->filename = (PUNICODE_STRING)&EMPTY_STRING;
+    /*XXX KeInitializeEvent will bugcheck under verifier if allocated
+     * from PagedPool? */
+    KeInitializeEvent(&entry->cond, SynchronizationEvent, FALSE);
+    ExInitializeFastMutex(&entry->lock);
+
+    if (clnt_sec_ctx == NULL) {
+        SeCaptureSubjectContext(&sec_ctx);
+        sec_qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+        sec_qos.ImpersonationLevel = SecurityImpersonation;
+        sec_qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+        sec_qos.EffectiveOnly = 0;
+        status = SeCreateClientSecurityFromSubjectContext(&sec_ctx, &sec_qos,
+                    1, &entry->sec_ctx);
+        if (status != STATUS_SUCCESS) {
+            print_error("nfs41_UpcallCreate: "
+                "SeCreateClientSecurityFromSubjectContext failed with %x\n",
+                status);
+            RxFreePool(entry);
+        } else
+            entry->psec_ctx = &entry->sec_ctx;
+        SeReleaseSubjectContext(&sec_ctx);
+    } else
+        entry->psec_ctx = clnt_sec_ctx;
+
+    *entry_out = entry;
+out:
+    return status;
+}
+
+NTSTATUS nfs41_UpcallWaitForReply(
+    IN nfs41_updowncall_entry *entry,
+    IN DWORD secs)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    nfs41_AddEntry(upcallLock, upcall, entry);
+    KeSetEvent(&upcallEvent, 0, FALSE);
+    if (!entry->async_op) {
+        LARGE_INTEGER timeout;
+        timeout.QuadPart = RELATIVE(SECONDS(secs));
+        /* 02/03/2011 AGLO: it is not clear what the "right" waiting design
+         * should be. Having non-interruptable waiting seems to be the right
+         * approach. However, when things go wrong, the only wait to proceed
+         * is a reboot (since "waits" are not interruptable we can't stop a
+         * hung task. Having interruptable wait causes issues with security
+         * context. For now, I'm making CLOSE non-interruptable but keeping
+         * the rest interruptable so that we don't have to reboot all the time
+         */
+        /* 02/15/2011 cbodley: added NFS41_UNLOCK for the same reason. locking
+         * tests were triggering an interrupted unlock, which led to a bugcheck
+         * in CloseSrvOpen() */
+#define MAKE_WAITONCLOSE_NONITERRUPTABLE
+#ifdef MAKE_WAITONCLOSE_NONITERRUPTABLE
+        if (entry->opcode == NFS41_CLOSE || entry->opcode == NFS41_UNLOCK)
+            status = KeWaitForSingleObject(&entry->cond, Executive,
+                        KernelMode, FALSE, &timeout);
+        else {
+            status = KeWaitForSingleObject(&entry->cond, Executive,
+                        UserMode, TRUE, &timeout);
+        }
+        if (status != STATUS_SUCCESS) {
+            print_wait_status(1, "[downcall]", status,
+                opcode2string(entry->opcode), entry, entry->xid);
+            if (status == STATUS_TIMEOUT)
+                status = STATUS_NETWORK_UNREACHABLE;
+        }
+#else
+
+        status = KeWaitForSingleObject(&entry->cond, Executive, KernelMode, FALSE, NULL);
+#endif
+        print_wait_status(0, "[downcall]", status, opcode2string(entry->opcode),
+            entry, entry->xid);
+    } else
+        goto out;
+
+    switch(status) {
+    case STATUS_SUCCESS: break;
+    case STATUS_USER_APC:
+    case STATUS_ALERTED:
+    default:
+        ExAcquireFastMutex(&entry->lock);
+        if (entry->state == NFS41_DONE_PROCESSING) {
+            ExReleaseFastMutex(&entry->lock);
+            break;
+        }
+        DbgP("[upcall] abandoning %s entry=%p xid=%lld\n",
+            opcode2string(entry->opcode), entry, entry->xid);
+        entry->state = NFS41_NOT_WAITING;
+        ExReleaseFastMutex(&entry->lock);
+        goto out;
+    }
+    nfs41_RemoveEntry(downcallLock, entry);
+out:
+    return status;
+}
+
+NTSTATUS nfs41_upcall(
+    IN PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    nfs41_updowncall_entry *entry = NULL;
+    ULONG len = 0;
+    PLIST_ENTRY pEntry;
+
+process_upcall:
+    nfs41_RemoveFirst(upcallLock, upcall, pEntry);
+    if (pEntry) {
+        entry = (nfs41_updowncall_entry *)CONTAINING_RECORD(pEntry,
+                    nfs41_updowncall_entry, next);
+        ExAcquireFastMutex(&entry->lock);
+        nfs41_AddEntry(downcallLock, downcall, entry);
+        status = handle_upcall(RxContext, entry, &len);
+        if (status == STATUS_SUCCESS &&
+                entry->state == NFS41_WAITING_FOR_UPCALL)
+            entry->state = NFS41_WAITING_FOR_DOWNCALL;
+        ExReleaseFastMutex(&entry->lock);
+        if (status) {
+            entry->status = status;
+            KeSetEvent(&entry->cond, 0, FALSE);
+            RxContext->InformationToReturn = 0;
+        } else
+            RxContext->InformationToReturn = len;
+    }
+    else {
+        status = KeWaitForSingleObject(&upcallEvent, Executive, UserMode, TRUE,
+            (PLARGE_INTEGER) NULL);
+        print_wait_status(0, "[upcall]", status, NULL, NULL, 0);
+        switch (status) {
+        case STATUS_SUCCESS: goto process_upcall;
+        case STATUS_USER_APC:
+        case STATUS_ALERTED:
+        default: goto out;
+        }
+    }
+out:
+    return status;
+}
+
+void unmarshal_nfs41_header(
+    nfs41_updowncall_entry *tmp,
+    unsigned char **buf)
+{
+    RtlZeroMemory(tmp, sizeof(nfs41_updowncall_entry));
+
+    RtlCopyMemory(&tmp->xid, *buf, sizeof(tmp->xid));
+    *buf += sizeof(tmp->xid);
+    RtlCopyMemory(&tmp->opcode, *buf, sizeof(tmp->opcode));
+    *buf += sizeof(tmp->opcode);
+    RtlCopyMemory(&tmp->status, *buf, sizeof(tmp->status));
+    *buf += sizeof(tmp->status);
+    RtlCopyMemory(&tmp->errno, *buf, sizeof(tmp->errno));
+    *buf += sizeof(tmp->errno);
+#ifdef DEBUG_MARSHAL_HEADER
+    DbgP("[downcall header] xid=%lld opcode=%s status=%d errno=%d\n", tmp->xid,
+        opcode2string(tmp->opcode), tmp->status, tmp->errno);
+#endif
+}
+
+void unmarshal_nfs41_mount(
+    nfs41_updowncall_entry *cur,
+    unsigned char **buf)
+{
+    RtlCopyMemory(&cur->session, *buf, sizeof(HANDLE));
+    *buf += sizeof(HANDLE);
+    RtlCopyMemory(&cur->version, *buf, sizeof(DWORD));
+    *buf += sizeof(DWORD);
+    RtlCopyMemory(&cur->u.Mount.lease_time, *buf, sizeof(DWORD));
+    *buf += sizeof(DWORD);
+    RtlCopyMemory(cur->u.Mount.FsAttrs, *buf, sizeof(FILE_FS_ATTRIBUTE_INFORMATION));
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("unmarshal_nfs41_mount: session pointer 0x%x version %d lease_time "
+         "%d\n", cur->session, cur->version, cur->u.Mount.lease_time);
+#endif
+}
+
+VOID unmarshal_nfs41_setattr(
+    nfs41_updowncall_entry *cur,
+    PULONGLONG dest_buf,
+    unsigned char **buf)
+{
+    RtlCopyMemory(dest_buf, *buf, sizeof(ULONGLONG));
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("unmarshal_nfs41_setattr: returned ChangeTime %llu\n", *dest_buf);
+#endif
+}
+
+NTSTATUS unmarshal_nfs41_rw(
+    nfs41_updowncall_entry *cur,
+    unsigned char **buf)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    RtlCopyMemory(&cur->buf_len, *buf, sizeof(cur->buf_len));
+    *buf += sizeof(cur->buf_len);
+    RtlCopyMemory(&cur->ChangeTime, *buf, sizeof(ULONGLONG));
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("unmarshal_nfs41_rw: returned len %lu ChangeTime %llu\n",
+        cur->buf_len, cur->ChangeTime);
+#endif
+#if 1
+    /* 08/27/2010: it looks like we really don't need to call
+        * MmUnmapLockedPages() eventhough we called
+        * MmMapLockedPagesSpecifyCache() as the MDL passed to us
+        * is already locked.
+        */
+    _SEH2_TRY {
+        MmUnmapLockedPages(cur->buf, cur->u.ReadWrite.MdlAddress);
+    } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+        NTSTATUS code;
+        code = _SEH2_GetExceptionCode();
+        print_error("Call to MmUnmapLockedPages failed due to"
+            " exception 0x%0x\n", code);
+        status = STATUS_ACCESS_DENIED;
+    } _SEH2_END;
+#endif
+    return status;
+}
+
+NTSTATUS unmarshal_nfs41_open(
+    nfs41_updowncall_entry *cur,
+    unsigned char **buf)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    _SEH2_TRY {
+        if (cur->u.Open.EaBuffer)
+            MmUnmapLockedPages(cur->u.Open.EaBuffer, cur->u.Open.EaMdl);
+    } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+        print_error("MmUnmapLockedPages thrown exception=0x%0x\n", _SEH2_GetExceptionCode());
+        status = cur->status = STATUS_ACCESS_DENIED;
+        goto out;
+    } _SEH2_END;
+
+    RtlCopyMemory(&cur->u.Open.binfo, *buf, sizeof(FILE_BASIC_INFORMATION));
+    *buf += sizeof(FILE_BASIC_INFORMATION);
+    RtlCopyMemory(&cur->u.Open.sinfo, *buf, sizeof(FILE_STANDARD_INFORMATION));
+    *buf += sizeof(FILE_STANDARD_INFORMATION);
+    RtlCopyMemory(&cur->open_state, *buf, sizeof(HANDLE));
+    *buf += sizeof(HANDLE);
+    RtlCopyMemory(&cur->u.Open.mode, *buf, sizeof(DWORD));
+    *buf += sizeof(DWORD);
+    RtlCopyMemory(&cur->ChangeTime, *buf, sizeof(ULONGLONG));
+    *buf += sizeof(ULONGLONG);
+    RtlCopyMemory(&cur->u.Open.deleg_type, *buf, sizeof(DWORD));
+    *buf += sizeof(DWORD);
+    if (cur->errno == ERROR_REPARSE) {
+        RtlCopyMemory(&cur->u.Open.symlink_embedded, *buf, sizeof(BOOLEAN));
+        *buf += sizeof(BOOLEAN);
+        RtlCopyMemory(&cur->u.Open.symlink.MaximumLength, *buf,
+            sizeof(USHORT));
+        *buf += sizeof(USHORT);
+        cur->u.Open.symlink.Length = cur->u.Open.symlink.MaximumLength -
+            sizeof(WCHAR);
+        cur->u.Open.symlink.Buffer = RxAllocatePoolWithTag(NonPagedPool,
+            cur->u.Open.symlink.MaximumLength, NFS41_MM_POOLTAG);
+        if (cur->u.Open.symlink.Buffer == NULL) {
+            cur->status = STATUS_INSUFFICIENT_RESOURCES;
+            status = STATUS_UNSUCCESSFUL;
+            goto out;
+        }
+        RtlCopyMemory(cur->u.Open.symlink.Buffer, *buf,
+            cur->u.Open.symlink.MaximumLength);
+#ifdef DEBUG_MARSHAL_DETAIL
+        DbgP("unmarshal_nfs41_open: ERROR_REPARSE -> '%wZ'\n", &cur->u.Open.symlink);
+#endif
+    }
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("unmarshal_nfs41_open: open_state 0x%x mode %o changeattr %llu "
+        "deleg_type %d\n", cur->open_state, cur->u.Open.mode,
+        cur->ChangeTime, cur->u.Open.deleg_type);
+#endif
+out:
+    return status;
+}
+
+NTSTATUS unmarshal_nfs41_dirquery(
+    nfs41_updowncall_entry *cur,
+    unsigned char **buf)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG buf_len;
+
+    RtlCopyMemory(&buf_len, *buf, sizeof(ULONG));
+#ifdef DEBUG_MARSHAL_DETAIL
+    DbgP("unmarshal_nfs41_dirquery: reply size %d\n", buf_len);
+#endif
+    *buf += sizeof(ULONG);
+    _SEH2_TRY {
+        MmUnmapLockedPages(cur->u.QueryFile.mdl_buf, cur->u.QueryFile.mdl);
+    } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+        NTSTATUS code;
+        code = _SEH2_GetExceptionCode();
+        print_error("MmUnmapLockedPages thrown exception=0x%0x\n", code);
+        status = STATUS_ACCESS_DENIED;
+    } _SEH2_END;
+    if (buf_len > cur->buf_len)
+        cur->status = STATUS_BUFFER_TOO_SMALL;
+    cur->buf_len = buf_len;
+
+    return status;
+}
+
+void unmarshal_nfs41_attrget(
+    nfs41_updowncall_entry *cur,
+    PVOID attr_value,
+    ULONG *attr_len,
+    unsigned char **buf)
+{
+    ULONG buf_len;
+    RtlCopyMemory(&buf_len, *buf, sizeof(ULONG));
+    if (buf_len > *attr_len) {
+        cur->status = STATUS_BUFFER_TOO_SMALL;
+        return;
+    }
+    *buf += sizeof(ULONG);
+    *attr_len = buf_len;
+    RtlCopyMemory(attr_value, *buf, buf_len);
+    *buf += buf_len;
+}
+
+void unmarshal_nfs41_eaget(
+    nfs41_updowncall_entry *cur,
+    unsigned char **buf)
+{
+    RtlCopyMemory(&cur->u.QueryEa.Overflow, *buf, sizeof(ULONG));
+    *buf += sizeof(ULONG);
+    RtlCopyMemory(&cur->buf_len, *buf, sizeof(ULONG));
+    *buf += sizeof(ULONG);
+    if (cur->u.QueryEa.Overflow != ERROR_INSUFFICIENT_BUFFER) {
+        RtlCopyMemory(cur->buf, *buf, cur->buf_len);
+        *buf += cur->buf_len;
+    }
+}
+
+void unmarshal_nfs41_getattr(
+    nfs41_updowncall_entry *cur,
+    unsigned char **buf)
+{
+    unmarshal_nfs41_attrget(cur, cur->buf, &cur->buf_len, buf);
+    RtlCopyMemory(&cur->ChangeTime, *buf, sizeof(LONGLONG));
+#ifdef DEBUG_MARSHAL_DETAIL
+    if (cur->u.QueryFile.InfoClass == FileBasicInformation)
+        DbgP("[unmarshal_nfs41_getattr] ChangeTime %llu\n", cur->ChangeTime);
+#endif
+}
+
+NTSTATUS unmarshal_nfs41_getacl(
+    nfs41_updowncall_entry *cur,
+    unsigned char **buf)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    DWORD buf_len;
+
+    RtlCopyMemory(&buf_len, *buf, sizeof(DWORD));
+    *buf += sizeof(DWORD);
+    cur->buf = RxAllocatePoolWithTag(NonPagedPool,
+        buf_len, NFS41_MM_POOLTAG_ACL);
+    if (cur->buf == NULL) {
+        cur->status = status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+    RtlCopyMemory(cur->buf, *buf, buf_len);
+    if (buf_len > cur->buf_len)
+        cur->status = STATUS_BUFFER_TOO_SMALL;
+    cur->buf_len = buf_len;
+
+out:
+    return status;
+}
+
+void unmarshal_nfs41_symlink(
+    nfs41_updowncall_entry *cur,
+    unsigned char **buf)
+{
+    if (cur->u.Symlink.set) return;
+
+    RtlCopyMemory(&cur->u.Symlink.target->Length, *buf, sizeof(USHORT));
+    *buf += sizeof(USHORT);
+    if (cur->u.Symlink.target->Length >
+            cur->u.Symlink.target->MaximumLength) {
+        cur->status = STATUS_BUFFER_TOO_SMALL;
+        return;
+    }
+    RtlCopyMemory(cur->u.Symlink.target->Buffer, *buf,
+        cur->u.Symlink.target->Length);
+    cur->u.Symlink.target->Length -= sizeof(UNICODE_NULL);
+}
+
+NTSTATUS nfs41_downcall(
+    IN PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
+    ULONG in_len = LowIoContext->ParamsFor.IoCtl.InputBufferLength;
+    unsigned char *buf = LowIoContext->ParamsFor.IoCtl.pInputBuffer;
+    PLIST_ENTRY pEntry;
+    nfs41_updowncall_entry *tmp, *cur= NULL;
+    BOOLEAN found = 0;
+
+    print_hexbuf(0, (unsigned char *)"downcall buffer", buf, in_len);
+
+    tmp = RxAllocatePoolWithTag(NonPagedPool, sizeof(nfs41_updowncall_entry),
+            NFS41_MM_POOLTAG_DOWN);
+    if (tmp == NULL) goto out;
+
+    unmarshal_nfs41_header(tmp, &buf);
+
+    ExAcquireFastMutex(&downcallLock);
+    pEntry = &downcall.head;
+    pEntry = pEntry->Flink;
+    while (pEntry != NULL) {
+        cur = (nfs41_updowncall_entry *)CONTAINING_RECORD(pEntry,
+                nfs41_updowncall_entry, next);
+        if (cur->xid == tmp->xid) {
+            found = 1;
+            break;
+        }
+        if (pEntry->Flink == &downcall.head)
+            break;
+        pEntry = pEntry->Flink;
+    }
+    ExReleaseFastMutex(&downcallLock);
+    SeStopImpersonatingClient();
+    if (!found) {
+        print_error("Didn't find xid=%lld entry\n", tmp->xid);
+        goto out_free;
+    }
+
+    ExAcquireFastMutex(&cur->lock);
+    if (cur->state == NFS41_NOT_WAITING) {
+        DbgP("[downcall] Nobody is waiting for this request!!!\n");
+        switch(cur->opcode) {
+        case NFS41_WRITE:
+        case NFS41_READ:
+            MmUnmapLockedPages(cur->buf, cur->u.ReadWrite.MdlAddress);
+            break;
+        case NFS41_DIR_QUERY:
+            MmUnmapLockedPages(cur->u.QueryFile.mdl_buf,
+                    cur->u.QueryFile.mdl);
+            IoFreeMdl(cur->u.QueryFile.mdl);
+            break;
+        case NFS41_OPEN:
+            if (cur->u.Open.EaMdl) {
+                MmUnmapLockedPages(cur->u.Open.EaBuffer,
+                        cur->u.Open.EaMdl);
+                IoFreeMdl(cur->u.Open.EaMdl);
+            }
+            break;
+        }
+        ExReleaseFastMutex(&cur->lock);
+        nfs41_RemoveEntry(downcallLock, cur);
+        RxFreePool(cur);
+        status = STATUS_UNSUCCESSFUL;
+        goto out_free;
+    }
+    cur->state = NFS41_DONE_PROCESSING;
+    cur->status = tmp->status;
+    cur->errno = tmp->errno;
+    status = STATUS_SUCCESS;
+
+    if (!tmp->status) {
+        switch (tmp->opcode) {
+        case NFS41_MOUNT:
+            unmarshal_nfs41_mount(cur, &buf);
+            break;
+        case NFS41_WRITE:
+        case NFS41_READ:
+            status = unmarshal_nfs41_rw(cur, &buf);
+            break;
+        case NFS41_OPEN:
+            status = unmarshal_nfs41_open(cur, &buf);
+            break;
+        case NFS41_DIR_QUERY:
+            status = unmarshal_nfs41_dirquery(cur, &buf);
+            break;
+        case NFS41_FILE_QUERY:
+            unmarshal_nfs41_getattr(cur, &buf);
+            break;
+        case NFS41_EA_GET:
+            unmarshal_nfs41_eaget(cur, &buf);
+            break;
+        case NFS41_SYMLINK:
+            unmarshal_nfs41_symlink(cur, &buf);
+            break;
+        case NFS41_VOLUME_QUERY:
+            unmarshal_nfs41_attrget(cur, cur->buf, &cur->buf_len, &buf);
+            break;
+        case NFS41_ACL_QUERY:
+            status = unmarshal_nfs41_getacl(cur, &buf);
+            break;
+        case NFS41_FILE_SET:
+            unmarshal_nfs41_setattr(cur, &cur->ChangeTime, &buf);
+            break;
+        case NFS41_EA_SET:
+            unmarshal_nfs41_setattr(cur, &cur->ChangeTime, &buf);
+            break;
+        case NFS41_ACL_SET:
+            unmarshal_nfs41_setattr(cur, &cur->ChangeTime, &buf);
+            break;
+        }
+    }
+    ExReleaseFastMutex(&cur->lock);
+    if (cur->async_op) {
+        if (cur->status == STATUS_SUCCESS) {
+            cur->u.ReadWrite.rxcontext->StoredStatus = STATUS_SUCCESS;
+            cur->u.ReadWrite.rxcontext->InformationToReturn =
+                cur->buf_len;
+        } else {
+            cur->u.ReadWrite.rxcontext->StoredStatus =
+                map_readwrite_errors(cur->status);
+            cur->u.ReadWrite.rxcontext->InformationToReturn = 0;
+        }
+        nfs41_RemoveEntry(downcallLock, cur);
+        RxLowIoCompletion(cur->u.ReadWrite.rxcontext);
+        RxFreePool(cur);
+    } else
+        KeSetEvent(&cur->cond, 0, FALSE);
+
+out_free:
+    RxFreePool(tmp);
+out:
+    return status;
+}
+
+NTSTATUS nfs41_shutdown_daemon(
+    DWORD version)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    nfs41_updowncall_entry *entry = NULL;
+
+    DbgEn();
+    status = nfs41_UpcallCreate(NFS41_SHUTDOWN, NULL, INVALID_HANDLE_VALUE,
+        INVALID_HANDLE_VALUE, version, NULL, &entry);
+    if (status) goto out;
+
+    status = nfs41_UpcallWaitForReply(entry, UPCALL_TIMEOUT_DEFAULT);
+    SeDeleteClientSecurity(&entry->sec_ctx);
+    if (status) goto out;
+
+    RxFreePool(entry);
+out:
+    DbgEx();
+    return status;
+}
+
+NTSTATUS SharedMemoryInit(
+    OUT PHANDLE phSection)
+{
+    NTSTATUS status;
+    HANDLE hSection;
+    UNICODE_STRING SectionName;
+    SECURITY_DESCRIPTOR SecurityDesc;
+    OBJECT_ATTRIBUTES SectionAttrs;
+    LARGE_INTEGER nSectionSize;
+
+    DbgEn();
+
+    RtlInitUnicodeString(&SectionName, NFS41_SHARED_MEMORY_NAME);
+
+    /* XXX: setting dacl=NULL grants access to everyone */
+    status = RtlCreateSecurityDescriptor(&SecurityDesc,
+        SECURITY_DESCRIPTOR_REVISION);
+    if (status) {
+        print_error("RtlCreateSecurityDescriptor() failed with %08X\n", status);
+        goto out;
+    }
+    status = RtlSetDaclSecurityDescriptor(&SecurityDesc, TRUE, NULL, FALSE);
+    if (status) {
+        print_error("RtlSetDaclSecurityDescriptor() failed with %08X\n", status);
+        goto out;
+    }
+
+    InitializeObjectAttributes(&SectionAttrs, &SectionName,
+        0, NULL, &SecurityDesc);
+
+    nSectionSize.QuadPart = sizeof(NFS41NP_SHARED_MEMORY);
+
+    status = ZwCreateSection(&hSection, SECTION_MAP_READ | SECTION_MAP_WRITE,
+        &SectionAttrs, &nSectionSize, PAGE_READWRITE, SEC_COMMIT, NULL);
+    switch (status) {
+    case STATUS_SUCCESS:
+        break;
+    case STATUS_OBJECT_NAME_COLLISION:
+        DbgP("section already created; returning success\n");
+        status = STATUS_SUCCESS;
+        goto out;
+    default:
+        DbgP("ZwCreateSection failed with %08X\n", status);
+        goto out;
+    }
+out:
+    DbgEx();
+    return status;
+}
+
+NTSTATUS SharedMemoryFree(
+    IN HANDLE hSection)
+{
+    NTSTATUS status;
+    DbgEn();
+    status = ZwClose(hSection);
+    DbgEx();
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_Start(
+#else
+NTSTATUS nfs41_Start(
+#endif
+    IN OUT PRX_CONTEXT RxContext,
+    IN OUT PRDBSS_DEVICE_OBJECT dev)
+{
+    NTSTATUS status;
+    NFS41GetDeviceExtension(RxContext, DevExt);
+
+    DbgEn();
+
+    status = SharedMemoryInit(&DevExt->SharedMemorySection);
+    if (status) {
+        print_error("InitSharedMemory failed with %08X\n", status);
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+
+    InterlockedCompareExchange((PLONG)&nfs41_start_state,
+        NFS41_START_DRIVER_STARTED,
+        NFS41_START_DRIVER_START_IN_PROGRESS);
+out:
+    DbgEx();
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_Stop(
+#else
+NTSTATUS nfs41_Stop(
+#endif
+    IN OUT PRX_CONTEXT RxContext,
+    IN OUT PRDBSS_DEVICE_OBJECT dev)
+{
+    NTSTATUS status;
+    NFS41GetDeviceExtension(RxContext, DevExt);
+    DbgEn();
+    status = SharedMemoryFree(DevExt->SharedMemorySection);
+    DbgEx();
+    return status;
+}
+
+NTSTATUS GetConnectionHandle(
+    IN PUNICODE_STRING ConnectionName,
+    IN PVOID EaBuffer,
+    IN ULONG EaLength,
+    OUT PHANDLE Handle)
+{
+    NTSTATUS status;
+    IO_STATUS_BLOCK IoStatusBlock;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+
+#ifdef DEBUG_MOUNT
+    DbgEn();
+#endif
+    InitializeObjectAttributes(&ObjectAttributes, ConnectionName,
+        OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL);
+
+    print_error("Len %d Buf %p\n", EaLength, EaBuffer);
+
+    status = ZwCreateFile(Handle, SYNCHRONIZE, &ObjectAttributes,
+        &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL,
+        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+        FILE_OPEN_IF,
+        FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT,
+        EaBuffer, EaLength);
+
+#ifdef DEBUG_MOUNT
+    DbgEx();
+#endif
+    return status;
+}
+
+NTSTATUS nfs41_GetConnectionInfoFromBuffer(
+    IN PVOID Buffer,
+    IN ULONG BufferLen,
+    OUT PUNICODE_STRING pConnectionName,
+    OUT PVOID *ppEaBuffer,
+    OUT PULONG pEaLength)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    USHORT NameLength, EaPadding;
+    ULONG EaLength, BufferLenExpected;
+    PBYTE ptr;
+
+    /* make sure buffer is at least big enough for header */
+    if (BufferLen < sizeof(USHORT) + sizeof(USHORT) + sizeof(ULONG)) {
+        status = STATUS_BAD_NETWORK_NAME;
+        print_error("Invalid input buffer.\n");
+        pConnectionName->Length = pConnectionName->MaximumLength = 0;
+        *ppEaBuffer = NULL;
+        *pEaLength = 0;
+        goto out;
+    }
+
+    ptr = Buffer;
+    NameLength = *(PUSHORT)ptr;
+    ptr += sizeof(USHORT);
+    EaPadding = *(PUSHORT)ptr;
+    ptr += sizeof(USHORT);
+    EaLength = *(PULONG)ptr;
+    ptr += sizeof(ULONG);
+
+    /* validate buffer length */
+    BufferLenExpected = sizeof(USHORT) + sizeof(USHORT) + sizeof(ULONG) +
+        NameLength + EaPadding + EaLength;
+    if (BufferLen != BufferLenExpected) {
+        status = STATUS_BAD_NETWORK_NAME;
+        print_error("Received buffer of length %lu, but expected %lu bytes.\n",
+            BufferLen, BufferLenExpected);
+        pConnectionName->Length = pConnectionName->MaximumLength = 0;
+        *ppEaBuffer = NULL;
+        *pEaLength = 0;
+        goto out;
+    }
+
+    pConnectionName->Buffer = (PWCH)ptr;
+    pConnectionName->Length = NameLength - sizeof(WCHAR);
+    pConnectionName->MaximumLength = NameLength;
+
+    if (EaLength)
+        *ppEaBuffer = ptr + NameLength + EaPadding;
+    else
+        *ppEaBuffer = NULL;
+    *pEaLength = EaLength;
+
+out:
+    return status;
+}
+
+NTSTATUS nfs41_CreateConnection(
+    IN PRX_CONTEXT RxContext,
+    OUT PBOOLEAN PostToFsp)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    HANDLE Handle = INVALID_HANDLE_VALUE;
+    PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
+    PVOID Buffer = LowIoContext->ParamsFor.IoCtl.pInputBuffer, EaBuffer;
+    ULONG BufferLen = LowIoContext->ParamsFor.IoCtl.InputBufferLength, EaLength;
+    UNICODE_STRING FileName;
+    BOOLEAN Wait = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT);
+
+#ifdef DEBUG_MOUNT
+    DbgEn();
+#endif
+
+    if (!Wait) {
+        //just post right now!
+        DbgP("returning STATUS_PENDING\n");
+        *PostToFsp = TRUE;
+        status = STATUS_PENDING;
+        goto out;
+    }
+
+    status = nfs41_GetConnectionInfoFromBuffer(Buffer, BufferLen,
+        &FileName, &EaBuffer, &EaLength);
+    if (status != STATUS_SUCCESS)
+        goto out;
+
+    status = GetConnectionHandle(&FileName, EaBuffer, EaLength, &Handle);
+    if (!status && Handle != INVALID_HANDLE_VALUE)
+        ZwClose(Handle);
+out:
+#ifdef DEBUG_MOUNT
+    DbgEx();
+#endif
+    return status;
+}
+
+#ifdef ENABLE_TIMINGS
+void print_op_stat(
+    const char *op_str,
+    nfs41_timings *time, BOOLEAN clear)
+{
+    DbgP("%-9s: num_ops=%-10d delta_ticks=%-10d size=%-10d\n", op_str,
+        time->tops, time->tops ? time->ticks/time->tops : 0,
+        time->sops ? time->size/time->sops : 0);
+    if (clear) {
+        time->tops = 0;
+        time->ticks = 0;
+        time->size = 0;
+        time->sops = 0;
+    }
+}
+#endif
+NTSTATUS nfs41_unmount(
+    HANDLE session,
+    DWORD version,
+    DWORD timeout)
+{
+    NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
+    nfs41_updowncall_entry *entry;
+
+#ifdef DEBUG_MOUNT
+    DbgEn();
+#endif
+    status = nfs41_UpcallCreate(NFS41_UNMOUNT, NULL, session,
+        INVALID_HANDLE_VALUE, version, NULL, &entry);
+    SeDeleteClientSecurity(&entry->sec_ctx);
+    if (status) goto out;
+
+    nfs41_UpcallWaitForReply(entry, timeout);
+    RxFreePool(entry);
+out:
+#ifdef ENABLE_TIMINGS
+    print_op_stat("lookup", &lookup, 1);
+    print_op_stat("open", &open, 1);
+    print_op_stat("close", &close, 1);
+    print_op_stat("volume", &volume, 1);
+    print_op_stat("getattr", &getattr, 1);
+    print_op_stat("setattr", &setattr, 1);
+    print_op_stat("getexattr", &getexattr, 1);
+    print_op_stat("setexattr", &setexattr, 1);
+    print_op_stat("readdir", &readdir, 1);
+    print_op_stat("getacl", &getacl, 1);
+    print_op_stat("setacl", &setacl, 1);
+    print_op_stat("read", &read, 1);
+    print_op_stat("write", &write, 1);
+    print_op_stat("lock", &lock, 1);
+    print_op_stat("unlock", &unlock, 1);
+#endif
+#ifdef DEBUG_MOUNT
+    DbgEx();
+#endif
+    return status;
+}
+
+NTSTATUS nfs41_DeleteConnection (
+    IN PRX_CONTEXT RxContext,
+    OUT PBOOLEAN PostToFsp)
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
+    PWCHAR ConnectName = LowIoContext->ParamsFor.IoCtl.pInputBuffer;
+    ULONG ConnectNameLen = LowIoContext->ParamsFor.IoCtl.InputBufferLength;
+    HANDLE Handle;
+    UNICODE_STRING FileName;
+    PFILE_OBJECT pFileObject;
+    BOOLEAN Wait = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT);
+
+#ifdef DEBUG_MOUNT
+    DbgEn();
+#endif
+
+    if (!Wait) {
+        //just post right now!
+        *PostToFsp = TRUE;
+        DbgP("returning STATUS_PENDING\n");
+        status = STATUS_PENDING;
+        goto out;
+    }
+
+    FileName.Buffer = ConnectName;
+    FileName.Length = (USHORT) ConnectNameLen - sizeof(WCHAR);
+    FileName.MaximumLength = (USHORT) ConnectNameLen;
+
+    status = GetConnectionHandle(&FileName, NULL, 0, &Handle);
+    if (status != STATUS_SUCCESS)
+        goto out;
+
+    status = ObReferenceObjectByHandle(Handle, 0L, NULL, KernelMode,
+                (PVOID *)&pFileObject, NULL);
+    if (NT_SUCCESS(status)) {
+        PV_NET_ROOT VNetRoot;
+
+        // VNetRoot exists as FOBx in the FsContext2
+        VNetRoot = (PV_NET_ROOT) pFileObject->FsContext2;
+        // make sure the node looks right
+        if (NodeType(VNetRoot) == RDBSS_NTC_V_NETROOT)
+        {
+#ifdef DEBUG_MOUNT
+            DbgP("Calling RxFinalizeConnection for NetRoot %p from VNetRoot %p\n",
+                VNetRoot->NetRoot, VNetRoot);
+#endif
+            status = RxFinalizeConnection(VNetRoot->NetRoot, VNetRoot, TRUE);
+        }
+        else
+            status = STATUS_BAD_NETWORK_NAME;
+
+        ObDereferenceObject(pFileObject);
+    }
+    ZwClose(Handle);
+out:
+#ifdef DEBUG_MOUNT
+    DbgEx();
+#endif
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_DevFcbXXXControlFile(
+#else
+NTSTATUS nfs41_DevFcbXXXControlFile(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
+    UCHAR op = RxContext->MajorFunction;
+    PLOWIO_CONTEXT io_ctx = &RxContext->LowIoContext;
+    ULONG fsop = io_ctx->ParamsFor.FsCtl.FsControlCode, state;
+    ULONG in_len = io_ctx->ParamsFor.IoCtl.InputBufferLength;
+    DWORD *buf = io_ctx->ParamsFor.IoCtl.pInputBuffer;
+    NFS41GetDeviceExtension(RxContext, DevExt);
+    DWORD nfs41d_version = 0;
+
+    //DbgEn();
+
+    print_ioctl(0, op);
+    switch(op) {
+    case IRP_MJ_FILE_SYSTEM_CONTROL:
+        status = STATUS_INVALID_DEVICE_REQUEST;
+        break;
+    case IRP_MJ_DEVICE_CONTROL:
+    case IRP_MJ_INTERNAL_DEVICE_CONTROL:
+        print_fs_ioctl(0, fsop);
+        switch (fsop) {
+        case IOCTL_NFS41_INVALCACHE:
+            nfs41_invalidate_cache(RxContext);
+            status = STATUS_SUCCESS;
+            break;
+        case IOCTL_NFS41_READ:
+            status = nfs41_upcall(RxContext);
+            break;
+        case IOCTL_NFS41_WRITE:
+            status = nfs41_downcall(RxContext);
+            break;
+        case IOCTL_NFS41_ADDCONN:
+            status = nfs41_CreateConnection(RxContext, &RxContext->PostRequest);
+            break;
+        case IOCTL_NFS41_DELCONN:
+            if (RxContext->RxDeviceObject->NumberOfActiveFcbs > 0) {
+                DbgP("device has open handles %d\n",
+                    RxContext->RxDeviceObject->NumberOfActiveFcbs);
+                status = STATUS_REDIRECTOR_HAS_OPEN_HANDLES;
+                break;
+            }
+            status = nfs41_DeleteConnection(RxContext, &RxContext->PostRequest);
+            break;
+        case IOCTL_NFS41_GETSTATE:
+            state = RDR_NULL_STATE;
+
+            if (io_ctx->ParamsFor.IoCtl.OutputBufferLength >= sizeof(ULONG)) {
+                // map the states to control app's equivalents
+                print_driver_state(nfs41_start_state);
+                switch (nfs41_start_state) {
+                case NFS41_START_DRIVER_STARTABLE:
+                case NFS41_START_DRIVER_STOPPED:
+                    state = RDR_STOPPED;
+                    break;
+                case NFS41_START_DRIVER_START_IN_PROGRESS:
+                    state = RDR_STARTING;
+                    break;
+                case NFS41_START_DRIVER_STARTED:
+                    state = RDR_STARTED;
+                    break;
+                }
+                *(ULONG *)io_ctx->ParamsFor.IoCtl.pOutputBuffer = state;
+                RxContext->InformationToReturn = sizeof(ULONG);
+                status = STATUS_SUCCESS;
+            } else
+                status = STATUS_INVALID_PARAMETER;
+            break;
+        case IOCTL_NFS41_START:
+            print_driver_state(nfs41_start_state);
+            if (in_len >= sizeof(DWORD)) {
+                RtlCopyMemory(&nfs41d_version, buf, sizeof(DWORD));
+                DbgP("NFS41 Daemon sent start request with version %d\n",
+                    nfs41d_version);
+                DbgP("Currently used NFS41 Daemon version is %d\n",
+                    DevExt->nfs41d_version);
+                DevExt->nfs41d_version = nfs41d_version;
+            }
+            switch(nfs41_start_state) {
+            case NFS41_START_DRIVER_STARTABLE:
+                (nfs41_start_driver_state)InterlockedCompareExchange(
+                              (PLONG)&nfs41_start_state,
+                              NFS41_START_DRIVER_START_IN_PROGRESS,
+                              NFS41_START_DRIVER_STARTABLE);
+                    //lack of break is intentional
+            case NFS41_START_DRIVER_START_IN_PROGRESS:
+                status = RxStartMinirdr(RxContext, &RxContext->PostRequest);
+                if (status == STATUS_REDIRECTOR_STARTED) {
+                    DbgP("redirector started\n");
+                    status = STATUS_SUCCESS;
+                } else if (status == STATUS_PENDING &&
+                            RxContext->PostRequest == TRUE) {
+                    DbgP("RxStartMinirdr pending %08lx\n", status);
+                    status = STATUS_MORE_PROCESSING_REQUIRED;
+                }
+                break;
+            case NFS41_START_DRIVER_STARTED:
+                status = STATUS_SUCCESS;
+                break;
+            default:
+                status = STATUS_INVALID_PARAMETER;
+            }
+            break;
+        case IOCTL_NFS41_STOP:
+            if (nfs41_start_state == NFS41_START_DRIVER_STARTED)
+                nfs41_shutdown_daemon(DevExt->nfs41d_version);
+            if (RxContext->RxDeviceObject->NumberOfActiveFcbs > 0) {
+                DbgP("device has open handles %d\n",
+                    RxContext->RxDeviceObject->NumberOfActiveFcbs);
+                status = STATUS_REDIRECTOR_HAS_OPEN_HANDLES;
+                break;
+            }
+
+            state = (nfs41_start_driver_state)InterlockedCompareExchange(
+                        (PLONG)&nfs41_start_state,
+                        NFS41_START_DRIVER_STARTABLE,
+                        NFS41_START_DRIVER_STARTED);
+
+            status = RxStopMinirdr(RxContext, &RxContext->PostRequest);
+            DbgP("RxStopMinirdr status %08lx\n", status);
+            if (status == STATUS_PENDING && RxContext->PostRequest == TRUE )
+                status = STATUS_MORE_PROCESSING_REQUIRED;
+            break;
+        default:
+            status = STATUS_INVALID_DEVICE_REQUEST;
+        };
+        break;
+    default:
+        status = STATUS_INVALID_DEVICE_REQUEST;
+    };
+
+    //DbgEx();
+    return status;
+}
+
+#ifndef __REACTOS__
+NTSTATUS _nfs41_CreateSrvCall(
+    PMRX_SRVCALL_CALLBACK_CONTEXT pCallbackContext)
+{
+#else
+NTSTATUS NTAPI _nfs41_CreateSrvCall(
+    PVOID pContext)
+{
+    PMRX_SRVCALL_CALLBACK_CONTEXT pCallbackContext = pContext;
+#endif
+    NTSTATUS status = STATUS_SUCCESS;
+    PMRX_SRVCALL_CALLBACK_CONTEXT SCCBC = pCallbackContext;
+    PMRX_SRV_CALL pSrvCall;
+    PMRX_SRVCALLDOWN_STRUCTURE SrvCalldownStructure =
+        (PMRX_SRVCALLDOWN_STRUCTURE)(SCCBC->SrvCalldownStructure);
+    PNFS41_SERVER_ENTRY pServerEntry = NULL;
+
+#ifdef DEBUG_MOUNT
+    DbgEn();
+#endif
+
+    pSrvCall = SrvCalldownStructure->SrvCall;
+
+    ASSERT( pSrvCall );
+    ASSERT( NodeType(pSrvCall) == RDBSS_NTC_SRVCALL );
+    print_srv_call(0, pSrvCall);
+
+    // validate the server name with the test name of 'pnfs'
+#ifdef DEBUG_MOUNT
+    DbgP("SrvCall: Connection Name Length: %d %wZ\n",
+        pSrvCall->pSrvCallName->Length, pSrvCall->pSrvCallName);
+#endif
+
+    if (pSrvCall->pSrvCallName->Length > SERVER_NAME_BUFFER_SIZE) {
+        print_error("Server name '%wZ' too long for server entry (max %u)\n",
+            pSrvCall->pSrvCallName, SERVER_NAME_BUFFER_SIZE);
+        status = STATUS_NAME_TOO_LONG;
+        goto out;
+    }
+
+    /* Let's create our own representation of the server */
+    pServerEntry = (PNFS41_SERVER_ENTRY)RxAllocatePoolWithTag(PagedPool,
+        sizeof(NFS41_SERVER_ENTRY), NFS41_MM_POOLTAG);
+    if (pServerEntry == NULL) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+    RtlZeroMemory(pServerEntry, sizeof(NFS41_SERVER_ENTRY));
+
+    pServerEntry->Name.Buffer = pServerEntry->NameBuffer;
+    pServerEntry->Name.Length = pSrvCall->pSrvCallName->Length;
+    pServerEntry->Name.MaximumLength = SERVER_NAME_BUFFER_SIZE;
+    RtlCopyMemory(pServerEntry->Name.Buffer, pSrvCall->pSrvCallName->Buffer,
+        pServerEntry->Name.Length);
+
+    pCallbackContext->RecommunicateContext = pServerEntry;
+#ifdef __REACTOS__
+    InterlockedExchangePointer((void * volatile *)&pServerEntry->pRdbssSrvCall, pSrvCall);
+#else
+    InterlockedExchangePointer(&pServerEntry->pRdbssSrvCall, pSrvCall);
+#endif
+
+out:
+    SCCBC->Status = status;
+    SrvCalldownStructure->CallBack(SCCBC);
+
+#ifdef DEBUG_MOUNT
+    DbgEx();
+#endif
+    return status;
+}
+
+#ifdef __REACTOS__
+VOID NTAPI _nfs41_CreateSrvCall_v(
+    PVOID pCallbackContext)
+{
+    _nfs41_CreateSrvCall(pCallbackContext);
+}
+#endif
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_CreateSrvCall(
+#else
+NTSTATUS nfs41_CreateSrvCall(
+#endif
+    PMRX_SRV_CALL pSrvCall,
+    PMRX_SRVCALL_CALLBACK_CONTEXT pCallbackContext)
+{
+    NTSTATUS status;
+
+    ASSERT( pSrvCall );
+    ASSERT( NodeType(pSrvCall) == RDBSS_NTC_SRVCALL );
+
+    if (IoGetCurrentProcess() == RxGetRDBSSProcess()) {
+        DbgP("executing with RDBSS context\n");
+        status = _nfs41_CreateSrvCall(pCallbackContext);
+    } else {
+        status = RxDispatchToWorkerThread(nfs41_dev, DelayedWorkQueue,
+#ifdef __REACTOS__
+            _nfs41_CreateSrvCall_v, pCallbackContext);
+#else
+            _nfs41_CreateSrvCall, pCallbackContext);
+#endif
+        if (status != STATUS_SUCCESS) {
+            print_error("RxDispatchToWorkerThread returned status %08lx\n",
+                status);
+            pCallbackContext->Status = status;
+            pCallbackContext->SrvCalldownStructure->CallBack(pCallbackContext);
+            status = STATUS_PENDING;
+        }
+    }
+    /* RDBSS expects MRxCreateSrvCall to return STATUS_PENDING */
+    if (status == STATUS_SUCCESS)
+        status = STATUS_PENDING;
+
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_SrvCallWinnerNotify(
+#else
+NTSTATUS nfs41_SrvCallWinnerNotify(
+#endif
+    IN OUT PMRX_SRV_CALL pSrvCall,
+    IN BOOLEAN ThisMinirdrIsTheWinner,
+    IN OUT PVOID pSrvCallContext)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    PNFS41_SERVER_ENTRY pServerEntry;
+
+    pServerEntry = (PNFS41_SERVER_ENTRY)pSrvCallContext;
+
+    if (!ThisMinirdrIsTheWinner) {
+        ASSERT(1);
+        goto out;
+    }
+
+    pSrvCall->Context = pServerEntry;
+out:
+    return status;
+}
+
+NTSTATUS map_mount_errors(
+    DWORD status)
+{
+    switch (status) {
+    case NO_ERROR:              return STATUS_SUCCESS;
+    case ERROR_NETWORK_UNREACHABLE: return STATUS_NETWORK_UNREACHABLE;
+    case ERROR_BAD_NET_RESP:    return STATUS_UNEXPECTED_NETWORK_ERROR;
+    case ERROR_BAD_NET_NAME:    return STATUS_BAD_NETWORK_NAME;
+    case ERROR_BAD_NETPATH:     return STATUS_BAD_NETWORK_PATH;
+    default:
+        print_error("failed to map windows error %d to NTSTATUS; "
+            "defaulting to STATUS_INSUFFICIENT_RESOURCES\n", status);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+}
+
+NTSTATUS nfs41_mount(
+    PNFS41_MOUNT_CONFIG config,
+    DWORD sec_flavor,
+    PHANDLE session,
+    DWORD *version,
+    PFILE_FS_ATTRIBUTE_INFORMATION FsAttrs)
+{
+    NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
+    nfs41_updowncall_entry *entry;
+
+#ifdef DEBUG_MOUNT
+    DbgEn();
+    DbgP("Server Name %wZ Mount Point %wZ SecFlavor %d\n",
+        &config->SrvName, &config->MntPt, sec_flavor);
+#endif
+    status = nfs41_UpcallCreate(NFS41_MOUNT, NULL, *session,
+        INVALID_HANDLE_VALUE, *version, &config->MntPt, &entry);
+    if (status) goto out;
+
+    entry->u.Mount.srv_name = &config->SrvName;
+    entry->u.Mount.root = &config->MntPt;
+    entry->u.Mount.rsize = config->ReadSize;
+    entry->u.Mount.wsize = config->WriteSize;
+    entry->u.Mount.sec_flavor = sec_flavor;
+    entry->u.Mount.FsAttrs = FsAttrs;
+
+    status = nfs41_UpcallWaitForReply(entry, config->timeout);
+    SeDeleteClientSecurity(&entry->sec_ctx);
+    if (status) goto out;
+    *session = entry->session;
+    if (entry->u.Mount.lease_time > config->timeout)
+        config->timeout = entry->u.Mount.lease_time;
+
+    /* map windows ERRORs to NTSTATUS */
+    status = map_mount_errors(entry->status);
+    if (status == STATUS_SUCCESS)
+        *version = entry->version;
+    RxFreePool(entry);
+out:
+#ifdef DEBUG_MOUNT
+    DbgEx();
+#endif
+    return status;
+}
+
+/* TODO: move mount config stuff to another file -cbodley */
+
+void nfs41_MountConfig_InitDefaults(
+    OUT PNFS41_MOUNT_CONFIG Config)
+{
+    RtlZeroMemory(Config, sizeof(NFS41_MOUNT_CONFIG));
+
+    Config->ReadSize = MOUNT_CONFIG_RW_SIZE_DEFAULT;
+    Config->WriteSize = MOUNT_CONFIG_RW_SIZE_DEFAULT;
+    Config->ReadOnly = FALSE;
+    Config->write_thru = FALSE;
+    Config->nocache = FALSE;
+    Config->SrvName.Length = 0;
+    Config->SrvName.MaximumLength = SERVER_NAME_BUFFER_SIZE;
+    Config->SrvName.Buffer = Config->srv_buffer;
+    Config->MntPt.Length = 0;
+    Config->MntPt.MaximumLength = MAX_PATH;
+    Config->MntPt.Buffer = Config->mntpt_buffer;
+    Config->SecFlavor.Length = 0;
+    Config->SecFlavor.MaximumLength = MAX_SEC_FLAVOR_LEN;
+    Config->SecFlavor.Buffer = Config->sec_flavor;
+    RtlCopyUnicodeString(&Config->SecFlavor, &AUTH_SYS_NAME);
+    Config->timeout = UPCALL_TIMEOUT_DEFAULT;
+}
+
+NTSTATUS nfs41_MountConfig_ParseBoolean(
+    IN PFILE_FULL_EA_INFORMATION Option,
+    IN PUNICODE_STRING usValue,
+    OUT PBOOLEAN Value)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    /* if no value is specified, assume TRUE
+     * if a value is specified, it must be a '1' */
+    if (Option->EaValueLength == 0 || *usValue->Buffer == L'1')
+        *Value = TRUE;
+    else
+        *Value = FALSE;
+
+    DbgP("    '%ls' -> '%wZ' -> %u\n",
+        (LPWSTR)Option->EaName, usValue, *Value);
+    return status;
+}
+
+NTSTATUS nfs41_MountConfig_ParseDword(
+    IN PFILE_FULL_EA_INFORMATION Option,
+    IN PUNICODE_STRING usValue,
+    OUT PDWORD Value,
+    IN DWORD Minimum,
+    IN DWORD Maximum)
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    LPWSTR Name = (LPWSTR)Option->EaName;
+
+    if (Option->EaValueLength) {
+        status = RtlUnicodeStringToInteger(usValue, 0, Value);
+        if (status == STATUS_SUCCESS) {
+#ifdef IMPOSE_MINMAX_RWSIZES
+            if (*Value < Minimum)
+                *Value = Minimum;
+            if (*Value > Maximum)
+                *Value = Maximum;
+            DbgP("    '%ls' -> '%wZ' -> %lu\n", Name, usValue, *Value);
+#endif
+        }
+        else
+            print_error("Failed to convert %s='%wZ' to unsigned long.\n",
+                Name, usValue);
+    }
+
+    return status;
+}
+
+NTSTATUS nfs41_MountConfig_ParseOptions(
+    IN PFILE_FULL_EA_INFORMATION EaBuffer,
+    IN ULONG EaLength,
+    IN OUT PNFS41_MOUNT_CONFIG Config)
+{
+    NTSTATUS  status = STATUS_SUCCESS;
+    PFILE_FULL_EA_INFORMATION Option;
+    LPWSTR Name;
+    size_t NameLen;
+    UNICODE_STRING  usValue;
+    Option = EaBuffer;
+    while (status == STATUS_SUCCESS) {
+        Name = (LPWSTR)Option->EaName;
+        NameLen = Option->EaNameLength/sizeof(WCHAR);
+
+        usValue.Length = usValue.MaximumLength = Option->EaValueLength;
+        usValue.Buffer = (PWCH)(Option->EaName +
+            Option->EaNameLength + sizeof(WCHAR));
+
+        if (wcsncmp(L"ro", Name, NameLen) == 0) {
+            status = nfs41_MountConfig_ParseBoolean(Option, &usValue,
+                &Config->ReadOnly);
+        }
+        else if (wcsncmp(L"writethru", Name, NameLen) == 0) {
+            status = nfs41_MountConfig_ParseBoolean(Option, &usValue,
+                &Config->write_thru);
+        }
+        else if (wcsncmp(L"nocache", Name, NameLen) == 0) {
+            status = nfs41_MountConfig_ParseBoolean(Option, &usValue,
+                &Config->nocache);
+        }
+        else if (wcsncmp(L"timeout", Name, NameLen) == 0) {
+            status = nfs41_MountConfig_ParseDword(Option, &usValue,
+                &Config->timeout, UPCALL_TIMEOUT_DEFAULT,
+                UPCALL_TIMEOUT_DEFAULT);
+        }
+        else if (wcsncmp(L"rsize", Name, NameLen) == 0) {
+            status = nfs41_MountConfig_ParseDword(Option, &usValue,
+                &Config->ReadSize, MOUNT_CONFIG_RW_SIZE_MIN,
+                MOUNT_CONFIG_RW_SIZE_MAX);
+        }
+        else if (wcsncmp(L"wsize", Name, NameLen) == 0) {
+            status = nfs41_MountConfig_ParseDword(Option, &usValue,
+                &Config->WriteSize, MOUNT_CONFIG_RW_SIZE_MIN,
+                MOUNT_CONFIG_RW_SIZE_MAX);
+        }
+        else if (wcsncmp(L"srvname", Name, NameLen) == 0) {
+            if (usValue.Length > Config->SrvName.MaximumLength)
+                status = STATUS_NAME_TOO_LONG;
+            else
+                RtlCopyUnicodeString(&Config->SrvName, &usValue);
+        }
+        else if (wcsncmp(L"mntpt", Name, NameLen) == 0) {
+            if (usValue.Length > Config->MntPt.MaximumLength)
+                status = STATUS_NAME_TOO_LONG;
+            else
+                RtlCopyUnicodeString(&Config->MntPt, &usValue);
+        }
+        else if (wcsncmp(L"sec", Name, NameLen) == 0) {
+            if (usValue.Length > Config->SecFlavor.MaximumLength)
+                status = STATUS_NAME_TOO_LONG;
+            else
+                RtlCopyUnicodeString(&Config->SecFlavor, &usValue);
+        }
+        else {
+            status = STATUS_INVALID_PARAMETER;
+            print_error("Unrecognized option '%ls' -> '%wZ'\n",
+                Name, usValue);
+        }
+
+        if (Option->NextEntryOffset == 0)
+            break;
+
+        Option = (PFILE_FULL_EA_INFORMATION)
+            ((PBYTE)Option + Option->NextEntryOffset);
+    }
+
+    return status;
+}
+
+NTSTATUS has_nfs_prefix(
+    IN PUNICODE_STRING SrvCallName,
+    IN PUNICODE_STRING NetRootName)
+{
+    NTSTATUS status = STATUS_BAD_NETWORK_NAME;
+
+    if (NetRootName->Length == SrvCallName->Length + NfsPrefix.Length) {
+        const UNICODE_STRING NetRootPrefix = {
+            NfsPrefix.Length,
+            NetRootName->MaximumLength - SrvCallName->Length,
+            &NetRootName->Buffer[SrvCallName->Length/2]
+        };
+        if (RtlCompareUnicodeString(&NetRootPrefix, &NfsPrefix, FALSE) == 0)
+            status = STATUS_SUCCESS;
+    }
+    return status;
+}
+
+NTSTATUS map_sec_flavor(
+    IN PUNICODE_STRING sec_flavor_name,
+    OUT PDWORD sec_flavor)
+{
+    if (RtlCompareUnicodeString(sec_flavor_name, &AUTH_SYS_NAME, FALSE) == 0)
+        *sec_flavor = RPCSEC_AUTH_SYS;
+    else if (RtlCompareUnicodeString(sec_flavor_name, &AUTHGSS_KRB5_NAME, FALSE) == 0)
+        *sec_flavor = RPCSEC_AUTHGSS_KRB5;
+    else if (RtlCompareUnicodeString(sec_flavor_name, &AUTHGSS_KRB5I_NAME, FALSE) == 0)
+        *sec_flavor = RPCSEC_AUTHGSS_KRB5I;
+    else if (RtlCompareUnicodeString(sec_flavor_name, &AUTHGSS_KRB5P_NAME, FALSE) == 0)
+        *sec_flavor = RPCSEC_AUTHGSS_KRB5P;
+    else return STATUS_INVALID_PARAMETER;
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS nfs41_GetLUID(
+    PLUID id)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    SECURITY_SUBJECT_CONTEXT sec_ctx;
+    SECURITY_QUALITY_OF_SERVICE sec_qos;
+    SECURITY_CLIENT_CONTEXT clnt_sec_ctx;
+
+    SeCaptureSubjectContext(&sec_ctx);
+    sec_qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+    sec_qos.ImpersonationLevel = SecurityIdentification;
+    sec_qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+    sec_qos.EffectiveOnly = 0;
+    status = SeCreateClientSecurityFromSubjectContext(&sec_ctx, &sec_qos, 1,
+                &clnt_sec_ctx);
+    if (status) {
+        print_error("nfs41_GetLUID: SeCreateClientSecurityFromSubjectContext "
+             "failed %x\n", status);
+        goto release_sec_ctx;
+    }
+    status = SeQueryAuthenticationIdToken(clnt_sec_ctx.ClientToken, id);
+    if (status) {
+        print_error("SeQueryAuthenticationIdToken failed %x\n", status);
+        goto release_clnt_sec_ctx;
+    }
+release_clnt_sec_ctx:
+    SeDeleteClientSecurity(&clnt_sec_ctx);
+release_sec_ctx:
+    SeReleaseSubjectContext(&sec_ctx);
+
+    return status;
+}
+
+NTSTATUS nfs41_get_sec_ctx(
+    IN enum _SECURITY_IMPERSONATION_LEVEL level,
+    OUT PSECURITY_CLIENT_CONTEXT out_ctx)
+{
+    NTSTATUS status;
+    SECURITY_SUBJECT_CONTEXT ctx;
+    SECURITY_QUALITY_OF_SERVICE sec_qos;
+
+    SeCaptureSubjectContext(&ctx);
+    sec_qos.ContextTrackingMode = SECURITY_STATIC_TRACKING;
+    sec_qos.ImpersonationLevel = level;
+    sec_qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+    sec_qos.EffectiveOnly = 0;
+    status = SeCreateClientSecurityFromSubjectContext(&ctx, &sec_qos, 1, out_ctx);
+    if (status != STATUS_SUCCESS) {
+        print_error("SeCreateClientSecurityFromSubjectContext "
+            "failed with %x\n", status);
+    }
+#ifdef DEBUG_MOUNT
+    DbgP("Created client security token %p\n", out_ctx->ClientToken);
+#endif
+    SeReleaseSubjectContext(&ctx);
+
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_CreateVNetRoot(
+#else
+NTSTATUS nfs41_CreateVNetRoot(
+#endif
+    IN OUT PMRX_CREATENETROOT_CONTEXT pCreateNetRootContext)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    NFS41_MOUNT_CONFIG *Config;
+    __notnull PMRX_V_NET_ROOT pVNetRoot = (PMRX_V_NET_ROOT)
+        pCreateNetRootContext->pVNetRoot;
+    __notnull PMRX_NET_ROOT pNetRoot = pVNetRoot->pNetRoot;
+    __notnull PMRX_SRV_CALL pSrvCall = pNetRoot->pSrvCall;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension(pNetRoot);
+    NFS41GetDeviceExtension(pCreateNetRootContext->RxContext,DevExt);
+    DWORD nfs41d_version = DevExt->nfs41d_version;
+    nfs41_mount_entry *existing_mount = NULL;
+    LUID luid;
+    BOOLEAN found_existing_mount = FALSE, found_matching_flavor = FALSE;
+
+    ASSERT((NodeType(pNetRoot) == RDBSS_NTC_NETROOT) &&
+        (NodeType(pNetRoot->pSrvCall) == RDBSS_NTC_SRVCALL));
+
+#ifdef DEBUG_MOUNT
+    DbgEn();
+    print_srv_call(0, pSrvCall);
+    print_net_root(0, pNetRoot);
+    print_v_net_root(0, pVNetRoot);
+
+    DbgP("pVNetRoot=%p pNetRoot=%p pSrvCall=%p\n", pVNetRoot, pNetRoot, pSrvCall);
+    DbgP("pNetRoot=%wZ Type=%d pSrvCallName=%wZ VirtualNetRootStatus=0x%x "
+        "NetRootStatus=0x%x\n", pNetRoot->pNetRootName,
+        pNetRoot->Type, pSrvCall->pSrvCallName,
+        pCreateNetRootContext->VirtualNetRootStatus,
+        pCreateNetRootContext->NetRootStatus);
+#endif
+
+    if (pNetRoot->Type != NET_ROOT_DISK && pNetRoot->Type != NET_ROOT_WILD) {
+        print_error("nfs41_CreateVNetRoot: Unsupported NetRoot Type %u\n",
+            pNetRoot->Type);
+        status = STATUS_NOT_SUPPORTED;
+        goto out;
+    }
+
+    pVNetRootContext->session = INVALID_HANDLE_VALUE;
+
+    /* In order to cooperate with other network providers, we must
+     * only claim paths of the form '\\server\nfs4\path' */
+    status = has_nfs_prefix(pSrvCall->pSrvCallName, pNetRoot->pNetRootName);
+    if (status) {
+        print_error("nfs41_CreateVNetRoot: NetRootName %wZ doesn't match "
+            "'\\nfs4'!\n", pNetRoot->pNetRootName);
+        goto out;
+    }
+    pNetRoot->MRxNetRootState = MRX_NET_ROOT_STATE_GOOD;
+    pNetRoot->DeviceType = FILE_DEVICE_DISK;
+
+    Config = RxAllocatePoolWithTag(NonPagedPool,
+            sizeof(NFS41_MOUNT_CONFIG), NFS41_MM_POOLTAG);
+    if (Config == NULL) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+    nfs41_MountConfig_InitDefaults(Config);
+
+    if (pCreateNetRootContext->RxContext->Create.EaLength) {
+        /* parse the extended attributes for mount options */
+        status = nfs41_MountConfig_ParseOptions(
+            pCreateNetRootContext->RxContext->Create.EaBuffer,
+            pCreateNetRootContext->RxContext->Create.EaLength,
+            Config);
+        if (status != STATUS_SUCCESS)
+            goto out_free;
+        pVNetRootContext->read_only = Config->ReadOnly;
+        pVNetRootContext->write_thru = Config->write_thru;
+        pVNetRootContext->nocache = Config->nocache;
+    } else {
+        /* use the SRV_CALL name (without leading \) as the hostname */
+        Config->SrvName.Buffer = pSrvCall->pSrvCallName->Buffer + 1;
+        Config->SrvName.Length =
+            pSrvCall->pSrvCallName->Length - sizeof(WCHAR);
+        Config->SrvName.MaximumLength =
+            pSrvCall->pSrvCallName->MaximumLength - sizeof(WCHAR);
+    }
+    pVNetRootContext->MountPathLen = Config->MntPt.Length;
+    pVNetRootContext->timeout = Config->timeout;
+
+    status = map_sec_flavor(&Config->SecFlavor, &pVNetRootContext->sec_flavor);
+    if (status != STATUS_SUCCESS) {
+        DbgP("Invalid rpcsec security flavor %wZ\n", &Config->SecFlavor);
+        goto out_free;
+    }
+
+    status = nfs41_GetLUID(&luid);
+    if (status)
+        goto out_free;
+
+    if (!pNetRootContext->mounts_init) {
+#ifdef DEBUG_MOUNT
+        DbgP("Initializing mount array\n");
+#endif
+        ExInitializeFastMutex(&pNetRootContext->mountLock);
+        InitializeListHead(&pNetRootContext->mounts.head);
+        pNetRootContext->mounts_init = TRUE;
+    } else {
+        PLIST_ENTRY pEntry;
+
+        ExAcquireFastMutex(&pNetRootContext->mountLock);
+        pEntry = &pNetRootContext->mounts.head;
+        pEntry = pEntry->Flink;
+        while (pEntry != NULL) {
+            existing_mount = (nfs41_mount_entry *)CONTAINING_RECORD(pEntry,
+                    nfs41_mount_entry, next);
+#ifdef DEBUG_MOUNT
+            DbgP("comparing %x.%x with %x.%x\n", luid.HighPart, luid.LowPart,
+                existing_mount->login_id.HighPart, existing_mount->login_id.LowPart);
+#endif
+            if (RtlEqualLuid(&luid, &existing_mount->login_id)) {
+#ifdef DEBUG_MOUNT
+                DbgP("Found a matching LUID entry\n");
+#endif
+                found_existing_mount = TRUE;
+                switch(pVNetRootContext->sec_flavor) {
+                case RPCSEC_AUTH_SYS:
+                    if (existing_mount->authsys_session != INVALID_HANDLE_VALUE)
+                        pVNetRootContext->session =
+                            existing_mount->authsys_session;
+                    break;
+                case RPCSEC_AUTHGSS_KRB5:
+                    if (existing_mount->gssi_session != INVALID_HANDLE_VALUE)
+                        pVNetRootContext->session = existing_mount->gss_session;
+                    break;
+                case RPCSEC_AUTHGSS_KRB5I:
+                    if (existing_mount->gss_session != INVALID_HANDLE_VALUE)
+                        pVNetRootContext->session = existing_mount->gssi_session;
+                    break;
+                case RPCSEC_AUTHGSS_KRB5P:
+                    if (existing_mount->gssp_session != INVALID_HANDLE_VALUE)
+                        pVNetRootContext->session = existing_mount->gssp_session;
+                    break;
+                }
+                if (pVNetRootContext->session &&
+                        pVNetRootContext->session != INVALID_HANDLE_VALUE)
+                    found_matching_flavor = 1;
+                break;
+            }
+            if (pEntry->Flink == &pNetRootContext->mounts.head)
+                break;
+            pEntry = pEntry->Flink;
+        }
+        ExReleaseFastMutex(&pNetRootContext->mountLock);
+#ifdef DEBUG_MOUNT
+        if (!found_matching_flavor)
+            DbgP("Didn't find matching security flavor\n");
+#endif
+    }
+
+    /* send the mount upcall */
+    status = nfs41_mount(Config, pVNetRootContext->sec_flavor,
+        &pVNetRootContext->session, &nfs41d_version,
+        &pVNetRootContext->FsAttrs);
+    if (status != STATUS_SUCCESS) {
+        BOOLEAN MountsEmpty;
+        nfs41_IsListEmpty(pNetRootContext->mountLock,
+            pNetRootContext->mounts, MountsEmpty);
+        if (!found_existing_mount && MountsEmpty)
+            pNetRootContext->mounts_init = FALSE;
+        pVNetRootContext->session = INVALID_HANDLE_VALUE;
+        goto out_free;
+    }
+    pVNetRootContext->timeout = Config->timeout;
+
+    if (!found_existing_mount) {
+        /* create a new mount entry and add it to the list */
+        nfs41_mount_entry *entry;
+        entry = RxAllocatePoolWithTag(NonPagedPool, sizeof(nfs41_mount_entry),
+            NFS41_MM_POOLTAG_MOUNT);
+        if (entry == NULL) {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            goto out_free;
+        }
+        entry->authsys_session = entry->gss_session =
+            entry->gssi_session = entry->gssp_session = INVALID_HANDLE_VALUE;
+        switch (pVNetRootContext->sec_flavor) {
+        case RPCSEC_AUTH_SYS:
+            entry->authsys_session = pVNetRootContext->session; break;
+        case RPCSEC_AUTHGSS_KRB5:
+            entry->gss_session = pVNetRootContext->session; break;
+        case RPCSEC_AUTHGSS_KRB5I:
+            entry->gssi_session = pVNetRootContext->session; break;
+        case RPCSEC_AUTHGSS_KRB5P:
+            entry->gssp_session = pVNetRootContext->session; break;
+        }
+        RtlCopyLuid(&entry->login_id, &luid);
+        nfs41_AddEntry(pNetRootContext->mountLock,
+            pNetRootContext->mounts, entry);
+    } else if (!found_matching_flavor) {
+        ASSERT(existing_mount != NULL);
+        /* modify existing mount entry */
+#ifdef DEBUG_MOUNT
+        DbgP("Using existing %d flavor session 0x%x\n",
+            pVNetRootContext->sec_flavor);
+#endif
+        switch (pVNetRootContext->sec_flavor) {
+        case RPCSEC_AUTH_SYS:
+            existing_mount->authsys_session = pVNetRootContext->session; break;
+        case RPCSEC_AUTHGSS_KRB5:
+            existing_mount->gss_session = pVNetRootContext->session; break;
+        case RPCSEC_AUTHGSS_KRB5I:
+            existing_mount->gssi_session = pVNetRootContext->session; break;
+        case RPCSEC_AUTHGSS_KRB5P:
+            existing_mount->gssp_session = pVNetRootContext->session; break;
+        }
+    }
+    pNetRootContext->nfs41d_version = nfs41d_version;
+#ifdef DEBUG_MOUNT
+    DbgP("Saving new session 0x%x\n", pVNetRootContext->session);
+#endif
+#ifdef STORE_MOUNT_SEC_CONTEXT
+    status = nfs41_get_sec_ctx(SecurityImpersonation,
+        &pVNetRootContext->mount_sec_ctx);
+#endif
+
+out_free:
+    RxFreePool(Config);
+out:
+    pCreateNetRootContext->VirtualNetRootStatus = status;
+    if (pNetRoot->Context == NULL)
+        pCreateNetRootContext->NetRootStatus = status;
+    pCreateNetRootContext->Callback(pCreateNetRootContext);
+
+    /* RDBSS expects that MRxCreateVNetRoot returns STATUS_PENDING
+     * on success or failure */
+    status = STATUS_PENDING;
+#ifdef DEBUG_MOUNT
+    DbgEx();
+#endif
+    return status;
+}
+
+#ifdef __REACTOS__
+VOID NTAPI nfs41_ExtractNetRootName(
+#else
+VOID nfs41_ExtractNetRootName(
+#endif
+    IN PUNICODE_STRING FilePathName,
+    IN PMRX_SRV_CALL SrvCall,
+    OUT PUNICODE_STRING NetRootName,
+    OUT PUNICODE_STRING RestOfName OPTIONAL)
+{
+    ULONG length = FilePathName->Length;
+    PWCH w = FilePathName->Buffer;
+    PWCH wlimit = (PWCH)(((PCHAR)w)+length);
+    PWCH wlow;
+
+    w += (SrvCall->pSrvCallName->Length/sizeof(WCHAR));
+    NetRootName->Buffer = wlow = w;
+    /* parse the entire path into NetRootName */
+#if USE_ENTIRE_PATH
+    w = wlimit;
+#else
+    for (;;) {
+        if (w >= wlimit)
+            break;
+        if ((*w == OBJ_NAME_PATH_SEPARATOR) && (w != wlow))
+            break;
+        w++;
+    }
+#endif
+    NetRootName->Length = NetRootName->MaximumLength
+                = (USHORT)((PCHAR)w - (PCHAR)wlow);
+#ifdef DEBUG_MOUNT
+    DbgP("In: pSrvCall %p PathName=%wZ SrvCallName=%wZ Out: NetRootName=%wZ\n",
+        SrvCall, FilePathName, SrvCall->pSrvCallName, NetRootName);
+#endif
+    return;
+
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_FinalizeSrvCall(
+#else
+NTSTATUS nfs41_FinalizeSrvCall(
+#endif
+    PMRX_SRV_CALL pSrvCall,
+    BOOLEAN Force)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    PNFS41_SERVER_ENTRY pServerEntry = (PNFS41_SERVER_ENTRY)(pSrvCall->Context);
+
+#ifdef DEBUG_MOUNT
+    DbgEn();
+#endif
+    print_srv_call(0, pSrvCall);
+
+    if (pSrvCall->Context == NULL)
+        goto out;
+
+#ifndef __REACTOS__
+    InterlockedCompareExchangePointer(&pServerEntry->pRdbssSrvCall,
+        NULL, pSrvCall);
+#else
+    InterlockedCompareExchangePointer((void * volatile *)&pServerEntry->pRdbssSrvCall,
+        NULL, pSrvCall);
+#endif
+    RxFreePool(pServerEntry);
+
+    pSrvCall->Context = NULL;
+out:
+#ifdef DEBUG_MOUNT
+    DbgEx();
+#endif
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_FinalizeNetRoot(
+#else
+NTSTATUS nfs41_FinalizeNetRoot(
+#endif
+    IN OUT PMRX_NET_ROOT pNetRoot,
+    IN PBOOLEAN ForceDisconnect)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension((PMRX_NET_ROOT)pNetRoot);
+    nfs41_updowncall_entry *tmp;
+    nfs41_mount_entry *mount_tmp;
+
+#ifdef DEBUG_MOUNT
+    DbgEn();
+    print_net_root(1, pNetRoot);
+#endif
+
+    if (pNetRoot->Type != NET_ROOT_DISK && pNetRoot->Type != NET_ROOT_WILD) {
+        status = STATUS_NOT_SUPPORTED;
+        goto out;
+    }
+
+    if (pNetRootContext == NULL || !pNetRootContext->mounts_init) {
+        print_error("nfs41_FinalizeNetRoot: No valid session established\n");
+        goto out;
+    }
+
+    if (pNetRoot->NumberOfFcbs > 0 || pNetRoot->NumberOfSrvOpens > 0) {
+        print_error("%d open Fcbs %d open SrvOpens\n", pNetRoot->NumberOfFcbs,
+            pNetRoot->NumberOfSrvOpens);
+        goto out;
+    }
+
+    do {
+        nfs41_GetFirstMountEntry(pNetRootContext->mountLock,
+            pNetRootContext->mounts, mount_tmp);
+        if (mount_tmp == NULL)
+            break;
+#ifdef DEBUG_MOUNT
+        DbgP("Removing entry luid %x.%x from mount list\n",
+            mount_tmp->login_id.HighPart, mount_tmp->login_id.LowPart);
+#endif
+        if (mount_tmp->authsys_session != INVALID_HANDLE_VALUE) {
+            status = nfs41_unmount(mount_tmp->authsys_session,
+                pNetRootContext->nfs41d_version, UPCALL_TIMEOUT_DEFAULT);
+            if (status)
+                print_error("nfs41_unmount AUTH_SYS failed with %d\n", status);
+        }
+        if (mount_tmp->gss_session != INVALID_HANDLE_VALUE) {
+            status = nfs41_unmount(mount_tmp->gss_session,
+                pNetRootContext->nfs41d_version, UPCALL_TIMEOUT_DEFAULT);
+            if (status)
+                print_error("nfs41_unmount RPCSEC_GSS_KRB5 failed with %d\n",
+                            status);
+        }
+        if (mount_tmp->gssi_session != INVALID_HANDLE_VALUE) {
+            status = nfs41_unmount(mount_tmp->gssi_session,
+                pNetRootContext->nfs41d_version, UPCALL_TIMEOUT_DEFAULT);
+            if (status)
+                print_error("nfs41_unmount RPCSEC_GSS_KRB5I failed with %d\n",
+                            status);
+        }
+        if (mount_tmp->gssp_session != INVALID_HANDLE_VALUE) {
+            status = nfs41_unmount(mount_tmp->gssp_session,
+                pNetRootContext->nfs41d_version, UPCALL_TIMEOUT_DEFAULT);
+            if (status)
+                print_error("nfs41_unmount RPCSEC_GSS_KRB5P failed with %d\n",
+                            status);
+        }
+        nfs41_RemoveEntry(pNetRootContext->mountLock, mount_tmp);
+        RxFreePool(mount_tmp);
+    } while (1);
+    /* ignore any errors from unmount */
+    status = STATUS_SUCCESS;
+
+    // check if there is anything waiting in the upcall or downcall queue
+    do {
+        nfs41_GetFirstEntry(upcallLock, upcall, tmp);
+        if (tmp != NULL) {
+            DbgP("Removing entry from upcall list\n");
+            nfs41_RemoveEntry(upcallLock, tmp);
+            tmp->status = STATUS_INSUFFICIENT_RESOURCES;
+            KeSetEvent(&tmp->cond, 0, FALSE);
+        } else
+            break;
+    } while (1);
+
+    do {
+        nfs41_GetFirstEntry(downcallLock, downcall, tmp);
+        if (tmp != NULL) {
+            DbgP("Removing entry from downcall list\n");
+            nfs41_RemoveEntry(downcallLock, tmp);
+            tmp->status = STATUS_INSUFFICIENT_RESOURCES;
+            KeSetEvent(&tmp->cond, 0, FALSE);
+        } else
+            break;
+    } while (1);
+out:
+#ifdef DEBUG_MOUNT
+    DbgEx();
+#endif
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_FinalizeVNetRoot(
+#else
+NTSTATUS nfs41_FinalizeVNetRoot(
+#endif
+    IN OUT PMRX_V_NET_ROOT pVNetRoot,
+    IN PBOOLEAN ForceDisconnect)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(pVNetRoot);
+#ifdef DEBUG_MOUNT
+    DbgEn();
+    print_v_net_root(1, pVNetRoot);
+#endif
+    if (pVNetRoot->pNetRoot->Type != NET_ROOT_DISK &&
+            pVNetRoot->pNetRoot->Type != NET_ROOT_WILD)
+        status = STATUS_NOT_SUPPORTED;
+#ifdef STORE_MOUNT_SEC_CONTEXT
+    else if (pVNetRootContext->session != INVALID_HANDLE_VALUE) {
+#ifdef DEBUG_MOUNT
+        DbgP("nfs41_FinalizeVNetRoot: deleting security context: %p\n",
+            pVNetRootContext->mount_sec_ctx.ClientToken);
+#endif
+        SeDeleteClientSecurity(&pVNetRootContext->mount_sec_ctx);
+    }
+#endif
+#ifdef DEBUG_MOUNT
+    DbgEx();
+#endif
+    return status;
+}
+
+BOOLEAN isDataAccess(
+    ACCESS_MASK mask)
+{
+    if (mask & (FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA))
+        return TRUE;
+    return FALSE;
+}
+
+BOOLEAN isOpen2Create(
+    ULONG disposition)
+{
+    if (disposition == FILE_CREATE || disposition == FILE_OPEN_IF ||
+            disposition == FILE_OVERWRITE_IF || disposition == FILE_SUPERSEDE)
+        return TRUE;
+    return FALSE;
+}
+
+BOOLEAN isFilenameTooLong(
+    PUNICODE_STRING name,
+    PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext)
+{
+    PFILE_FS_ATTRIBUTE_INFORMATION attrs = &pVNetRootContext->FsAttrs;
+    LONG len = attrs->MaximumComponentNameLength, count = 1, i;
+    PWCH p = name->Buffer;
+    for (i = 0; i < name->Length / 2; i++) {
+        if (p[0] == L'\\') count = 1;
+        else {
+            if (p[0] == L'\0') return FALSE;
+            if (count > len) return TRUE;
+            count++;
+        }
+        p++;
+    }
+    return FALSE;
+}
+
+BOOLEAN isStream(
+    PUNICODE_STRING name)
+{
+    LONG i;
+    PWCH p = name->Buffer;
+    for (i = 0; i < name->Length / 2; i++) {
+        if (p[0] == L':') return TRUE;
+        else if (p[0] == L'\0') return FALSE;
+        p++;
+    }
+    return FALSE;
+}
+
+BOOLEAN areOpenParamsValid(NT_CREATE_PARAMETERS *params)
+{
+    /* from ms-fsa page 52 */
+    if ((params->CreateOptions & FILE_DELETE_ON_CLOSE) &&
+            !(params->DesiredAccess & DELETE))
+        return FALSE;
+    if ((params->CreateOptions & FILE_DIRECTORY_FILE) &&
+            (params->Disposition == FILE_SUPERSEDE ||
+                params->Disposition == FILE_OVERWRITE ||
+                params->Disposition == FILE_OVERWRITE_IF))
+        return FALSE;
+    if ((params->CreateOptions & FILE_NO_INTERMEDIATE_BUFFERING) &&
+            (params->DesiredAccess & FILE_APPEND_DATA) &&
+            !(params->DesiredAccess & FILE_WRITE_DATA))
+        return FALSE;
+    /* from ms-fsa 3.1.5.1.1 page 56 */
+    if ((params->CreateOptions & FILE_DIRECTORY_FILE) &&
+            (params->FileAttributes & FILE_ATTRIBUTE_TEMPORARY))
+        return FALSE;
+    return TRUE;
+}
+
+NTSTATUS map_open_errors(
+    DWORD status,
+    USHORT len)
+{
+    switch (status) {
+    case NO_ERROR:                      return STATUS_SUCCESS;
+    case ERROR_ACCESS_DENIED:
+        if (len > 0)                    return STATUS_ACCESS_DENIED;
+        else                            return STATUS_SUCCESS;
+    case ERROR_INVALID_REPARSE_DATA:
+    case ERROR_INVALID_NAME:            return STATUS_OBJECT_NAME_INVALID;
+    case ERROR_FILE_EXISTS:             return STATUS_OBJECT_NAME_COLLISION;
+    case ERROR_FILE_INVALID:            return STATUS_FILE_INVALID;
+    case ERROR_FILE_NOT_FOUND:          return STATUS_OBJECT_NAME_NOT_FOUND;
+    case ERROR_FILENAME_EXCED_RANGE:    return STATUS_NAME_TOO_LONG;
+    case ERROR_NETWORK_ACCESS_DENIED:   return STATUS_NETWORK_ACCESS_DENIED;
+    case ERROR_PATH_NOT_FOUND:          return STATUS_OBJECT_PATH_NOT_FOUND;
+    case ERROR_BAD_NETPATH:             return STATUS_BAD_NETWORK_PATH;
+    case ERROR_SHARING_VIOLATION:       return STATUS_SHARING_VIOLATION;
+    case ERROR_REPARSE:                 return STATUS_REPARSE;
+    case ERROR_TOO_MANY_LINKS:          return STATUS_TOO_MANY_LINKS;
+    case ERROR_DIRECTORY:               return STATUS_FILE_IS_A_DIRECTORY;
+    case ERROR_BAD_FILE_TYPE:           return STATUS_NOT_A_DIRECTORY;
+    default:
+        print_error("[ERROR] nfs41_Create: upcall returned %d returning "
+            "STATUS_INSUFFICIENT_RESOURCES\n", status);
+    case ERROR_OUTOFMEMORY:             return STATUS_INSUFFICIENT_RESOURCES;
+    }
+}
+
+DWORD map_disposition_to_create_retval(
+    DWORD disposition,
+    DWORD errno)
+{
+    switch(disposition) {
+    case FILE_SUPERSEDE:
+        if (errno == ERROR_FILE_NOT_FOUND)  return FILE_CREATED;
+        else                                return FILE_SUPERSEDED;
+    case FILE_CREATE:                       return FILE_CREATED;
+    case FILE_OPEN:                         return FILE_OPENED;
+    case FILE_OPEN_IF:
+        if (errno == ERROR_FILE_NOT_FOUND)  return FILE_CREATED;
+        else                                return FILE_OPENED;
+    case FILE_OVERWRITE:                    return FILE_OVERWRITTEN;
+    case FILE_OVERWRITE_IF:
+        if (errno == ERROR_FILE_NOT_FOUND)  return FILE_CREATED;
+        else                                return FILE_OVERWRITTEN;
+    default:
+        print_error("unknown disposition %d\n", disposition);
+        return FILE_OPENED;
+    }
+}
+
+static BOOLEAN create_should_pass_ea(
+    IN PFILE_FULL_EA_INFORMATION ea,
+    IN ULONG disposition)
+{
+    /* don't pass cygwin EAs */
+    if (AnsiStrEq(&NfsV3Attributes, ea->EaName, ea->EaNameLength)
+        || AnsiStrEq(&NfsActOnLink, ea->EaName, ea->EaNameLength)
+        || AnsiStrEq(&NfsSymlinkTargetName, ea->EaName, ea->EaNameLength))
+        return FALSE;
+    /* only set EAs on file creation */
+    return disposition == FILE_SUPERSEDE || disposition == FILE_CREATE
+        || disposition == FILE_OPEN_IF || disposition == FILE_OVERWRITE
+        || disposition == FILE_OVERWRITE_IF;
+}
+
+NTSTATUS check_nfs41_create_args(
+    IN PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    PNT_CREATE_PARAMETERS params = &RxContext->Create.NtCreateParameters;
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PFILE_FS_ATTRIBUTE_INFORMATION FsAttrs =
+        &pVNetRootContext->FsAttrs;
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+    __notnull PMRX_FCB Fcb = RxContext->pFcb;
+    __notnull PNFS41_FCB nfs41_fcb = (PNFS41_FCB)Fcb->Context;
+    PFILE_FULL_EA_INFORMATION ea = (PFILE_FULL_EA_INFORMATION)
+        RxContext->CurrentIrp->AssociatedIrp.SystemBuffer;
+
+    if (Fcb->pNetRoot->Type != NET_ROOT_DISK &&
+            Fcb->pNetRoot->Type != NET_ROOT_WILD) {
+        print_error("nfs41_Create: Unsupported NetRoot Type %u\n",
+            Fcb->pNetRoot->Type);
+        status = STATUS_NOT_SUPPORTED;
+        goto out;
+    }
+
+    if (FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE )) {
+        print_error("FCB_STATE_PAGING_FILE not implemented\n");
+        status = STATUS_NOT_IMPLEMENTED;
+        goto out;
+    }
+
+    if (!pNetRootContext->mounts_init) {
+        print_error("nfs41_Create: No valid session established\n");
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out;
+    }
+
+    if (isStream(SrvOpen->pAlreadyPrefixedName)) {
+        status = STATUS_NOT_SUPPORTED;
+        goto out;
+    }
+
+    if (pVNetRootContext->read_only &&
+            (params->DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
+        status = STATUS_NETWORK_ACCESS_DENIED;
+        goto out;
+    }
+
+    /* if FCB was marked for deletion and opened multiple times, as soon
+     * as first close happen, FCB transitions into delete_pending state
+     * no more opens allowed
+     */
+    if (Fcb->OpenCount && nfs41_fcb->DeletePending) {
+        status = STATUS_DELETE_PENDING;
+        goto out;
+    }
+
+    /* ms-fsa: 3.1.5.1.2.1 page 68 */
+    if (Fcb->OpenCount && nfs41_fcb->StandardInfo.DeletePending &&
+            !(params->ShareAccess & FILE_SHARE_DELETE) &&
+                (params->DesiredAccess & (FILE_EXECUTE | FILE_READ_DATA |
+                    FILE_WRITE_DATA | FILE_APPEND_DATA))) {
+        status = STATUS_SHARING_VIOLATION;
+        goto out;
+    }
+
+    /* rdbss seems miss this sharing_violation check */
+    if (Fcb->OpenCount && params->Disposition == FILE_SUPERSEDE) {
+#ifdef __REACTOS__
+        if ((!RxContext->CurrentIrpSp->FileObject->SharedRead &&
+                (params->DesiredAccess & FILE_READ_DATA)) ||
+            ((!RxContext->CurrentIrpSp->FileObject->SharedWrite &&
+                (params->DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA |
+                    FILE_WRITE_ATTRIBUTES))) ||
+            (!RxContext->CurrentIrpSp->FileObject->SharedDelete &&
+                (params->DesiredAccess & DELETE)))) {
+#else
+        if ((!RxContext->CurrentIrpSp->FileObject->SharedRead &&
+                (params->DesiredAccess & FILE_READ_DATA)) ||
+            (!RxContext->CurrentIrpSp->FileObject->SharedWrite &&
+                (params->DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA |
+                    FILE_WRITE_ATTRIBUTES)) ||
+            (!RxContext->CurrentIrpSp->FileObject->SharedDelete &&
+                (params->DesiredAccess & DELETE)))) {
+#endif
+            status = STATUS_SHARING_VIOLATION;
+            goto out;
+        }
+    }
+    if (isFilenameTooLong(SrvOpen->pAlreadyPrefixedName, pVNetRootContext)) {
+        status = STATUS_OBJECT_NAME_INVALID;
+        goto out;
+    }
+
+    if (!areOpenParamsValid(params)) {
+        status = STATUS_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* from ms-fsa 3.1.5.1.1 page 56 */
+    if ((params->CreateOptions & FILE_DELETE_ON_CLOSE) &&
+            (params->FileAttributes & FILE_ATTRIBUTE_READONLY)) {
+        status = STATUS_CANNOT_DELETE;
+        goto out;
+    }
+
+    if (ea) {
+        /* ignore cygwin EAs when checking support and access */
+        if (!AnsiStrEq(&NfsV3Attributes, ea->EaName, ea->EaNameLength) &&
+            !AnsiStrEq(&NfsActOnLink, ea->EaName, ea->EaNameLength) &&
+            !AnsiStrEq(&NfsSymlinkTargetName, ea->EaName, ea->EaNameLength)) {
+            if (!(FsAttrs->FileSystemAttributes & FILE_SUPPORTS_EXTENDED_ATTRIBUTES)) {
+                status = STATUS_EAS_NOT_SUPPORTED;
+                goto out;
+            }
+        }
+    } else if (RxContext->CurrentIrpSp->Parameters.Create.EaLength) {
+        status = STATUS_INVALID_PARAMETER;
+        goto out;
+    }
+
+out:
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_Create(
+#else
+NTSTATUS nfs41_Create(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
+    nfs41_updowncall_entry *entry = NULL;
+    PNT_CREATE_PARAMETERS params = &RxContext->Create.NtCreateParameters;
+    PFILE_FULL_EA_INFORMATION ea = (PFILE_FULL_EA_INFORMATION)
+        RxContext->CurrentIrp->AssociatedIrp.SystemBuffer;
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+    __notnull PMRX_FCB Fcb = RxContext->pFcb;
+    __notnull PNFS41_FCB nfs41_fcb = (PNFS41_FCB)Fcb->Context;
+    PNFS41_FOBX nfs41_fobx = NULL;
+    BOOLEAN oldDeletePending = nfs41_fcb->StandardInfo.DeletePending;
+#ifdef ENABLE_TIMINGS
+    LARGE_INTEGER t1, t2;
+    t1 = KeQueryPerformanceCounter(NULL);
+#endif
+
+    ASSERT( NodeType(SrvOpen) == RDBSS_NTC_SRVOPEN );
+
+#ifdef DEBUG_OPEN
+    DbgEn();
+    print_debug_header(RxContext);
+    print_nt_create_params(1, RxContext->Create.NtCreateParameters);
+    if (ea) print_ea_info(0, ea);
+#endif
+
+    status = check_nfs41_create_args(RxContext);
+    if (status) goto out;
+
+#if defined(STORE_MOUNT_SEC_CONTEXT) && defined (USE_MOUNT_SEC_CONTEXT)
+    status = nfs41_UpcallCreate(NFS41_OPEN, &pVNetRootContext->mount_sec_ctx,
+#else
+    status = nfs41_UpcallCreate(NFS41_OPEN, NULL,
+#endif
+        pVNetRootContext->session, INVALID_HANDLE_VALUE,
+        pNetRootContext->nfs41d_version,
+        SrvOpen->pAlreadyPrefixedName, &entry);
+    if (status) goto out;
+
+    entry->u.Open.access_mask = params->DesiredAccess;
+    entry->u.Open.access_mode = params->ShareAccess;
+    entry->u.Open.attrs = params->FileAttributes;
+    if (!(params->CreateOptions & FILE_DIRECTORY_FILE))
+        entry->u.Open.attrs |= FILE_ATTRIBUTE_ARCHIVE;
+    entry->u.Open.disp = params->Disposition;
+    entry->u.Open.copts = params->CreateOptions;
+    entry->u.Open.srv_open = SrvOpen;
+    /* treat the NfsActOnLink ea as FILE_OPEN_REPARSE_POINT */
+    if ((ea && AnsiStrEq(&NfsActOnLink, ea->EaName, ea->EaNameLength)) ||
+            (entry->u.Open.access_mask & DELETE))
+        entry->u.Open.copts |= FILE_OPEN_REPARSE_POINT;
+    if (isDataAccess(params->DesiredAccess) || isOpen2Create(params->Disposition))
+        entry->u.Open.open_owner_id = InterlockedIncrement(&open_owner_id);
+    // if we are creating a file check if nfsv3attributes were passed in
+    if (params->Disposition != FILE_OPEN && params->Disposition != FILE_OVERWRITE) {
+        entry->u.Open.mode = 0777;
+        if (ea && AnsiStrEq(&NfsV3Attributes, ea->EaName, ea->EaNameLength)) {
+            nfs3_attrs *attrs = (nfs3_attrs *)(ea->EaName + ea->EaNameLength + 1);
+#ifdef DEBUG_OPEN
+            DbgP("creating file with mode %o\n", attrs->mode);
+#endif
+            entry->u.Open.mode = attrs->mode;
+        }
+        if (params->FileAttributes & FILE_ATTRIBUTE_READONLY)
+            entry->u.Open.mode = 0444;
+    }
+    if (entry->u.Open.disp == FILE_CREATE && ea &&
+            AnsiStrEq(&NfsSymlinkTargetName, ea->EaName, ea->EaNameLength)) {
+        /* for a cygwin symlink, given as a unicode string */
+        entry->u.Open.symlink.Buffer = (PWCH)(ea->EaName + ea->EaNameLength + 1);
+        entry->u.Open.symlink.MaximumLength = entry->u.Open.symlink.Length = ea->EaValueLength;
+    }
+retry_on_link:
+    if (ea && create_should_pass_ea(ea, params->Disposition)) {
+        /* lock the extended attribute buffer for read access in user space */
+        entry->u.Open.EaMdl = IoAllocateMdl(ea,
+            RxContext->CurrentIrpSp->Parameters.Create.EaLength,
+            FALSE, FALSE, NULL);
+        if (entry->u.Open.EaMdl == NULL) {
+            status = STATUS_INTERNAL_ERROR;
+            RxFreePool(entry);
+            goto out;
+        }
+        entry->u.Open.EaMdl->MdlFlags |= MDL_MAPPING_CAN_FAIL;
+        MmProbeAndLockPages(entry->u.Open.EaMdl, KernelMode, IoModifyAccess);
+    }
+
+    status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
+#ifndef USE_MOUNT_SEC_CONTEXT
+    SeDeleteClientSecurity(&entry->sec_ctx);
+#endif
+    if (status) goto out;
+
+    if (entry->u.Open.EaMdl) {
+        MmUnlockPages(entry->u.Open.EaMdl);
+        IoFreeMdl(entry->u.Open.EaMdl);
+    }
+
+    if (entry->status == NO_ERROR && entry->errno == ERROR_REPARSE) {
+        /* symbolic link handling. when attempting to open a symlink when the
+         * FILE_OPEN_REPARSE_POINT flag is not set, replace the filename with
+         * the symlink target's by calling RxPrepareToReparseSymbolicLink()
+         * and returning STATUS_REPARSE. the object manager will attempt to
+         * open the new path, and return its handle for the original open */
+        PRDBSS_DEVICE_OBJECT DeviceObject = RxContext->RxDeviceObject;
+        PV_NET_ROOT VNetRoot = (PV_NET_ROOT)
+            RxContext->pRelevantSrvOpen->pVNetRoot;
+        PUNICODE_STRING VNetRootPrefix = &VNetRoot->PrefixEntry.Prefix;
+        UNICODE_STRING AbsPath;
+        PCHAR buf;
+        BOOLEAN ReparseRequired;
+
+        /* allocate the string for RxPrepareToReparseSymbolicLink(), and
+         * format an absolute path "DeviceName+VNetRootName+symlink" */
+        AbsPath.Length = DeviceObject->DeviceName.Length +
+            VNetRootPrefix->Length + entry->u.Open.symlink.Length;
+        AbsPath.MaximumLength = AbsPath.Length + sizeof(UNICODE_NULL);
+        AbsPath.Buffer = RxAllocatePoolWithTag(NonPagedPool,
+            AbsPath.MaximumLength, NFS41_MM_POOLTAG);
+        if (AbsPath.Buffer == NULL) {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            goto out_free;
+        }
+
+        buf = (PCHAR)AbsPath.Buffer;
+        RtlCopyMemory(buf, DeviceObject->DeviceName.Buffer,
+            DeviceObject->DeviceName.Length);
+        buf += DeviceObject->DeviceName.Length;
+        RtlCopyMemory(buf, VNetRootPrefix->Buffer, VNetRootPrefix->Length);
+        buf += VNetRootPrefix->Length;
+        RtlCopyMemory(buf, entry->u.Open.symlink.Buffer,
+            entry->u.Open.symlink.Length);
+        RxFreePool(entry->u.Open.symlink.Buffer);
+        buf += entry->u.Open.symlink.Length;
+        *(PWCHAR)buf = UNICODE_NULL;
+
+        status = RxPrepareToReparseSymbolicLink(RxContext,
+            entry->u.Open.symlink_embedded, &AbsPath, TRUE, &ReparseRequired);
+#ifdef DEBUG_OPEN
+        DbgP("RxPrepareToReparseSymbolicLink(%u, '%wZ') returned %08lX, "
+            "FileName is '%wZ'\n", entry->u.Open.symlink_embedded,
+            &AbsPath, status, &RxContext->CurrentIrpSp->FileObject->FileName);
+#endif
+        if (status == STATUS_SUCCESS) {
+            /* if a reparse is not required, reopen the link itself.  this
+             * happens with operations on cygwin symlinks, where the reparse
+             * flag is not set */
+            if (!ReparseRequired) {
+                entry->u.Open.symlink.Length = 0;
+                entry->u.Open.copts |= FILE_OPEN_REPARSE_POINT;
+                goto retry_on_link;
+            }
+            status = STATUS_REPARSE;
+        }
+        goto out_free;
+    }
+
+    status = map_open_errors(entry->status,
+                SrvOpen->pAlreadyPrefixedName->Length);
+    if (status) {
+#ifdef DEBUG_OPEN
+        print_open_error(1, status);
+#endif
+        goto out_free;
+    }
+
+    if (!RxIsFcbAcquiredExclusive(Fcb)) {
+        ASSERT(!RxIsFcbAcquiredShared(Fcb));
+        RxAcquireExclusiveFcbResourceInMRx(Fcb);
+    }
+
+    RxContext->pFobx = RxCreateNetFobx(RxContext, SrvOpen);
+    if (RxContext->pFobx == NULL) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto out_free;
+    }
+#ifdef DEBUG_OPEN
+    DbgP("nfs41_Create: created FOBX %p\n", RxContext->pFobx);
+#endif
+    nfs41_fobx = (PNFS41_FOBX)(RxContext->pFobx)->Context;
+    nfs41_fobx->nfs41_open_state = entry->open_state;
+#ifndef USE_MOUNT_SEC_CONTEXT
+    status = nfs41_get_sec_ctx(SecurityImpersonation, &nfs41_fobx->sec_ctx);
+    if (status)
+        goto out_free;
+#else
+    RtlCopyMemory(&nfs41_fobx->sec_ctx, &pVNetRootContext->mount_sec_ctx,
+        sizeof(nfs41_fobx->sec_ctx));
+#endif
+
+    // we get attributes only for data access and file (not directories)
+    if (Fcb->OpenCount == 0 ||
+            (Fcb->OpenCount > 0 &&
+                nfs41_fcb->changeattr != entry->ChangeTime)) {
+        FCB_INIT_PACKET InitPacket;
+        RX_FILE_TYPE StorageType = FileTypeNotYetKnown;
+        RtlCopyMemory(&nfs41_fcb->BasicInfo, &entry->u.Open.binfo,
+            sizeof(entry->u.Open.binfo));
+        RtlCopyMemory(&nfs41_fcb->StandardInfo, &entry->u.Open.sinfo,
+            sizeof(entry->u.Open.sinfo));
+        nfs41_fcb->mode = entry->u.Open.mode;
+        nfs41_fcb->changeattr = entry->ChangeTime;
+        if (((params->CreateOptions & FILE_DELETE_ON_CLOSE) &&
+                !pVNetRootContext->read_only) || oldDeletePending)
+            nfs41_fcb->StandardInfo.DeletePending = TRUE;
+
+        RxFormInitPacket(InitPacket,
+            &entry->u.Open.binfo.FileAttributes,
+            &entry->u.Open.sinfo.NumberOfLinks,
+            &entry->u.Open.binfo.CreationTime,
+            &entry->u.Open.binfo.LastAccessTime,
+            &entry->u.Open.binfo.LastWriteTime,
+            &entry->u.Open.binfo.ChangeTime,
+            &entry->u.Open.sinfo.AllocationSize,
+            &entry->u.Open.sinfo.EndOfFile,
+            &entry->u.Open.sinfo.EndOfFile);
+
+        if (entry->u.Open.sinfo.Directory)
+            StorageType = FileTypeDirectory;
+        else
+            StorageType = FileTypeFile;
+
+        RxFinishFcbInitialization(Fcb, RDBSS_STORAGE_NTC(StorageType),
+                                    &InitPacket);
+    }
+#ifdef DEBUG_OPEN
+    else
+        DbgP("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
+
+    print_basic_info(1, &nfs41_fcb->BasicInfo);
+    print_std_info(1, &nfs41_fcb->StandardInfo);
+#endif
+
+    /* aglo: 05/10/2012. it seems like always have to invalid the cache if the
+     * file has been opened before and being opened again for data access.
+     * If the file was opened before, RDBSS might have cached (unflushed) data
+     * and by opening it again, we will not have the correct representation of
+     * the file size and data content. fileio tests 208, 219, 221.
+     */
+    if (Fcb->OpenCount > 0 && (isDataAccess(params->DesiredAccess) ||
+            nfs41_fcb->changeattr != entry->ChangeTime) &&
+                !nfs41_fcb->StandardInfo.Directory) {
+        ULONG flag = DISABLE_CACHING;
+#ifdef DEBUG_OPEN
+        DbgP("nfs41_Create: reopening (changed) file %wZ\n",
+            SrvOpen->pAlreadyPrefixedName);
+#endif
+        RxChangeBufferingState((PSRV_OPEN)SrvOpen, ULongToPtr(flag), 1);
+    }
+    if (!nfs41_fcb->StandardInfo.Directory &&
+            isDataAccess(params->DesiredAccess)) {
+        nfs41_fobx->deleg_type = entry->u.Open.deleg_type;
+#ifdef DEBUG_OPEN
+        DbgP("nfs41_Create: received delegation %d\n", entry->u.Open.deleg_type);
+#endif
+        if (!(params->CreateOptions & FILE_WRITE_THROUGH) &&
+                !pVNetRootContext->write_thru &&
+                (entry->u.Open.deleg_type == 2 ||
+                (params->DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA)))) {
+#ifdef DEBUG_OPEN
+            DbgP("nfs41_Create: enabling write buffering\n");
+#endif
+            SrvOpen->BufferingFlags |=
+                (FCB_STATE_WRITECACHING_ENABLED |
+                FCB_STATE_WRITEBUFFERING_ENABLED);
+        } else if (params->CreateOptions & FILE_WRITE_THROUGH ||
+                    pVNetRootContext->write_thru)
+            nfs41_fobx->write_thru = TRUE;
+        if (entry->u.Open.deleg_type >= 1 ||
+                params->DesiredAccess & FILE_READ_DATA) {
+#ifdef DEBUG_OPEN
+            DbgP("nfs41_Create: enabling read buffering\n");
+#endif
+            SrvOpen->BufferingFlags |=
+                (FCB_STATE_READBUFFERING_ENABLED |
+                FCB_STATE_READCACHING_ENABLED);
+        }
+        if (pVNetRootContext->nocache ||
+                (params->CreateOptions & FILE_NO_INTERMEDIATE_BUFFERING)) {
+#ifdef DEBUG_OPEN
+            DbgP("nfs41_Create: disabling buffering\n");
+#endif
+            SrvOpen->BufferingFlags = FCB_STATE_DISABLE_LOCAL_BUFFERING;
+            nfs41_fobx->nocache = TRUE;
+        } else if (!entry->u.Open.deleg_type && !Fcb->OpenCount) {
+            nfs41_fcb_list_entry *oentry;
+#ifdef DEBUG_OPEN
+            DbgP("nfs41_Create: received no delegations: srv_open=%p "
+                "ctime=%llu\n", SrvOpen, entry->ChangeTime);
+#endif
+            oentry = RxAllocatePoolWithTag(NonPagedPool,
+                sizeof(nfs41_fcb_list_entry), NFS41_MM_POOLTAG_OPEN);
+            if (oentry == NULL) {
+                status = STATUS_INSUFFICIENT_RESOURCES;
+                goto out_free;
+            }
+            oentry->fcb = RxContext->pFcb;
+            oentry->nfs41_fobx = nfs41_fobx;
+            oentry->session = pVNetRootContext->session;
+            oentry->ChangeTime = entry->ChangeTime;
+            oentry->skip = FALSE;
+            nfs41_AddEntry(fcblistLock, openlist, oentry);
+        }
+    }
+
+    if ((params->CreateOptions & FILE_DELETE_ON_CLOSE) &&
+            !pVNetRootContext->read_only)
+        nfs41_fcb->StandardInfo.DeletePending = TRUE;
+
+    RxContext->Create.ReturnedCreateInformation =
+        map_disposition_to_create_retval(params->Disposition, entry->errno);
+
+    RxContext->pFobx->OffsetOfNextEaToReturn = 1;
+#ifndef __REACTOS__
+    RxContext->CurrentIrp->IoStatus.Information =
+        RxContext->Create.ReturnedCreateInformation;
+#endif
+    status = RxContext->CurrentIrp->IoStatus.Status = STATUS_SUCCESS;
+
+out_free:
+    if (entry)
+        RxFreePool(entry);
+out:
+#ifdef ENABLE_TIMINGS
+    t2 = KeQueryPerformanceCounter(NULL);
+    if ((params->DesiredAccess & FILE_READ_DATA) ||
+            (params->DesiredAccess & FILE_WRITE_DATA) ||
+            (params->DesiredAccess & FILE_APPEND_DATA) ||
+            (params->DesiredAccess & FILE_EXECUTE)) {
+        InterlockedIncrement(&open.tops);
+        InterlockedAdd64(&open.ticks, t2.QuadPart - t1.QuadPart);
+#ifdef ENABLE_INDV_TIMINGS
+    DbgP("nfs41_Create open delta = %d op=%d sum=%d\n",
+        t2.QuadPart - t1.QuadPart, open.tops, open.ticks);
+#endif
+    } else {
+        InterlockedIncrement(&lookup.tops);
+        InterlockedAdd64(&lookup.ticks, t2.QuadPart - t1.QuadPart);
+#ifdef ENABLE_INDV_TIMINGS
+    DbgP("nfs41_Create lookup delta = %d op=%d sum=%d\n",
+        t2.QuadPart - t1.QuadPart, lookup.tops, lookup.ticks);
+#endif
+    }
+#endif
+#ifdef DEBUG_OPEN
+    DbgEx();
+#endif
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_CollapseOpen(
+#else
+NTSTATUS nfs41_CollapseOpen(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED;
+    DbgEn();
+    DbgEx();
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_ShouldTryToCollapseThisOpen(
+#else
+NTSTATUS nfs41_ShouldTryToCollapseThisOpen(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    if (RxContext->pRelevantSrvOpen == NULL)
+        return STATUS_SUCCESS;
+    else return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+#ifdef __REACTOS__
+ULONG NTAPI nfs41_ExtendForCache(
+#else
+ULONG nfs41_ExtendForCache(
+#endif
+    IN OUT PRX_CONTEXT RxContext,
+    IN PLARGE_INTEGER pNewFileSize,
+    OUT PLARGE_INTEGER pNewAllocationSize)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
+#ifdef DEBUG_CACHE
+    PLOWIO_CONTEXT LowIoContext  = &RxContext->LowIoContext;
+    DbgEn();
+    print_debug_header(RxContext);
+    DbgP("input: byte count 0x%x filesize 0x%x alloc size 0x%x\n",
+        LowIoContext->ParamsFor.ReadWrite.ByteCount, *pNewFileSize,
+        *pNewAllocationSize);
+#endif
+    pNewAllocationSize->QuadPart = pNewFileSize->QuadPart + 8192;
+    nfs41_fcb->StandardInfo.AllocationSize.QuadPart =
+        pNewAllocationSize->QuadPart;
+    nfs41_fcb->StandardInfo.EndOfFile.QuadPart = pNewFileSize->QuadPart;
+#ifdef DEBUG_CACHE
+    DbgP("new filesize 0x%x new allocation size 0x%x\n", *pNewFileSize,
+        *pNewAllocationSize);
+#endif
+#ifdef DEBUG_CACHE
+    DbgEx();
+#endif
+    return status;
+}
+
+VOID nfs41_remove_fcb_entry(
+    PMRX_FCB fcb)
+{
+    PLIST_ENTRY pEntry;
+    nfs41_fcb_list_entry *cur;
+    ExAcquireFastMutex(&fcblistLock);
+
+    pEntry = openlist.head.Flink;
+    while (!IsListEmpty(&openlist.head)) {
+        cur = (nfs41_fcb_list_entry *)CONTAINING_RECORD(pEntry,
+                nfs41_fcb_list_entry, next);
+        if (cur->fcb == fcb) {
+#ifdef DEBUG_CLOSE
+            DbgP("nfs41_remove_srvopen_entry: Found match for fcb=%p\n", fcb);
+#endif
+            RemoveEntryList(pEntry);
+            RxFreePool(cur);
+            break;
+        }
+        if (pEntry->Flink == &openlist.head) {
+#ifdef DEBUG_CLOSE
+            DbgP("nfs41_remove_srvopen_entry: reached EOL looking for fcb "
+                "%p\n", fcb);
+#endif
+            break;
+        }
+        pEntry = pEntry->Flink;
+    }
+    ExReleaseFastMutex(&fcblistLock);
+}
+
+NTSTATUS map_close_errors(
+    DWORD status)
+{
+    switch (status) {
+    case NO_ERROR:              return STATUS_SUCCESS;
+    case ERROR_NETNAME_DELETED: return STATUS_NETWORK_NAME_DELETED;
+    case ERROR_NOT_EMPTY:       return STATUS_DIRECTORY_NOT_EMPTY;
+    case ERROR_FILE_INVALID:    return STATUS_FILE_INVALID;
+    default:
+        print_error("failed to map windows error %d to NTSTATUS; "
+            "defaulting to STATUS_INTERNAL_ERROR\n", status);
+    case ERROR_INTERNAL_ERROR: return STATUS_INTERNAL_ERROR;
+    }
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_CloseSrvOpen(
+#else
+NTSTATUS nfs41_CloseSrvOpen(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
+    nfs41_updowncall_entry *entry;
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+    __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
+    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
+#ifdef ENABLE_TIMINGS
+    LARGE_INTEGER t1, t2;
+    t1 = KeQueryPerformanceCounter(NULL);
+#endif
+
+#ifdef DEBUG_CLOSE
+    DbgEn();
+    print_debug_header(RxContext);
+#endif
+
+    if (!nfs41_fobx->deleg_type && !nfs41_fcb->StandardInfo.Directory &&
+            !RxContext->pFcb->OpenCount) {
+        nfs41_remove_fcb_entry(RxContext->pFcb);
+    }
+
+    status = nfs41_UpcallCreate(NFS41_CLOSE, &nfs41_fobx->sec_ctx,
+        pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
+        pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
+    if (status) goto out;
+
+    entry->u.Close.srv_open = SrvOpen;
+    if (nfs41_fcb->StandardInfo.DeletePending)
+        nfs41_fcb->DeletePending = TRUE;
+    if (!RxContext->pFcb->OpenCount ||
+            (nfs41_fcb->StandardInfo.DeletePending &&
+                nfs41_fcb->StandardInfo.Directory))
+        entry->u.Close.remove = nfs41_fcb->StandardInfo.DeletePending;
+    if (!RxContext->pFcb->OpenCount)
+        entry->u.Close.renamed = nfs41_fcb->Renamed;
+
+    status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
+#ifndef USE_MOUNT_SEC_CONTEXT
+    SeDeleteClientSecurity(&nfs41_fobx->sec_ctx);
+#endif
+    if (status) goto out;
+
+    /* map windows ERRORs to NTSTATUS */
+    status = map_close_errors(entry->status);
+    RxFreePool(entry);
+out:
+#ifdef ENABLE_TIMINGS
+    t2 = KeQueryPerformanceCounter(NULL);
+    InterlockedIncrement(&close.tops);
+    InterlockedAdd64(&close.ticks, t2.QuadPart - t1.QuadPart);
+#ifdef ENABLE_INDV_TIMINGS
+    DbgP("nfs41_CloseSrvOpen delta = %d op=%d sum=%d\n",
+        t2.QuadPart - t1.QuadPart, close.tops, close.ticks);
+#endif
+#endif
+#ifdef DEBUG_CLOSE
+    DbgEx();
+#endif
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_Flush(
+#else
+NTSTATUS nfs41_Flush(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    return STATUS_SUCCESS;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_DeallocateForFcb(
+#else
+NTSTATUS nfs41_DeallocateForFcb(
+#endif
+    IN OUT PMRX_FCB pFcb)
+{
+    return STATUS_SUCCESS;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_DeallocateForFobx(
+#else
+NTSTATUS nfs41_DeallocateForFobx(
+#endif
+    IN OUT PMRX_FOBX pFobx)
+{
+    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(pFobx);
+    if (nfs41_fobx->acl)
+        RxFreePool(nfs41_fobx->acl);
+    return STATUS_SUCCESS;
+}
+
+void print_debug_filedirquery_header(
+    PRX_CONTEXT RxContext)
+{
+    print_debug_header(RxContext);
+    DbgP("FileName='%wZ', InfoClass = %s\n",
+        GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext),
+        print_file_information_class(RxContext->Info.FileInformationClass));
+}
+
+void print_querydir_args(
+    PRX_CONTEXT RxContext)
+{
+    print_debug_filedirquery_header(RxContext);
+    DbgP("Filter='%wZ', Index=%d, Restart/Single/Specified/Init=%d/%d/%d/%d\n",
+        &RxContext->pFobx->UnicodeQueryTemplate,
+        RxContext->QueryDirectory.FileIndex,
+        RxContext->QueryDirectory.RestartScan,
+        RxContext->QueryDirectory.ReturnSingleEntry,
+        RxContext->QueryDirectory.IndexSpecified,
+        RxContext->QueryDirectory.InitialQuery);
+}
+
+NTSTATUS map_querydir_errors(
+    DWORD status)
+{
+    switch (status) {
+    case ERROR_ACCESS_DENIED:       return STATUS_ACCESS_DENIED;
+    case ERROR_BUFFER_OVERFLOW:     return STATUS_BUFFER_OVERFLOW;
+    case ERROR_FILE_NOT_FOUND:      return STATUS_NO_SUCH_FILE;
+    case ERROR_NETNAME_DELETED:     return STATUS_NETWORK_NAME_DELETED;
+    case ERROR_INVALID_PARAMETER:   return STATUS_INVALID_PARAMETER;
+    case ERROR_NO_MORE_FILES:       return STATUS_NO_MORE_FILES;
+    case ERROR_OUTOFMEMORY:         return STATUS_INSUFFICIENT_RESOURCES;
+    case ERROR_FILENAME_EXCED_RANGE: return STATUS_NAME_TOO_LONG;
+    default:
+        print_error("failed to map windows error %d to NTSTATUS; "
+            "defaulting to STATUS_INVALID_NETWORK_RESPONSE\n", status);
+    case ERROR_BAD_NET_RESP:        return STATUS_INVALID_NETWORK_RESPONSE;
+    }
+}
+
+NTSTATUS check_nfs41_dirquery_args(
+    IN PRX_CONTEXT RxContext)
+{
+    if (RxContext->Info.Buffer == NULL)
+        return STATUS_INVALID_USER_BUFFER;
+    return STATUS_SUCCESS;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_QueryDirectory(
+#else
+NTSTATUS nfs41_QueryDirectory(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    nfs41_updowncall_entry *entry;
+    FILE_INFORMATION_CLASS InfoClass = RxContext->Info.FileInformationClass;
+    PUNICODE_STRING Filter = &RxContext->pFobx->UnicodeQueryTemplate;
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
+#ifdef ENABLE_TIMINGS
+    LARGE_INTEGER t1, t2;
+    t1 = KeQueryPerformanceCounter(NULL);
+#endif
+
+#ifdef DEBUG_DIR_QUERY
+    DbgEn();
+    print_querydir_args(RxContext);
+#endif
+
+    status = check_nfs41_dirquery_args(RxContext);
+    if (status) goto out;
+
+    switch (InfoClass) {
+    /* classes handled in readdir_copy_entry() and readdir_size_for_entry() */
+    case FileNamesInformation:
+    case FileDirectoryInformation:
+    case FileFullDirectoryInformation:
+    case FileIdFullDirectoryInformation:
+    case FileBothDirectoryInformation:
+    case FileIdBothDirectoryInformation:
+        break;
+    default:
+        print_error("nfs41_QueryDirectory: unhandled dir query class %d\n",
+            InfoClass);
+        status = STATUS_NOT_SUPPORTED;
+        goto out;
+    }
+    status = nfs41_UpcallCreate(NFS41_DIR_QUERY, &nfs41_fobx->sec_ctx,
+        pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
+        pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
+    if (status) goto out;
+
+    entry->u.QueryFile.InfoClass = InfoClass;
+    entry->buf_len = RxContext->Info.LengthRemaining;
+    entry->buf = RxContext->Info.Buffer;
+    entry->u.QueryFile.mdl = IoAllocateMdl(RxContext->Info.Buffer,
+        RxContext->Info.LengthRemaining, FALSE, FALSE, NULL);
+    if (entry->u.QueryFile.mdl == NULL) {
+        status = STATUS_INTERNAL_ERROR;
+        RxFreePool(entry);
+        goto out;
+    }
+    entry->u.QueryFile.mdl->MdlFlags |= MDL_MAPPING_CAN_FAIL;
+    MmProbeAndLockPages(entry->u.QueryFile.mdl, KernelMode, IoModifyAccess);
+
+    entry->u.QueryFile.filter = Filter;
+    entry->u.QueryFile.initial_query = RxContext->QueryDirectory.InitialQuery;
+    entry->u.QueryFile.restart_scan = RxContext->QueryDirectory.RestartScan;
+    entry->u.QueryFile.return_single = RxContext->QueryDirectory.ReturnSingleEntry;
+
+    status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
+    if (status) goto out;
+    MmUnlockPages(entry->u.QueryFile.mdl);
+
+    if (entry->status == STATUS_BUFFER_TOO_SMALL) {
+        DbgP("nfs41_QueryDirectory: buffer too small provided %d need %lu\n",
+            RxContext->Info.LengthRemaining, entry->buf_len);
+        RxContext->InformationToReturn = entry->buf_len;
+        status = STATUS_BUFFER_TOO_SMALL;
+    } else if (entry->status == STATUS_SUCCESS) {
+#ifdef ENABLE_TIMINGS
+        InterlockedIncrement(&readdir.sops);
+        InterlockedAdd64(&readdir.size, entry->u.QueryFile.buf_len);
+#endif
+        RxContext->Info.LengthRemaining -= entry->buf_len;
+        status = STATUS_SUCCESS;
+    } else {
+        /* map windows ERRORs to NTSTATUS */
+        status = map_querydir_errors(entry->status);
+    }
+    IoFreeMdl(entry->u.QueryFile.mdl);
+    RxFreePool(entry);
+out:
+#ifdef ENABLE_TIMINGS
+    t2 = KeQueryPerformanceCounter(NULL);
+    InterlockedIncrement(&readdir.tops);
+    InterlockedAdd64(&readdir.ticks, t2.QuadPart - t1.QuadPart);
+#ifdef ENABLE_INDV_TIMINGS
+    DbgP("nfs41_QueryDirectory delta = %d ops=%d sum=%d\n",
+        t2.QuadPart - t1.QuadPart, readdir.tops, readdir.ticks);
+#endif
+#endif
+#ifdef DEBUG_DIR_QUERY
+    DbgEx();
+#endif
+    return status;
+}
+
+void print_queryvolume_args(
+    PRX_CONTEXT RxContext)
+{
+    print_debug_header(RxContext);
+    DbgP("FileName='%wZ', InfoClass = %s BufferLen = %d\n",
+        GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext),
+        print_fs_information_class(RxContext->Info.FileInformationClass),
+        RxContext->Info.LengthRemaining);
+}
+
+NTSTATUS map_volume_errors(
+    DWORD status)
+{
+    switch (status) {
+    case ERROR_ACCESS_DENIED:       return STATUS_ACCESS_DENIED;
+    case ERROR_VC_DISCONNECTED:     return STATUS_CONNECTION_DISCONNECTED;
+    case ERROR_NETNAME_DELETED:     return STATUS_NETWORK_NAME_DELETED;
+    case ERROR_INVALID_PARAMETER:   return STATUS_INVALID_PARAMETER;
+    case ERROR_OUTOFMEMORY:         return STATUS_INSUFFICIENT_RESOURCES;
+    default:
+        print_error("failed to map windows error %d to NTSTATUS; "
+            "defaulting to STATUS_INVALID_NETWORK_RESPONSE\n", status);
+    case ERROR_BAD_NET_RESP:        return STATUS_INVALID_NETWORK_RESPONSE;
+    }
+}
+
+void nfs41_create_volume_info(PFILE_FS_VOLUME_INFORMATION pVolInfo, DWORD *len)
+{
+    DECLARE_CONST_UNICODE_STRING(VolName, VOL_NAME);
+
+    RtlZeroMemory(pVolInfo, sizeof(FILE_FS_VOLUME_INFORMATION));
+    pVolInfo->VolumeSerialNumber = 0xBABAFACE;
+    pVolInfo->VolumeLabelLength = VolName.Length;
+    RtlCopyMemory(&pVolInfo->VolumeLabel[0], (PVOID)VolName.Buffer,
+        VolName.MaximumLength);
+    *len = sizeof(FILE_FS_VOLUME_INFORMATION) + VolName.Length;
+}
+
+static BOOLEAN is_root_directory(
+    PRX_CONTEXT RxContext)
+{
+    __notnull PV_NET_ROOT VNetRoot = (PV_NET_ROOT)
+        RxContext->pRelevantSrvOpen->pVNetRoot;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(RxContext->pRelevantSrvOpen->pVNetRoot);
+
+    /* calculate the root directory's length, including vnetroot prefix,
+     * mount path, and a trailing \ */
+    const USHORT RootPathLen = VNetRoot->PrefixEntry.Prefix.Length +
+            pVNetRootContext->MountPathLen + sizeof(WCHAR);
+
+    return RxContext->CurrentIrpSp->FileObject->FileName.Length <= RootPathLen;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_QueryVolumeInformation(
+#else
+NTSTATUS nfs41_QueryVolumeInformation(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    nfs41_updowncall_entry *entry;
+    ULONG RemainingLength = RxContext->Info.LengthRemaining, SizeUsed;
+    FS_INFORMATION_CLASS InfoClass = RxContext->Info.FsInformationClass;
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
+    NFS41GetDeviceExtension(RxContext, DevExt);
+
+#ifdef ENABLE_TIMINGS
+    LARGE_INTEGER t1, t2;
+    t1 = KeQueryPerformanceCounter(NULL);
+#endif
+
+#ifdef DEBUG_VOLUME_QUERY
+    DbgEn();
+    print_queryvolume_args(RxContext);
+#endif
+
+    status = check_nfs41_dirquery_args(RxContext);
+    if (status) goto out;
+
+    switch (InfoClass) {
+    case FileFsVolumeInformation:
+        if ((ULONG)RxContext->Info.LengthRemaining >= DevExt->VolAttrsLen) {
+            RtlCopyMemory(RxContext->Info.Buffer, DevExt->VolAttrs,
+                DevExt->VolAttrsLen);
+            RxContext->Info.LengthRemaining -= DevExt->VolAttrsLen;
+            status = STATUS_SUCCESS;
+        } else {
+            RtlCopyMemory(RxContext->Info.Buffer, DevExt->VolAttrs,
+                RxContext->Info.LengthRemaining);
+            status = STATUS_BUFFER_OVERFLOW;
+        }
+        goto out;
+    case FileFsDeviceInformation:
+    {
+        PFILE_FS_DEVICE_INFORMATION pDevInfo = RxContext->Info.Buffer;
+
+        SizeUsed = sizeof(FILE_FS_DEVICE_INFORMATION);
+        if (RemainingLength < SizeUsed) {
+            status = STATUS_BUFFER_TOO_SMALL;
+            RxContext->InformationToReturn = SizeUsed;
+            goto out;
+        }
+        pDevInfo->DeviceType = RxContext->pFcb->pNetRoot->DeviceType;
+        pDevInfo->Characteristics = FILE_REMOTE_DEVICE | FILE_DEVICE_IS_MOUNTED;
+        RxContext->Info.LengthRemaining -= SizeUsed;
+        status = STATUS_SUCCESS;
+        goto out;
+    }
+    case FileAccessInformation:
+        status = STATUS_NOT_SUPPORTED;
+        goto out;
+
+    case FileFsAttributeInformation:
+        if (RxContext->Info.LengthRemaining < FS_ATTR_LEN) {
+            RxContext->InformationToReturn = FS_ATTR_LEN;
+            status = STATUS_BUFFER_TOO_SMALL;
+            goto out;
+        }
+
+        /* on attribute queries for the root directory,
+         * use cached volume attributes from mount */
+        if (is_root_directory(RxContext)) {
+            PFILE_FS_ATTRIBUTE_INFORMATION attrs =
+                (PFILE_FS_ATTRIBUTE_INFORMATION)RxContext->Info.Buffer;
+            DECLARE_CONST_UNICODE_STRING(FsName, FS_NAME);
+
+            RtlCopyMemory(attrs, &pVNetRootContext->FsAttrs,
+                sizeof(pVNetRootContext->FsAttrs));
+
+            /* fill in the FileSystemName */
+            RtlCopyMemory(attrs->FileSystemName, FsName.Buffer,
+                FsName.MaximumLength); /* 'MaximumLength' to include null */
+            attrs->FileSystemNameLength = FsName.Length;
+
+            RxContext->Info.LengthRemaining -= FS_ATTR_LEN;
+            goto out;
+        }
+        /* else fall through and send the upcall */
+    case FileFsSizeInformation:
+    case FileFsFullSizeInformation:
+        break;
+
+    default:
+        print_error("nfs41_QueryVolumeInformation: unhandled class %d\n", InfoClass);
+        status = STATUS_NOT_SUPPORTED;
+        goto out;
+    }
+    status = nfs41_UpcallCreate(NFS41_VOLUME_QUERY, &nfs41_fobx->sec_ctx,
+        pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
+        pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
+    if (status) goto out;
+
+    entry->u.Volume.query = InfoClass;
+    entry->buf = RxContext->Info.Buffer;
+    entry->buf_len = RxContext->Info.LengthRemaining;
+
+    status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
+    if (status) goto out;
+
+    if (entry->status == STATUS_BUFFER_TOO_SMALL) {
+        RxContext->InformationToReturn = entry->buf_len;
+        status = STATUS_BUFFER_TOO_SMALL;
+    } else if (entry->status == STATUS_SUCCESS) {
+        if (InfoClass == FileFsAttributeInformation) {
+            /* fill in the FileSystemName */
+            PFILE_FS_ATTRIBUTE_INFORMATION attrs =
+                (PFILE_FS_ATTRIBUTE_INFORMATION)RxContext->Info.Buffer;
+            DECLARE_CONST_UNICODE_STRING(FsName, FS_NAME);
+
+            RtlCopyMemory(attrs->FileSystemName, FsName.Buffer,
+                FsName.MaximumLength); /* 'MaximumLength' to include null */
+            attrs->FileSystemNameLength = FsName.Length;
+
+            entry->buf_len = FS_ATTR_LEN;
+        }
+#ifdef ENABLE_TIMINGS
+        InterlockedIncrement(&volume.sops);
+        InterlockedAdd64(&volume.size, entry->u.Volume.buf_len);
+#endif
+        RxContext->Info.LengthRemaining -= entry->buf_len;
+        status = STATUS_SUCCESS;
+    } else {
+        status = map_volume_errors(entry->status);
+    }
+    RxFreePool(entry);
+out:
+#ifdef ENABLE_TIMINGS
+    t2 = KeQueryPerformanceCounter(NULL);
+    InterlockedIncrement(&volume.tops);
+    InterlockedAdd64(&volume.ticks, t2.QuadPart - t1.QuadPart);
+#ifdef ENABLE_INDV_TIMINGS
+    DbgP("nfs41_QueryVolumeInformation delta = %d op=%d sum=%d\n",
+        t2.QuadPart - t1.QuadPart, volume.tops, volume.ticks);
+#endif
+#endif
+#ifdef DEBUG_VOLUME_QUERY
+    DbgEx();
+#endif
+    return status;
+}
+
+VOID nfs41_update_fcb_list(
+    PMRX_FCB fcb,
+    ULONGLONG ChangeTime)
+{
+    PLIST_ENTRY pEntry;
+    nfs41_fcb_list_entry *cur;
+    ExAcquireFastMutex(&fcblistLock);
+    pEntry = openlist.head.Flink;
+    while (!IsListEmpty(&openlist.head)) {
+        cur = (nfs41_fcb_list_entry *)CONTAINING_RECORD(pEntry,
+                nfs41_fcb_list_entry, next);
+        if (cur->fcb == fcb &&
+                cur->ChangeTime != ChangeTime) {
+#if defined(DEBUG_FILE_SET) || defined(DEBUG_ACL_SET) || \
+    defined(DEBUG_WRITE) || defined(DEBUG_EA_SET)
+            DbgP("nfs41_update_fcb_list: Found match for fcb %p: updating "
+                "%llu to %llu\n", fcb, cur->ChangeTime, ChangeTime);
+#endif
+            cur->ChangeTime = ChangeTime;
+            break;
+        }
+        /* place an upcall for this srv_open */
+        if (pEntry->Flink == &openlist.head) {
+#if defined(DEBUG_FILE_SET) || defined(DEBUG_ACL_SET) || \
+    defined(DEBUG_WRITE) || defined(DEBUG_EA_SET)
+            DbgP("nfs41_update_fcb_list: reached EOL loooking for "
+                "fcb=%p\n", fcb);
+#endif
+            break;
+        }
+        pEntry = pEntry->Flink;
+    }
+    ExReleaseFastMutex(&fcblistLock);
+}
+
+void print_nfs3_attrs(
+    nfs3_attrs *attrs)
+{
+    DbgP("type=%d mode=%o nlink=%d size=%d atime=%x mtime=%x ctime=%x\n",
+        attrs->type, attrs->mode, attrs->nlink, attrs->size, attrs->atime,
+        attrs->mtime, attrs->ctime);
+}
+
+void file_time_to_nfs_time(
+    IN const PLARGE_INTEGER file_time,
+    OUT LONGLONG *nfs_time)
+{
+    LARGE_INTEGER diff = unix_time_diff;
+    diff.QuadPart = file_time->QuadPart - diff.QuadPart;
+    *nfs_time = diff.QuadPart / 10000000;
+}
+
+void create_nfs3_attrs(
+    nfs3_attrs *attrs,
+    PNFS41_FCB nfs41_fcb)
+{
+    RtlZeroMemory(attrs, sizeof(nfs3_attrs));
+    if (nfs41_fcb->BasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
+        attrs->type = NF3LNK;
+    else if (nfs41_fcb->StandardInfo.Directory)
+        attrs->type = NF3DIR;
+    else
+        attrs->type = NF3REG;
+    attrs->mode = nfs41_fcb->mode;
+    attrs->nlink = nfs41_fcb->StandardInfo.NumberOfLinks;
+    attrs->size.QuadPart = attrs->used.QuadPart =
+        nfs41_fcb->StandardInfo.EndOfFile.QuadPart;
+    file_time_to_nfs_time(&nfs41_fcb->BasicInfo.LastAccessTime, &attrs->atime);
+    file_time_to_nfs_time(&nfs41_fcb->BasicInfo.ChangeTime, &attrs->mtime);
+    file_time_to_nfs_time(&nfs41_fcb->BasicInfo.CreationTime, &attrs->ctime);
+}
+
+
+NTSTATUS map_setea_error(
+    DWORD error)
+{
+    switch (error) {
+    case NO_ERROR:                      return STATUS_SUCCESS;
+    case ERROR_FILE_NOT_FOUND:          return STATUS_NO_EAS_ON_FILE;
+    case ERROR_ACCESS_DENIED:           return STATUS_ACCESS_DENIED;
+    case ERROR_NETWORK_ACCESS_DENIED:   return STATUS_NETWORK_ACCESS_DENIED;
+    case ERROR_NETNAME_DELETED:         return STATUS_NETWORK_NAME_DELETED;
+    case ERROR_FILE_TOO_LARGE:          return STATUS_EA_TOO_LARGE;
+    case ERROR_BUFFER_OVERFLOW:         return STATUS_BUFFER_OVERFLOW;
+    case STATUS_BUFFER_TOO_SMALL:
+    case ERROR_INSUFFICIENT_BUFFER:     return STATUS_BUFFER_TOO_SMALL;
+    case ERROR_INVALID_EA_HANDLE:       return STATUS_NONEXISTENT_EA_ENTRY;
+    case ERROR_NO_MORE_FILES:           return STATUS_NO_MORE_EAS;
+    case ERROR_EA_FILE_CORRUPT:         return STATUS_EA_CORRUPT_ERROR;
+    default:
+        print_error("failed to map windows error %d to NTSTATUS; "
+            "defaulting to STATUS_INVALID_PARAMETER\n", error);
+    case ERROR_INVALID_PARAMETER:       return STATUS_INVALID_PARAMETER;
+    }
+}
+
+NTSTATUS check_nfs41_setea_args(
+    IN PRX_CONTEXT RxContext)
+{
+    NTSTATUS status;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(RxContext->pRelevantSrvOpen->pVNetRoot);
+    __notnull PFILE_FS_ATTRIBUTE_INFORMATION FsAttrs =
+        &pVNetRootContext->FsAttrs;
+    __notnull PFILE_FULL_EA_INFORMATION ea =
+        (PFILE_FULL_EA_INFORMATION)RxContext->Info.Buffer;
+
+    status = check_nfs41_dirquery_args(RxContext);
+    if (status) goto out;
+
+    if (ea == NULL) {
+        status = STATUS_INVALID_PARAMETER;
+        goto out;
+    }
+    if (AnsiStrEq(&NfsActOnLink, ea->EaName, ea->EaNameLength) ||
+        AnsiStrEq(&NfsSymlinkTargetName, ea->EaName, ea->EaNameLength)) {
+        status = STATUS_INVALID_PARAMETER; /* only allowed on create */
+        goto out;
+    }
+    /* ignore cygwin EAs when checking support */
+    if (!(FsAttrs->FileSystemAttributes & FILE_SUPPORTS_EXTENDED_ATTRIBUTES)
+        && !AnsiStrEq(&NfsV3Attributes, ea->EaName, ea->EaNameLength)) {
+        status = STATUS_EAS_NOT_SUPPORTED;
+        goto out;
+    }
+    if ((RxContext->pRelevantSrvOpen->DesiredAccess & FILE_WRITE_EA) == 0) {
+        status = STATUS_ACCESS_DENIED;
+        goto out;
+    }
+    if (pVNetRootContext->read_only) {
+        print_error("check_nfs41_setattr_args: Read-only mount\n");
+        status = STATUS_ACCESS_DENIED;
+        goto out;
+    }
+out:
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_SetEaInformation(
+#else
+NTSTATUS nfs41_SetEaInformation(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_EAS_NOT_SUPPORTED;
+    nfs41_updowncall_entry *entry;
+    __notnull PFILE_FULL_EA_INFORMATION eainfo =
+        (PFILE_FULL_EA_INFORMATION)RxContext->Info.Buffer;
+    nfs3_attrs *attrs = NULL;
+    ULONG buflen = RxContext->CurrentIrpSp->Parameters.SetEa.Length, error_offset;
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+    __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
+    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
+#ifdef ENABLE_TIMINGS
+    LARGE_INTEGER t1, t2;
+    t1 = KeQueryPerformanceCounter(NULL);
+#endif
+
+#ifdef DEBUG_EA_SET
+    DbgEn();
+    print_debug_header(RxContext);
+    print_ea_info(1, eainfo);
+#endif
+
+    status = check_nfs41_setea_args(RxContext);
+    if (status) goto out;
+
+    status = nfs41_UpcallCreate(NFS41_EA_SET, &nfs41_fobx->sec_ctx,
+        pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
+        pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
+    if (status) goto out;
+
+    if (AnsiStrEq(&NfsV3Attributes, eainfo->EaName, eainfo->EaNameLength)) {
+        attrs = (nfs3_attrs *)(eainfo->EaName + eainfo->EaNameLength + 1);
+#ifdef DEBUG_EA_SET
+        print_nfs3_attrs(attrs);
+        DbgP("old mode is %o new mode is %o\n", nfs41_fcb->mode, attrs->mode);
+#endif
+        entry->u.SetEa.mode = attrs->mode;
+    } else {
+        entry->u.SetEa.mode = 0;
+        status = IoCheckEaBufferValidity(eainfo, buflen, &error_offset);
+        if (status) {
+            RxFreePool(entry);
+            goto out;
+        }
+    }
+    entry->buf = eainfo;
+    entry->buf_len = buflen;
+
+    status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
+    if (status) goto out;
+#ifdef ENABLE_TIMINGS
+    if (entry->status == STATUS_SUCCESS) {
+        InterlockedIncrement(&setexattr.sops);
+        InterlockedAdd64(&setexattr.size, entry->u.SetEa.buf_len);
+    }
+#endif
+    status = map_setea_error(entry->status);
+    if (!status) {
+        if (!nfs41_fobx->deleg_type && entry->ChangeTime &&
+                (SrvOpen->DesiredAccess &
+                (FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA)))
+            nfs41_update_fcb_list(RxContext->pFcb, entry->ChangeTime);
+        nfs41_fcb->changeattr = entry->ChangeTime;
+        nfs41_fcb->mode = entry->u.SetEa.mode;
+    }
+    RxFreePool(entry);
+out:
+#ifdef ENABLE_TIMINGS
+    t2 = KeQueryPerformanceCounter(NULL);
+    InterlockedIncrement(&setexattr.tops);
+    InterlockedAdd64(&setexattr.ticks, t2.QuadPart - t1.QuadPart);
+#ifdef ENABLE_INDV_TIMINGS
+    DbgP("nfs41_SetEaInformation delta = %d op=%d sum=%d\n",
+        t2.QuadPart - t1.QuadPart, setexattr.tops, setexattr.ticks);
+#endif
+#endif
+#ifdef DEBUG_EA_SET
+    DbgEx();
+#endif
+    return status;
+}
+
+NTSTATUS check_nfs41_queryea_args(
+    IN PRX_CONTEXT RxContext)
+{
+    NTSTATUS status;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(RxContext->pRelevantSrvOpen->pVNetRoot);
+    __notnull PFILE_FS_ATTRIBUTE_INFORMATION FsAttrs =
+        &pVNetRootContext->FsAttrs;
+    PFILE_GET_EA_INFORMATION ea = (PFILE_GET_EA_INFORMATION)
+            RxContext->CurrentIrpSp->Parameters.QueryEa.EaList;
+
+    status = check_nfs41_dirquery_args(RxContext);
+    if (status) goto out;
+
+    if (!(FsAttrs->FileSystemAttributes & FILE_SUPPORTS_EXTENDED_ATTRIBUTES)) {
+        if (ea == NULL) {
+            status = STATUS_EAS_NOT_SUPPORTED;
+            goto out;
+        }
+        /* ignore cygwin EAs when checking support */
+        if (!AnsiStrEq(&NfsV3Attributes, ea->EaName, ea->EaNameLength) &&
+            !AnsiStrEq(&NfsActOnLink, ea->EaName, ea->EaNameLength) &&
+            !AnsiStrEq(&NfsSymlinkTargetName, ea->EaName, ea->EaNameLength)) {
+            status = STATUS_EAS_NOT_SUPPORTED;
+            goto out;
+        }
+    }
+    if ((RxContext->pRelevantSrvOpen->DesiredAccess & FILE_READ_EA) == 0) {
+        status = STATUS_ACCESS_DENIED;
+        goto out;
+    }
+out:
+    return status;
+}
+
+static NTSTATUS QueryCygwinSymlink(
+    IN OUT PRX_CONTEXT RxContext,
+    IN PFILE_GET_EA_INFORMATION query,
+    OUT PFILE_FULL_EA_INFORMATION info)
+{
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION VNetRootContext =
+            NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION NetRootContext =
+            NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+    __notnull PNFS41_FOBX Fobx = NFS41GetFobxExtension(RxContext->pFobx);
+    nfs41_updowncall_entry *entry;
+    UNICODE_STRING TargetName;
+    const USHORT HeaderLen = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) +
+        query->EaNameLength + 1;
+    NTSTATUS status;
+
+    if (RxContext->Info.LengthRemaining < HeaderLen) {
+        status = STATUS_BUFFER_TOO_SMALL;
+        RxContext->InformationToReturn = HeaderLen;
+        goto out;
+    }
+
+    TargetName.Buffer = (PWCH)(info->EaName + query->EaNameLength + 1);
+    TargetName.MaximumLength = (USHORT)min(RxContext->Info.LengthRemaining -
+        HeaderLen, 0xFFFF);
+
+    status = nfs41_UpcallCreate(NFS41_SYMLINK, &Fobx->sec_ctx,
+        VNetRootContext->session, Fobx->nfs41_open_state,
+        NetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
+    if (status) goto out;
+
+    entry->u.Symlink.target = &TargetName;
+    entry->u.Symlink.set = FALSE;
+
+    status = nfs41_UpcallWaitForReply(entry, VNetRootContext->timeout);
+    if (status) goto out;
+
+    status = map_setea_error(entry->status);
+    if (status == STATUS_SUCCESS) {
+        info->NextEntryOffset = 0;
+        info->Flags = 0;
+        info->EaNameLength = query->EaNameLength;
+        info->EaValueLength = TargetName.Length - sizeof(UNICODE_NULL);
+        TargetName.Buffer[TargetName.Length/sizeof(WCHAR)] = UNICODE_NULL;
+        RtlCopyMemory(info->EaName, query->EaName, query->EaNameLength);
+        RxContext->Info.LengthRemaining = HeaderLen + info->EaValueLength;
+    } else if (status == STATUS_BUFFER_TOO_SMALL) {
+        RxContext->InformationToReturn = HeaderLen +
+            entry->u.Symlink.target->Length;
+    }
+    RxFreePool(entry);
+out:
+    return status;
+}
+
+static NTSTATUS QueryCygwinEA(
+    IN OUT PRX_CONTEXT RxContext,
+    IN PFILE_GET_EA_INFORMATION query,
+    OUT PFILE_FULL_EA_INFORMATION info)
+{
+    NTSTATUS status = STATUS_NONEXISTENT_EA_ENTRY;
+
+    if (query == NULL)
+        goto out;
+
+    if (AnsiStrEq(&NfsSymlinkTargetName, query->EaName, query->EaNameLength)) {
+        __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
+        if (nfs41_fcb->BasicInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+            status = QueryCygwinSymlink(RxContext, query, info);
+            goto out;
+        } else {
+            const LONG LengthRequired = sizeof(FILE_FULL_EA_INFORMATION) +
+                NfsSymlinkTargetName.Length - sizeof(CHAR);
+            if (LengthRequired > RxContext->Info.LengthRemaining) {
+                status = STATUS_BUFFER_TOO_SMALL;
+                RxContext->InformationToReturn = LengthRequired;
+                goto out;
+            }
+            info->NextEntryOffset = 0;
+            info->Flags = 0;
+            info->EaValueLength = 0;
+            info->EaNameLength = (UCHAR)NfsActOnLink.Length;
+            RtlCopyMemory(info->EaName, NfsSymlinkTargetName.Buffer,
+                NfsSymlinkTargetName.Length);
+            RxContext->Info.LengthRemaining = LengthRequired;
+            status = STATUS_SUCCESS;
+            goto out;
+        }
+    }
+
+    if (AnsiStrEq(&NfsV3Attributes, query->EaName, query->EaNameLength)) {
+        nfs3_attrs attrs;
+
+        const LONG LengthRequired = sizeof(FILE_FULL_EA_INFORMATION) +
+            NfsV3Attributes.Length + sizeof(nfs3_attrs) - sizeof(CHAR);
+        if (LengthRequired > RxContext->Info.LengthRemaining) {
+            status = STATUS_BUFFER_TOO_SMALL;
+            RxContext->InformationToReturn = LengthRequired;
+            goto out;
+        }
+
+        create_nfs3_attrs(&attrs, NFS41GetFcbExtension(RxContext->pFcb));
+#ifdef DEBUG_EA_QUERY
+        print_nfs3_attrs(&attrs);
+#endif
+
+        info->NextEntryOffset = 0;
+        info->Flags = 0;
+        info->EaNameLength = (UCHAR)NfsV3Attributes.Length;
+        info->EaValueLength = sizeof(nfs3_attrs);
+        RtlCopyMemory(info->EaName, NfsV3Attributes.Buffer,
+            NfsV3Attributes.Length);
+        RtlCopyMemory(info->EaName + info->EaNameLength + 1, &attrs,
+            sizeof(nfs3_attrs));
+        RxContext->Info.LengthRemaining = LengthRequired;
+        status = STATUS_SUCCESS;
+        goto out;
+    }
+
+    if (AnsiStrEq(&NfsActOnLink, query->EaName, query->EaNameLength)) {
+
+        const LONG LengthRequired = sizeof(FILE_FULL_EA_INFORMATION) +
+            query->EaNameLength - sizeof(CHAR);
+        if (LengthRequired > RxContext->Info.LengthRemaining) {
+            status = STATUS_BUFFER_TOO_SMALL;
+            RxContext->InformationToReturn = LengthRequired;
+            goto out;
+        }
+
+        info->NextEntryOffset = 0;
+        info->Flags = 0;
+        info->EaNameLength = query->EaNameLength;
+        info->EaValueLength = 0;
+        RtlCopyMemory(info->EaName, query->EaName, query->EaNameLength);
+        RxContext->Info.LengthRemaining = LengthRequired;
+        status = STATUS_SUCCESS;
+        goto out;
+    }
+out:
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_QueryEaInformation(
+#else
+NTSTATUS nfs41_QueryEaInformation(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_EAS_NOT_SUPPORTED;
+    nfs41_updowncall_entry *entry;
+    PFILE_GET_EA_INFORMATION query = (PFILE_GET_EA_INFORMATION)
+            RxContext->CurrentIrpSp->Parameters.QueryEa.EaList;
+    ULONG buflen = RxContext->CurrentIrpSp->Parameters.QueryEa.Length;
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+            NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+            NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
+#ifdef ENABLE_TIMINGS
+    LARGE_INTEGER t1, t2;
+    t1 = KeQueryPerformanceCounter(NULL);
+#endif
+
+#ifdef DEBUG_EA_QUERY
+    DbgEn();
+    print_debug_header(RxContext);
+    print_get_ea(1, query);
+#endif
+    status = check_nfs41_queryea_args(RxContext);
+    if (status) goto out;
+
+    /* handle queries for cygwin EAs */
+    status = QueryCygwinEA(RxContext, query,
+        (PFILE_FULL_EA_INFORMATION)RxContext->Info.Buffer);
+    if (status != STATUS_NONEXISTENT_EA_ENTRY)
+        goto out;
+
+    status = nfs41_UpcallCreate(NFS41_EA_GET, &nfs41_fobx->sec_ctx,
+        pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
+        pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
+    if (status) goto out;
+
+    entry->buf_len = buflen;
+    entry->buf = RxContext->Info.Buffer;
+    entry->u.QueryEa.EaList = query;
+    entry->u.QueryEa.EaListLength = query == NULL ? 0 :
+        RxContext->QueryEa.UserEaListLength;
+    entry->u.QueryEa.EaIndex = RxContext->QueryEa.IndexSpecified ?
+        RxContext->QueryEa.UserEaIndex : 0;
+    entry->u.QueryEa.RestartScan = RxContext->QueryEa.RestartScan;
+    entry->u.QueryEa.ReturnSingleEntry = RxContext->QueryEa.ReturnSingleEntry;
+
+    status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
+    if (status) goto out;
+
+    if (entry->status == STATUS_SUCCESS) {
+        switch (entry->u.QueryEa.Overflow) {
+        case ERROR_INSUFFICIENT_BUFFER:
+            status = STATUS_BUFFER_TOO_SMALL;
+            break;
+        case ERROR_BUFFER_OVERFLOW:
+            status = RxContext->IoStatusBlock.Status = STATUS_BUFFER_OVERFLOW;
+            break;
+        default:
+            RxContext->IoStatusBlock.Status = STATUS_SUCCESS;
+            break;
+        }
+        RxContext->InformationToReturn = entry->buf_len;
+#ifdef ENABLE_TIMINGS
+        InterlockedIncrement(&getexattr.sops);
+        InterlockedAdd64(&getexattr.size, entry->u.QueryEa.buf_len);
+#endif
+    } else {
+        status = map_setea_error(entry->status);
+    }
+    RxFreePool(entry);
+out:
+#ifdef ENABLE_TIMINGS
+    t2 = KeQueryPerformanceCounter(NULL);
+    InterlockedIncrement(&getexattr.tops);
+    InterlockedAdd64(&getexattr.ticks, t2.QuadPart - t1.QuadPart);
+#ifdef ENABLE_INDV_TIMINGS
+    DbgP("nfs41_QueryEaInformation delta = %d op=%d sum=%d\n",
+        t2.QuadPart - t1.QuadPart, getexattr.tops, getexattr.ticks);
+#endif
+#endif
+#ifdef DEBUG_EA_QUERY
+    DbgEx();
+#endif
+    return status;
+}
+
+NTSTATUS map_query_acl_error(
+    DWORD error)
+{
+    switch (error) {
+    case NO_ERROR:                  return STATUS_SUCCESS;
+    case ERROR_NOT_SUPPORTED:       return STATUS_NOT_SUPPORTED;
+    case ERROR_ACCESS_DENIED:       return STATUS_ACCESS_DENIED;
+    case ERROR_FILE_NOT_FOUND:      return STATUS_OBJECT_NAME_NOT_FOUND;
+    case ERROR_INVALID_PARAMETER:   return STATUS_INVALID_PARAMETER;
+    default:
+        print_error("failed to map windows error %d to NTSTATUS; "
+            "defaulting to STATUS_INVALID_NETWORK_RESPONSE\n", error);
+    case ERROR_BAD_NET_RESP:        return STATUS_INVALID_NETWORK_RESPONSE;
+    }
+}
+
+NTSTATUS check_nfs41_getacl_args(
+    PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    SECURITY_INFORMATION info_class =
+        RxContext->CurrentIrpSp->Parameters.QuerySecurity.SecurityInformation;
+
+    /* we don't support sacls */
+    if (info_class == SACL_SECURITY_INFORMATION ||
+            info_class == LABEL_SECURITY_INFORMATION) {
+        status = STATUS_NOT_SUPPORTED;
+        goto out;
+    }
+    if (RxContext->CurrentIrp->UserBuffer == NULL &&
+            RxContext->CurrentIrpSp->Parameters.QuerySecurity.Length)
+        status = STATUS_INVALID_USER_BUFFER;
+out:
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_QuerySecurityInformation(
+#else
+NTSTATUS nfs41_QuerySecurityInformation(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_NOT_SUPPORTED;
+    nfs41_updowncall_entry *entry;
+    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+    SECURITY_INFORMATION info_class =
+        RxContext->CurrentIrpSp->Parameters.QuerySecurity.SecurityInformation;
+#ifdef ENABLE_TIMINGS
+    LARGE_INTEGER t1, t2;
+    t1 = KeQueryPerformanceCounter(NULL);
+#endif
+
+#ifdef DEBUG_ACL_QUERY
+    DbgEn();
+    print_debug_header(RxContext);
+    print_acl_args(info_class);
+#endif
+
+    status = check_nfs41_getacl_args(RxContext);
+    if (status) goto out;
+
+    if (nfs41_fobx->acl && nfs41_fobx->acl_len) {
+        LARGE_INTEGER current_time;
+        KeQuerySystemTime(&current_time);
+#ifdef DEBUG_ACL_QUERY
+        DbgP("CurrentTime %lx Saved Acl time %lx\n",
+            current_time.QuadPart, nfs41_fobx->time.QuadPart);
+#endif
+        if (current_time.QuadPart - nfs41_fobx->time.QuadPart <= 20*1000) {
+            PSECURITY_DESCRIPTOR sec_desc = (PSECURITY_DESCRIPTOR)
+                RxContext->CurrentIrp->UserBuffer;
+            RtlCopyMemory(sec_desc, nfs41_fobx->acl, nfs41_fobx->acl_len);
+            RxContext->IoStatusBlock.Information =
+                RxContext->InformationToReturn = nfs41_fobx->acl_len;
+            RxContext->IoStatusBlock.Status = status = STATUS_SUCCESS;
+#ifdef ENABLE_TIMINGS
+            InterlockedIncrement(&getacl.sops);
+            InterlockedAdd64(&getacl.size, nfs41_fobx->acl_len);
+#endif
+        } else status = 1;
+        RxFreePool(nfs41_fobx->acl);
+        nfs41_fobx->acl = NULL;
+        nfs41_fobx->acl_len = 0;
+        if (!status)
+            goto out;
+    }
+
+    status = nfs41_UpcallCreate(NFS41_ACL_QUERY, &nfs41_fobx->sec_ctx,
+        pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
+        pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
+    if (status) goto out;
+
+    entry->u.Acl.query = info_class;
+    /* we can't provide RxContext->CurrentIrp->UserBuffer to the upcall thread
+     * because it becomes an invalid pointer with that execution context
+     */
+    entry->buf_len = RxContext->CurrentIrpSp->Parameters.QuerySecurity.Length;
+
+    status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
+    if (status) goto out;
+
+    if (entry->status == STATUS_BUFFER_TOO_SMALL) {
+#ifdef DEBUG_ACL_QUERY
+        DbgP("nfs41_QuerySecurityInformation: provided buffer size=%d but we "
+             "need %lu\n",
+             RxContext->CurrentIrpSp->Parameters.QuerySecurity.Length,
+             entry->buf_len);
+#endif
+        status = STATUS_BUFFER_OVERFLOW;
+        RxContext->InformationToReturn = entry->buf_len;
+
+        /* Save ACL buffer */
+        nfs41_fobx->acl = entry->buf;
+        nfs41_fobx->acl_len = entry->buf_len;
+        KeQuerySystemTime(&nfs41_fobx->time);
+    } else if (entry->status == STATUS_SUCCESS) {
+        PSECURITY_DESCRIPTOR sec_desc = (PSECURITY_DESCRIPTOR)
+            RxContext->CurrentIrp->UserBuffer;
+        RtlCopyMemory(sec_desc, entry->buf, entry->buf_len);
+#ifdef ENABLE_TIMINGS
+        InterlockedIncrement(&getacl.sops);
+        InterlockedAdd64(&getacl.size, entry->u.Acl.buf_len);
+#endif
+        RxFreePool(entry->buf);
+        nfs41_fobx->acl = NULL;
+        nfs41_fobx->acl_len = 0;
+        RxContext->IoStatusBlock.Information = RxContext->InformationToReturn =
+            entry->buf_len;
+        RxContext->IoStatusBlock.Status = status = STATUS_SUCCESS;
+    } else {
+        status = map_query_acl_error(entry->status);
+    }
+    RxFreePool(entry);
+out:
+#ifdef ENABLE_TIMINGS
+    t2 = KeQueryPerformanceCounter(NULL);
+    /* only count getacl that we made an upcall for */
+    if (status == STATUS_BUFFER_OVERFLOW) {
+        InterlockedIncrement(&getacl.tops);
+        InterlockedAdd64(&getacl.ticks, t2.QuadPart - t1.QuadPart);
+    }
+#ifdef ENABLE_INDV_TIMINGS
+    DbgP("nfs41_QuerySecurityInformation: delta = %d op=%d sum=%d\n",
+        t2.QuadPart - t1.QuadPart, getacl.tops, getacl.ticks);
+#endif
+#endif
+#ifdef DEBUG_ACL_QUERY
+    DbgEx();
+#endif
+    return status;
+}
+
+NTSTATUS check_nfs41_setacl_args(
+    PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(RxContext->pRelevantSrvOpen->pVNetRoot);
+    SECURITY_INFORMATION info_class =
+        RxContext->CurrentIrpSp->Parameters.SetSecurity.SecurityInformation;
+
+    if (pVNetRootContext->read_only) {
+        print_error("check_nfs41_setacl_args: Read-only mount\n");
+        status = STATUS_ACCESS_DENIED;
+        goto out;
+    }
+    /* we don't support sacls */
+    if (info_class == SACL_SECURITY_INFORMATION  ||
+            info_class == LABEL_SECURITY_INFORMATION) {
+        status = STATUS_NOT_SUPPORTED;
+        goto out;
+    }
+out:
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_SetSecurityInformation(
+#else
+NTSTATUS nfs41_SetSecurityInformation(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_NOT_SUPPORTED;
+    nfs41_updowncall_entry *entry;
+    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+    __notnull PSECURITY_DESCRIPTOR sec_desc =
+        RxContext->CurrentIrpSp->Parameters.SetSecurity.SecurityDescriptor;
+    __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
+    SECURITY_INFORMATION info_class =
+        RxContext->CurrentIrpSp->Parameters.SetSecurity.SecurityInformation;
+#ifdef ENABLE_TIMINGS
+    LARGE_INTEGER t1, t2;
+    t1 = KeQueryPerformanceCounter(NULL);
+#endif
+
+#ifdef DEBUG_ACL_SET
+    DbgEn();
+    print_debug_header(RxContext);
+    print_acl_args(info_class);
+#endif
+
+    status = check_nfs41_setacl_args(RxContext);
+    if (status) goto out;
+
+    /* check that ACL is present */
+    if (info_class & DACL_SECURITY_INFORMATION) {
+        PACL acl;
+        BOOLEAN present, dacl_default;
+        status = RtlGetDaclSecurityDescriptor(sec_desc, &present, &acl,
+                    &dacl_default);
+        if (status) {
+            DbgP("RtlGetDaclSecurityDescriptor failed %x\n", status);
+            goto out;
+        }
+        if (present == FALSE) {
+            DbgP("NO ACL present\n");
+            goto out;
+        }
+    }
+
+    status = nfs41_UpcallCreate(NFS41_ACL_SET, &nfs41_fobx->sec_ctx,
+        pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
+        pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
+    if (status) goto out;
+
+    entry->u.Acl.query = info_class;
+    entry->buf = sec_desc;
+    entry->buf_len = RtlLengthSecurityDescriptor(sec_desc);
+#ifdef ENABLE_TIMINGS
+    InterlockedIncrement(&setacl.sops);
+    InterlockedAdd64(&setacl.size, entry->u.Acl.buf_len);
+#endif
+
+    status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
+    if (status) goto out;
+
+    status = map_query_acl_error(entry->status);
+    if (!status) {
+        if (!nfs41_fobx->deleg_type && entry->ChangeTime &&
+                (SrvOpen->DesiredAccess &
+                (FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA)))
+            nfs41_update_fcb_list(RxContext->pFcb, entry->ChangeTime);
+        nfs41_fcb->changeattr = entry->ChangeTime;
+    }
+    RxFreePool(entry);
+out:
+#ifdef ENABLE_TIMINGS
+    t2 = KeQueryPerformanceCounter(NULL);
+    InterlockedIncrement(&setacl.tops);
+    InterlockedAdd64(&setacl.ticks, t2.QuadPart - t1.QuadPart);
+#ifdef ENABLE_INDV_TIMINGS
+    DbgP("nfs41_SetSecurityInformation delta = %d op=%d sum=%d\n",
+        t2.QuadPart - t1.QuadPart, setacl.tops, setacl.ticks);
+#endif
+#endif
+#ifdef DEBUG_ACL_SET
+    DbgEx();
+#endif
+    return status;
+}
+
+NTSTATUS map_queryfile_error(
+    DWORD error)
+{
+    switch (error) {
+    case ERROR_ACCESS_DENIED:       return STATUS_ACCESS_DENIED;
+    case ERROR_NETNAME_DELETED:     return STATUS_NETWORK_NAME_DELETED;
+    case ERROR_INVALID_PARAMETER:   return STATUS_INVALID_PARAMETER;
+    default:
+        print_error("failed to map windows error %d to NTSTATUS; "
+            "defaulting to STATUS_INVALID_NETWORK_RESPONSE\n", error);
+    case ERROR_BAD_NET_RESP:        return STATUS_INVALID_NETWORK_RESPONSE;
+    }
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_QueryFileInformation(
+#else
+NTSTATUS nfs41_QueryFileInformation(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_OBJECT_NAME_NOT_FOUND;
+    FILE_INFORMATION_CLASS InfoClass = RxContext->Info.FileInformationClass;
+    nfs41_updowncall_entry *entry;
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+    __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
+    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
+#ifdef ENABLE_TIMINGS
+    LARGE_INTEGER t1, t2;
+    t1 = KeQueryPerformanceCounter(NULL);
+#endif
+
+#ifdef DEBUG_FILE_QUERY
+    DbgEn();
+    print_debug_filedirquery_header(RxContext);
+#endif
+
+    status = check_nfs41_dirquery_args(RxContext);
+    if (status) goto out;
+
+    switch (InfoClass) {
+    case FileEaInformation:
+    {
+        PFILE_EA_INFORMATION info =
+            (PFILE_EA_INFORMATION)RxContext->Info.Buffer;
+        info->EaSize = 0;
+        RxContext->Info.LengthRemaining -= sizeof(FILE_EA_INFORMATION);
+        status = STATUS_SUCCESS;
+        goto out;
+    }
+    case FileBasicInformation:
+    case FileStandardInformation:
+    case FileInternalInformation:
+    case FileAttributeTagInformation:
+    case FileNetworkOpenInformation:
+        break;
+    default:
+        print_error("nfs41_QueryFileInformation: unhandled class %d\n", InfoClass);
+        status = STATUS_NOT_SUPPORTED;
+        goto out;
+    }
+
+    status = nfs41_UpcallCreate(NFS41_FILE_QUERY, &nfs41_fobx->sec_ctx,
+        pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
+        pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
+    if (status) goto out;
+
+    entry->u.QueryFile.InfoClass = InfoClass;
+    entry->buf = RxContext->Info.Buffer;
+    entry->buf_len = RxContext->Info.LengthRemaining;
+
+    status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
+    if (status) goto out;
+
+    if (entry->status == STATUS_BUFFER_TOO_SMALL) {
+        RxContext->InformationToReturn = entry->buf_len;
+        status = STATUS_BUFFER_TOO_SMALL;
+    } else if (entry->status == STATUS_SUCCESS) {
+        BOOLEAN DeletePending = FALSE;
+#ifdef ENABLE_TIMINGS
+        InterlockedIncrement(&getattr.sops);
+        InterlockedAdd64(&getattr.size, entry->u.QueryFile.buf_len);
+#endif
+        RxContext->Info.LengthRemaining -= entry->buf_len;
+        status = STATUS_SUCCESS;
+
+        switch (InfoClass) {
+        case FileBasicInformation:
+            RtlCopyMemory(&nfs41_fcb->BasicInfo, RxContext->Info.Buffer,
+                sizeof(nfs41_fcb->BasicInfo));
+#ifdef DEBUG_FILE_QUERY
+            print_basic_info(1, &nfs41_fcb->BasicInfo);
+#endif
+            break;
+        case FileStandardInformation:
+            /* this a fix for RDBSS behaviour when it first calls ExtendForCache,
+             * then it sends a file query irp for standard attributes and
+             * expects to receive EndOfFile of value set by the ExtendForCache.
+             * It seems to cache the filesize based on that instead of sending
+             * a file size query for after doing the write.
+             */
+        {
+            PFILE_STANDARD_INFORMATION std_info;
+            std_info = (PFILE_STANDARD_INFORMATION)RxContext->Info.Buffer;
+            if (nfs41_fcb->StandardInfo.AllocationSize.QuadPart >
+                    std_info->AllocationSize.QuadPart) {
+#ifdef DEBUG_FILE_QUERY
+                DbgP("Old AllocationSize is bigger: saving %x\n",
+                    nfs41_fcb->StandardInfo.AllocationSize.QuadPart);
+#endif
+                std_info->AllocationSize.QuadPart =
+                    nfs41_fcb->StandardInfo.AllocationSize.QuadPart;
+            }
+            if (nfs41_fcb->StandardInfo.EndOfFile.QuadPart >
+                    std_info->EndOfFile.QuadPart) {
+#ifdef DEBUG_FILE_QUERY
+                DbgP("Old EndOfFile is bigger: saving %x\n",
+                    nfs41_fcb->StandardInfo.EndOfFile);
+#endif
+                std_info->EndOfFile.QuadPart =
+                    nfs41_fcb->StandardInfo.EndOfFile.QuadPart;
+            }
+            std_info->DeletePending = nfs41_fcb->DeletePending;
+        }
+            if (nfs41_fcb->StandardInfo.DeletePending)
+                DeletePending = TRUE;
+            RtlCopyMemory(&nfs41_fcb->StandardInfo, RxContext->Info.Buffer,
+                sizeof(nfs41_fcb->StandardInfo));
+            nfs41_fcb->StandardInfo.DeletePending = DeletePending;
+#ifdef DEBUG_FILE_QUERY
+            print_std_info(1, &nfs41_fcb->StandardInfo);
+#endif
+            break;
+        }
+    } else {
+        status = map_queryfile_error(entry->status);
+    }
+    RxFreePool(entry);
+out:
+#ifdef ENABLE_TIMINGS
+    t2 = KeQueryPerformanceCounter(NULL);
+    InterlockedIncrement(&getattr.tops);
+    InterlockedAdd64(&getattr.ticks, t2.QuadPart - t1.QuadPart);
+#ifdef ENABLE_INDV_TIMINGS
+    DbgP("nfs41_QueryFileInformation delta = %d op=%d sum=%d\n",
+        t2.QuadPart - t1.QuadPart, getattr.tops, getattr.ticks);
+#endif
+#endif
+#ifdef DEBUG_FILE_QUERY
+    DbgEx();
+#endif
+    return status;
+}
+
+NTSTATUS map_setfile_error(
+    DWORD error)
+{
+    switch (error) {
+    case NO_ERROR:                      return STATUS_SUCCESS;
+    case ERROR_NOT_EMPTY:               return STATUS_DIRECTORY_NOT_EMPTY;
+    case ERROR_FILE_EXISTS:             return STATUS_OBJECT_NAME_COLLISION;
+    case ERROR_FILE_NOT_FOUND:          return STATUS_OBJECT_NAME_NOT_FOUND;
+    case ERROR_PATH_NOT_FOUND:          return STATUS_OBJECT_PATH_NOT_FOUND;
+    case ERROR_ACCESS_DENIED:           return STATUS_ACCESS_DENIED;
+    case ERROR_FILE_INVALID:            return STATUS_FILE_INVALID;
+    case ERROR_NOT_SAME_DEVICE:         return STATUS_NOT_SAME_DEVICE;
+    case ERROR_NOT_SUPPORTED:           return STATUS_NOT_IMPLEMENTED;
+    case ERROR_NETWORK_ACCESS_DENIED:   return STATUS_NETWORK_ACCESS_DENIED;
+    case ERROR_NETNAME_DELETED:         return STATUS_NETWORK_NAME_DELETED;
+    case ERROR_BUFFER_OVERFLOW:         return STATUS_INSUFFICIENT_RESOURCES;
+    default:
+        print_error("failed to map windows error %d to NTSTATUS; "
+            "defaulting to STATUS_INVALID_PARAMETER\n", error);
+    case ERROR_INVALID_PARAMETER:       return STATUS_INVALID_PARAMETER;
+    }
+}
+
+NTSTATUS check_nfs41_setattr_args(
+    IN PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    FILE_INFORMATION_CLASS InfoClass = RxContext->Info.FileInformationClass;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(RxContext->pRelevantSrvOpen->pVNetRoot);
+
+    if (pVNetRootContext->read_only) {
+        print_error("check_nfs41_setattr_args: Read-only mount\n");
+        status = STATUS_ACCESS_DENIED;
+        goto out;
+    }
+
+    /* http://msdn.microsoft.com/en-us/library/ff469355(v=PROT.10).aspx
+     * http://msdn.microsoft.com/en-us/library/ff469424(v=PROT.10).aspx
+     * If Open.GrantedAccess does not contain FILE_WRITE_DATA, the operation
+     * MUST be failed with STATUS_ACCESS_DENIED.
+     */
+    if (InfoClass == FileAllocationInformation ||
+            InfoClass == FileEndOfFileInformation) {
+        if (!(RxContext->pRelevantSrvOpen->DesiredAccess & FILE_WRITE_DATA)) {
+            status = STATUS_ACCESS_DENIED;
+            goto out;
+        }
+    }
+    status = check_nfs41_dirquery_args(RxContext);
+    if (status) goto out;
+
+    switch (InfoClass) {
+    case FileRenameInformation:
+    {
+        PFILE_RENAME_INFORMATION rinfo =
+            (PFILE_RENAME_INFORMATION)RxContext->Info.Buffer;
+        UNICODE_STRING dst = { (USHORT)rinfo->FileNameLength,
+            (USHORT)rinfo->FileNameLength, rinfo->FileName };
+#ifdef DEBUG_FILE_SET
+        DbgP("Attempting to rename to '%wZ'\n", &dst);
+#endif
+        if (isFilenameTooLong(&dst, pVNetRootContext)) {
+            status = STATUS_OBJECT_NAME_INVALID;
+            goto out;
+        }
+        if (rinfo->RootDirectory) {
+            status = STATUS_INVALID_PARAMETER;
+            goto out;
+        }
+        break;
+    }
+    case FileLinkInformation:
+    {
+        PFILE_LINK_INFORMATION linfo =
+            (PFILE_LINK_INFORMATION)RxContext->Info.Buffer;
+        UNICODE_STRING dst = { (USHORT)linfo->FileNameLength,
+            (USHORT)linfo->FileNameLength, linfo->FileName };
+#ifdef DEBUG_FILE_SET
+        DbgP("Attempting to add link as '%wZ'\n", &dst);
+#endif
+        if (isFilenameTooLong(&dst, pVNetRootContext)) {
+            status = STATUS_OBJECT_NAME_INVALID;
+            goto out;
+        }
+        if (linfo->RootDirectory) {
+            status = STATUS_INVALID_PARAMETER;
+            goto out;
+        }
+        break;
+    }
+    case FileDispositionInformation:
+    {
+        PFILE_DISPOSITION_INFORMATION dinfo =
+            (PFILE_DISPOSITION_INFORMATION)RxContext->Info.Buffer;
+        __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
+        if (dinfo->DeleteFile && nfs41_fcb->DeletePending) {
+            status = STATUS_DELETE_PENDING;
+            goto out;
+        }
+        break;
+    }
+    case FileBasicInformation:
+    case FileAllocationInformation:
+    case FileEndOfFileInformation:
+        break;
+    default:
+        print_error("nfs41_SetFileInformation: unhandled class %d\n", InfoClass);
+        status = STATUS_NOT_SUPPORTED;
+    }
+
+out:
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_SetFileInformation(
+#else
+NTSTATUS nfs41_SetFileInformation(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_INVALID_PARAMETER;
+    nfs41_updowncall_entry *entry;
+    FILE_INFORMATION_CLASS InfoClass = RxContext->Info.FileInformationClass;
+    FILE_RENAME_INFORMATION rinfo;
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+    __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
+    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
+#ifdef ENABLE_TIMINGS
+    LARGE_INTEGER t1, t2;
+    t1 = KeQueryPerformanceCounter(NULL);
+#endif
+
+#ifdef DEBUG_FILE_SET
+    DbgEn();
+    print_debug_filedirquery_header(RxContext);
+#endif
+
+    status = check_nfs41_setattr_args(RxContext);
+    if (status) goto out;
+
+    switch (InfoClass) {
+    case FileDispositionInformation:
+        {
+            PFILE_DISPOSITION_INFORMATION dinfo =
+                (PFILE_DISPOSITION_INFORMATION)RxContext->Info.Buffer;
+            if (dinfo->DeleteFile) {
+                nfs41_fcb->DeletePending = TRUE;
+                // we can delete directories right away
+                if (nfs41_fcb->StandardInfo.Directory)
+                    break;
+                nfs41_fcb->StandardInfo.DeletePending = TRUE;
+                if (RxContext->pFcb->OpenCount > 1) {
+                    rinfo.ReplaceIfExists = 0;
+                    rinfo.RootDirectory = INVALID_HANDLE_VALUE;
+                    rinfo.FileNameLength = 0;
+                    rinfo.FileName[0] = L'\0';
+                    InfoClass = FileRenameInformation;
+                    nfs41_fcb->Renamed = TRUE;
+                    break;
+                }
+            } else {
+                /* section 4.3.3 of [FSBO]
+                 * "file system behavior in the microsoft windows environment"
+                 */
+                if (nfs41_fcb->DeletePending) {
+                    nfs41_fcb->DeletePending = 0;
+                    nfs41_fcb->StandardInfo.DeletePending = 0;
+                }
+            }
+            status = STATUS_SUCCESS;
+            goto out;
+        }
+    case FileEndOfFileInformation:
+        {
+            PFILE_END_OF_FILE_INFORMATION info =
+                (PFILE_END_OF_FILE_INFORMATION)RxContext->Info.Buffer;
+            nfs41_fcb->StandardInfo.AllocationSize =
+                nfs41_fcb->StandardInfo.EndOfFile = info->EndOfFile;
+            break;
+        }
+    case FileRenameInformation:
+        {
+            /* noop if filename and destination are the same */
+            PFILE_RENAME_INFORMATION prinfo =
+                (PFILE_RENAME_INFORMATION)RxContext->Info.Buffer;
+            const UNICODE_STRING dst = { (USHORT)prinfo->FileNameLength,
+                (USHORT)prinfo->FileNameLength, prinfo->FileName };
+            if (RtlCompareUnicodeString(&dst,
+                    SrvOpen->pAlreadyPrefixedName, FALSE) == 0) {
+                status = STATUS_SUCCESS;
+                goto out;
+            }
+        }
+    }
+
+    status = nfs41_UpcallCreate(NFS41_FILE_SET, &nfs41_fobx->sec_ctx,
+        pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
+        pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
+    if (status) goto out;
+
+    entry->u.SetFile.InfoClass = InfoClass;
+
+    /* original irp has infoclass for remove but we need to rename instead,
+     * thus we changed the local variable infoclass */
+    if (RxContext->Info.FileInformationClass == FileDispositionInformation &&
+            InfoClass == FileRenameInformation) {
+        entry->buf = &rinfo;
+        entry->buf_len = sizeof(rinfo);
+    } else {
+        entry->buf = RxContext->Info.Buffer;
+        entry->buf_len = RxContext->Info.Length;
+    }
+#ifdef ENABLE_TIMINGS
+    InterlockedIncrement(&setattr.sops);
+    InterlockedAdd64(&setattr.size, entry->u.SetFile.buf_len);
+#endif
+
+    status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
+    if (status) goto out;
+
+    status = map_setfile_error(entry->status);
+    if (!status) {
+        if (!nfs41_fobx->deleg_type && entry->ChangeTime &&
+                (SrvOpen->DesiredAccess &
+                (FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA)))
+            nfs41_update_fcb_list(RxContext->pFcb, entry->ChangeTime);
+        nfs41_fcb->changeattr = entry->ChangeTime;
+    }
+    RxFreePool(entry);
+out:
+#ifdef ENABLE_TIMINGS
+    t2 = KeQueryPerformanceCounter(NULL);
+    InterlockedIncrement(&setattr.tops);
+    InterlockedAdd64(&setattr.ticks, t2.QuadPart - t1.QuadPart);
+#ifdef ENABLE_INDV_TIMINGS
+    DbgP("nfs41_SetFileInformation delta = %d op=%d sum=%d\n",
+        t2.QuadPart - t1.QuadPart, setattr.tops, setattr.ticks);
+#endif
+#endif
+#ifdef DEBUG_FILE_SET
+    DbgEx();
+#endif
+    return status;
+}
+
+NTSTATUS nfs41_SetFileInformationAtCleanup(
+      IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status;
+    DbgEn();
+    status = nfs41_SetFileInformation(RxContext);
+    DbgEx();
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_IsValidDirectory (
+#else
+NTSTATUS nfs41_IsValidDirectory (
+#endif
+    IN OUT PRX_CONTEXT RxContext,
+    IN PUNICODE_STRING DirectoryName)
+{
+    return STATUS_SUCCESS;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_ComputeNewBufferingState(
+#else
+NTSTATUS nfs41_ComputeNewBufferingState(
+#endif
+    IN OUT PMRX_SRV_OPEN pSrvOpen,
+    IN PVOID pMRxContext,
+    OUT ULONG *pNewBufferingState)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    ULONG flag = PtrToUlong(pMRxContext), oldFlags = pSrvOpen->BufferingFlags;
+
+    switch(flag) {
+    case DISABLE_CACHING:
+        if (pSrvOpen->BufferingFlags &
+            (FCB_STATE_READBUFFERING_ENABLED | FCB_STATE_READCACHING_ENABLED))
+            pSrvOpen->BufferingFlags &=
+                ~(FCB_STATE_READBUFFERING_ENABLED |
+                  FCB_STATE_READCACHING_ENABLED);
+        if (pSrvOpen->BufferingFlags &
+            (FCB_STATE_WRITECACHING_ENABLED | FCB_STATE_WRITEBUFFERING_ENABLED))
+            pSrvOpen->BufferingFlags &=
+                ~(FCB_STATE_WRITECACHING_ENABLED |
+                  FCB_STATE_WRITEBUFFERING_ENABLED);
+        pSrvOpen->BufferingFlags |= FCB_STATE_DISABLE_LOCAL_BUFFERING;
+        break;
+    case ENABLE_READ_CACHING:
+        pSrvOpen->BufferingFlags |=
+            (FCB_STATE_READBUFFERING_ENABLED | FCB_STATE_READCACHING_ENABLED);
+        break;
+    case ENABLE_WRITE_CACHING:
+        pSrvOpen->BufferingFlags |=
+            (FCB_STATE_WRITECACHING_ENABLED | FCB_STATE_WRITEBUFFERING_ENABLED);
+        break;
+    case ENABLE_READWRITE_CACHING:
+        pSrvOpen->BufferingFlags =
+            (FCB_STATE_READBUFFERING_ENABLED | FCB_STATE_READCACHING_ENABLED |
+            FCB_STATE_WRITECACHING_ENABLED | FCB_STATE_WRITEBUFFERING_ENABLED);
+    }
+#ifdef DEBUG_TIME_BASED_COHERENCY
+    DbgP("nfs41_ComputeNewBufferingState: %wZ pSrvOpen %p Old %08x New %08x\n",
+         pSrvOpen->pAlreadyPrefixedName, pSrvOpen, oldFlags,
+         pSrvOpen->BufferingFlags);
+    *pNewBufferingState = pSrvOpen->BufferingFlags;
+#endif
+    return status;
+}
+
+void print_readwrite_args(
+    PRX_CONTEXT RxContext)
+{
+    PLOWIO_CONTEXT LowIoContext  = &RxContext->LowIoContext;
+
+    print_debug_header(RxContext);
+    DbgP("Bytecount 0x%x Byteoffset 0x%x Buffer %p\n",
+        LowIoContext->ParamsFor.ReadWrite.ByteCount,
+        LowIoContext->ParamsFor.ReadWrite.ByteOffset,
+        LowIoContext->ParamsFor.ReadWrite.Buffer);
+}
+
+void enable_caching(
+    PMRX_SRV_OPEN SrvOpen,
+    PNFS41_FOBX nfs41_fobx,
+    ULONGLONG ChangeTime,
+    HANDLE session)
+{
+    ULONG flag = 0;
+    PLIST_ENTRY pEntry;
+    nfs41_fcb_list_entry *cur;
+    BOOLEAN found = FALSE;
+
+    if (SrvOpen->DesiredAccess & FILE_READ_DATA)
+        flag = ENABLE_READ_CACHING;
+    if ((SrvOpen->DesiredAccess & FILE_WRITE_DATA) &&
+            !nfs41_fobx->write_thru)
+        flag = ENABLE_WRITE_CACHING;
+    if ((SrvOpen->DesiredAccess & FILE_READ_DATA) &&
+            (SrvOpen->DesiredAccess & FILE_WRITE_DATA) &&
+            !nfs41_fobx->write_thru)
+        flag = ENABLE_READWRITE_CACHING;
+
+#if defined(DEBUG_TIME_BASED_COHERENCY) || \
+        defined(DEBUG_WRITE) || defined(DEBUG_READ)
+    print_caching_level(1, flag, SrvOpen->pAlreadyPrefixedName);
+#endif
+
+    if (!flag)
+        return;
+
+    RxChangeBufferingState((PSRV_OPEN)SrvOpen, ULongToPtr(flag), 1);
+
+    ExAcquireFastMutex(&fcblistLock);
+    pEntry = openlist.head.Flink;
+    while (!IsListEmpty(&openlist.head)) {
+        cur = (nfs41_fcb_list_entry *)CONTAINING_RECORD(pEntry,
+                nfs41_fcb_list_entry, next);
+        if (cur->fcb == SrvOpen->pFcb) {
+#ifdef DEBUG_TIME_BASED_COHERENCY
+            DbgP("enable_caching: Looked&Found match for fcb=%p %wZ\n",
+                SrvOpen->pFcb, SrvOpen->pAlreadyPrefixedName);
+#endif
+            cur->skip = FALSE;
+            found = TRUE;
+            break;
+        }
+        if (pEntry->Flink == &openlist.head) {
+#ifdef DEBUG_TIME_BASED_COHERENCY
+            DbgP("enable_caching: reached EOL looking for fcb=%p %wZ\n",
+                SrvOpen->pFcb, SrvOpen->pAlreadyPrefixedName);
+#endif
+            break;
+        }
+        pEntry = pEntry->Flink;
+    }
+    if (!found && nfs41_fobx->deleg_type) {
+        nfs41_fcb_list_entry *oentry;
+#ifdef DEBUG_TIME_BASED_COHERENCY
+        DbgP("enable_caching: delegation recalled: srv_open=%p\n", SrvOpen);
+#endif
+        oentry = RxAllocatePoolWithTag(NonPagedPool,
+            sizeof(nfs41_fcb_list_entry), NFS41_MM_POOLTAG_OPEN);
+        if (oentry == NULL) return;
+        oentry->fcb = SrvOpen->pFcb;
+        oentry->session = session;
+        oentry->nfs41_fobx = nfs41_fobx;
+        oentry->ChangeTime = ChangeTime;
+        oentry->skip = FALSE;
+        InsertTailList(&openlist.head, &oentry->next);
+        nfs41_fobx->deleg_type = 0;
+    }
+    ExReleaseFastMutex(&fcblistLock);
+}
+
+NTSTATUS map_readwrite_errors(
+    DWORD status)
+{
+    switch (status) {
+    case ERROR_ACCESS_DENIED:           return STATUS_ACCESS_DENIED;
+    case ERROR_HANDLE_EOF:              return STATUS_END_OF_FILE;
+    case ERROR_FILE_INVALID:            return STATUS_FILE_INVALID;
+    case ERROR_INVALID_PARAMETER:       return STATUS_INVALID_PARAMETER;
+    case ERROR_LOCK_VIOLATION:          return STATUS_FILE_LOCK_CONFLICT;
+    case ERROR_NETWORK_ACCESS_DENIED:   return STATUS_NETWORK_ACCESS_DENIED;
+    case ERROR_NETNAME_DELETED:         return STATUS_NETWORK_NAME_DELETED;
+    default:
+        print_error("failed to map windows error %d to NTSTATUS; "
+            "defaulting to STATUS_NET_WRITE_FAULT\n", status);
+    case ERROR_NET_WRITE_FAULT:         return STATUS_NET_WRITE_FAULT;
+    }
+}
+
+NTSTATUS check_nfs41_read_args(
+    IN PRX_CONTEXT RxContext)
+{
+    if (!RxContext->LowIoContext.ParamsFor.ReadWrite.Buffer)
+        return STATUS_INVALID_USER_BUFFER;
+    return STATUS_SUCCESS;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_Read(
+#else
+NTSTATUS nfs41_Read(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
+    nfs41_updowncall_entry *entry;
+    BOOLEAN async = FALSE;
+    PLOWIO_CONTEXT LowIoContext  = &RxContext->LowIoContext;
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+    __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
+    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
+    DWORD io_delay;
+#ifdef ENABLE_TIMINGS
+    LARGE_INTEGER t1, t2;
+    t1 = KeQueryPerformanceCounter(NULL);
+#endif
+
+#ifdef DEBUG_READ
+    DbgEn();
+    print_readwrite_args(RxContext);
+#endif
+    status = check_nfs41_read_args(RxContext);
+    if (status) goto out;
+
+    status = nfs41_UpcallCreate(NFS41_READ, &nfs41_fobx->sec_ctx,
+        pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
+        pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
+    if (status) goto out;
+
+    entry->u.ReadWrite.MdlAddress = LowIoContext->ParamsFor.ReadWrite.Buffer;
+    entry->buf_len = LowIoContext->ParamsFor.ReadWrite.ByteCount;
+    entry->u.ReadWrite.offset = LowIoContext->ParamsFor.ReadWrite.ByteOffset;
+    if (FlagOn(RxContext->CurrentIrpSp->FileObject->Flags,
+            FO_SYNCHRONOUS_IO) == FALSE) {
+        entry->u.ReadWrite.rxcontext = RxContext;
+        async = entry->async_op = TRUE;
+    }
+
+    /* assume network speed is 100MB/s and disk speed is 100MB/s so add
+     * time to transfer requested bytes over the network and read from disk
+     */
+    io_delay = pVNetRootContext->timeout + 2 * entry->buf_len / 104857600;
+    status = nfs41_UpcallWaitForReply(entry, io_delay);
+    if (status) goto out;
+
+    if (async) {
+#ifdef DEBUG_READ
+        DbgP("This is asynchronous read, returning control back to the user\n");
+#endif
+        status = STATUS_PENDING;
+        goto out;
+    }
+
+    if (entry->status == NO_ERROR) {
+#ifdef ENABLE_TIMINGS
+        InterlockedIncrement(&read.sops);
+        InterlockedAdd64(&read.size, entry->u.ReadWrite.len);
+#endif
+        status = RxContext->CurrentIrp->IoStatus.Status = STATUS_SUCCESS;
+        RxContext->IoStatusBlock.Information = entry->buf_len;
+
+        if ((!BooleanFlagOn(LowIoContext->ParamsFor.ReadWrite.Flags,
+                LOWIO_READWRITEFLAG_PAGING_IO) &&
+                (SrvOpen->DesiredAccess & FILE_READ_DATA) &&
+                !pVNetRootContext->nocache && !nfs41_fobx->nocache &&
+                !(SrvOpen->BufferingFlags &
+                (FCB_STATE_READBUFFERING_ENABLED |
+                 FCB_STATE_READCACHING_ENABLED)))) {
+            enable_caching(SrvOpen, nfs41_fobx, nfs41_fcb->changeattr,
+                pVNetRootContext->session);
+        }
+    } else {
+        status = map_readwrite_errors(entry->status);
+        RxContext->CurrentIrp->IoStatus.Status = status;
+        RxContext->IoStatusBlock.Information = 0;
+    }
+    RxFreePool(entry);
+out:
+#ifdef ENABLE_TIMINGS
+    t2 = KeQueryPerformanceCounter(NULL);
+    InterlockedIncrement(&read.tops);
+    InterlockedAdd64(&read.ticks, t2.QuadPart - t1.QuadPart);
+#ifdef ENABLE_INDV_TIMINGS
+    DbgP("nfs41_Read delta = %d op=%d sum=%d\n", t2.QuadPart - t1.QuadPart,
+        read.tops, read.ticks);
+#endif
+#endif
+#ifdef DEBUG_READ
+    DbgEx();
+#endif
+    return status;
+}
+
+NTSTATUS check_nfs41_write_args(
+    IN PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(RxContext->pRelevantSrvOpen->pVNetRoot);
+
+    if (!RxContext->LowIoContext.ParamsFor.ReadWrite.Buffer) {
+        status = STATUS_INVALID_USER_BUFFER;
+        goto out;
+    }
+
+    if (pVNetRootContext->read_only) {
+        print_error("check_nfs41_write_args: Read-only mount\n");
+        status = STATUS_ACCESS_DENIED;
+        goto out;
+    }
+out:
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_Write(
+#else
+NTSTATUS nfs41_Write(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
+    nfs41_updowncall_entry *entry;
+    BOOLEAN async = FALSE;
+    PLOWIO_CONTEXT LowIoContext  = &RxContext->LowIoContext;
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+    __notnull PNFS41_FCB nfs41_fcb = NFS41GetFcbExtension(RxContext->pFcb);
+    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
+    DWORD io_delay;
+#ifdef ENABLE_TIMINGS
+    LARGE_INTEGER t1, t2;
+    t1 = KeQueryPerformanceCounter(NULL);
+#endif
+
+#ifdef DEBUG_WRITE
+    DbgEn();
+    print_readwrite_args(RxContext);
+#endif
+
+    status = check_nfs41_write_args(RxContext);
+    if (status) goto out;
+
+    status = nfs41_UpcallCreate(NFS41_WRITE, &nfs41_fobx->sec_ctx,
+        pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
+        pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
+    if (status) goto out;
+
+    entry->u.ReadWrite.MdlAddress = LowIoContext->ParamsFor.ReadWrite.Buffer;
+    entry->buf_len = LowIoContext->ParamsFor.ReadWrite.ByteCount;
+    entry->u.ReadWrite.offset = LowIoContext->ParamsFor.ReadWrite.ByteOffset;
+
+    if (FlagOn(RxContext->CurrentIrpSp->FileObject->Flags,
+            FO_SYNCHRONOUS_IO) == FALSE) {
+        entry->u.ReadWrite.rxcontext = RxContext;
+        async = entry->async_op = TRUE;
+    }
+
+    /* assume network speed is 100MB/s and disk speed is 100MB/s so add
+     * time to transfer requested bytes over the network and write to disk
+     */
+    io_delay = pVNetRootContext->timeout + 2 * entry->buf_len / 104857600;
+    status = nfs41_UpcallWaitForReply(entry, io_delay);
+    if (status) goto out;
+
+    if (async) {
+#ifdef DEBUG_WRITE
+        DbgP("This is asynchronous write, returning control back to the user\n");
+#endif
+        status = STATUS_PENDING;
+        goto out;
+    }
+
+    if (entry->status == NO_ERROR) {
+        //update cached file attributes
+#ifdef ENABLE_TIMINGS
+        InterlockedIncrement(&write.sops);
+        InterlockedAdd64(&write.size, entry->u.ReadWrite.len);
+#endif
+        nfs41_fcb->StandardInfo.EndOfFile.QuadPart = entry->buf_len +
+            entry->u.ReadWrite.offset;
+        status = RxContext->CurrentIrp->IoStatus.Status = STATUS_SUCCESS;
+        RxContext->IoStatusBlock.Information = entry->buf_len;
+        nfs41_fcb->changeattr = entry->ChangeTime;
+
+        //re-enable write buffering
+        if (!BooleanFlagOn(LowIoContext->ParamsFor.ReadWrite.Flags,
+                LOWIO_READWRITEFLAG_PAGING_IO) &&
+                (SrvOpen->DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA)) &&
+                !pVNetRootContext->write_thru &&
+                !pVNetRootContext->nocache &&
+                !nfs41_fobx->write_thru && !nfs41_fobx->nocache &&
+                !(SrvOpen->BufferingFlags &
+                (FCB_STATE_WRITEBUFFERING_ENABLED |
+                 FCB_STATE_WRITECACHING_ENABLED))) {
+            enable_caching(SrvOpen, nfs41_fobx, nfs41_fcb->changeattr,
+                pVNetRootContext->session);
+        } else if (!nfs41_fobx->deleg_type)
+            nfs41_update_fcb_list(RxContext->pFcb, entry->ChangeTime);
+
+    } else {
+        status = map_readwrite_errors(entry->status);
+        RxContext->CurrentIrp->IoStatus.Status = status;
+        RxContext->IoStatusBlock.Information = 0;
+    }
+    RxFreePool(entry);
+out:
+#ifdef ENABLE_TIMINGS
+    t2 = KeQueryPerformanceCounter(NULL);
+    InterlockedIncrement(&write.tops);
+    InterlockedAdd64(&write.ticks, t2.QuadPart - t1.QuadPart);
+#ifdef ENABLE_INDV_TIMINGS
+    DbgP("nfs41_Write delta = %d op=%d sum=%d\n", t2.QuadPart - t1.QuadPart,
+        write.tops, write.ticks);
+#endif
+#endif
+#ifdef DEBUG_WRITE
+    DbgEx();
+#endif
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_IsLockRealizable(
+#else
+NTSTATUS nfs41_IsLockRealizable(
+#endif
+    IN OUT PMRX_FCB pFcb,
+    IN PLARGE_INTEGER  ByteOffset,
+    IN PLARGE_INTEGER  Length,
+    IN ULONG  LowIoLockFlags)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+#ifdef DEBUG_LOCK
+    DbgEn();
+    DbgP("offset 0x%llx, length 0x%llx, exclusive=%u, blocking=%u\n",
+        ByteOffset->QuadPart,Length->QuadPart,
+        BooleanFlagOn(LowIoLockFlags, SL_EXCLUSIVE_LOCK),
+        !BooleanFlagOn(LowIoLockFlags, SL_FAIL_IMMEDIATELY));
+#endif
+
+    /* NFS lock operations with length=0 MUST fail with NFS4ERR_INVAL */
+    if (Length->QuadPart == 0)
+        status = STATUS_NOT_SUPPORTED;
+
+#ifdef DEBUG_LOCK
+    DbgEx();
+#endif
+    return status;
+}
+
+NTSTATUS map_lock_errors(
+    DWORD status)
+{
+    switch (status) {
+    case NO_ERROR:                  return STATUS_SUCCESS;
+    case ERROR_NETNAME_DELETED:     return STATUS_NETWORK_NAME_DELETED;
+    case ERROR_LOCK_FAILED:         return STATUS_LOCK_NOT_GRANTED;
+    case ERROR_NOT_LOCKED:          return STATUS_RANGE_NOT_LOCKED;
+    case ERROR_ATOMIC_LOCKS_NOT_SUPPORTED: return STATUS_UNSUCCESSFUL;
+    case ERROR_OUTOFMEMORY:         return STATUS_INSUFFICIENT_RESOURCES;
+    case ERROR_SHARING_VIOLATION:   return STATUS_SHARING_VIOLATION;
+    case ERROR_FILE_INVALID:        return STATUS_FILE_INVALID;
+    /* if we return ERROR_INVALID_PARAMETER, Windows translates that to
+     * success!! */
+    case ERROR_INVALID_PARAMETER:   return STATUS_LOCK_NOT_GRANTED;
+    default:
+        print_error("failed to map windows error %d to NTSTATUS; "
+            "defaulting to STATUS_INVALID_NETWORK_RESPONSE\n", status);
+    case ERROR_BAD_NET_RESP:        return STATUS_INVALID_NETWORK_RESPONSE;
+    }
+}
+
+void print_lock_args(
+    PRX_CONTEXT RxContext)
+{
+    PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
+    const ULONG flags = LowIoContext->ParamsFor.Locks.Flags;
+    print_debug_header(RxContext);
+    DbgP("offset 0x%llx, length 0x%llx, exclusive=%u, blocking=%u\n",
+        LowIoContext->ParamsFor.Locks.ByteOffset,
+        LowIoContext->ParamsFor.Locks.Length,
+        BooleanFlagOn(flags, SL_EXCLUSIVE_LOCK),
+        !BooleanFlagOn(flags, SL_FAIL_IMMEDIATELY));
+}
+
+
+/* use exponential backoff between polls for blocking locks */
+#define MSEC_TO_RELATIVE_WAIT   (-10000)
+#define MIN_LOCK_POLL_WAIT      (500 * MSEC_TO_RELATIVE_WAIT) /* 500ms */
+#define MAX_LOCK_POLL_WAIT      (30000 * MSEC_TO_RELATIVE_WAIT) /* 30s */
+
+void denied_lock_backoff(
+    IN OUT PLARGE_INTEGER delay)
+{
+    if (delay->QuadPart == 0)
+        delay->QuadPart = MIN_LOCK_POLL_WAIT;
+    else
+        delay->QuadPart <<= 1;
+
+    if (delay->QuadPart < MAX_LOCK_POLL_WAIT)
+        delay->QuadPart = MAX_LOCK_POLL_WAIT;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_Lock(
+#else
+NTSTATUS nfs41_Lock(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    nfs41_updowncall_entry *entry;
+    PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
+    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+    const ULONG flags = LowIoContext->ParamsFor.Locks.Flags;
+#ifdef _MSC_VER
+    LARGE_INTEGER poll_delay = {0};
+#else
+    LARGE_INTEGER poll_delay;
+#endif
+#ifdef ENABLE_TIMINGS
+    LARGE_INTEGER t1, t2;
+    t1 = KeQueryPerformanceCounter(NULL);
+#endif
+
+#ifndef _MSC_VER
+    poll_delay.QuadPart = 0;
+#endif
+
+#ifdef DEBUG_LOCK
+    DbgEn();
+    print_lock_args(RxContext);
+#endif
+
+/*  RxReleaseFcbResourceForThreadInMRx(RxContext, RxContext->pFcb,
+        LowIoContext->ResourceThreadId); */
+
+    status = nfs41_UpcallCreate(NFS41_LOCK, &nfs41_fobx->sec_ctx,
+        pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
+        pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
+    if (status) goto out;
+
+    entry->u.Lock.offset = LowIoContext->ParamsFor.Locks.ByteOffset;
+    entry->u.Lock.length = LowIoContext->ParamsFor.Locks.Length;
+    entry->u.Lock.exclusive = BooleanFlagOn(flags, SL_EXCLUSIVE_LOCK);
+    entry->u.Lock.blocking = !BooleanFlagOn(flags, SL_FAIL_IMMEDIATELY);
+
+retry_upcall:
+    status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
+    if (status) goto out;
+
+    /* blocking locks keep trying until it succeeds */
+    if (entry->status == ERROR_LOCK_FAILED && entry->u.Lock.blocking) {
+        denied_lock_backoff(&poll_delay);
+        DbgP("returned ERROR_LOCK_FAILED; retrying in %llums\n",
+            poll_delay.QuadPart / MSEC_TO_RELATIVE_WAIT);
+        KeDelayExecutionThread(KernelMode, FALSE, &poll_delay);
+        entry->state = NFS41_WAITING_FOR_UPCALL;
+        goto retry_upcall;
+    }
+
+    status = map_lock_errors(entry->status);
+    RxContext->CurrentIrp->IoStatus.Status = status;
+
+    RxFreePool(entry);
+out:
+#ifdef ENABLE_TIMINGS
+    t2 = KeQueryPerformanceCounter(NULL);
+    InterlockedIncrement(&lock.tops);
+    InterlockedAdd64(&lock.ticks, t2.QuadPart - t1.QuadPart);
+#ifdef ENABLE_INDV_TIMINGS
+    DbgP("nfs41_Lock delta = %d op=%d sum=%d\n", t2.QuadPart - t1.QuadPart,
+        lock.tops, lock.ticks);
+#endif
+#endif
+#ifdef DEBUG_LOCK
+    DbgEx();
+#endif
+    return status;
+}
+
+void print_unlock_args(
+    PRX_CONTEXT RxContext)
+{
+    PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
+    print_debug_header(RxContext);
+    if (LowIoContext->Operation == LOWIO_OP_UNLOCK_MULTIPLE) {
+        PLOWIO_LOCK_LIST lock = LowIoContext->ParamsFor.Locks.LockList;
+        DbgP("LOWIO_OP_UNLOCK_MULTIPLE:");
+        while (lock) {
+            DbgP(" (offset=%llu, length=%llu)", lock->ByteOffset, lock->Length);
+            lock = lock->Next;
+        }
+        DbgP("\n");
+    } else {
+        DbgP("LOWIO_OP_UNLOCK: offset=%llu, length=%llu\n",
+            LowIoContext->ParamsFor.Locks.ByteOffset,
+            LowIoContext->ParamsFor.Locks.Length);
+    }
+}
+
+__inline ULONG unlock_list_count(
+    PLOWIO_LOCK_LIST lock)
+{
+    ULONG count = 0;
+    while (lock) {
+        count++;
+        lock = lock->Next;
+    }
+    return count;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_Unlock(
+#else
+NTSTATUS nfs41_Unlock(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    nfs41_updowncall_entry *entry;
+    PLOWIO_CONTEXT LowIoContext  = &RxContext->LowIoContext;
+    __notnull PNFS41_FOBX nfs41_fobx = NFS41GetFobxExtension(RxContext->pFobx);
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+#ifdef ENABLE_TIMINGS
+    LARGE_INTEGER t1, t2;
+    t1 = KeQueryPerformanceCounter(NULL);
+#endif
+#ifdef DEBUG_LOCK
+    DbgEn();
+    print_lock_args(RxContext);
+#endif
+
+/*  RxReleaseFcbResourceForThreadInMRx(RxContext, RxContext->pFcb,
+        LowIoContext->ResourceThreadId); */
+
+    status = nfs41_UpcallCreate(NFS41_UNLOCK, &nfs41_fobx->sec_ctx,
+        pVNetRootContext->session, nfs41_fobx->nfs41_open_state,
+        pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
+    if (status) goto out;
+
+    if (LowIoContext->Operation == LOWIO_OP_UNLOCK_MULTIPLE) {
+        entry->u.Unlock.count = unlock_list_count(
+            LowIoContext->ParamsFor.Locks.LockList);
+        RtlCopyMemory(&entry->u.Unlock.locks,
+            LowIoContext->ParamsFor.Locks.LockList,
+            sizeof(LOWIO_LOCK_LIST));
+    } else {
+        entry->u.Unlock.count = 1;
+        entry->u.Unlock.locks.ByteOffset =
+            LowIoContext->ParamsFor.Locks.ByteOffset;
+        entry->u.Unlock.locks.Length =
+            LowIoContext->ParamsFor.Locks.Length;
+    }
+
+    status = nfs41_UpcallWaitForReply(entry, pVNetRootContext->timeout);
+    if (status) goto out;
+
+    status = map_lock_errors(entry->status);
+    RxContext->CurrentIrp->IoStatus.Status = status;
+    RxFreePool(entry);
+out:
+#ifdef ENABLE_TIMINGS
+    t2 = KeQueryPerformanceCounter(NULL);
+    InterlockedIncrement(&unlock.tops);
+    InterlockedAdd64(&unlock.ticks, t2.QuadPart - t1.QuadPart);
+#ifdef ENABLE_INDV_TIMINGS
+    DbgP("nfs41_Unlock delta = %d op=%d sum=%d\n", t2.QuadPart - t1.QuadPart,
+        unlock.tops, unlock.ticks);
+#endif
+#endif
+#ifdef DEBUG_LOCK
+    DbgEx();
+#endif
+    return status;
+}
+
+NTSTATUS map_symlink_errors(
+    NTSTATUS status)
+{
+    switch (status) {
+    case NO_ERROR:                  return STATUS_SUCCESS;
+    case ERROR_INVALID_REPARSE_DATA: return STATUS_IO_REPARSE_DATA_INVALID;
+    case ERROR_NOT_A_REPARSE_POINT: return STATUS_NOT_A_REPARSE_POINT;
+    case ERROR_ACCESS_DENIED:       return STATUS_ACCESS_DENIED;
+    case ERROR_NOT_EMPTY:           return STATUS_DIRECTORY_NOT_EMPTY;
+    case ERROR_OUTOFMEMORY:         return STATUS_INSUFFICIENT_RESOURCES;
+    case ERROR_INSUFFICIENT_BUFFER: return STATUS_BUFFER_TOO_SMALL;
+    case STATUS_BUFFER_TOO_SMALL:
+    case ERROR_BUFFER_OVERFLOW:     return STATUS_BUFFER_OVERFLOW;
+    default:
+        print_error("failed to map windows error %d to NTSTATUS; "
+            "defaulting to STATUS_INVALID_NETWORK_RESPONSE\n", status);
+    case ERROR_BAD_NET_RESP:        return STATUS_INVALID_NETWORK_RESPONSE;
+    }
+}
+
+void print_reparse_buffer(
+    PREPARSE_DATA_BUFFER Reparse)
+{
+    UNICODE_STRING name;
+    DbgP("ReparseTag:           %08X\n", Reparse->ReparseTag);
+    DbgP("ReparseDataLength:    %8u\n", Reparse->ReparseDataLength);
+    DbgP("Reserved:             %8u\n", Reparse->Reserved);
+    DbgP("SubstituteNameOffset: %8u\n",
+         Reparse->SymbolicLinkReparseBuffer.SubstituteNameOffset);
+    DbgP("SubstituteNameLength: %8u\n",
+         Reparse->SymbolicLinkReparseBuffer.SubstituteNameLength);
+    DbgP("PrintNameOffset:      %8u\n",
+         Reparse->SymbolicLinkReparseBuffer.PrintNameOffset);
+    DbgP("PrintNameLength:      %8u\n",
+         Reparse->SymbolicLinkReparseBuffer.PrintNameLength);
+    DbgP("Flags:                %08X\n",
+         Reparse->SymbolicLinkReparseBuffer.Flags);
+
+    name.Buffer = &Reparse->SymbolicLinkReparseBuffer.PathBuffer[
+        Reparse->SymbolicLinkReparseBuffer.SubstituteNameOffset/sizeof(WCHAR)];
+    name.MaximumLength = name.Length =
+        Reparse->SymbolicLinkReparseBuffer.SubstituteNameLength;
+    DbgP("SubstituteName:       %wZ\n", &name);
+
+    name.Buffer = &Reparse->SymbolicLinkReparseBuffer.PathBuffer[
+        Reparse->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR)];
+    name.MaximumLength = name.Length =
+        Reparse->SymbolicLinkReparseBuffer.PrintNameLength;
+    DbgP("PrintName:            %wZ\n", &name);
+}
+
+NTSTATUS check_nfs41_setreparse_args(
+    IN PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    __notnull XXCTL_LOWIO_COMPONENT *FsCtl = &RxContext->LowIoContext.ParamsFor.FsCtl;
+    __notnull PREPARSE_DATA_BUFFER Reparse = (PREPARSE_DATA_BUFFER)FsCtl->pInputBuffer;
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION VNetRootContext =
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    const ULONG HeaderLen = REPARSE_DATA_BUFFER_HEADER_SIZE;
+
+    /* access checks */
+    if (VNetRootContext->read_only) {
+        status = STATUS_MEDIA_WRITE_PROTECTED;
+        goto out;
+    }
+    if (!(SrvOpen->DesiredAccess & (FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES))) {
+        status = STATUS_ACCESS_DENIED;
+        goto out;
+    }
+
+    /* must have a filename longer than vnetroot name,
+     * or it's trying to operate on the volume itself */
+    if (is_root_directory(RxContext)) {
+        status = STATUS_INVALID_PARAMETER;
+        goto out;
+    }
+    if (FsCtl->pOutputBuffer != NULL) {
+        status = STATUS_INVALID_PARAMETER;
+        goto out;
+    }
+
+    /* validate input buffer and length */
+    if (!Reparse) {
+        status = STATUS_INVALID_BUFFER_SIZE;
+        goto out;
+    }
+
+    if (FsCtl->InputBufferLength < HeaderLen ||
+            FsCtl->InputBufferLength > MAXIMUM_REPARSE_DATA_BUFFER_SIZE) {
+        status = STATUS_IO_REPARSE_DATA_INVALID;
+        goto out;
+    }
+    if (FsCtl->InputBufferLength != HeaderLen + Reparse->ReparseDataLength) {
+        status = STATUS_IO_REPARSE_DATA_INVALID;
+        goto out;
+    }
+
+    /* validate reparse tag */
+    if (!IsReparseTagValid(Reparse->ReparseTag)) {
+        status = STATUS_IO_REPARSE_TAG_INVALID;
+        goto out;
+    }
+    if (Reparse->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
+        status = STATUS_IO_REPARSE_TAG_MISMATCH;
+        goto out;
+    }
+out:
+    return status;
+}
+
+NTSTATUS nfs41_SetReparsePoint(
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status;
+    UNICODE_STRING TargetName;
+    __notnull XXCTL_LOWIO_COMPONENT *FsCtl = &RxContext->LowIoContext.ParamsFor.FsCtl;
+    __notnull PREPARSE_DATA_BUFFER Reparse = (PREPARSE_DATA_BUFFER)FsCtl->pInputBuffer;
+    __notnull PNFS41_FOBX Fobx = NFS41GetFobxExtension(RxContext->pFobx);
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION VNetRootContext =
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+    nfs41_updowncall_entry *entry;
+
+#ifdef DEBUG_SYMLINK
+    DbgEn();
+    print_debug_header(RxContext);
+    print_reparse_buffer(Reparse);
+#endif
+    status = check_nfs41_setreparse_args(RxContext);
+    if (status) goto out;
+
+    TargetName.MaximumLength = TargetName.Length =
+        Reparse->SymbolicLinkReparseBuffer.PrintNameLength;
+    TargetName.Buffer = &Reparse->SymbolicLinkReparseBuffer.PathBuffer[
+        Reparse->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR)];
+
+    status = nfs41_UpcallCreate(NFS41_SYMLINK, &Fobx->sec_ctx,
+        VNetRootContext->session, Fobx->nfs41_open_state,
+        pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
+    if (status) goto out;
+
+    entry->u.Symlink.target = &TargetName;
+    entry->u.Symlink.set = TRUE;
+
+    status = nfs41_UpcallWaitForReply(entry, VNetRootContext->timeout);
+    if (status) goto out;
+
+    status = map_symlink_errors(entry->status);
+    RxFreePool(entry);
+out:
+#ifdef DEBUG_SYMLINK
+    DbgEx();
+#endif
+    return status;
+}
+
+NTSTATUS check_nfs41_getreparse_args(
+    PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    XXCTL_LOWIO_COMPONENT *FsCtl = &RxContext->LowIoContext.ParamsFor.FsCtl;
+    const USHORT HeaderLen = FIELD_OFFSET(REPARSE_DATA_BUFFER,
+        SymbolicLinkReparseBuffer.PathBuffer);
+
+    /* must have a filename longer than vnetroot name,
+     * or it's trying to operate on the volume itself */
+    if (is_root_directory(RxContext)) {
+        status = STATUS_INVALID_PARAMETER;
+        goto out;
+    }
+    /* ifs reparse tests expect STATUS_INVALID_PARAMETER,
+     * but 'dir' passes a buffer here when querying symlinks
+    if (FsCtl->pInputBuffer != NULL) {
+        status = STATUS_INVALID_PARAMETER;
+        goto out;
+    } */
+    if (!FsCtl->pOutputBuffer) {
+        status = STATUS_INVALID_USER_BUFFER;
+        goto out;
+    }
+    if (!BooleanFlagOn(RxContext->pFcb->Attributes,
+            FILE_ATTRIBUTE_REPARSE_POINT)) {
+        status = STATUS_NOT_A_REPARSE_POINT;
+        DbgP("FILE_ATTRIBUTE_REPARSE_POINT is not set!\n");
+        goto out;
+    }
+
+    if (FsCtl->OutputBufferLength < HeaderLen) {
+        RxContext->InformationToReturn = HeaderLen;
+        status = STATUS_BUFFER_TOO_SMALL;
+        goto out;
+    }
+out:
+    return status;
+}
+
+NTSTATUS nfs41_GetReparsePoint(
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status;
+    UNICODE_STRING TargetName;
+    XXCTL_LOWIO_COMPONENT *FsCtl = &RxContext->LowIoContext.ParamsFor.FsCtl;
+    __notnull PNFS41_FOBX Fobx = NFS41GetFobxExtension(RxContext->pFobx);
+    __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+    __notnull PNFS41_V_NET_ROOT_EXTENSION VNetRootContext =
+        NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+    __notnull PNFS41_NETROOT_EXTENSION pNetRootContext =
+        NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+    nfs41_updowncall_entry *entry;
+    const USHORT HeaderLen = FIELD_OFFSET(REPARSE_DATA_BUFFER,
+        SymbolicLinkReparseBuffer.PathBuffer);
+
+#ifdef DEBUG_SYMLINK
+    DbgEn();
+#endif
+    status = check_nfs41_getreparse_args(RxContext);
+    if (status) goto out;
+
+    TargetName.Buffer = (PWCH)((PBYTE)FsCtl->pOutputBuffer + HeaderLen);
+    TargetName.MaximumLength = (USHORT)min(FsCtl->OutputBufferLength -
+        HeaderLen, 0xFFFF);
+
+    status = nfs41_UpcallCreate(NFS41_SYMLINK, &Fobx->sec_ctx,
+        VNetRootContext->session, Fobx->nfs41_open_state,
+        pNetRootContext->nfs41d_version, SrvOpen->pAlreadyPrefixedName, &entry);
+    if (status) goto out;
+
+    entry->u.Symlink.target = &TargetName;
+    entry->u.Symlink.set = FALSE;
+
+    status = nfs41_UpcallWaitForReply(entry, VNetRootContext->timeout);
+    if (status) goto out;
+
+    status = map_symlink_errors(entry->status);
+    if (status == STATUS_SUCCESS) {
+        /* fill in the output buffer */
+        PREPARSE_DATA_BUFFER Reparse = (PREPARSE_DATA_BUFFER)
+            FsCtl->pOutputBuffer;
+        Reparse->ReparseTag = IO_REPARSE_TAG_SYMLINK;
+        Reparse->ReparseDataLength = HeaderLen + TargetName.Length -
+            REPARSE_DATA_BUFFER_HEADER_SIZE;
+        Reparse->Reserved = 0;
+        Reparse->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
+        /* PrintName and SubstituteName point to the same string */
+        Reparse->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
+        Reparse->SymbolicLinkReparseBuffer.SubstituteNameLength =
+            TargetName.Length;
+        Reparse->SymbolicLinkReparseBuffer.PrintNameOffset = 0;
+        Reparse->SymbolicLinkReparseBuffer.PrintNameLength = TargetName.Length;
+        print_reparse_buffer(Reparse);
+
+        RxContext->IoStatusBlock.Information = HeaderLen + TargetName.Length;
+    } else if (status == STATUS_BUFFER_TOO_SMALL) {
+        RxContext->InformationToReturn = HeaderLen + TargetName.Length;
+    }
+    RxFreePool(entry);
+out:
+#ifdef DEBUG_SYMLINK
+    DbgEx();
+#endif
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_FsCtl(
+#else
+NTSTATUS nfs41_FsCtl(
+#endif
+    IN OUT PRX_CONTEXT RxContext)
+{
+    NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
+#ifdef DEBUG_MISC
+    DbgEn();
+    print_debug_header(RxContext);
+#endif
+    switch (RxContext->LowIoContext.ParamsFor.FsCtl.FsControlCode) {
+    case FSCTL_SET_REPARSE_POINT:
+        status = nfs41_SetReparsePoint(RxContext);
+        break;
+
+    case FSCTL_GET_REPARSE_POINT:
+        status = nfs41_GetReparsePoint(RxContext);
+        break;
+#ifdef DEBUG_MISC
+    default:
+        DbgP("FsControlCode: %d\n",
+             RxContext->LowIoContext.ParamsFor.FsCtl.FsControlCode);
+#endif
+    }
+#ifdef DEBUG_MISC
+    DbgEx();
+#endif
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_CompleteBufferingStateChangeRequest(
+#else
+NTSTATUS nfs41_CompleteBufferingStateChangeRequest(
+#endif
+    IN OUT PRX_CONTEXT RxContext,
+    IN OUT PMRX_SRV_OPEN SrvOpen,
+    IN PVOID pContext)
+{
+    return STATUS_SUCCESS;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_FsdDispatch (
+#else
+NTSTATUS nfs41_FsdDispatch (
+#endif
+    IN PDEVICE_OBJECT dev,
+    IN PIRP Irp)
+{
+#ifdef DEBUG_FSDDISPATCH
+    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
+#endif
+    NTSTATUS status;
+
+#ifdef DEBUG_FSDDISPATCH
+    DbgEn();
+    DbgP("CURRENT IRP = %d.%d\n", IrpSp->MajorFunction, IrpSp->MinorFunction);
+    if(IrpSp->FileObject)
+        DbgP("FileOject %p Filename %wZ\n", IrpSp->FileObject,
+                &IrpSp->FileObject->FileName);
+#endif
+
+    if (dev != (PDEVICE_OBJECT)nfs41_dev) {
+        print_error("*** not ours ***\n");
+        Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
+        Irp->IoStatus.Information = 0;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT );
+        status = STATUS_INVALID_DEVICE_REQUEST;
+        goto out;
+    }
+
+    status = RxFsdDispatch((PRDBSS_DEVICE_OBJECT)dev,Irp);
+    /* AGLO: 08/05/2009 - looks like RxFsdDispatch frees IrpSp */
+
+out:
+#ifdef DEBUG_FSDDISPATCH
+    DbgP("IoStatus status = 0x%x info = 0x%x\n", Irp->IoStatus.Status,
+         Irp->IoStatus.Information);
+    DbgEx();
+#endif
+    return status;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_Unimplemented(
+#else
+NTSTATUS nfs41_Unimplemented(
+#endif
+    PRX_CONTEXT RxContext)
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI nfs41_AreFilesAliased(
+#else
+NTSTATUS nfs41_AreFilesAliased(
+#endif
+    PFCB a,
+    PFCB b)
+{
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS nfs41_init_ops()
+{
+    DbgEn();
+
+    ZeroAndInitializeNodeType(&nfs41_ops, RDBSS_NTC_MINIRDR_DISPATCH,
+        sizeof(MINIRDR_DISPATCH));
+
+    nfs41_ops.MRxFlags = (RDBSS_MANAGE_NET_ROOT_EXTENSION |
+                            RDBSS_MANAGE_V_NET_ROOT_EXTENSION |
+                            RDBSS_MANAGE_FCB_EXTENSION |
+                            RDBSS_MANAGE_FOBX_EXTENSION);
+
+    nfs41_ops.MRxSrvCallSize  = 0; // srvcall extension is not handled in rdbss
+    nfs41_ops.MRxNetRootSize  = sizeof(NFS41_NETROOT_EXTENSION);
+    nfs41_ops.MRxVNetRootSize = sizeof(NFS41_V_NET_ROOT_EXTENSION);
+    nfs41_ops.MRxFcbSize      = sizeof(NFS41_FCB);
+    nfs41_ops.MRxFobxSize     = sizeof(NFS41_FOBX);
+
+    // Mini redirector cancel routine ..
+
+    nfs41_ops.MRxCancel = NULL;
+
+    //
+    // Mini redirector Start/Stop. Each mini-rdr can be started or stopped
+    // while the others continue to operate.
+    //
+
+    nfs41_ops.MRxStart                = nfs41_Start;
+    nfs41_ops.MRxStop                 = nfs41_Stop;
+    nfs41_ops.MRxDevFcbXXXControlFile = nfs41_DevFcbXXXControlFile;
+
+    //
+    // Mini redirector name resolution.
+    //
+
+    nfs41_ops.MRxCreateSrvCall       = nfs41_CreateSrvCall;
+    nfs41_ops.MRxSrvCallWinnerNotify = nfs41_SrvCallWinnerNotify;
+    nfs41_ops.MRxCreateVNetRoot      = nfs41_CreateVNetRoot;
+    nfs41_ops.MRxExtractNetRootName  = nfs41_ExtractNetRootName;
+    nfs41_ops.MRxFinalizeSrvCall     = nfs41_FinalizeSrvCall;
+    nfs41_ops.MRxFinalizeNetRoot     = nfs41_FinalizeNetRoot;
+    nfs41_ops.MRxFinalizeVNetRoot    = nfs41_FinalizeVNetRoot;
+
+    //
+    // File System Object Creation/Deletion.
+    //
+
+    nfs41_ops.MRxCreate            = nfs41_Create;
+    nfs41_ops.MRxCollapseOpen      = nfs41_CollapseOpen;
+    nfs41_ops.MRxShouldTryToCollapseThisOpen = nfs41_ShouldTryToCollapseThisOpen;
+    nfs41_ops.MRxExtendForCache    = nfs41_ExtendForCache;
+    nfs41_ops.MRxExtendForNonCache = nfs41_ExtendForCache;
+    nfs41_ops.MRxCloseSrvOpen      = nfs41_CloseSrvOpen;
+    nfs41_ops.MRxFlush             = nfs41_Flush;
+    nfs41_ops.MRxDeallocateForFcb  = nfs41_DeallocateForFcb;
+    nfs41_ops.MRxDeallocateForFobx = nfs41_DeallocateForFobx;
+    nfs41_ops.MRxIsLockRealizable    = nfs41_IsLockRealizable;
+
+    //
+    // File System Objects query/Set
+    //
+
+    nfs41_ops.MRxQueryDirectory       = nfs41_QueryDirectory;
+    nfs41_ops.MRxQueryVolumeInfo      = nfs41_QueryVolumeInformation;
+    nfs41_ops.MRxQueryEaInfo          = nfs41_QueryEaInformation;
+    nfs41_ops.MRxSetEaInfo            = nfs41_SetEaInformation;
+    nfs41_ops.MRxQuerySdInfo          = nfs41_QuerySecurityInformation;
+    nfs41_ops.MRxSetSdInfo            = nfs41_SetSecurityInformation;
+    nfs41_ops.MRxQueryFileInfo        = nfs41_QueryFileInformation;
+    nfs41_ops.MRxSetFileInfo          = nfs41_SetFileInformation;
+
+    //
+    // Buffering state change
+    //
+
+    nfs41_ops.MRxComputeNewBufferingState = nfs41_ComputeNewBufferingState;
+
+    //
+    // File System Object I/O
+    //
+
+    nfs41_ops.MRxLowIOSubmit[LOWIO_OP_READ]            = nfs41_Read;
+    nfs41_ops.MRxLowIOSubmit[LOWIO_OP_WRITE]           = nfs41_Write;
+    nfs41_ops.MRxLowIOSubmit[LOWIO_OP_SHAREDLOCK]      = nfs41_Lock;
+    nfs41_ops.MRxLowIOSubmit[LOWIO_OP_EXCLUSIVELOCK]   = nfs41_Lock;
+    nfs41_ops.MRxLowIOSubmit[LOWIO_OP_UNLOCK]          = nfs41_Unlock;
+    nfs41_ops.MRxLowIOSubmit[LOWIO_OP_UNLOCK_MULTIPLE] = nfs41_Unlock;
+    nfs41_ops.MRxLowIOSubmit[LOWIO_OP_FSCTL]           = nfs41_FsCtl;
+
+    //
+    // Miscellanous
+    //
+
+    nfs41_ops.MRxCompleteBufferingStateChangeRequest =
+        nfs41_CompleteBufferingStateChangeRequest;
+    nfs41_ops.MRxIsValidDirectory     = nfs41_IsValidDirectory;
+
+    nfs41_ops.MRxTruncate = nfs41_Unimplemented;
+    nfs41_ops.MRxZeroExtend = nfs41_Unimplemented;
+    nfs41_ops.MRxAreFilesAliased = nfs41_AreFilesAliased;
+    nfs41_ops.MRxQueryQuotaInfo = nfs41_Unimplemented;
+    nfs41_ops.MRxSetQuotaInfo = nfs41_Unimplemented;
+    nfs41_ops.MRxSetVolumeInfo = nfs41_Unimplemented;
+
+    DbgR();
+    return(STATUS_SUCCESS);
+}
+
+KSTART_ROUTINE fcbopen_main;
+#ifdef __REACTOS__
+VOID NTAPI fcbopen_main(PVOID ctx)
+#else
+VOID fcbopen_main(PVOID ctx)
+#endif
+{
+    NTSTATUS status;
+    LARGE_INTEGER timeout;
+
+    DbgEn();
+    timeout.QuadPart = RELATIVE(SECONDS(30));
+    while(1) {
+        PLIST_ENTRY pEntry;
+        nfs41_fcb_list_entry *cur;
+        status = KeDelayExecutionThread(KernelMode, TRUE, &timeout);
+        ExAcquireFastMutex(&fcblistLock);
+        pEntry = openlist.head.Flink;
+        while (!IsListEmpty(&openlist.head)) {
+            PNFS41_NETROOT_EXTENSION pNetRootContext;
+            nfs41_updowncall_entry *entry;
+            FILE_BASIC_INFORMATION binfo;
+            PNFS41_FCB nfs41_fcb;
+            cur = (nfs41_fcb_list_entry *)CONTAINING_RECORD(pEntry,
+                    nfs41_fcb_list_entry, next);
+
+#ifdef DEBUG_TIME_BASED_COHERENCY
+            DbgP("fcbopen_main: Checking attributes for fcb=%p "
+                "change_time=%llu skipping=%d\n", cur->fcb,
+                cur->ChangeTime, cur->skip);
+#endif
+            if (cur->skip) goto out;
+            pNetRootContext =
+                NFS41GetNetRootExtension(cur->fcb->pNetRoot);
+            /* place an upcall for this srv_open */
+            status = nfs41_UpcallCreate(NFS41_FILE_QUERY,
+                &cur->nfs41_fobx->sec_ctx, cur->session,
+                cur->nfs41_fobx->nfs41_open_state,
+                pNetRootContext->nfs41d_version, NULL, &entry);
+            if (status) goto out;
+
+            entry->u.QueryFile.InfoClass = FileBasicInformation;
+            entry->buf = &binfo;
+            entry->buf_len = sizeof(binfo);
+
+            status = nfs41_UpcallWaitForReply(entry, UPCALL_TIMEOUT_DEFAULT);
+            if (status) goto out;
+
+            if (cur->ChangeTime != entry->ChangeTime) {
+                ULONG flag = DISABLE_CACHING;
+                PMRX_SRV_OPEN srv_open;
+                PLIST_ENTRY psrvEntry;
+#ifdef DEBUG_TIME_BASED_COHERENCY
+                DbgP("fcbopen_main: old ctime=%llu new_ctime=%llu\n",
+                    cur->ChangeTime, entry->ChangeTime);
+#endif
+                cur->ChangeTime = entry->ChangeTime;
+                cur->skip = TRUE;
+                psrvEntry = &cur->fcb->SrvOpenList;
+                psrvEntry = psrvEntry->Flink;
+                while (!IsListEmpty(&cur->fcb->SrvOpenList)) {
+                    srv_open = (PMRX_SRV_OPEN)CONTAINING_RECORD(psrvEntry,
+                            MRX_SRV_OPEN, SrvOpenQLinks);
+                    if (srv_open->DesiredAccess &
+                            (FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA)) {
+#ifdef DEBUG_TIME_BASED_COHERENCY
+                        DbgP("fcbopen_main: ************ Invalidate the cache %wZ"
+                             "************\n", srv_open->pAlreadyPrefixedName);
+#endif
+                        RxIndicateChangeOfBufferingStateForSrvOpen(
+                            cur->fcb->pNetRoot->pSrvCall, srv_open,
+                            srv_open->Key, ULongToPtr(flag));
+                    }
+                    if (psrvEntry->Flink == &cur->fcb->SrvOpenList) {
+#ifdef DEBUG_TIME_BASED_COHERENCY
+                        DbgP("fcbopen_main: reached end of srvopen for fcb %p\n",
+                            cur->fcb);
+#endif
+                        break;
+                    }
+                    psrvEntry = psrvEntry->Flink;
+                };
+            }
+            nfs41_fcb = (PNFS41_FCB)cur->fcb->Context;
+            nfs41_fcb->changeattr = entry->ChangeTime;
+            RxFreePool(entry);
+out:
+            if (pEntry->Flink == &openlist.head) {
+#ifdef DEBUG_TIME_BASED_COHERENCY
+                DbgP("fcbopen_main: reached end of the fcb list\n");
+#endif
+                break;
+            }
+            pEntry = pEntry->Flink;
+        }
+        ExReleaseFastMutex(&fcblistLock);
+    }
+    DbgEx();
+}
+
+#ifdef __REACTOS__
+NTSTATUS NTAPI DriverEntry(
+#else
+NTSTATUS DriverEntry(
+#endif
+    IN PDRIVER_OBJECT drv,
+    IN PUNICODE_STRING path)
+{
+    NTSTATUS status;
+    ULONG flags = 0, i;
+    UNICODE_STRING dev_name, user_dev_name;
+    PNFS41_DEVICE_EXTENSION dev_exts;
+    TIME_FIELDS jan_1_1970 = {1970, 1, 1, 0, 0, 0, 0, 0};
+    ACCESS_MASK mask = 0;
+    OBJECT_ATTRIBUTES oattrs;
+
+    DbgEn();
+
+    status = RxDriverEntry(drv, path);
+    if (status != STATUS_SUCCESS) {
+        print_error("RxDriverEntry failed: %08lx\n", status);
+        goto out;
+    }
+
+    RtlInitUnicodeString(&dev_name, NFS41_DEVICE_NAME);
+    SetFlag(flags, RX_REGISTERMINI_FLAG_DONT_PROVIDE_MAILSLOTS);
+
+    status = nfs41_init_ops();
+    if (status != STATUS_SUCCESS) {
+        print_error("nfs41_init_ops failed to initialize dispatch table\n");
+        goto out;
+    }
+
+    DbgP("calling RxRegisterMinirdr\n");
+    status = RxRegisterMinirdr(&nfs41_dev, drv, &nfs41_ops, flags, &dev_name,
+                sizeof(NFS41_DEVICE_EXTENSION),
+                FILE_DEVICE_NETWORK_FILE_SYSTEM, FILE_REMOTE_DEVICE);
+    if (status != STATUS_SUCCESS) {
+        print_error("RxRegisterMinirdr failed: %08lx\n", status);
+        goto out;
+    }
+#ifndef __REACTOS__
+    nfs41_dev->Flags |= DO_BUFFERED_IO;
+#endif
+
+    dev_exts = (PNFS41_DEVICE_EXTENSION)
+        ((PBYTE)(nfs41_dev) + sizeof(RDBSS_DEVICE_OBJECT));
+
+    RxDefineNode(dev_exts, NFS41_DEVICE_EXTENSION);
+    dev_exts->DeviceObject = nfs41_dev;
+    nfs41_create_volume_info((PFILE_FS_VOLUME_INFORMATION)dev_exts->VolAttrs,
+        &dev_exts->VolAttrsLen);
+
+    RtlInitUnicodeString(&user_dev_name, NFS41_SHADOW_DEVICE_NAME);
+    DbgP("calling IoCreateSymbolicLink %wZ %wZ\n", &user_dev_name, &dev_name);
+    status = IoCreateSymbolicLink(&user_dev_name, &dev_name);
+    if (status != STATUS_SUCCESS) {
+        print_error("Device name IoCreateSymbolicLink failed: %08lx\n", status);
+        goto out_unregister;
+    }
+
+    KeInitializeEvent(&upcallEvent, SynchronizationEvent, FALSE );
+    ExInitializeFastMutex(&upcallLock);
+    ExInitializeFastMutex(&downcallLock);
+    ExInitializeFastMutex(&xidLock);
+    ExInitializeFastMutex(&openOwnerLock);
+    ExInitializeFastMutex(&fcblistLock);
+    InitializeListHead(&upcall.head);
+    InitializeListHead(&downcall.head);
+    InitializeListHead(&openlist.head);
+    InitializeObjectAttributes(&oattrs, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+    status = PsCreateSystemThread(&dev_exts->openlistHandle, mask,
+        &oattrs, NULL, NULL, &fcbopen_main, NULL);
+    if (status != STATUS_SUCCESS)
+        goto out_unregister;
+
+    drv->DriverUnload = nfs41_driver_unload;
+
+    for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
+        drv->MajorFunction[i] = (PDRIVER_DISPATCH)nfs41_FsdDispatch;
+
+    RtlTimeFieldsToTime(&jan_1_1970, &unix_time_diff);
+
+out_unregister:
+    if (status != STATUS_SUCCESS)
+        RxUnregisterMinirdr(nfs41_dev);
+out:
+    DbgEx();
+    return status;
+}
+
+#ifdef __REACTOS__
+VOID NTAPI nfs41_driver_unload(IN PDRIVER_OBJECT drv)
+#else
+VOID nfs41_driver_unload(IN PDRIVER_OBJECT drv)
+#endif
+{
+    PRX_CONTEXT RxContext;
+    NTSTATUS    status;
+    UNICODE_STRING dev_name, pipe_name;
+
+    DbgEn();
+
+    RxContext = RxCreateRxContext(NULL, nfs41_dev, RX_CONTEXT_FLAG_IN_FSP);
+    if (RxContext == NULL) {
+        status = STATUS_INSUFFICIENT_RESOURCES;
+        goto unload;
+    }
+    status = RxStopMinirdr(RxContext, &RxContext->PostRequest);
+    RxDereferenceAndDeleteRxContext(RxContext);
+
+unload:
+    RtlInitUnicodeString(&dev_name, NFS41_SHADOW_DEVICE_NAME);
+    status = IoDeleteSymbolicLink(&dev_name);
+    if (status != STATUS_SUCCESS) {
+        print_error("couldn't delete device symbolic link\n");
+    }
+    RtlInitUnicodeString(&pipe_name, NFS41_SHADOW_PIPE_NAME);
+    status = IoDeleteSymbolicLink(&pipe_name);
+    if (status != STATUS_SUCCESS) {
+        print_error("couldn't delete pipe symbolic link\n");
+    }
+    RxUnload(drv);
+
+    DbgP("driver unloaded %p\n", drv);
+    DbgR();
+}
diff --git a/reactos/drivers/filesystems/nfs/nfs41_driver.h b/reactos/drivers/filesystems/nfs/nfs41_driver.h
new file mode 100644 (file)
index 0000000..7b3d9fe
--- /dev/null
@@ -0,0 +1,93 @@
+/* NFSv4.1 client for Windows
+ * Copyright © 2012 The Regents of the University of Michigan
+ *
+ * Olga Kornievskaia <aglo@umich.edu>
+ * Casey Bodley <cbodley@umich.edu>
+ *
+ * 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 Street, Fifth Floor, Boston, MA
+ */
+
+#ifndef _NFS41_DRIVER_
+#define _NFS41_DRIVER_
+
+#define NFS41_DEVICE_NAME L"\\Device\\nfs41_driver"
+#define NFS41_SHADOW_DEVICE_NAME L"\\??\\nfs41_driver"
+#define NFS41_USER_DEVICE_NAME L"\\\\.\\nfs41_driver"
+#define NFS41_USER_DEVICE_NAME_A "\\\\.\\nfs41_driver"
+#define NFS41_PROVIDER_NAME_A "NFS41 Network"
+#define NFS41_PROVIDER_NAME_U L"NFS41 Network"
+
+#define NFS41_PIPE_NAME L"\\Device\\nfs41_pipe"
+#define NFS41_SHADOW_PIPE_NAME L"\\??\\nfs41_pipe"
+#define NFS41_USER_PIPE_NAME L"\\\\.\\nfs41_pipe"
+
+#define NFS41_SHARED_MEMORY_NAME L"\\BaseNamedObjects\\nfs41_shared_memory"
+#define NFS41_USER_SHARED_MEMORY_NAME "Global\\nfs41_shared_memory"
+
+// See "Defining I/O Control Codes" in WDK docs
+#define _RDR_CTL_CODE(code, method) \
+    CTL_CODE(FILE_DEVICE_NETWORK_REDIRECTOR, 0x800 | (code), method, FILE_ANY_ACCESS)
+
+#define IOCTL_NFS41_START       _RDR_CTL_CODE(0, METHOD_BUFFERED)
+#define IOCTL_NFS41_STOP        _RDR_CTL_CODE(1, METHOD_NEITHER)
+#define IOCTL_NFS41_GETSTATE    _RDR_CTL_CODE(3, METHOD_NEITHER)
+#define IOCTL_NFS41_ADDCONN     _RDR_CTL_CODE(4, METHOD_BUFFERED)
+#define IOCTL_NFS41_DELCONN     _RDR_CTL_CODE(5, METHOD_BUFFERED)
+#define IOCTL_NFS41_READ        _RDR_CTL_CODE(6, METHOD_BUFFERED)
+#define IOCTL_NFS41_WRITE       _RDR_CTL_CODE(7, METHOD_BUFFERED)
+#define IOCTL_NFS41_INVALCACHE  _RDR_CTL_CODE(8, METHOD_BUFFERED)
+
+typedef enum _nfs41_opcodes {
+    NFS41_MOUNT,
+    NFS41_UNMOUNT,
+    NFS41_OPEN,
+    NFS41_CLOSE,
+    NFS41_READ,
+    NFS41_WRITE,
+    NFS41_LOCK,
+    NFS41_UNLOCK,
+    NFS41_DIR_QUERY,
+    NFS41_FILE_QUERY,
+    NFS41_FILE_SET,
+    NFS41_EA_GET,
+    NFS41_EA_SET,
+    NFS41_SYMLINK,
+    NFS41_VOLUME_QUERY,
+    NFS41_ACL_QUERY,
+    NFS41_ACL_SET,
+    NFS41_SHUTDOWN,
+    INVALID_OPCODE
+} nfs41_opcodes;
+
+enum rpcsec_flavors {
+    RPCSEC_AUTH_SYS,
+    RPCSEC_AUTHGSS_KRB5,
+    RPCSEC_AUTHGSS_KRB5I,
+    RPCSEC_AUTHGSS_KRB5P
+};
+
+typedef enum _nfs41_init_driver_state {
+   NFS41_INIT_DRIVER_STARTABLE,
+   NFS41_INIT_DRIVER_START_IN_PROGRESS,
+   NFS41_INIT_DRIVER_STARTED
+} nfs41_init_driver_state;
+
+typedef enum _nfs41_start_driver_state {
+   NFS41_START_DRIVER_STARTABLE,
+   NFS41_START_DRIVER_START_IN_PROGRESS,
+   NFS41_START_DRIVER_STARTED,
+   NFS41_START_DRIVER_STOPPED
+} nfs41_start_driver_state;
+#endif