From 42e5eee699e12857c72be706fe8303f543b25244 Mon Sep 17 00:00:00 2001 From: Pierre Schweitzer Date: Sun, 18 Jun 2017 20:49:46 +0000 Subject: [PATCH] [NFS] 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 --- reactos/dll/CMakeLists.txt | 1 + reactos/dll/np/CMakeLists.txt | 1 + reactos/dll/np/nfs/CMakeLists.txt | 17 + reactos/dll/np/nfs/dllmain.c | 74 + reactos/dll/np/nfs/nfs41_np.c | 910 +++ reactos/dll/np/nfs/nfs41_np.h | 50 + reactos/dll/np/nfs/nfs41_np.spec | 13 + reactos/dll/np/nfs/nfsnp.rc | 5 + reactos/dll/np/nfs/options.c | 102 + reactos/dll/np/nfs/options.h | 82 + reactos/drivers/filesystems/CMakeLists.txt | 1 + .../drivers/filesystems/nfs/CMakeLists.txt | 21 + reactos/drivers/filesystems/nfs/nfs.rc | 5 + reactos/drivers/filesystems/nfs/nfs41_debug.c | 749 ++ reactos/drivers/filesystems/nfs/nfs41_debug.h | 102 + .../drivers/filesystems/nfs/nfs41_driver.c | 7140 +++++++++++++++++ .../drivers/filesystems/nfs/nfs41_driver.h | 93 + 17 files changed, 9366 insertions(+) create mode 100644 reactos/dll/np/CMakeLists.txt create mode 100644 reactos/dll/np/nfs/CMakeLists.txt create mode 100644 reactos/dll/np/nfs/dllmain.c create mode 100644 reactos/dll/np/nfs/nfs41_np.c create mode 100644 reactos/dll/np/nfs/nfs41_np.h create mode 100644 reactos/dll/np/nfs/nfs41_np.spec create mode 100644 reactos/dll/np/nfs/nfsnp.rc create mode 100644 reactos/dll/np/nfs/options.c create mode 100644 reactos/dll/np/nfs/options.h create mode 100644 reactos/drivers/filesystems/nfs/CMakeLists.txt create mode 100644 reactos/drivers/filesystems/nfs/nfs.rc create mode 100644 reactos/drivers/filesystems/nfs/nfs41_debug.c create mode 100644 reactos/drivers/filesystems/nfs/nfs41_debug.h create mode 100644 reactos/drivers/filesystems/nfs/nfs41_driver.c create mode 100644 reactos/drivers/filesystems/nfs/nfs41_driver.h diff --git a/reactos/dll/CMakeLists.txt b/reactos/dll/CMakeLists.txt index 3037cc67706..2e913af571e 100644 --- a/reactos/dll/CMakeLists.txt +++ b/reactos/dll/CMakeLists.txt @@ -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 index 00000000000..6a90722eefd --- /dev/null +++ b/reactos/dll/np/CMakeLists.txt @@ -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 index 00000000000..756be2e3079 --- /dev/null +++ b/reactos/dll/np/nfs/CMakeLists.txt @@ -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 index 00000000000..928f44d88a4 --- /dev/null +++ b/reactos/dll/np/nfs/dllmain.c @@ -0,0 +1,74 @@ +/* NFSv4.1 client for Windows + * Copyright © 2012 The Regents of the University of Michigan + * + * Olga Kornievskaia + * Casey Bodley + * + * 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 +#include + + +// 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 index 00000000000..d25f808baf5 --- /dev/null +++ b/reactos/dll/np/nfs/nfs41_np.c @@ -0,0 +1,910 @@ +/* NFSv4.1 client for Windows + * Copyright © 2012 The Regents of the University of Michigan + * + * Olga Kornievskaia + * Casey Bodley + * + * 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 +#include +#include +#include + +#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\;:\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 index 00000000000..710246e443a --- /dev/null +++ b/reactos/dll/np/nfs/nfs41_np.h @@ -0,0 +1,50 @@ +/* NFSv4.1 client for Windows + * Copyright © 2012 The Regents of the University of Michigan + * + * Olga Kornievskaia + * Casey Bodley + * + * 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 index 00000000000..8c04a7b2fd1 --- /dev/null +++ b/reactos/dll/np/nfs/nfs41_np.spec @@ -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 index 00000000000..10d170f16be --- /dev/null +++ b/reactos/dll/np/nfs/nfsnp.rc @@ -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 diff --git a/reactos/dll/np/nfs/options.c b/reactos/dll/np/nfs/options.c new file mode 100644 index 00000000000..f32b1fe4c52 --- /dev/null +++ b/reactos/dll/np/nfs/options.c @@ -0,0 +1,102 @@ +/* NFSv4.1 client for Windows + * Copyright © 2012 The Regents of the University of Michigan + * + * Olga Kornievskaia + * Casey Bodley + * + * 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 +#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 index 00000000000..5cd057c3891 --- /dev/null +++ b/reactos/dll/np/nfs/options.h @@ -0,0 +1,82 @@ +/* NFSv4.1 client for Windows + * Copyright © 2012 The Regents of the University of Michigan + * + * Olga Kornievskaia + * Casey Bodley + * + * 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__ */ diff --git a/reactos/drivers/filesystems/CMakeLists.txt b/reactos/drivers/filesystems/CMakeLists.txt index ef6e644f571..4cb3c5adf3a 100644 --- a/reactos/drivers/filesystems/CMakeLists.txt +++ b/reactos/drivers/filesystems/CMakeLists.txt @@ -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 index 00000000000..4f44cf4349a --- /dev/null +++ b/reactos/drivers/filesystems/nfs/CMakeLists.txt @@ -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 index 00000000000..c07c7009b67 --- /dev/null +++ b/reactos/drivers/filesystems/nfs/nfs.rc @@ -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 diff --git a/reactos/drivers/filesystems/nfs/nfs41_debug.c b/reactos/drivers/filesystems/nfs/nfs41_debug.c new file mode 100644 index 00000000000..29b8dd7abdc --- /dev/null +++ b/reactos/drivers/filesystems/nfs/nfs41_debug.c @@ -0,0 +1,749 @@ +/* NFSv4.1 client for Windows + * Copyright © 2012 The Regents of the University of Michigan + * + * Olga Kornievskaia + * Casey Bodley + * + * 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 + +#include "nfs41_driver.h" +#include "nfs41_debug.h" +#include +#include +#include +#include + +#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(×tamp); + ExSystemTimeToLocalTime(×tamp,&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(×tamp); + ExSystemTimeToLocalTime(×tamp,&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(×tamp); + ExSystemTimeToLocalTime(×tamp,&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 index 00000000000..4bc4e9ad153 --- /dev/null +++ b/reactos/drivers/filesystems/nfs/nfs41_debug.h @@ -0,0 +1,102 @@ +/* NFSv4.1 client for Windows + * Copyright © 2012 The Regents of the University of Michigan + * + * Olga Kornievskaia + * Casey Bodley + * + * 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 index 00000000000..31ab6b6bfdf --- /dev/null +++ b/reactos/drivers/filesystems/nfs/nfs41_driver.c @@ -0,0 +1,7140 @@ +/* NFSv4.1 client for Windows + * Copyright © 2012 The Regents of the University of Michigan + * + * Olga Kornievskaia + * Casey Bodley + * + * 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 +#include +#include + +#include + +#ifdef __REACTOS__ +#include +#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(¤t_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 index 00000000000..7b3d9fe0311 --- /dev/null +++ b/reactos/drivers/filesystems/nfs/nfs41_driver.h @@ -0,0 +1,93 @@ +/* NFSv4.1 client for Windows + * Copyright © 2012 The Regents of the University of Michigan + * + * Olga Kornievskaia + * Casey Bodley + * + * 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 -- 2.17.1