diff --git a/src/dll/library.def b/src/dll/library.def index b0b591f9..0f5fc5e4 100644 --- a/src/dll/library.def +++ b/src/dll/library.def @@ -3,3 +3,6 @@ EXPORTS DllUnregisterServer PRIVATE NPGetCaps PRIVATE NPGetConnection PRIVATE + NPAddConnection PRIVATE + NPAddConnection3 PRIVATE + NPCancelConnection PRIVATE diff --git a/src/dll/np.c b/src/dll/np.c index 977554f5..ab3632d4 100644 --- a/src/dll/np.c +++ b/src/dll/np.c @@ -16,6 +16,7 @@ */ #include +#include #include #define FSP_NP_NAME LIBRARY_NAME ".Np" @@ -33,14 +34,17 @@ DWORD APIENTRY NPGetCaps(DWORD Index) return 0; case WNNC_CONNECTION: /* - * WNNC_CON_ADDCONECTION + * WNNC_CON_ADDCONNECTION * WNNC_CON_CANCELCONNECTION * WNNC_CON_GETCONNECTIONS - * WNNC_CON_ADDCONECTION3 + * WNNC_CON_ADDCONNECTION3 * WNNC_CON_GETPERFORMANCE * WNNC_CON_DEFER */ - return WNNC_CON_GETCONNECTIONS; + return + WNNC_CON_GETCONNECTIONS | + WNNC_CON_ADDCONNECTION | WNNC_CON_ADDCONNECTION3 | + WNNC_CON_CANCELCONNECTION; case WNNC_DIALOG: /* * WNNC_DLG_DEVICEMODE @@ -75,6 +79,88 @@ DWORD APIENTRY NPGetCaps(DWORD Index) } } +static inline BOOLEAN FspNpCheckLocalName(PWSTR LocalName) +{ + return 0 != LocalName && + ( + (L'A' <= LocalName[0] && LocalName[0] <= L'Z') || + (L'a' <= LocalName[0] && LocalName[0] <= L'z') + ) && + L':' == LocalName[1] || L'\0' == LocalName[2]; +} + +static inline BOOLEAN FspNpCheckRemoteName(PWSTR RemoteName) +{ + return 0 != RemoteName && L'\\' == RemoteName[0] && L'\\' == RemoteName[1] && + sizeof(((FSP_FSCTL_VOLUME_PARAMS *)0)->Prefix) / sizeof(WCHAR) >= lstrlenW(RemoteName); +} + +static inline BOOLEAN FspNpParseRemoteName(PWSTR RemoteName, + PWSTR *PClassName, PULONG PClassNameLen, + PWSTR *PInstanceName, PULONG PInstanceNameLen) +{ + PWSTR ClassName, InstanceName, P; + ULONG ClassNameLen, InstanceNameLen; + + ClassName = RemoteName + 2; /* skip \\ */ + for (P = ClassName; *P; P++) + if (L'\\' == *P) + break; + if (ClassName == P || L'\\' != *P) + return FALSE; + ClassNameLen = (ULONG)(P - ClassName); + + InstanceName = P + 1; + for (P = InstanceName; *P; P++) + ; + for (;;) + { + if (InstanceName == P) + return FALSE; + if (L'\\' != P[-1]) + break; + P--; + } + InstanceNameLen = (ULONG)(P - InstanceName); + + *PClassName = ClassName; *PClassNameLen = ClassNameLen; + *PInstanceName = InstanceName; *PInstanceNameLen = InstanceNameLen; + + return TRUE; +} + +static inline DWORD FspNpCallLauncherPipe(PWSTR PipeBuf, ULONG SendSize, ULONG RecvSize) +{ + DWORD NpResult; + DWORD LastError, BytesTransferred; + + LastError = CallNamedPipeW(L"" LAUNCHER_PIPE_NAME, PipeBuf, SendSize, PipeBuf, RecvSize, + &BytesTransferred, NMPWAIT_USE_DEFAULT_WAIT) ? 0 : GetLastError(); + + if (0 != LastError) + 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]) + { + if (BytesTransferred < RecvSize) + PipeBuf[BytesTransferred / sizeof(WCHAR)] = L'\0'; + else + PipeBuf[RecvSize / sizeof(WCHAR) - 1] = L'\0'; + + if (0 == lstrcmpW(L"183"/*ERROR_ALREADY_EXISTS*/, PipeBuf + 1)) + NpResult = WN_ALREADY_CONNECTED; + else + NpResult = WN_NO_NETWORK; + } + else + NpResult = WN_NO_NETWORK; + + return NpResult; +} + static NTSTATUS FspNpGetVolumeList( PWCHAR *PVolumeListBuf, PSIZE_T PVolumeListSize) { @@ -107,7 +193,8 @@ static NTSTATUS FspNpGetVolumeList( } } -DWORD APIENTRY NPGetConnection(LPWSTR lpLocalName, LPWSTR lpRemoteName, LPDWORD lpnBufferLen) +DWORD APIENTRY NPGetConnection( + LPWSTR lpLocalName, LPWSTR lpRemoteName, LPDWORD lpnBufferLen) { DWORD NpResult; NTSTATUS Result; @@ -117,15 +204,10 @@ DWORD APIENTRY NPGetConnection(LPWSTR lpLocalName, LPWSTR lpRemoteName, LPDWORD SIZE_T VolumeListSize, VolumeNameSize; ULONG Backslashes; - if (0 == lpLocalName || - !( - (L'A' <= lpLocalName[0] && lpLocalName[0] <= L'Z') || - (L'a' <= lpLocalName[0] && lpLocalName[0] <= L'z') - ) || - L':' != lpLocalName[1]) + if (!FspNpCheckLocalName(lpLocalName)) return WN_BAD_LOCALNAME; - LocalNameBuf[0] = lpLocalName[0]; + LocalNameBuf[0] = lpLocalName[0] & ~0x20; /* convert to uppercase */ LocalNameBuf[1] = L':'; LocalNameBuf[2] = L'\0'; @@ -188,6 +270,111 @@ DWORD APIENTRY NPGetConnection(LPWSTR lpLocalName, LPWSTR lpRemoteName, LPDWORD return NpResult; } +DWORD APIENTRY NPAddConnection(LPNETRESOURCEW lpNetResource, LPWSTR lpPassword, LPWSTR lpUserName) +{ + return NPAddConnection3(0, lpNetResource, lpPassword, lpUserName, 0); +} + +DWORD APIENTRY NPAddConnection3(HWND hwndOwner, + LPNETRESOURCEW lpNetResource, LPWSTR lpPassword, LPWSTR lpUserName, DWORD dwFlags) +{ + DWORD NpResult; + DWORD dwType = lpNetResource->dwType; + LPWSTR lpRemoteName = lpNetResource->lpRemoteName; + LPWSTR lpLocalName = lpNetResource->lpLocalName; + WCHAR LocalNameBuf[3]; + PWSTR ClassName, InstanceName, RemoteName, P; + ULONG ClassNameLen, InstanceNameLen; + PWSTR PipeBuf = 0; + + if (dwType & RESOURCETYPE_PRINT) + return WN_BAD_VALUE; + + if (!FspNpCheckRemoteName(lpRemoteName)) + return WN_BAD_NETNAME; + + if (!FspNpParseRemoteName(lpRemoteName, + &ClassName, &ClassNameLen, &InstanceName, &InstanceNameLen)) + return WN_BAD_NETNAME; + RemoteName = lpRemoteName + 1; + + LocalNameBuf[0] = L'\0'; + if (0 != lpLocalName && L'\0' != lpLocalName[0]) + { + if (!FspNpCheckLocalName(lpLocalName)) + return WN_BAD_LOCALNAME; + + LocalNameBuf[0] = lpLocalName[0] & ~0x20; /* convert to uppercase */ + LocalNameBuf[1] = L':'; + LocalNameBuf[2] = L'\0'; + + if (GetLogicalDrives() & (1 << (LocalNameBuf[0] - 'A'))) + return WN_ALREADY_CONNECTED; + } + + PipeBuf = MemAlloc(LAUNCHER_PIPE_BUFFER_SIZE); + if (0 == PipeBuf) + return WN_OUT_OF_MEMORY; + + P = PipeBuf; + *P++ = 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; + + NpResult = FspNpCallLauncherPipe( + PipeBuf, (ULONG)(P - PipeBuf) * sizeof(WCHAR), LAUNCHER_PIPE_BUFFER_SIZE); + + MemFree(PipeBuf); + + return NpResult; +} + +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; + ULONG ClassNameLen, InstanceNameLen; + PWSTR PipeBuf = 0; + + if (FspNpCheckLocalName(lpName)) + { + RemoteNameSize = sizeof RemoteNameBuf / sizeof(WCHAR); + NpResult = NPGetConnection(lpName, RemoteNameBuf, &RemoteNameSize); + if (WN_SUCCESS != NpResult) + return NpResult; + + RemoteName = RemoteNameBuf; + } + else if (FspNpCheckRemoteName(lpName)) + RemoteName = lpName; + else + return WN_BAD_NETNAME; + + if (!FspNpParseRemoteName(RemoteName, + &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'; + + NpResult = FspNpCallLauncherPipe( + PipeBuf, (ULONG)(P - PipeBuf) * sizeof(WCHAR), LAUNCHER_PIPE_BUFFER_SIZE); + + MemFree(PipeBuf); + + return NpResult; +} + NTSTATUS FspNpRegister(VOID) { extern HINSTANCE DllInstance; diff --git a/src/launcher/launchctl.c b/src/launcher/launchctl.c index b9d68d06..f97047fc 100644 --- a/src/launcher/launchctl.c +++ b/src/launcher/launchctl.c @@ -65,14 +65,14 @@ static int call_pipe_and_report(PWSTR PipeBuf, ULONG SendSize, ULONG RecvSize) { DWORD LastError, BytesTransferred; - LastError = CallNamedPipeW(L"" PIPE_NAME, PipeBuf, SendSize, PipeBuf, RecvSize, + LastError = CallNamedPipeW(L"" LAUNCHER_PIPE_NAME, PipeBuf, SendSize, PipeBuf, RecvSize, &BytesTransferred, NMPWAIT_USE_DEFAULT_WAIT) ? 0 : GetLastError(); if (0 != LastError) warn("KO CallNamedPipeW = %ld", LastError); - else if (0 == BytesTransferred) + else if (sizeof(WCHAR) > BytesTransferred) warn("KO launcher: empty buffer"); - else if (L'$' == PipeBuf[0]) + else if (LauncherSuccess == PipeBuf[0]) { if (sizeof(WCHAR) == BytesTransferred) info("OK"); @@ -97,7 +97,7 @@ static int call_pipe_and_report(PWSTR PipeBuf, ULONG SendSize, ULONG RecvSize) info("OK\n%S", PipeBuf + 1); } } - else if (L'!' == PipeBuf[0]) + else if (LauncherFailure == PipeBuf[0]) { if (BytesTransferred < RecvSize) PipeBuf[BytesTransferred / sizeof(WCHAR)] = L'\0'; @@ -213,7 +213,7 @@ int wmain(int argc, wchar_t **argv) PWSTR PipeBuf = 0; /* allocate our PipeBuf early on; freed on process exit by the system */ - PipeBuf = MemAlloc(PIPE_BUFFER_SIZE); + PipeBuf = MemAlloc(LAUNCHER_PIPE_BUFFER_SIZE); if (0 == PipeBuf) return ERROR_NO_SYSTEM_RESOURCES; @@ -228,7 +228,7 @@ int wmain(int argc, wchar_t **argv) if (3 > argc || argc > 12) usage(); - return start(PipeBuf, PIPE_BUFFER_SIZE, argv[1], argv[2], argc - 3, argv + 3); + return start(PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE, argv[1], argv[2], argc - 3, argv + 3); } else if (0 == lstrcmpW(L"stop", argv[0])) @@ -236,7 +236,7 @@ int wmain(int argc, wchar_t **argv) if (3 != argc) usage(); - return stop(PipeBuf, PIPE_BUFFER_SIZE, argv[1], argv[2]); + return stop(PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE, argv[1], argv[2]); } else if (0 == lstrcmpW(L"info", argv[0])) @@ -244,7 +244,7 @@ int wmain(int argc, wchar_t **argv) if (3 != argc) usage(); - return getinfo(PipeBuf, PIPE_BUFFER_SIZE, argv[1], argv[2]); + return getinfo(PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE, argv[1], argv[2]); } else if (0 == lstrcmpW(L"list", argv[0])) @@ -252,7 +252,7 @@ int wmain(int argc, wchar_t **argv) if (1 != argc) usage(); - return list(PipeBuf, PIPE_BUFFER_SIZE); + return list(PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE); } else if (0 == lstrcmpW(L"quit", argv[0])) @@ -261,7 +261,7 @@ int wmain(int argc, wchar_t **argv) usage(); /* works only against DEBUG version of launcher */ - return quit(PipeBuf, PIPE_BUFFER_SIZE); + return quit(PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE); } else usage(); diff --git a/src/launcher/launcher.c b/src/launcher/launcher.c index 2d3ec8df..b8461308 100644 --- a/src/launcher/launcher.c +++ b/src/launcher/launcher.c @@ -487,7 +487,7 @@ NTSTATUS SvcInstanceStop(HANDLE ClientToken, if (!NT_SUCCESS(Result)) goto exit; - KillProcess(SvcInstance->ProcessId, SvcInstance->Process, KILL_TIMEOUT); + KillProcess(SvcInstance->ProcessId, SvcInstance->Process, LAUNCHER_KILL_TIMEOUT); Result = STATUS_SUCCESS; @@ -588,12 +588,12 @@ NTSTATUS SvcInstanceStopAndWaitAll(VOID) { SvcInstance = CONTAINING_RECORD(ListEntry, SVC_INSTANCE, ListEntry); - KillProcess(SvcInstance->ProcessId, SvcInstance->Process, KILL_TIMEOUT); + KillProcess(SvcInstance->ProcessId, SvcInstance->Process, LAUNCHER_KILL_TIMEOUT); } LeaveCriticalSection(&SvcInstanceLock); - WaitForSingleObject(SvcInstanceEvent, STOP_TIMEOUT); + WaitForSingleObject(SvcInstanceEvent, LAUNCHER_STOP_TIMEOUT); return STATUS_SUCCESS; } @@ -621,7 +621,7 @@ static NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) SecurityAttributes.nLength = sizeof SecurityAttributes; SecurityAttributes.bInheritHandle = FALSE; - if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(L"" PIPE_SDDL, SDDL_REVISION_1, + if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(L"" LAUNCHER_PIPE_SDDL, SDDL_REVISION_1, &SecurityAttributes.lpSecurityDescriptor, 0)) goto fail; @@ -654,11 +654,12 @@ static NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) if (0 == SvcOverlapped.hEvent) goto fail; - SvcPipe = CreateNamedPipeW(L"" PIPE_NAME, + SvcPipe = CreateNamedPipeW(L"" LAUNCHER_PIPE_NAME, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS, - 1, PIPE_BUFFER_SIZE, PIPE_BUFFER_SIZE, PIPE_DEFAULT_TIMEOUT, &SecurityAttributes); + 1, LAUNCHER_PIPE_BUFFER_SIZE, LAUNCHER_PIPE_BUFFER_SIZE, LAUNCHER_PIPE_DEFAULT_TIMEOUT, + &SecurityAttributes); if (INVALID_HANDLE_VALUE == SvcPipe) goto fail; @@ -708,8 +709,8 @@ static NTSTATUS SvcStop(FSP_SERVICE *Service) if (GetCurrentThreadId() != SvcThreadId) { SetEvent(SvcEvent); - FspServiceRequestTime(Service, STOP_TIMEOUT); - WaitForSingleObject(SvcThread, STOP_TIMEOUT); + FspServiceRequestTime(Service, LAUNCHER_STOP_TIMEOUT); + WaitForSingleObject(SvcThread, LAUNCHER_STOP_TIMEOUT); } /* @@ -779,7 +780,7 @@ static DWORD WINAPI SvcPipeServer(PVOID Context) HANDLE ClientToken; DWORD LastError, BytesTransferred; - PipeBuf = MemAlloc(PIPE_BUFFER_SIZE); + PipeBuf = MemAlloc(LAUNCHER_PIPE_BUFFER_SIZE); if (0 == PipeBuf) { FspServiceSetExitCode(Service, ERROR_NO_SYSTEM_RESOURCES); @@ -802,7 +803,7 @@ static DWORD WINAPI SvcPipeServer(PVOID Context) } LastError = SvcPipeWaitResult( - ReadFile(SvcPipe, PipeBuf, PIPE_BUFFER_SIZE, &BytesTransferred, &SvcOverlapped), + ReadFile(SvcPipe, PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE, &BytesTransferred, &SvcOverlapped), SvcEvent, SvcPipe, &SvcOverlapped, &BytesTransferred); if (-1 == LastError) break; @@ -892,11 +893,12 @@ static inline VOID SvcPipeTransactResult(NTSTATUS Result, PWSTR PipeBuf, PULONG { if (NT_SUCCESS(Result)) { - *PipeBuf = L'$'; + *PipeBuf = LauncherSuccess; *PSize += sizeof(WCHAR); } else - *PSize = (wsprintfW(PipeBuf, L"!%ld", FspWin32FromNtStatus(Result)) + 1) * sizeof(WCHAR); + *PSize = (wsprintfW(PipeBuf, L"%c%ld", LauncherFailure, FspWin32FromNtStatus(Result)) + 1) * + sizeof(WCHAR); } static VOID SvcPipeTransact(HANDLE ClientToken, PWSTR PipeBuf, PULONG PSize) @@ -945,7 +947,7 @@ static VOID SvcPipeTransact(HANDLE ClientToken, PWSTR PipeBuf, PULONG PSize) Result = STATUS_INVALID_PARAMETER; if (0 != ClassName && 0 != InstanceName) { - *PSize = PIPE_BUFFER_SIZE - 1; + *PSize = LAUNCHER_PIPE_BUFFER_SIZE - 1; Result = SvcInstanceGetInfo(ClientToken, ClassName, InstanceName, PipeBuf + 1, PSize); } @@ -953,7 +955,7 @@ static VOID SvcPipeTransact(HANDLE ClientToken, PWSTR PipeBuf, PULONG PSize) break; case LauncherSvcInstanceList: - *PSize = PIPE_BUFFER_SIZE - 1; + *PSize = LAUNCHER_PIPE_BUFFER_SIZE - 1; Result = SvcInstanceGetNameList(ClientToken, PipeBuf + 1, PSize); SvcPipeTransactResult(Result, PipeBuf, PSize); diff --git a/src/launcher/launcher.h b/src/launcher/launcher.h index 42f24aa0..7f92e275 100644 --- a/src/launcher/launcher.h +++ b/src/launcher/launcher.h @@ -21,12 +21,12 @@ #include #include -#define STOP_TIMEOUT 5500 -#define KILL_TIMEOUT 5000 +#define LAUNCHER_STOP_TIMEOUT 5500 +#define LAUNCHER_KILL_TIMEOUT 5000 -#define PIPE_NAME "\\\\.\\pipe\\WinFsp.{14E7137D-22B4-437A-B0C1-D21D1BDF3767}" -#define PIPE_BUFFER_SIZE 2048 -#define PIPE_DEFAULT_TIMEOUT 3000 +#define LAUNCHER_PIPE_NAME "\\\\.\\pipe\\WinFsp.{14E7137D-22B4-437A-B0C1-D21D1BDF3767}" +#define LAUNCHER_PIPE_BUFFER_SIZE 2048 +#define LAUNCHER_PIPE_DEFAULT_TIMEOUT 3000 /* * The launcher named pipe SDDL gives full access to LocalSystem and Administrators. @@ -38,7 +38,7 @@ * the FILE_CREATE_PIPE_INSTANCE right is that the server creates the named pipe with * MaxInstances == 1 (and therefore no client can create additional instances). */ -#define PIPE_SDDL "D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;WD)" +#define LAUNCHER_PIPE_SDDL "D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;WD)" /* * The default service instance SDDL gives full access to LocalSystem and Administrators. @@ -60,6 +60,9 @@ enum LauncherSvcInstanceInfo = 'I', /* requires: SERVICE_QUERY_STATUS */ LauncherSvcInstanceList = 'L', /* requires: none*/ LauncherQuit = 'Q', /* DEBUG version only */ + + LauncherSuccess = '$', + LauncherFailure = '!', }; #endif diff --git a/tst/memfs/memfs-main.c b/tst/memfs/memfs-main.c index 8d1c1b81..86c87992 100644 --- a/tst/memfs/memfs-main.c +++ b/tst/memfs/memfs-main.c @@ -82,6 +82,9 @@ NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) if (arge > argp) goto usage; + if (MemfsDisk == Flags && 0 == MountPoint) + goto usage; + Result = MemfsCreate(Flags, FileInfoTimeout, MaxFileNodes, MaxFileSize, VolumePrefix, RootSddl, &Memfs); if (!NT_SUCCESS(Result)) @@ -90,12 +93,15 @@ NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) goto exit; } - Result = FspFileSystemSetMountPoint(MemfsFileSystem(Memfs), - MountPoint && MountPoint[0] ? MountPoint : 0); - if (!NT_SUCCESS(Result)) + if (0 != MountPoint && L'\0' != MountPoint[0]) { - fail(L"cannot mount MEMFS"); - goto exit; + Result = FspFileSystemSetMountPoint(MemfsFileSystem(Memfs), + L'*' == MountPoint[0] && L'\0' == MountPoint[1] ? 0 : MountPoint); + if (!NT_SUCCESS(Result)) + { + fail(L"cannot mount MEMFS"); + goto exit; + } } Result = MemfsStart(Memfs); @@ -107,11 +113,11 @@ NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) MountPoint = FspFileSystemMountPoint(MemfsFileSystem(Memfs)); - info(L"%s -t %ld -n %ld -s %ld%s%s%s%s -m %s", + info(L"%s -t %ld -n %ld -s %ld%s%s%s%s%s%s", L"" PROGNAME, FileInfoTimeout, MaxFileNodes, MaxFileSize, RootSddl ? L" -S " : L"", RootSddl ? RootSddl : L"", VolumePrefix ? L" -u " : L"", VolumePrefix ? VolumePrefix : L"", - MountPoint); + MountPoint ? L" -m " : L"", MountPoint ? MountPoint : L""); Service->UserContext = Memfs; Result = STATUS_SUCCESS; @@ -132,7 +138,7 @@ usage: " -s MaxFileSize [bytes]\n" " -S RootSddl [file rights: FA, etc; NO generic rights: GA, etc.]\n" " -u \\Server\\Share [UNC prefix (single backslash)]\n" - " -m MountPoint [X:]\n"; + " -m MountPoint [X:|* (required if no UNC prefix)]\n"; fail(usage, L"" PROGNAME);