diff --git a/inc/winfsp/winfsp.h b/inc/winfsp/winfsp.h
index db7020ea..f1438968 100644
--- a/inc/winfsp/winfsp.h
+++ b/inc/winfsp/winfsp.h
@@ -855,9 +855,13 @@ FSP_API VOID FspFileSystemDelete(FSP_FILE_SYSTEM *FileSystem);
/**
* Set the mount point for a file system.
*
- * This function currently only supports drive letters (X:) as mount points. Refer to the
- * documentation of the DefineDosDevice Windows API to better understand how drive letters are
- * created.
+ * This function supports drive letters (X:) or directories as mount points:
+ *
+ * - Drive letters: Refer to the documentation of the DefineDosDevice Windows API
+ * to better understand how they are created.
+ * - Directories: They can be used as mount points for disk based file systems. They cannot
+ * be used for network file systems. This is a limitation that Windows imposes on junctions.
+ *
*
* @param FileSystem
* The file system object.
diff --git a/src/dll/fs.c b/src/dll/fs.c
index e8f950e1..6d695faf 100644
--- a/src/dll/fs.c
+++ b/src/dll/fs.c
@@ -126,10 +126,24 @@ static NTSTATUS FspFileSystemSetMountPoint_CreateDirectory(PWSTR MountPoint, PWS
NTSTATUS Result;
HANDLE DirHandle;
BOOL Success;
- DWORD Bytes;
+ DWORD Backslashes, Bytes;
USHORT VolumeNameLength, BackslashLength, ReparseDataLength;
PREPARSE_DATA_BUFFER ReparseData = 0;
- PWSTR PathBuffer;
+ PWSTR P, PathBuffer;
+
+ /*
+ * Windows does not allow mount points (junctions) to point to network file systems.
+ *
+ * Count how many backslashes our VolumeName. If it is 3 or more this is a network
+ * file system. Preemptively return STATUS_NETWORK_ACCESS_DENIED.
+ */
+ for (P = VolumeName, Backslashes = 0; *P; P++)
+ if (L'\\' == *P)
+ if (3 == ++Backslashes)
+ {
+ Result = STATUS_NETWORK_ACCESS_DENIED;
+ goto exit;
+ }
if (!CreateDirectoryW(MountPoint, 0))
{
diff --git a/tools/run-tests.bat b/tools/run-tests.bat
index 38a4ea52..6923fef0 100644
--- a/tools/run-tests.bat
+++ b/tools/run-tests.bat
@@ -78,6 +78,12 @@ if errorlevel 1 goto fail
echo winfsp-tests-x64 --case-randomize
winfsp-tests-x64 --case-randomize
if errorlevel 1 goto fail
+echo winfsp-tests-x64 --mountpoint=X:
+winfsp-tests-x64 --mountpoint=X:
+if errorlevel 1 goto fail
+echo winfsp-tests-x64 --mountpoint=mymnt
+winfsp-tests-x64 --mountpoint=mymnt
+if errorlevel 1 goto fail
exit /b 0
:winfsp-tests-x86
@@ -87,6 +93,12 @@ if errorlevel 1 goto fail
echo winfsp-tests-x86 --case-randomize
winfsp-tests-x86 --case-randomize
if errorlevel 1 goto fail
+echo winfsp-tests-x86 --mountpoint=X:
+winfsp-tests-x86 --mountpoint=X:
+if errorlevel 1 goto fail
+echo winfsp-tests-x86 --mountpoint=mymnt
+winfsp-tests-x86 --mountpoint=mymnt
+if errorlevel 1 goto fail
exit /b 0
:fsx-memfs-x64
diff --git a/tst/winfsp-tests/create-test.c b/tst/winfsp-tests/create-test.c
index d0c4c08f..713be4b4 100644
--- a/tst/winfsp-tests/create-test.c
+++ b/tst/winfsp-tests/create-test.c
@@ -62,10 +62,13 @@ void create_dotest(ULONG Flags, PWSTR Prefix)
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\\\\\file0",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
- Handle = CreateFileW(FilePath,
- GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
- ASSERT(INVALID_HANDLE_VALUE == Handle);
- ASSERT(ERROR_INVALID_NAME == GetLastError());
+ if (0 == OptMountPoint)
+ {
+ Handle = CreateFileW(FilePath,
+ GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
+ ASSERT(INVALID_HANDLE_VALUE == Handle);
+ ASSERT(ERROR_INVALID_NAME == GetLastError());
+ }
}
/* invalid chars (wildcards) not allowed */
@@ -146,10 +149,13 @@ void create_dotest(ULONG Flags, PWSTR Prefix)
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\file0\\",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
- Handle = CreateFileW(FilePath,
- GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
- ASSERT(INVALID_HANDLE_VALUE == Handle);
- ASSERT(ERROR_INVALID_NAME == GetLastError());
+ if (0 == OptMountPoint)
+ {
+ Handle = CreateFileW(FilePath,
+ GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
+ ASSERT(INVALID_HANDLE_VALUE == Handle);
+ ASSERT(ERROR_INVALID_NAME == GetLastError());
+ }
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\\\",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
diff --git a/tst/winfsp-tests/memfs-test.c b/tst/winfsp-tests/memfs-test.c
index 2e740328..3c2714a9 100644
--- a/tst/winfsp-tests/memfs-test.c
+++ b/tst/winfsp-tests/memfs-test.c
@@ -5,6 +5,8 @@
#include "winfsp-tests.h"
+int memfs_running;
+
void *memfs_start_ex(ULONG Flags, ULONG FileInfoTimeout)
{
if (-1 == Flags)
@@ -24,9 +26,17 @@ void *memfs_start_ex(ULONG Flags, ULONG FileInfoTimeout)
ASSERT(NT_SUCCESS(Result));
ASSERT(0 != Memfs);
+ if (OptMountPoint)
+ {
+ Result = FspFileSystemSetMountPoint(MemfsFileSystem(Memfs), OptMountPoint);
+ ASSERT(NT_SUCCESS(Result));
+ }
+
Result = MemfsStart(Memfs);
ASSERT(NT_SUCCESS(Result));
+ memfs_running = 1;
+
return Memfs;
}
@@ -40,6 +50,8 @@ void memfs_stop(void *data)
if (0 == data)
return;
+ memfs_running = 0;
+
MEMFS *Memfs = data;
MemfsStop(Memfs);
diff --git a/tst/winfsp-tests/reparse-test.c b/tst/winfsp-tests/reparse-test.c
index 8bcc74ac..3d6beaaa 100644
--- a/tst/winfsp-tests/reparse-test.c
+++ b/tst/winfsp-tests/reparse-test.c
@@ -434,7 +434,7 @@ static void reparse_symlink_relative_dotest(ULONG Flags, PWSTR Prefix, ULONG Fil
{
ASSERT(ERROR_PRIVILEGE_NOT_HELD == GetLastError());
FspDebugLog(__FUNCTION__ ": need SE_CREATE_SYMBOLIC_LINK_PRIVILEGE\n");
- return;
+ goto exit;
}
my_mkdir(L"\\1");
@@ -491,6 +491,7 @@ static void reparse_symlink_relative_dotest(ULONG Flags, PWSTR Prefix, ULONG Fil
my_rmdir(L"\\1\\1.1");
my_rmdir(L"\\1");
+exit:
memfs_stop(memfs);
}
diff --git a/tst/winfsp-tests/winfsp-tests.c b/tst/winfsp-tests/winfsp-tests.c
index 41a5fa81..827a02fe 100644
--- a/tst/winfsp-tests/winfsp-tests.c
+++ b/tst/winfsp-tests/winfsp-tests.c
@@ -9,6 +9,7 @@ int WinFspNetTests = 1;
BOOLEAN OptCaseInsensitive = FALSE;
BOOLEAN OptCaseRandomize = FALSE;
+WCHAR OptMountPointBuf[MAX_PATH], *OptMountPoint;
int mywcscmp(PWSTR a, int alen, PWSTR b, int blen)
{
@@ -57,12 +58,16 @@ HANDLE HookCreateFileW(
HANDLE hTemplateFile)
{
static WCHAR DevicePrefix[] =
- L"\\\\?\\GLOBALROOT\\Device\\Volume{01234567-0123-0123-0101-010101010101}\\";
+ L"\\\\?\\GLOBALROOT\\Device\\Volume{01234567-0123-0123-0101-010101010101}";
+ static WCHAR MemfsSharePrefix[] =
+ L"\\\\memfs\\share";
static const TogglePercent = 25;
WCHAR FileNameBuf[1024];
PWSTR P, EndP;
+ size_t L1, L2;
wcscpy_s(FileNameBuf, sizeof FileNameBuf / sizeof(WCHAR), lpFileName);
+
if (OptCaseRandomize)
{
if (L'\\' == FileNameBuf[0] && L'\\' == FileNameBuf[1] &&
@@ -83,6 +88,37 @@ HANDLE HookCreateFileW(
*P = togglealpha(*P);
}
+ if (OptMountPoint && memfs_running)
+ {
+ if (L'\\' == FileNameBuf[0] && L'\\' == FileNameBuf[1] &&
+ L'?' == FileNameBuf[2] && L'\\' == FileNameBuf[3] &&
+ testalpha(FileNameBuf[4]) && L':' == FileNameBuf[5] && L'\\' == FileNameBuf[6])
+ ;
+ else if (0 == wcsncmp(FileNameBuf, DevicePrefix, wcschr(DevicePrefix, L'{') - DevicePrefix))
+ {
+ P = FileNameBuf + wcslen(DevicePrefix);
+ L1 = wcslen(P) + 1;
+ L2 = wcslen(OptMountPoint);
+ memmove(FileNameBuf + 1024 - L1, P, L1 * sizeof(WCHAR));
+ memmove(FileNameBuf, OptMountPoint, L2 * sizeof(WCHAR));
+ memmove(FileNameBuf + L2, P, L1 * sizeof(WCHAR));
+ }
+ else if (0 == mywcscmp(
+ FileNameBuf, (int)wcslen(MemfsSharePrefix), MemfsSharePrefix, (int)wcslen(MemfsSharePrefix)))
+ {
+ P = FileNameBuf + wcslen(MemfsSharePrefix);
+ L1 = wcslen(P) + 1;
+ L2 = wcslen(OptMountPoint);
+ memmove(FileNameBuf + 1024 - L1, P, L1 * sizeof(WCHAR));
+ memmove(FileNameBuf, OptMountPoint, L2 * sizeof(WCHAR));
+ memmove(FileNameBuf + L2, P, L1 * sizeof(WCHAR));
+ }
+ else if (testalpha(FileNameBuf[0]) && L':' == FileNameBuf[1] && L'\\' == FileNameBuf[2])
+ ;
+ else
+ abort();
+ }
+
HANDLE h = CreateFileW(
FileNameBuf,
dwDesiredAccess,
@@ -149,6 +185,21 @@ int main(int argc, char *argv[])
OptCaseInsensitive = TRUE;
rmarg(argv, argc, argi);
}
+ else if (0 == strncmp("--mountpoint=", a, sizeof "--mountpoint=" - 1))
+ {
+ if (0 != MultiByteToWideChar(CP_UTF8, 0,
+ a + sizeof "--mountpoint=" - 1, -1, OptMountPointBuf, MAX_PATH))
+ {
+ OptMountPoint = OptMountPointBuf;
+ rmarg(argv, argc, argi);
+
+ NtfsTests = 0;
+ if (!(testalpha(OptMountPoint[0]) &&
+ L':' == OptMountPoint[1] &&
+ L'\0' == OptMountPoint[2]))
+ WinFspNetTests = 0;
+ }
+ }
}
}
diff --git a/tst/winfsp-tests/winfsp-tests.h b/tst/winfsp-tests/winfsp-tests.h
index 16952d03..87268eca 100644
--- a/tst/winfsp-tests/winfsp-tests.h
+++ b/tst/winfsp-tests/winfsp-tests.h
@@ -5,13 +5,6 @@ void *memfs_start(ULONG Flags);
void memfs_stop(void *data);
PWSTR memfs_volumename(void *data);
-extern int NtfsTests;
-extern int WinFspDiskTests;
-extern int WinFspNetTests;
-
-extern BOOLEAN OptCaseInsensitive;
-extern BOOLEAN OptCaseRandomize;
-
int mywcscmp(PWSTR a, int alen, PWSTR b, int blen);
#define CreateFileW HookCreateFileW
@@ -23,3 +16,13 @@ HANDLE HookCreateFileW(
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);
+
+extern int NtfsTests;
+extern int WinFspDiskTests;
+extern int WinFspNetTests;
+
+extern BOOLEAN OptCaseInsensitive;
+extern BOOLEAN OptCaseRandomize;
+extern WCHAR OptMountPointBuf[], *OptMountPoint;
+
+extern int memfs_running;