diff --git a/build/VStudio/winfsp_dll.vcxproj b/build/VStudio/winfsp_dll.vcxproj index 784bfe71..54553b37 100644 --- a/build/VStudio/winfsp_dll.vcxproj +++ b/build/VStudio/winfsp_dll.vcxproj @@ -39,6 +39,7 @@ + diff --git a/build/VStudio/winfsp_dll.vcxproj.filters b/build/VStudio/winfsp_dll.vcxproj.filters index b93ce5e2..00f55d7f 100644 --- a/build/VStudio/winfsp_dll.vcxproj.filters +++ b/build/VStudio/winfsp_dll.vcxproj.filters @@ -115,6 +115,9 @@ Source + + Source + diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h index 6892acc9..d74b50aa 100644 --- a/inc/winfsp/winfsp.h +++ b/inc/winfsp/winfsp.h @@ -1703,6 +1703,133 @@ FSP_API NTSTATUS FspServiceContextCheck(HANDLE Token, PBOOLEAN PIsLocalSystem); FSP_API VOID FspServiceLog(ULONG Type, PWSTR Format, ...); FSP_API VOID FspServiceLogV(ULONG Type, PWSTR Format, va_list ap); +/** + * @group Launch Control + */ +/** + * Call launcher pipe. + * + * This function is used to send a command to the launcher and receive a response. + * + * @param Command + * Launcher command to send. For example, the 'L' launcher command instructs + * the launcher to list all running service instances. + * @param Argc + * Command argument count. May be 0. + * @param Argv + * Command argument array. May be NULL. + * @param Argl + * Command argument length array. May be NULL. If this is NULL all command arguments + * are assumed to be NULL-terminated strings. It is also possible for specific arguments + * to be NULL-terminated; in this case pass -1 in the corresponding Argl position. + * @param Buffer + * Buffer that receives the command response. May be NULL. + * @param PSize + * Pointer to a ULONG. On input it contains the size of the Buffer. On output it + * contains the number of bytes transferred. May be NULL. + * @param PLauncherError + * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. + * @return + * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher + * returns an error. Other status codes indicate a communication error. Launcher errors are + * reported through PLauncherError. + */ +FSP_API NTSTATUS FspLaunchCallLauncherPipe( + WCHAR Command, ULONG Argc, PWSTR *Argv, ULONG *Argl, + PWSTR Buffer, PULONG PSize, PULONG PLauncherError); +/** + * Start a service instance. + * + * @param ClassName + * Class name of the service instance to start. + * @param InstanceName + * Instance name of the service instance to start. + * @param Argc + * Service instance argument count. May be 0. + * @param Argv + * Service instance argument array. May be NULL. + * @param HasSecret + * Whether the last argument in Argv is assumed to be a secret (e.g. password) or not. + * Secrets are passed to service instances through standard input rather than the command + * line. + * @param PLauncherError + * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. + * @return + * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher + * returns an error. Other status codes indicate a communication error. Launcher errors are + * reported through PLauncherError. + */ +FSP_API NTSTATUS FspLaunchStart( + PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv0, + BOOLEAN HasSecret, + PULONG PLauncherError); +/** + * Stop a service instance. + * + * @param ClassName + * Class name of the service instance to stop. + * @param InstanceName + * Instance name of the service instance to stop. + * @param PLauncherError + * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. + * @return + * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher + * returns an error. Other status codes indicate a communication error. Launcher errors are + * reported through PLauncherError. + */ +FSP_API NTSTATUS FspLaunchStop( + PWSTR ClassName, PWSTR InstanceName, + PULONG PLauncherError); +/** + * Get information about a service instance. + * + * The information is a list of NULL-terminated strings: the class name of the service instance, + * the instance name of the service instance and the full command line used to start the service + * instance. + * + * @param ClassName + * Class name of the service instance to stop. + * @param InstanceName + * Instance name of the service instance to stop. + * @param Buffer + * Buffer that receives the command response. May be NULL. + * @param PSize + * Pointer to a ULONG. On input it contains the size of the Buffer. On output it + * contains the number of bytes transferred. May be NULL. + * @param PLauncherError + * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. + * @return + * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher + * returns an error. Other status codes indicate a communication error. Launcher errors are + * reported through PLauncherError. + */ +FSP_API NTSTATUS FspLaunchGetInfo( + PWSTR ClassName, PWSTR InstanceName, + PWSTR Buffer, PULONG PSize, + PULONG PLauncherError); +/** + * List service instances. + * + * The information is a list of pairs of NULL-terminated strings. Each pair contains the class + * name and instance name of a service instance. All currently running service instances are + * listed. + * + * @param Buffer + * Buffer that receives the command response. May be NULL. + * @param PSize + * Pointer to a ULONG. On input it contains the size of the Buffer. On output it + * contains the number of bytes transferred. May be NULL. + * @param PLauncherError + * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. + * @return + * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher + * returns an error. Other status codes indicate a communication error. Launcher errors are + * reported through PLauncherError. + */ +FSP_API NTSTATUS FspLaunchGetNameList( + PWSTR Buffer, PULONG PSize, + PULONG PLauncherError); + /* * Utility */ diff --git a/src/dll/fs.c b/src/dll/fs.c index 4da26737..66a277b9 100644 --- a/src/dll/fs.c +++ b/src/dll/fs.c @@ -16,7 +16,6 @@ */ #include -#include enum { @@ -191,64 +190,25 @@ FSP_API VOID FspFileSystemDelete(FSP_FILE_SYSTEM *FileSystem) static NTSTATUS FspFileSystemLauncherDefineDosDevice( WCHAR Sign, PWSTR MountPoint, PWSTR VolumeName) { - NTSTATUS Result; - ULONG MountPointLen, VolumeNameLen; - PWSTR PipeBuf = 0, P; - DWORD BytesTransferred; - - MountPointLen = lstrlenW(MountPoint); - VolumeNameLen = lstrlenW(VolumeName); - - if (2 != MountPointLen || - FSP_FSCTL_VOLUME_NAME_SIZEMAX / sizeof(WCHAR) <= VolumeNameLen) + if (2 != lstrlenW(MountPoint) || + FSP_FSCTL_VOLUME_NAME_SIZEMAX / sizeof(WCHAR) <= lstrlenW(VolumeName)) return STATUS_INVALID_PARAMETER; - PipeBuf = MemAlloc(LAUNCHER_PIPE_BUFFER_SIZE); - if (0 == PipeBuf) - { - Result = STATUS_INSUFFICIENT_RESOURCES; - goto exit; - } + WCHAR Argv0[4]; + PWSTR Argv[2]; + NTSTATUS Result; + ULONG ErrorCode; - P = PipeBuf; - *P++ = LauncherDefineDosDevice; - *P++ = Sign; - memcpy(P, MountPoint, MountPointLen * sizeof(WCHAR)); P += MountPointLen; *P++ = L'\0'; - memcpy(P, VolumeName, VolumeNameLen * sizeof(WCHAR)); P += VolumeNameLen; *P++ = L'\0'; + Argv0[0] = Sign; + Argv0[1] = MountPoint[0]; + Argv0[2] = MountPoint[1]; + Argv0[3] = L'\0'; - Result = FspCallNamedPipeSecurely(L"" LAUNCHER_PIPE_NAME, - PipeBuf, (ULONG)(P - PipeBuf) * sizeof(WCHAR), PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE, - &BytesTransferred, NMPWAIT_USE_DEFAULT_WAIT, LAUNCHER_PIPE_OWNER); - if (!NT_SUCCESS(Result)) - goto exit; + Argv[0] = Argv0; + Argv[1] = VolumeName; - if (sizeof(WCHAR) > BytesTransferred) - Result = RPC_NT_PROTOCOL_ERROR; - else if (LauncherSuccess == PipeBuf[0]) - Result = STATUS_SUCCESS; - else if (LauncherFailure == PipeBuf[0]) - { - DWORD ErrorCode = 0; - - for (PWSTR P = PipeBuf + 1, EndP = PipeBuf + BytesTransferred / sizeof(WCHAR); EndP > P; P++) - { - if (L'0' > *P || *P > L'9') - break; - - ErrorCode = 10 * ErrorCode + (*P - L'0'); - } - - Result = FspNtStatusFromWin32(ErrorCode); - if (0 == Result) - Result = RPC_NT_PROTOCOL_ERROR; - } - else - Result = RPC_NT_PROTOCOL_ERROR; - -exit: - MemFree(PipeBuf); - - return Result; + Result = FspLaunchCallLauncherPipe('D', 2, Argv, 0, 0, 0, &ErrorCode); + return !NT_SUCCESS(Result) ? Result : FspNtStatusFromWin32(ErrorCode); } static NTSTATUS FspFileSystemSetMountPoint_Drive(PWSTR MountPoint, PWSTR VolumeName, diff --git a/src/dll/launch.c b/src/dll/launch.c new file mode 100644 index 00000000..03042f4c --- /dev/null +++ b/src/dll/launch.c @@ -0,0 +1,152 @@ +/** + * @file dll/launch.c + * + * @copyright 2015-2018 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * software. + */ + +#include +#include + +FSP_API NTSTATUS FspLaunchCallLauncherPipe( + WCHAR Command, ULONG Argc, PWSTR *Argv, ULONG *Argl, + PWSTR Buffer, PULONG PSize, PULONG PLauncherError) +{ + PWSTR PipeBuf = 0, P; + ULONG Length, BytesTransferred; + NTSTATUS Result; + ULONG ErrorCode; + + if (0 != PSize) + *PSize = 0; + *PLauncherError = 0; + + PipeBuf = MemAlloc(LAUNCHER_PIPE_BUFFER_SIZE); + if (0 == PipeBuf) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + + P = PipeBuf; + *P++ = Command; + for (ULONG I = 0; Argc > I; I++) + if (0 != Argv[I]) + { + Length = 0 == Argl || -1 == Argl[I] ? lstrlenW(Argv[I]) : Argl[I]; + if (LAUNCHER_PIPE_BUFFER_SIZE < ((ULONG)(P - PipeBuf) + Length + 1) * sizeof(WCHAR)) + { + Result = STATUS_INVALID_PARAMETER; + goto exit; + } + memcpy(P, Argv[I], Length * sizeof(WCHAR)); P += Length; *P++ = L'\0'; + } + + Result = FspCallNamedPipeSecurely(L"" LAUNCHER_PIPE_NAME, + PipeBuf, (ULONG)(P - PipeBuf) * sizeof(WCHAR), PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE, + &BytesTransferred, NMPWAIT_USE_DEFAULT_WAIT, LAUNCHER_PIPE_OWNER); + if (!NT_SUCCESS(Result)) + goto exit; + + Result = STATUS_SUCCESS; + ErrorCode = ERROR_BROKEN_PIPE; /* protocol error! */ + if (sizeof(WCHAR) <= BytesTransferred) + { + if (LauncherSuccess == PipeBuf[0]) + { + ErrorCode = 0; + + if (0 != PSize) + { + BytesTransferred -= sizeof(WCHAR); + memcpy(Buffer, PipeBuf, *PSize < BytesTransferred ? *PSize : BytesTransferred); + *PSize = BytesTransferred; + } + } + else if (LauncherFailure == PipeBuf[0]) + { + ErrorCode = 0; + + for (PWSTR P = PipeBuf + 1, EndP = PipeBuf + BytesTransferred / sizeof(WCHAR); EndP > P; P++) + { + if (L'0' > *P || *P > L'9') + break; + ErrorCode = 10 * ErrorCode + (*P - L'0'); + } + + if (0 == ErrorCode) + ErrorCode = ERROR_BROKEN_PIPE; /* protocol error! */ + } + } + + *PLauncherError = ErrorCode; + +exit: + MemFree(PipeBuf); + + return Result; +} + +FSP_API NTSTATUS FspLaunchStart( + PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv0, + BOOLEAN HasSecret, + PULONG PLauncherError) +{ + PWSTR Argv[9 + 2]; + + if (9 < Argc) + return STATUS_INVALID_PARAMETER; + + Argv[0] = ClassName; + Argv[1] = InstanceName; + memcpy(Argv + 2, Argv, Argc * sizeof(PWSTR)); + + return FspLaunchCallLauncherPipe( + HasSecret ? LauncherSvcInstanceStartWithSecret : LauncherSvcInstanceStart, + Argc + 2, Argv, 0, 0, 0, PLauncherError); +} + +FSP_API NTSTATUS FspLaunchStop( + PWSTR ClassName, PWSTR InstanceName, + PULONG PLauncherError) +{ + PWSTR Argv[2]; + + Argv[0] = ClassName; + Argv[1] = InstanceName; + + return FspLaunchCallLauncherPipe(LauncherSvcInstanceStop, + 2, Argv, 0, 0, 0, PLauncherError); +} + +FSP_API NTSTATUS FspLaunchGetInfo( + PWSTR ClassName, PWSTR InstanceName, + PWSTR Buffer, PULONG PSize, + PULONG PLauncherError) +{ + PWSTR Argv[2]; + + Argv[0] = ClassName; + Argv[1] = InstanceName; + + return FspLaunchCallLauncherPipe(LauncherSvcInstanceInfo, + 2, Argv, 0, Buffer, PSize, PLauncherError); +} + +FSP_API NTSTATUS FspLaunchGetNameList( + PWSTR Buffer, PULONG PSize, + PULONG PLauncherError) +{ + return FspLaunchCallLauncherPipe(LauncherSvcInstanceList, + 0, 0, 0, Buffer, PSize, PLauncherError); +} diff --git a/src/dll/np.c b/src/dll/np.c index ff9955f6..e0f95cb1 100644 --- a/src/dll/np.c +++ b/src/dll/np.c @@ -168,39 +168,17 @@ static inline BOOLEAN FspNpParseRemoteUserName(PWSTR RemoteName, return FALSE; } -static inline DWORD FspNpCallLauncherPipe(PWSTR PipeBuf, ULONG SendSize, ULONG RecvSize) +static inline DWORD FspNpCallLauncherPipe( + WCHAR Command, ULONG Argc, PWSTR *Argv, ULONG *Argl, + PWSTR Buffer, PULONG PSize) { - DWORD NpResult; NTSTATUS Result; - DWORD BytesTransferred; + ULONG ErrorCode; - Result = FspCallNamedPipeSecurely(L"" LAUNCHER_PIPE_NAME, PipeBuf, SendSize, PipeBuf, RecvSize, - &BytesTransferred, NMPWAIT_USE_DEFAULT_WAIT, LAUNCHER_PIPE_OWNER); - - if (!NT_SUCCESS(Result)) - NpResult = WN_NO_NETWORK; - else if (sizeof(WCHAR) > BytesTransferred) - NpResult = WN_NO_NETWORK; - else if (LauncherSuccess == PipeBuf[0]) - NpResult = WN_SUCCESS; - else if (LauncherFailure == PipeBuf[0]) - { - NpResult = 0; - for (PWSTR P = PipeBuf + 1, EndP = PipeBuf + BytesTransferred / sizeof(WCHAR); EndP > P; P++) - { - if (L'0' > *P || *P > L'9') - break; - - NpResult = 10 * NpResult + (*P - L'0'); - } - - if (0 == NpResult) - NpResult = WN_NO_NETWORK; - } - else - NpResult = WN_NO_NETWORK; - - return NpResult; + Result = FspLaunchCallLauncherPipe(Command, Argc, Argv, Argl, Buffer, PSize, &ErrorCode); + return !NT_SUCCESS(Result) ? + WN_NO_NETWORK : + (ERROR_BROKEN_PIPE == ErrorCode ? WN_NO_NETWORK : ErrorCode); } static NTSTATUS FspNpGetVolumeList( @@ -490,7 +468,9 @@ DWORD APIENTRY NPAddConnection(LPNETRESOURCEW lpNetResource, LPWSTR lpPassword, PWSTR ClassName, InstanceName, RemoteName, P; ULONG ClassNameLen, InstanceNameLen; DWORD CredentialsKind; - PWSTR PipeBuf = 0; + ULONG Argc; + PWSTR Argv[6]; + ULONG Argl[6]; #if defined(FSP_NP_CREDENTIAL_MANAGER) PCREDENTIALW Credential = 0; #endif @@ -556,32 +536,24 @@ DWORD APIENTRY NPAddConnection(LPNETRESOURCEW lpNetResource, LPWSTR lpPassword, } } - PipeBuf = MemAlloc(LAUNCHER_PIPE_BUFFER_SIZE); - if (0 == PipeBuf) - { - NpResult = WN_OUT_OF_MEMORY; - goto exit; - } - - /* we do not explicitly check, but assumption is it all fits in LAUNCHER_PIPE_BUFFER_SIZE */ - P = PipeBuf; - *P++ = FSP_NP_CREDENTIALS_NONE != CredentialsKind ? - LauncherSvcInstanceStartWithSecret : LauncherSvcInstanceStart; - memcpy(P, ClassName, ClassNameLen * sizeof(WCHAR)); P += ClassNameLen; *P++ = L'\0'; - memcpy(P, InstanceName, InstanceNameLen * sizeof(WCHAR)); P += InstanceNameLen; *P++ = L'\0'; - lstrcpyW(P, RemoteName); P += lstrlenW(RemoteName) + 1; - lstrcpyW(P, LocalNameBuf); P += lstrlenW(LocalNameBuf) + 1; + Argc = 0; + Argv[Argc] = ClassName; Argl[Argc] = ClassNameLen; Argc++; + Argv[Argc] = InstanceName; Argl[Argc] = InstanceNameLen; Argc++; + Argv[Argc] = RemoteName; Argl[Argc] = -1; Argc++; + Argv[Argc] = LocalNameBuf; Argl[Argc] = -1; Argc++; if (FSP_NP_CREDENTIALS_USERPASS == CredentialsKind) { - lstrcpyW(P, lpUserName); P += lstrlenW(lpUserName) + 1; + Argv[Argc] = lpUserName; Argl[Argc] = -1; Argc++; } if (FSP_NP_CREDENTIALS_NONE != CredentialsKind) { - lstrcpyW(P, lpPassword); P += lstrlenW(lpPassword) + 1; + Argv[Argc] = lpPassword; Argl[Argc] = -1; Argc++; } NpResult = FspNpCallLauncherPipe( - PipeBuf, (ULONG)(P - PipeBuf) * sizeof(WCHAR), LAUNCHER_PIPE_BUFFER_SIZE); + FSP_NP_CREDENTIALS_NONE != CredentialsKind ? + LauncherSvcInstanceStartWithSecret : LauncherSvcInstanceStart, + Argc, Argv, Argl, 0, 0); switch (NpResult) { case WN_SUCCESS: @@ -627,13 +599,13 @@ DWORD APIENTRY NPAddConnection(LPNETRESOURCEW lpNetResource, LPWSTR lpPassword, break; } - P = PipeBuf; - *P++ = LauncherSvcInstanceInfo; - memcpy(P, ClassName, ClassNameLen * sizeof(WCHAR)); P += ClassNameLen; *P++ = L'\0'; - memcpy(P, InstanceName, InstanceNameLen * sizeof(WCHAR)); P += InstanceNameLen; *P++ = L'\0'; + Argc = 0; + Argv[Argc] = ClassName; Argl[Argc] = ClassNameLen; Argc++; + Argv[Argc] = InstanceName; Argl[Argc] = InstanceNameLen; Argc++; if (WN_SUCCESS != FspNpCallLauncherPipe( - PipeBuf, (ULONG)(P - PipeBuf) * sizeof(WCHAR), LAUNCHER_PIPE_BUFFER_SIZE)) + LauncherSvcInstanceInfo, + Argc, Argv, Argl, 0, 0)) { /* looks like the file system is gone! */ NpResult = WN_NO_NETWORK; @@ -677,8 +649,6 @@ DWORD APIENTRY NPAddConnection(LPNETRESOURCEW lpNetResource, LPWSTR lpPassword, } exit: - MemFree(PipeBuf); - #if defined(FSP_NP_CREDENTIAL_MANAGER) if (0 != Credential) CredFree(Credential); @@ -767,9 +737,11 @@ DWORD APIENTRY NPCancelConnection(LPWSTR lpName, BOOL fForce) DWORD NpResult; WCHAR RemoteNameBuf[sizeof(((FSP_FSCTL_VOLUME_PARAMS *)0)->Prefix) / sizeof(WCHAR)]; DWORD RemoteNameSize; - PWSTR ClassName, InstanceName, RemoteName, P; + PWSTR ClassName, InstanceName, RemoteName; ULONG ClassNameLen, InstanceNameLen; - PWSTR PipeBuf = 0; + ULONG Argc; + PWSTR Argv[2]; + ULONG Argl[2]; if (FspNpCheckLocalName(lpName)) { @@ -789,17 +761,13 @@ DWORD APIENTRY NPCancelConnection(LPWSTR lpName, BOOL fForce) &ClassName, &ClassNameLen, &InstanceName, &InstanceNameLen)) return WN_BAD_NETNAME; - PipeBuf = MemAlloc(LAUNCHER_PIPE_BUFFER_SIZE); - if (0 == PipeBuf) - return WN_OUT_OF_MEMORY; - - P = PipeBuf; - *P++ = LauncherSvcInstanceStop; - memcpy(P, ClassName, ClassNameLen * sizeof(WCHAR)); P += ClassNameLen; *P++ = L'\0'; - memcpy(P, InstanceName, InstanceNameLen * sizeof(WCHAR)); P += InstanceNameLen; *P++ = L'\0'; + Argc = 0; + Argv[Argc] = ClassName; Argl[Argc] = ClassNameLen; Argc++; + Argv[Argc] = InstanceName; Argl[Argc] = InstanceNameLen; Argc++; NpResult = FspNpCallLauncherPipe( - PipeBuf, (ULONG)(P - PipeBuf) * sizeof(WCHAR), LAUNCHER_PIPE_BUFFER_SIZE); + LauncherSvcInstanceStop, + Argc, Argv, Argl, 0, 0); switch (NpResult) { case WN_SUCCESS: @@ -812,8 +780,6 @@ DWORD APIENTRY NPCancelConnection(LPWSTR lpName, BOOL fForce) break; } - MemFree(PipeBuf); - return NpResult; }