mirror of
https://github.com/winfsp/winfsp.git
synced 2025-07-03 17:32:57 -05:00
Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
b87c907af8 | |||
9d5efe5f98 | |||
c5b850be35 | |||
0af0bfbe7c | |||
1dbcae3985 | |||
a5726c820b | |||
870c54253a | |||
f9cbbea386 | |||
a939d9997b | |||
c803ef24f8 | |||
2d5d058d2f | |||
a372c6ee40 | |||
c8a5f52fb1 | |||
40052b143e | |||
8006763367 | |||
7ed9c56005 | |||
12fd8942f7 | |||
3eb1f48174 | |||
ac26bde9ee | |||
6421dd92a9 | |||
b05d5e286e | |||
2d0df701e3 | |||
2f1a5b98e2 | |||
21a636aaae | |||
3b90908e01 | |||
90e86d4592 |
1
.github/workflows/avm.yml
vendored
1
.github/workflows/avm.yml
vendored
@ -13,3 +13,4 @@ jobs:
|
||||
files: |
|
||||
https://github.com/billziss-gh/winfsp/releases/download/v1.6/winfsp-1.6.20027.msi
|
||||
https://github.com/billziss-gh/winfsp/releases/download/v1.7B1/winfsp-1.7.20038.msi
|
||||
https://github.com/billziss-gh/winfsp/releases/download/v1.8/winfsp-1.8.20304.msi
|
||||
|
@ -1,6 +1,20 @@
|
||||
= Changelog
|
||||
|
||||
|
||||
v1.9B2 (2021 Beta2)::
|
||||
|
||||
Changes since v1.8:
|
||||
|
||||
* [NEW] WinFsp now supports file change notifications and cache invalidations. This functionality is offered via the following new API's:
|
||||
** Native API: `FspFileSystemNotify`
|
||||
** FUSE API: `fuse_notify`
|
||||
** .NET API: `FileSystemHost.Notify`
|
||||
|
||||
* [NEW] A native file system (notifyfs) and a .NET file system (notifyfs-dotnet) have been added to showcase the new file change notification API.
|
||||
|
||||
* [FIX] WinFsp-FUSE correctly maps SID's from trusted domains to POSIX UID's in a multi-domain environment (using the "trustPosixOffset" attribute). Previously WinFsp-FUSE only handled SID's from the primary domain correctly.
|
||||
|
||||
|
||||
v1.9B1 (2021 Beta1)::
|
||||
|
||||
Changes since v1.8:
|
||||
|
@ -54,6 +54,7 @@ This CONTRIBUTOR AGREEMENT applies to any contribution that you make to the WinF
|
||||
CONTRIBUTOR LIST
|
||||
----------------
|
||||
|===
|
||||
|Alberto Alonso |alberto at alonso.xyz
|
||||
|Ben Rubson |ben.rubson at gmail.com
|
||||
|Bill Zissimopoulos |billziss at navimatics.com
|
||||
|Brett Dutro |brett.dutro at gmail.com
|
||||
@ -68,4 +69,5 @@ CONTRIBUTOR LIST
|
||||
|Sam Kelly (DuroSoft Technologies LLC, https://durosoft.com) |sam at durosoft.com
|
||||
|Santiago Ganis |sganis at gmail.com
|
||||
|Tobias Urlaub |saibotu at outlook.de
|
||||
|Victor Gao |victgm at outlook.com
|
||||
|===
|
||||
|
@ -538,6 +538,31 @@
|
||||
<File Name="passthrough-dotnet.csproj" KeyPath="yes" />
|
||||
</Component>
|
||||
</Directory>
|
||||
<Directory Id="SMPDIR.notifyfs" Name="notifyfs">
|
||||
<Component Id="C.notifyfs.c">
|
||||
<File Name="notifyfs.c" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.notifyfs.sln">
|
||||
<File Name="notifyfs.sln" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.notifyfs.vcxproj">
|
||||
<File Name="notifyfs.vcxproj" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.notifyfs.vcxproj.filters">
|
||||
<File Name="notifyfs.vcxproj.filters" KeyPath="yes" />
|
||||
</Component>
|
||||
</Directory>
|
||||
<Directory Id="SMPDIR.notifyfs_dotnet" Name="notifyfs-dotnet">
|
||||
<Component Id="C.notifyfs_dotnet.Program.cs">
|
||||
<File Id="FILE.notifyfs_dotnet.Program.cs" Name="Program.cs" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.notifyfs_dotnet.sln">
|
||||
<File Name="notifyfs-dotnet.sln" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.notifyfs_dotnet.csproj">
|
||||
<File Name="notifyfs-dotnet.csproj" KeyPath="yes" />
|
||||
</Component>
|
||||
</Directory>
|
||||
</DirectoryRef>
|
||||
<DirectoryRef Id="SYMDIR">
|
||||
<Component Id="C.winfsp_x64.sys.pdb">
|
||||
@ -674,6 +699,10 @@
|
||||
<ComponentRef Id="C.passthrough_fuse3.vcxproj.filters" />
|
||||
<ComponentRef Id="C.passthrough_fuse3.Makefile" />
|
||||
<ComponentRef Id="C.passthrough_fuse3.README.md" />
|
||||
<ComponentRef Id="C.notifyfs.c" />
|
||||
<ComponentRef Id="C.notifyfs.sln" />
|
||||
<ComponentRef Id="C.notifyfs.vcxproj" />
|
||||
<ComponentRef Id="C.notifyfs.vcxproj.filters" />
|
||||
</ComponentGroup>
|
||||
<ComponentGroup Id="C.WinFsp.sym">
|
||||
<ComponentRef Id="C.winfsp_x64.sys.pdb" />
|
||||
@ -703,6 +732,9 @@
|
||||
<ComponentRef Id="C.passthrough_dotnet.Program.cs" />
|
||||
<ComponentRef Id="C.passthrough_dotnet.sln" />
|
||||
<ComponentRef Id="C.passthrough_dotnet.csproj" />
|
||||
<ComponentRef Id="C.notifyfs_dotnet.Program.cs" />
|
||||
<ComponentRef Id="C.notifyfs_dotnet.sln" />
|
||||
<ComponentRef Id="C.notifyfs_dotnet.csproj" />
|
||||
</ComponentGroup>
|
||||
|
||||
<Feature
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
<MyCanonicalVersion>1.9</MyCanonicalVersion>
|
||||
|
||||
<MyProductVersion>2021 Beta1</MyProductVersion>
|
||||
<MyProductVersion>2021 Beta2</MyProductVersion>
|
||||
<MyProductStage>Beta</MyProductStage>
|
||||
|
||||
<MyVersion>$(MyCanonicalVersion).$(MyBuildNumber)</MyVersion>
|
||||
|
@ -187,6 +187,7 @@
|
||||
<ClCompile Include="..\..\src\sys\shutdown.c" />
|
||||
<ClCompile Include="..\..\src\sys\silo.c" />
|
||||
<ClCompile Include="..\..\src\sys\statistics.c" />
|
||||
<ClCompile Include="..\..\src\sys\trace.c" />
|
||||
<ClCompile Include="..\..\src\sys\util.c" />
|
||||
<ClCompile Include="..\..\src\sys\volinfo.c" />
|
||||
<ClCompile Include="..\..\src\sys\volume.c" />
|
||||
|
@ -125,6 +125,9 @@
|
||||
<ClCompile Include="..\..\src\sys\silo.c">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\sys\trace.c">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\src\sys\driver.h">
|
||||
|
@ -1826,6 +1826,10 @@ FSP_API VOID FspDeleteSid(PSID Sid, NTSTATUS (*CreateFunc)());
|
||||
FSP_API NTSTATUS FspPosixMapPermissionsToSecurityDescriptor(
|
||||
UINT32 Uid, UINT32 Gid, UINT32 Mode,
|
||||
PSECURITY_DESCRIPTOR *PSecurityDescriptor);
|
||||
FSP_API NTSTATUS FspPosixMergePermissionsToSecurityDescriptor(
|
||||
UINT32 Uid, UINT32 Gid, UINT32 Mode,
|
||||
PSECURITY_DESCRIPTOR ExistingSecurityDescriptor,
|
||||
PSECURITY_DESCRIPTOR *PSecurityDescriptor);
|
||||
FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions(
|
||||
PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||||
PUINT32 PUid, PUINT32 PGid, PUINT32 PMode);
|
||||
|
@ -254,6 +254,7 @@ FSP_API NTSTATUS FspFsctlPreflight(PWSTR DevicePath)
|
||||
|
||||
static NTSTATUS FspFsctlStartService(VOID)
|
||||
{
|
||||
static SRWLOCK Lock = SRWLOCK_INIT;
|
||||
PWSTR DriverName = L"" FSP_FSCTL_DRIVER_NAME;
|
||||
SC_HANDLE ScmHandle = 0;
|
||||
SC_HANDLE SvcHandle = 0;
|
||||
@ -261,6 +262,8 @@ static NTSTATUS FspFsctlStartService(VOID)
|
||||
DWORD LastError;
|
||||
NTSTATUS Result;
|
||||
|
||||
AcquireSRWLockExclusive(&Lock);
|
||||
|
||||
/* Determine if we are running inside container.
|
||||
*
|
||||
* See https://github.com/microsoft/perfview/blob/V1.9.65/src/TraceEvent/TraceEventSession.cs#L525
|
||||
@ -331,6 +334,8 @@ exit:
|
||||
if (0 != ScmHandle)
|
||||
CloseServiceHandle(ScmHandle);
|
||||
|
||||
ReleaseSRWLockExclusive(&Lock);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include <dll/fuse/library.h>
|
||||
#include <sddl.h>
|
||||
|
||||
struct fuse_chan
|
||||
{
|
||||
@ -105,6 +106,8 @@ static struct fuse_opt fsp_fuse_core_opts[] =
|
||||
FUSE_OPT_KEY("ExactFileSystemName=", 'E'),
|
||||
FUSE_OPT_KEY("--ExactFileSystemName=", 'E'),
|
||||
|
||||
FUSE_OPT_KEY("FileSecurity=", 's'),
|
||||
FUSE_OPT_KEY("--FileSecurity=", 's'),
|
||||
FSP_FUSE_CORE_OPT("UserName=", set_uid, 1),
|
||||
FUSE_OPT_KEY("UserName=", 'u'),
|
||||
FSP_FUSE_CORE_OPT("--UserName=", set_uid, 1),
|
||||
@ -266,6 +269,29 @@ FSP_FUSE_API int fsp_fuse_is_lib_option(struct fsp_fuse_env *env,
|
||||
return fsp_fuse_opt_match(env, fsp_fuse_core_opts, opt);
|
||||
}
|
||||
|
||||
static int fsp_fuse_sddl_to_security(const char *Sddl, PUINT8 Security, PULONG PSecuritySize)
|
||||
{
|
||||
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
||||
ULONG SecurityDescriptorSize;
|
||||
int res = -1;
|
||||
|
||||
if (ConvertStringSecurityDescriptorToSecurityDescriptorA(
|
||||
Sddl, SDDL_REVISION_1, &SecurityDescriptor, &SecurityDescriptorSize))
|
||||
{
|
||||
if (*PSecuritySize >= SecurityDescriptorSize)
|
||||
{
|
||||
memcpy(Security, SecurityDescriptor, SecurityDescriptorSize);
|
||||
*PSecuritySize = SecurityDescriptorSize;
|
||||
|
||||
res = 0;
|
||||
}
|
||||
|
||||
LocalFree(SecurityDescriptor);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int fsp_fuse_username_to_uid(const char *username, int *puid)
|
||||
{
|
||||
union
|
||||
@ -303,6 +329,63 @@ static int fsp_fuse_username_to_uid(const char *username, int *puid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsp_fuse_utf8towcs_trunc(
|
||||
const char *Str, int StrLen,
|
||||
PWSTR Wcs, int WcsLen)
|
||||
{
|
||||
if (0 == StrLen)
|
||||
return 0;
|
||||
|
||||
int Size = MultiByteToWideChar(CP_UTF8, 0, Str, StrLen, Wcs, WcsLen);
|
||||
if (0 != Size)
|
||||
return Size;
|
||||
|
||||
if (0 == WcsLen)
|
||||
return 0;
|
||||
|
||||
PWSTR Buf = 0;
|
||||
|
||||
Size = MultiByteToWideChar(CP_UTF8, 0, Str, StrLen, 0, 0);
|
||||
if (0 == Size)
|
||||
goto exit;
|
||||
|
||||
Buf = MemAlloc(Size * sizeof(WCHAR));
|
||||
if (0 == Buf)
|
||||
{
|
||||
SetLastError(ERROR_NO_SYSTEM_RESOURCES);
|
||||
Size = 0;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
Size = MultiByteToWideChar(CP_UTF8, 0, Str, StrLen, Buf, Size);
|
||||
if (0 == Size)
|
||||
goto exit;
|
||||
|
||||
if (-1 == StrLen)
|
||||
{
|
||||
if (Size >= WcsLen)
|
||||
{
|
||||
Size = WcsLen - 1;
|
||||
memcpy(Wcs, Buf, Size * sizeof(WCHAR));
|
||||
Wcs[Size] = L'\0';
|
||||
Size++;
|
||||
}
|
||||
else
|
||||
memcpy(Wcs, Buf, Size * sizeof(WCHAR));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Size >= WcsLen)
|
||||
Size = WcsLen;
|
||||
memcpy(Wcs, Buf, Size * sizeof(WCHAR));
|
||||
}
|
||||
|
||||
exit:
|
||||
MemFree(Buf);
|
||||
|
||||
return Size;
|
||||
}
|
||||
|
||||
static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
|
||||
struct fuse_args *outargs)
|
||||
{
|
||||
@ -317,6 +400,7 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
|
||||
FspServiceLog(EVENTLOG_ERROR_TYPE, L""
|
||||
FSP_FUSE_LIBRARY_NAME " options:\n"
|
||||
" -o umask=MASK set file permissions (octal)\n"
|
||||
" -o FileSecurity=SDDL set file DACL (SDDL format)\n"
|
||||
" -o create_umask=MASK set newly created file permissions (octal)\n"
|
||||
" -o create_file_umask=MASK for files only\n"
|
||||
" -o create_dir_umask=MASK for directories only\n"
|
||||
@ -367,7 +451,7 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
|
||||
arg += sizeof "VolumePrefix=" - 1;
|
||||
else if ('V' == arg[2])
|
||||
arg += sizeof "--VolumePrefix=" - 1;
|
||||
if (0 == MultiByteToWideChar(CP_UTF8, 0, arg, -1,
|
||||
if (0 == fsp_fuse_utf8towcs_trunc(arg, -1,
|
||||
opt_data->VolumeParams.Prefix, sizeof opt_data->VolumeParams.Prefix / sizeof(WCHAR)))
|
||||
return -1;
|
||||
opt_data->VolumeParams.Prefix
|
||||
@ -383,7 +467,7 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
|
||||
arg += sizeof "FileSystemName=" - 1;
|
||||
else if ('F' == arg[2])
|
||||
arg += sizeof "--FileSystemName=" - 1;
|
||||
if (0 == MultiByteToWideChar(CP_UTF8, 0, arg, -1,
|
||||
if (0 == fsp_fuse_utf8towcs_trunc(arg, -1,
|
||||
opt_data->VolumeParams.FileSystemName + 5,
|
||||
sizeof opt_data->VolumeParams.FileSystemName / sizeof(WCHAR) - 5))
|
||||
return -1;
|
||||
@ -396,13 +480,25 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
|
||||
arg += sizeof "ExactFileSystemName=" - 1;
|
||||
else if ('E' == arg[2])
|
||||
arg += sizeof "--ExactFileSystemName=" - 1;
|
||||
if (0 == MultiByteToWideChar(CP_UTF8, 0, arg, -1,
|
||||
if (0 == fsp_fuse_utf8towcs_trunc(arg, -1,
|
||||
opt_data->VolumeParams.FileSystemName,
|
||||
sizeof opt_data->VolumeParams.FileSystemName / sizeof(WCHAR)))
|
||||
return -1;
|
||||
opt_data->VolumeParams.FileSystemName
|
||||
[sizeof opt_data->VolumeParams.FileSystemName / sizeof(WCHAR) - 1] = L'\0';
|
||||
return 0;
|
||||
case 's':
|
||||
if ('F' == arg[0])
|
||||
arg += sizeof "FileSecurity=" - 1;
|
||||
else if ('F' == arg[2])
|
||||
arg += sizeof "--FileSecurity=" - 1;
|
||||
opt_data->FileSecuritySize = sizeof opt_data->FileSecurityBuf;
|
||||
if (-1 == fsp_fuse_sddl_to_security(arg, opt_data->FileSecurityBuf, &opt_data->FileSecuritySize))
|
||||
{
|
||||
opt_data->FileSecuritySize = (ULONG)-1;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
case 'u':
|
||||
if ('U' == arg[0])
|
||||
arg += sizeof "UserName=" - 1;
|
||||
@ -428,7 +524,7 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
|
||||
case 'v':
|
||||
arg += sizeof "volname=" - 1;
|
||||
opt_data->VolumeLabelLength = (UINT16)(sizeof(WCHAR) *
|
||||
MultiByteToWideChar(CP_UTF8, 0, arg, lstrlenA(arg),
|
||||
fsp_fuse_utf8towcs_trunc(arg, lstrlenA(arg),
|
||||
opt_data->VolumeLabel, sizeof opt_data->VolumeLabel / sizeof(WCHAR)));
|
||||
if (0 == opt_data->VolumeLabelLength)
|
||||
return -1;
|
||||
@ -470,7 +566,12 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env,
|
||||
|
||||
if (-1 == fsp_fuse_core_opt_parse(env, args, &opt_data, /*help=*/1))
|
||||
{
|
||||
if (-1 == opt_data.username_to_uid_result)
|
||||
if ((ULONG)-1 == opt_data.FileSecuritySize)
|
||||
{
|
||||
ErrorMessage = L": invalid file security.";
|
||||
goto fail;
|
||||
}
|
||||
else if (-1 == opt_data.username_to_uid_result)
|
||||
{
|
||||
ErrorMessage = L": invalid user or group name.";
|
||||
goto fail;
|
||||
@ -539,7 +640,7 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env,
|
||||
if (L'\0' == opt_data.VolumeParams.FileSystemName[0])
|
||||
memcpy(opt_data.VolumeParams.FileSystemName, L"FUSE", 5 * sizeof(WCHAR));
|
||||
|
||||
f = fsp_fuse_obj_alloc(env, sizeof *f);
|
||||
f = fsp_fuse_obj_alloc(env, sizeof *f + opt_data.FileSecuritySize);
|
||||
if (0 == f)
|
||||
goto fail;
|
||||
|
||||
@ -559,6 +660,11 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env,
|
||||
memcpy(&f->VolumeParams, &opt_data.VolumeParams, sizeof opt_data.VolumeParams);
|
||||
f->VolumeLabelLength = opt_data.VolumeLabelLength;
|
||||
memcpy(&f->VolumeLabel, &opt_data.VolumeLabel, opt_data.VolumeLabelLength);
|
||||
if (0 != opt_data.FileSecuritySize)
|
||||
{
|
||||
memcpy(f->FileSecurityBuf, opt_data.FileSecurityBuf, opt_data.FileSecuritySize);
|
||||
f->FileSecurity = f->FileSecurityBuf;
|
||||
}
|
||||
|
||||
Size = (lstrlenW(ch->MountPoint) + 1) * sizeof(WCHAR);
|
||||
f->MountPoint = fsp_fuse_obj_alloc(env, Size);
|
||||
|
@ -472,7 +472,8 @@ static NTSTATUS fsp_fuse_intf_GetSecurityEx(FSP_FILE_SYSTEM *FileSystem,
|
||||
|
||||
if (0 != PSecurityDescriptorSize)
|
||||
{
|
||||
Result = FspPosixMapPermissionsToSecurityDescriptor(Uid, Gid, Mode, &SecurityDescriptor);
|
||||
Result = FspPosixMergePermissionsToSecurityDescriptor(Uid, Gid, Mode, f->FileSecurity,
|
||||
&SecurityDescriptor);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
@ -498,7 +499,7 @@ static NTSTATUS fsp_fuse_intf_GetSecurityEx(FSP_FILE_SYSTEM *FileSystem,
|
||||
exit:
|
||||
if (0 != SecurityDescriptor)
|
||||
FspDeleteSecurityDescriptor(SecurityDescriptor,
|
||||
FspPosixMapPermissionsToSecurityDescriptor);
|
||||
FspPosixMergePermissionsToSecurityDescriptor);
|
||||
|
||||
return Result;
|
||||
}
|
||||
@ -1682,7 +1683,8 @@ static NTSTATUS fsp_fuse_intf_SetSecurity(FSP_FILE_SYSTEM *FileSystem,
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
Result = FspPosixMapPermissionsToSecurityDescriptor(Uid, Gid, Mode, &SecurityDescriptor);
|
||||
Result = FspPosixMergePermissionsToSecurityDescriptor(Uid, Gid, Mode, f->FileSecurity,
|
||||
&SecurityDescriptor);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
@ -1729,7 +1731,7 @@ exit:
|
||||
|
||||
if (0 != SecurityDescriptor)
|
||||
FspDeleteSecurityDescriptor(SecurityDescriptor,
|
||||
FspPosixMapPermissionsToSecurityDescriptor);
|
||||
FspPosixMergePermissionsToSecurityDescriptor);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
@ -72,6 +72,8 @@ struct fuse
|
||||
FSP_FILE_SYSTEM *FileSystem;
|
||||
volatile int exited;
|
||||
struct fuse3 *fuse3;
|
||||
PSECURITY_DESCRIPTOR FileSecurity;
|
||||
FSP_FSCTL_DECLSPEC_ALIGN UINT8 FileSecurityBuf[];
|
||||
};
|
||||
struct fsp_fuse_context_header
|
||||
{
|
||||
@ -156,6 +158,8 @@ struct fsp_fuse_core_opt_data
|
||||
FSP_FSCTL_VOLUME_PARAMS VolumeParams;
|
||||
UINT16 VolumeLabelLength;
|
||||
WCHAR VolumeLabel[sizeof ((FSP_FSCTL_VOLUME_INFO *)0)->VolumeLabel / sizeof(WCHAR)];
|
||||
ULONG FileSecuritySize;
|
||||
FSP_FSCTL_DECLSPEC_ALIGN UINT8 FileSecurityBuf[1024];
|
||||
};
|
||||
FSP_FSCTL_STATIC_ASSERT(
|
||||
sizeof ((struct fuse *)0)->VolumeLabel == sizeof ((struct fsp_fuse_core_opt_data *)0)->VolumeLabel,
|
||||
|
@ -742,7 +742,7 @@ DWORD APIENTRY NPAddConnection3(HWND hwndOwner,
|
||||
DWORD AuthPackage, CredentialsKind;
|
||||
WCHAR UserName[CREDUI_MAX_USERNAME_LENGTH + 1], Password[CREDUI_MAX_PASSWORD_LENGTH + 1];
|
||||
#if defined(FSP_NP_CREDENTIAL_MANAGER)
|
||||
BOOL Save = TRUE;
|
||||
BOOL Save = FALSE;
|
||||
#endif
|
||||
|
||||
//dwFlags |= CONNECT_INTERACTIVE | CONNECT_PROMPT; /* TESTING ONLY! */
|
||||
|
@ -488,7 +488,8 @@ FSP_API VOID FspDeleteSecurityDescriptor(PSECURITY_DESCRIPTOR SecurityDescriptor
|
||||
return;
|
||||
|
||||
if ((NTSTATUS (*)())FspAccessCheckEx == CreateFunc ||
|
||||
(NTSTATUS (*)())FspPosixMapPermissionsToSecurityDescriptor == CreateFunc)
|
||||
(NTSTATUS (*)())FspPosixMapPermissionsToSecurityDescriptor == CreateFunc ||
|
||||
(NTSTATUS (*)())FspPosixMergePermissionsToSecurityDescriptor == CreateFunc)
|
||||
MemFree(SecurityDescriptor);
|
||||
else
|
||||
if ((NTSTATUS (*)())FspCreateSecurityDescriptor == CreateFunc ||
|
||||
|
@ -315,7 +315,7 @@ namespace Fsp.Interop
|
||||
{
|
||||
internal const int FileNameBufSize = 1024 * 2/*FSP_FSCTL_TRANSACT_PATH_SIZEMAX*/;
|
||||
internal static int FileNameBufOffset =
|
||||
(int)Marshal.OffsetOf(typeof(DirInfo), "FileNameBuf");
|
||||
(int)Marshal.OffsetOf(typeof(NotifyInfoInternal), "FileNameBuf");
|
||||
|
||||
internal UInt16 Size;
|
||||
internal UInt32 Filter;
|
||||
@ -337,6 +337,47 @@ namespace Fsp.Interop
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of all the possible values for NotifyInfo.Action
|
||||
/// </summary>
|
||||
public enum NotifyAction : UInt32
|
||||
{
|
||||
Added = 1,
|
||||
Removed = 2,
|
||||
Modified = 3,
|
||||
RenamedOldName = 4,
|
||||
RenamedNewName = 5,
|
||||
AddedStream = 6,
|
||||
RemovedStream = 7,
|
||||
ModifiedStream = 8,
|
||||
RemovedByDelete = 9,
|
||||
IdNotTunnelled = 10,
|
||||
TunnelledIdCollision = 11,
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of all the possible values for NotifyInfo.Filter
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum NotifyFilter : UInt32
|
||||
{
|
||||
None = 0x00000,
|
||||
ChangeFileName = 0x00001,
|
||||
ChangeDirName = 0x00002,
|
||||
ChangeName = ChangeFileName | ChangeDirName,
|
||||
ChangeAttributes = 0x00004,
|
||||
ChangeSize = 0x00008,
|
||||
ChangeLastWrite = 0x00010,
|
||||
ChangeLastAccess = 0x00020,
|
||||
ChangeCreation = 0x00040,
|
||||
ChangeEa = 0x00080,
|
||||
ChangeSecurity = 0x00100,
|
||||
ChangeStreamName = 0x00200,
|
||||
ChangeStreamSize = 0x00400,
|
||||
ChangeStreamWrite = 0x00800,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains file change notification information.
|
||||
/// </summary>
|
||||
@ -344,8 +385,8 @@ namespace Fsp.Interop
|
||||
public struct NotifyInfo
|
||||
{
|
||||
public String FileName;
|
||||
public UInt32 Action;
|
||||
public UInt32 Filter;
|
||||
public NotifyAction Action;
|
||||
public NotifyFilter Filter;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
@ -1147,10 +1188,11 @@ namespace Fsp.Interop
|
||||
int Length = 0;
|
||||
for (int I = 0; NotifyInfoArray.Length > I; I++)
|
||||
{
|
||||
Length = (Length + 7) & ~7; // align to next qword boundary
|
||||
Length += NotifyInfoInternal.FileNameBufOffset +
|
||||
NotifyInfoArray[I].FileName.Length * 2;
|
||||
Length = (Length + 7) & ~7; // align to next qword boundary
|
||||
}
|
||||
|
||||
Byte[] Buffer = new Byte[Length];
|
||||
UInt32 BytesTransferred = default(UInt32);
|
||||
fixed (Byte *P = Buffer)
|
||||
@ -1158,8 +1200,8 @@ namespace Fsp.Interop
|
||||
for (int I = 0; NotifyInfoArray.Length > I; I++)
|
||||
{
|
||||
NotifyInfoInternal Internal = default(NotifyInfoInternal);
|
||||
Internal.Action = NotifyInfoArray[I].Action;
|
||||
Internal.Filter = NotifyInfoArray[I].Filter;
|
||||
Internal.Action = (UInt32)NotifyInfoArray[I].Action;
|
||||
Internal.Filter = (UInt32)NotifyInfoArray[I].Filter;
|
||||
Internal.SetFileNameBuf(NotifyInfoArray[I].FileName);
|
||||
FspFileSystemAddNotifyInfo(
|
||||
ref Internal, (IntPtr)P, (UInt32)Length, out BytesTransferred);
|
||||
|
@ -41,6 +41,10 @@ FSP_API VOID FspDeleteSid(PSID Sid, NTSTATUS (*CreateFunc)());
|
||||
FSP_API NTSTATUS FspPosixMapPermissionsToSecurityDescriptor(
|
||||
UINT32 Uid, UINT32 Gid, UINT32 Mode,
|
||||
PSECURITY_DESCRIPTOR *PSecurityDescriptor);
|
||||
FSP_API NTSTATUS FspPosixMergePermissionsToSecurityDescriptor(
|
||||
UINT32 Uid, UINT32 Gid, UINT32 Mode,
|
||||
PSECURITY_DESCRIPTOR ExistingSecurityDescriptor,
|
||||
PSECURITY_DESCRIPTOR *PSecurityDescriptor);
|
||||
FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions(
|
||||
PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||||
PUINT32 PUid, PUINT32 PGid, PUINT32 PMode);
|
||||
@ -59,6 +63,7 @@ FSP_API VOID FspPosixDecodeWindowsPath(PWSTR WindowsPath, ULONG Size);
|
||||
#pragma alloc_text(PAGE, FspPosixCreateSid)
|
||||
#pragma alloc_text(PAGE, FspDeleteSid)
|
||||
#pragma alloc_text(PAGE, FspPosixMapPermissionsToSecurityDescriptor)
|
||||
#pragma alloc_text(PAGE, FspPosixMergePermissionsToSecurityDescriptor)
|
||||
#pragma alloc_text(PAGE, FspPosixMapSecurityDescriptorToPermissions)
|
||||
#pragma alloc_text(PAGE, FspPosixMapWindowsToPosixPathEx)
|
||||
#pragma alloc_text(PAGE, FspPosixMapPosixToWindowsPathEx)
|
||||
@ -898,6 +903,101 @@ lasterror:
|
||||
goto exit;
|
||||
}
|
||||
|
||||
FSP_API NTSTATUS FspPosixMergePermissionsToSecurityDescriptor(
|
||||
UINT32 Uid, UINT32 Gid, UINT32 Mode,
|
||||
PSECURITY_DESCRIPTOR ExistingSecurityDescriptor,
|
||||
PSECURITY_DESCRIPTOR *PSecurityDescriptor)
|
||||
{
|
||||
FSP_KU_CODE;
|
||||
|
||||
if (0 == ExistingSecurityDescriptor)
|
||||
return FspPosixMapPermissionsToSecurityDescriptor(Uid, Gid, Mode, PSecurityDescriptor);
|
||||
|
||||
PSID ExistingOwnerSid = 0, ExistingGroupSid = 0;
|
||||
BOOL Defaulted, ExistingDaclPresent;
|
||||
PACL ExistingAcl = 0;
|
||||
PSID OwnerSid = 0, GroupSid = 0;
|
||||
SECURITY_DESCRIPTOR SecurityDescriptor;
|
||||
PSECURITY_DESCRIPTOR RelativeSecurityDescriptor = 0;
|
||||
ULONG Size;
|
||||
NTSTATUS Result;
|
||||
|
||||
*PSecurityDescriptor = 0;
|
||||
|
||||
if (!GetSecurityDescriptorOwner(ExistingSecurityDescriptor, &ExistingOwnerSid, &Defaulted))
|
||||
goto lasterror;
|
||||
if (!GetSecurityDescriptorGroup(ExistingSecurityDescriptor, &ExistingGroupSid, &Defaulted))
|
||||
goto lasterror;
|
||||
if (!GetSecurityDescriptorDacl(ExistingSecurityDescriptor, &ExistingDaclPresent, &ExistingAcl, &Defaulted))
|
||||
goto lasterror;
|
||||
|
||||
if (0 == ExistingOwnerSid)
|
||||
{
|
||||
Result = FspPosixMapUidToSid(Uid, &OwnerSid);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
ExistingOwnerSid = OwnerSid;
|
||||
}
|
||||
|
||||
if (0 == ExistingGroupSid)
|
||||
{
|
||||
Result = FspPosixMapUidToSid(Gid, &GroupSid);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
ExistingGroupSid = GroupSid;
|
||||
}
|
||||
|
||||
if (!InitializeSecurityDescriptor(&SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION))
|
||||
goto lasterror;
|
||||
|
||||
if (!SetSecurityDescriptorOwner(&SecurityDescriptor, ExistingOwnerSid, FALSE))
|
||||
goto lasterror;
|
||||
if (!SetSecurityDescriptorGroup(&SecurityDescriptor, ExistingGroupSid, FALSE))
|
||||
goto lasterror;
|
||||
if (0 != ExistingAcl)
|
||||
{
|
||||
if (!SetSecurityDescriptorControl(&SecurityDescriptor, SE_DACL_PROTECTED, SE_DACL_PROTECTED))
|
||||
goto lasterror;
|
||||
if (!SetSecurityDescriptorDacl(&SecurityDescriptor, TRUE, ExistingAcl, FALSE))
|
||||
goto lasterror;
|
||||
}
|
||||
|
||||
Size = 0;
|
||||
if (!MakeSelfRelativeSD(&SecurityDescriptor, 0, &Size) &&
|
||||
ERROR_INSUFFICIENT_BUFFER != GetLastError())
|
||||
goto lasterror;
|
||||
|
||||
RelativeSecurityDescriptor = MemAlloc(Size);
|
||||
if (0 == RelativeSecurityDescriptor)
|
||||
{
|
||||
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!MakeSelfRelativeSD(&SecurityDescriptor, RelativeSecurityDescriptor, &Size))
|
||||
goto lasterror;
|
||||
|
||||
*PSecurityDescriptor = RelativeSecurityDescriptor;
|
||||
|
||||
Result = STATUS_SUCCESS;
|
||||
|
||||
exit:
|
||||
if (!NT_SUCCESS(Result))
|
||||
MemFree(RelativeSecurityDescriptor);
|
||||
|
||||
if (0 != GroupSid)
|
||||
FspDeleteSid(GroupSid, FspPosixMapUidToSid);
|
||||
|
||||
if (0 != OwnerSid)
|
||||
FspDeleteSid(OwnerSid, FspPosixMapUidToSid);
|
||||
|
||||
return Result;
|
||||
|
||||
lasterror:
|
||||
Result = FspNtStatusFromWin32(GetLastError());
|
||||
goto exit;
|
||||
}
|
||||
|
||||
static inline ACCESS_MASK FspPosixCanonicalizeAccessMask(ACCESS_MASK AccessMask)
|
||||
{
|
||||
PGENERIC_MAPPING Mapping = FspGetFileGenericMapping();
|
||||
|
@ -84,8 +84,9 @@ static NTSTATUS FspFsvolClose(
|
||||
FspFileDescDelete(FileDesc); /* this will also close the MainFileObject if any */
|
||||
FspFileNodeDereference(FileNode);
|
||||
|
||||
/* if we are closing files in the context of a rename make it synchronous */
|
||||
if (FspFsvolDeviceFileRenameIsAcquiredExclusive(FsvolDeviceObject))
|
||||
/* if closing in the context of a rename or IOQ is above the watermark make it synchronous */
|
||||
if (FspFsvolDeviceFileRenameIsAcquiredExclusive(FsvolDeviceObject) ||
|
||||
FspIoqPendingAboveWatermark(FspFsvolDeviceExtension(FsvolDeviceObject)->Ioq, 50))
|
||||
{
|
||||
/* acquire ownership of the Request */
|
||||
Request->Hint = (UINT_PTR)Irp;
|
||||
|
@ -22,6 +22,10 @@
|
||||
#include <sys/driver.h>
|
||||
|
||||
#if DBG
|
||||
|
||||
#undef STATUS_INSUFFICIENT_RESOURCES
|
||||
#define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC000009AL)
|
||||
|
||||
#define SYM(x) case x: return #x;
|
||||
#define SYMBRC(x) case x: return "[" #x "]";
|
||||
|
||||
@ -330,4 +334,5 @@ VOID FspDebugLogIrp(const char *func, PIRP Irp, NTSTATUS Result)
|
||||
NtStatusSym(Result),
|
||||
(LONGLONG)Irp->IoStatus.Information);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -38,6 +38,8 @@ NTSTATUS DriverEntry(
|
||||
{
|
||||
FSP_ENTER_DRV();
|
||||
|
||||
FSP_TRACE_INIT();
|
||||
|
||||
/* setup the driver object */
|
||||
DriverObject->MajorFunction[IRP_MJ_CREATE] = FspCreate;
|
||||
DriverObject->MajorFunction[IRP_MJ_CLOSE] = FspClose;
|
||||
@ -157,6 +159,8 @@ exit:
|
||||
FspSiloFinalize();
|
||||
if (InitDoneGRes)
|
||||
ExDeleteResourceLite(&FspDeviceGlobalResource);
|
||||
|
||||
FSP_TRACE_FINI();
|
||||
}
|
||||
|
||||
#pragma prefast(suppress:28175, "We are in DriverEntry: ok to access DriverName")
|
||||
|
@ -147,6 +147,40 @@ VOID FspDebugLogIrp(const char *func, PIRP Irp, NTSTATUS Result);
|
||||
#define DEBUGTEST_EX(C, Percent, Deflt) (Deflt)
|
||||
#endif
|
||||
|
||||
/* trace */
|
||||
#if FSP_TRACE_ENABLED
|
||||
VOID FspTraceInitialize(VOID);
|
||||
VOID FspTraceFinalize(VOID);
|
||||
VOID FspTrace(const char *file, int line, const char *func);
|
||||
VOID FspTraceNtStatus(const char *file, int line, const char *func, NTSTATUS Status);
|
||||
#define FSP_TRACE_INIT() \
|
||||
FspTraceInitialize()
|
||||
#define FSP_TRACE_FINI() \
|
||||
FspTraceFinalize()
|
||||
#define FSP_TRACE() \
|
||||
FspTrace( \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
__FUNCTION__)
|
||||
#define FSP_TRACE_NTSTATUS(Status) \
|
||||
FspTraceNtStatus( \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
__FUNCTION__, \
|
||||
Status)
|
||||
#else
|
||||
#define FSP_TRACE_INIT() \
|
||||
((VOID)0)
|
||||
#define FSP_TRACE_FINI() \
|
||||
((VOID)0)
|
||||
#define FSP_TRACE() \
|
||||
((VOID)0)
|
||||
#define FSP_TRACE_NTSTATUS(Result) \
|
||||
((VOID)0)
|
||||
#endif
|
||||
#undef STATUS_INSUFFICIENT_RESOURCES
|
||||
#define STATUS_INSUFFICIENT_RESOURCES (FSP_TRACE_NTSTATUS(0xC000009AL), (NTSTATUS)0xC000009AL)
|
||||
|
||||
/* FSP_ENTER/FSP_LEAVE */
|
||||
#if DBG
|
||||
#define FSP_DEBUGLOG_(fmt, rfmt, ...) \
|
||||
@ -919,6 +953,7 @@ BOOLEAN FspIoqPostIrpEx(FSP_IOQ *Ioq, PIRP Irp, BOOLEAN BestEffort, NTSTATUS *PR
|
||||
PIRP FspIoqNextPendingIrp(FSP_IOQ *Ioq, PIRP BoundaryIrp, PLARGE_INTEGER Timeout,
|
||||
PIRP CancellableIrp);
|
||||
ULONG FspIoqPendingIrpCount(FSP_IOQ *Ioq);
|
||||
BOOLEAN FspIoqPendingAboveWatermark(FSP_IOQ *Ioq, ULONG Watermark);
|
||||
BOOLEAN FspIoqStartProcessingIrp(FSP_IOQ *Ioq, PIRP Irp);
|
||||
PIRP FspIoqEndProcessingIrp(FSP_IOQ *Ioq, UINT_PTR IrpHint);
|
||||
ULONG FspIoqProcessIrpCount(FSP_IOQ *Ioq);
|
||||
|
@ -643,6 +643,16 @@ ULONG FspIoqPendingIrpCount(FSP_IOQ *Ioq)
|
||||
return Result;
|
||||
}
|
||||
|
||||
BOOLEAN FspIoqPendingAboveWatermark(FSP_IOQ *Ioq, ULONG Watermark)
|
||||
{
|
||||
BOOLEAN Result;
|
||||
KIRQL Irql;
|
||||
KeAcquireSpinLock(&Ioq->SpinLock, &Irql);
|
||||
Result = Watermark < 100 * Ioq->PendingIrpCount / Ioq->PendingIrpCapacity;
|
||||
KeReleaseSpinLock(&Ioq->SpinLock, Irql);
|
||||
return Result;
|
||||
}
|
||||
|
||||
BOOLEAN FspIoqStartProcessingIrp(FSP_IOQ *Ioq, PIRP Irp)
|
||||
{
|
||||
NTSTATUS Result;
|
||||
|
146
src/sys/trace.c
Normal file
146
src/sys/trace.c
Normal file
@ -0,0 +1,146 @@
|
||||
/**
|
||||
* @file sys/trace.c
|
||||
*
|
||||
* @copyright 2015-2020 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 software
|
||||
* in accordance with the commercial license agreement provided in
|
||||
* conjunction with the software. The terms and conditions of any such
|
||||
* commercial license agreement shall govern, supersede, and render
|
||||
* ineffective any application of the GPLv3 license to this software,
|
||||
* notwithstanding of any reference thereto in the software or
|
||||
* associated repository.
|
||||
*/
|
||||
|
||||
#include <sys/driver.h>
|
||||
|
||||
#if FSP_TRACE_ENABLED
|
||||
|
||||
#undef STATUS_INSUFFICIENT_RESOURCES
|
||||
#define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC000009AL)
|
||||
|
||||
static struct
|
||||
{
|
||||
HANDLE Handle;
|
||||
PKEVENT Event;
|
||||
} FspLowMemoryCondition, FspLowNonPagedPoolCondition, FspLowPagedPoolCondition;
|
||||
|
||||
VOID FspTrace(const char *file, int line, const char *func)
|
||||
{
|
||||
for (const char *p = file; '\0' != *p; p++)
|
||||
if ('\\' == *p)
|
||||
file = p + 1;
|
||||
|
||||
ASSERT(DISPATCH_LEVEL >= KeGetCurrentIrql());
|
||||
DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_TRACE_LEVEL,
|
||||
DRIVER_NAME ": %s[%s:%d]\n", func, file, line);
|
||||
}
|
||||
|
||||
VOID FspTraceNtStatus(const char *file, int line, const char *func, NTSTATUS Status)
|
||||
{
|
||||
for (const char *p = file; '\0' != *p; p++)
|
||||
if ('\\' == *p)
|
||||
file = p + 1;
|
||||
|
||||
ASSERT(DISPATCH_LEVEL >= KeGetCurrentIrql());
|
||||
BOOLEAN LowMemoryCondition, LowNonPagedPoolCondition, LowPagedPoolCondition;
|
||||
switch (Status)
|
||||
{
|
||||
case STATUS_INSUFFICIENT_RESOURCES:
|
||||
LowMemoryCondition = 0 != FspLowMemoryCondition.Event &&
|
||||
!!KeReadStateEvent(FspLowMemoryCondition.Event);
|
||||
LowNonPagedPoolCondition = 0 != FspLowNonPagedPoolCondition.Event &&
|
||||
!!KeReadStateEvent(FspLowNonPagedPoolCondition.Event);
|
||||
LowPagedPoolCondition = 0 != FspLowPagedPoolCondition.Event &&
|
||||
!!KeReadStateEvent(FspLowPagedPoolCondition.Event);
|
||||
DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_TRACE_LEVEL,
|
||||
DRIVER_NAME ": %s[%s:%d]: STATUS_INSUFFICIENT_RESOURCES (Memory=%c%c%c)\n",
|
||||
func, file, line,
|
||||
LowMemoryCondition ? 'M' : '-',
|
||||
LowNonPagedPoolCondition ? 'N' : '-',
|
||||
LowPagedPoolCondition ? 'P' : '-');
|
||||
break;
|
||||
default:
|
||||
DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_TRACE_LEVEL,
|
||||
DRIVER_NAME ": %s[%s:%d]: Status=%lx\n",
|
||||
func, file, line,
|
||||
Status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static PKEVENT FspOpenEvent(PWSTR Name, PHANDLE PHandle)
|
||||
{
|
||||
UNICODE_STRING ObjectName;
|
||||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
HANDLE Handle;
|
||||
PKEVENT Event;
|
||||
NTSTATUS Result;
|
||||
|
||||
*PHandle = 0;
|
||||
|
||||
RtlInitUnicodeString(&ObjectName, Name);
|
||||
InitializeObjectAttributes(&ObjectAttributes,
|
||||
&ObjectName, OBJ_KERNEL_HANDLE, 0, 0);
|
||||
Result = ZwOpenEvent(&Handle,
|
||||
EVENT_ALL_ACCESS, &ObjectAttributes);
|
||||
if (!NT_SUCCESS(Result))
|
||||
return 0;
|
||||
|
||||
Result = ObReferenceObjectByHandle(Handle,
|
||||
0, *ExEventObjectType, KernelMode, &Event, 0);
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
ZwClose(Handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*PHandle = Handle;
|
||||
|
||||
return Event;
|
||||
}
|
||||
|
||||
static VOID FspCloseEvent(PKEVENT Event, HANDLE Handle)
|
||||
{
|
||||
if (0 != Event)
|
||||
ObDereferenceObject(Event);
|
||||
|
||||
if (0 != Handle)
|
||||
ZwClose(Handle);
|
||||
}
|
||||
|
||||
VOID FspTraceInitialize(VOID)
|
||||
{
|
||||
#define OPEN_EVENT(NAME) \
|
||||
(Fsp ## NAME.Event = FspOpenEvent(L"\\KernelObjects\\" #NAME, &Fsp ## NAME.Handle))
|
||||
|
||||
OPEN_EVENT(LowMemoryCondition);
|
||||
OPEN_EVENT(LowNonPagedPoolCondition);
|
||||
OPEN_EVENT(LowPagedPoolCondition);
|
||||
|
||||
#undef OPEN_EVENT
|
||||
|
||||
FSP_TRACE();
|
||||
}
|
||||
|
||||
VOID FspTraceFinalize(VOID)
|
||||
{
|
||||
FSP_TRACE();
|
||||
|
||||
#define CLOSE_EVENT(NAME) \
|
||||
(FspCloseEvent(Fsp ## NAME.Event, Fsp ## NAME.Handle), Fsp ## NAME.Event = 0, Fsp ## NAME.Handle = 0)
|
||||
|
||||
CLOSE_EVENT(LowMemoryCondition);
|
||||
CLOSE_EVENT(LowNonPagedPoolCondition);
|
||||
CLOSE_EVENT(LowPagedPoolCondition);
|
||||
|
||||
#undef CLOSE_EVENT
|
||||
}
|
||||
#endif
|
9
tst/notifyfs-dotnet/.gitignore
vendored
Normal file
9
tst/notifyfs-dotnet/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
build
|
||||
*.ncb
|
||||
*.suo
|
||||
*.vcproj.*
|
||||
*.vcxproj.user
|
||||
*.csproj.user
|
||||
*.VC.db
|
||||
*.VC.opendb
|
||||
.vs
|
394
tst/notifyfs-dotnet/Program.cs
Normal file
394
tst/notifyfs-dotnet/Program.cs
Normal file
@ -0,0 +1,394 @@
|
||||
/**
|
||||
* @file Program.cs
|
||||
*
|
||||
* @copyright 2015-2020 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 software
|
||||
* in accordance with the commercial license agreement provided in
|
||||
* conjunction with the software. The terms and conditions of any such
|
||||
* commercial license agreement shall govern, supersede, and render
|
||||
* ineffective any application of the GPLv3 license to this software,
|
||||
* notwithstanding of any reference thereto in the software or
|
||||
* associated repository.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.AccessControl;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
using Fsp;
|
||||
using VolumeInfo = Fsp.Interop.VolumeInfo;
|
||||
using FileInfo = Fsp.Interop.FileInfo;
|
||||
using NotifyInfo = Fsp.Interop.NotifyInfo;
|
||||
using NotifyAction = Fsp.Interop.NotifyAction;
|
||||
using NotifyFilter = Fsp.Interop.NotifyFilter;
|
||||
|
||||
namespace notifyfs
|
||||
{
|
||||
class Notifyfs : FileSystemBase
|
||||
{
|
||||
public override Int32 Init(Object Host)
|
||||
{
|
||||
_Host = (FileSystemHost)Host;
|
||||
_Host.SectorSize = ALLOCATION_UNIT;
|
||||
_Host.SectorsPerAllocationUnit = 1;
|
||||
_Host.FileInfoTimeout = 1000;
|
||||
_Host.CaseSensitiveSearch = false;
|
||||
_Host.CasePreservedNames = true;
|
||||
_Host.UnicodeOnDisk = true;
|
||||
_Host.PersistentAcls = false;
|
||||
_Host.PostCleanupWhenModifiedOnly = true;
|
||||
_Host.VolumeCreationTime = 0;
|
||||
_Host.VolumeSerialNumber = 0;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
public override Int32 Mounted(Object Host)
|
||||
{
|
||||
_Timer = new Timer(this.Tick, null, 0, 1000);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
public override void Unmounted(Object Host)
|
||||
{
|
||||
WaitHandle Event = new ManualResetEvent(false);
|
||||
_Timer.Dispose(Event);
|
||||
Event.WaitOne();
|
||||
}
|
||||
public override Int32 GetVolumeInfo(
|
||||
out VolumeInfo VolumeInfo)
|
||||
{
|
||||
VolumeInfo = default(VolumeInfo);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
public override Int32 GetSecurityByName(
|
||||
String FileName,
|
||||
out UInt32 FileAttributes/* or ReparsePointIndex */,
|
||||
ref Byte[] SecurityDescriptor)
|
||||
{
|
||||
int Index = FileLookup(FileName);
|
||||
if (-1 == Index)
|
||||
{
|
||||
FileAttributes = default(UInt32);
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
FileAttributes = 0 == Index ? (UInt32)System.IO.FileAttributes.Directory : 0;
|
||||
if (null != SecurityDescriptor)
|
||||
SecurityDescriptor = DefaultSecurity;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
public override Int32 Open(
|
||||
String FileName,
|
||||
UInt32 CreateOptions,
|
||||
UInt32 GrantedAccess,
|
||||
out Object FileNode,
|
||||
out Object FileDesc,
|
||||
out FileInfo FileInfo,
|
||||
out String NormalizedName)
|
||||
{
|
||||
FileNode = default(Object);
|
||||
FileDesc = default(Object);
|
||||
FileInfo = default(FileInfo);
|
||||
NormalizedName = default(String);
|
||||
|
||||
int Index = FileLookup(FileName);
|
||||
if (-1 == Index)
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
|
||||
FileNode = Index;
|
||||
FillFileInfo(Index, out FileInfo);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
public override Int32 Read(
|
||||
Object FileNode,
|
||||
Object FileDesc,
|
||||
IntPtr Buffer,
|
||||
UInt64 Offset,
|
||||
UInt32 Length,
|
||||
out UInt32 BytesTransferred)
|
||||
{
|
||||
int Index = (int)FileNode;
|
||||
UInt64 EndOffset;
|
||||
Byte[] Contents = FileContents(Index);
|
||||
|
||||
if (Offset >= (UInt64)Contents.Length)
|
||||
{
|
||||
BytesTransferred = 0;
|
||||
return STATUS_END_OF_FILE;
|
||||
}
|
||||
|
||||
EndOffset = Offset + Length;
|
||||
if (EndOffset > (UInt64)Contents.Length)
|
||||
EndOffset = (UInt64)Contents.Length;
|
||||
|
||||
BytesTransferred = (UInt32)(EndOffset - Offset);
|
||||
Marshal.Copy(Contents, (int)Offset, Buffer, (int)BytesTransferred);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
public override Int32 GetFileInfo(
|
||||
Object FileNode,
|
||||
Object FileDesc,
|
||||
out FileInfo FileInfo)
|
||||
{
|
||||
int Index = (int)FileNode;
|
||||
|
||||
FillFileInfo(Index, out FileInfo);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
public override Boolean ReadDirectoryEntry(
|
||||
Object FileNode,
|
||||
Object FileDesc,
|
||||
String Pattern,
|
||||
String Marker,
|
||||
ref Object Context,
|
||||
out String FileName,
|
||||
out FileInfo FileInfo)
|
||||
{
|
||||
IEnumerator<String> Enumerator = (IEnumerator<String>)Context;
|
||||
|
||||
if (null == Enumerator)
|
||||
{
|
||||
List<String> ChildrenFileNames = new List<String>();
|
||||
for (int Index = 1, Count = FileCount(); Count >= Index; Index++)
|
||||
ChildrenFileNames.Add(String.Format("{0}", Index));
|
||||
Context = Enumerator = ChildrenFileNames.GetEnumerator();
|
||||
}
|
||||
|
||||
while (Enumerator.MoveNext())
|
||||
{
|
||||
FileName = Enumerator.Current;
|
||||
FillFileInfo(int.Parse(FileName), out FileInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
FileName = default(String);
|
||||
FileInfo = default(FileInfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int CountFromTicks(int Ticks)
|
||||
{
|
||||
/*
|
||||
* The formula below produces the periodic sequence:
|
||||
* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
||||
* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
||||
* ...
|
||||
*/
|
||||
int div10 = (Ticks % 20) / 10;
|
||||
int mod10 = Ticks % 10;
|
||||
int mdv10 = 1 - div10;
|
||||
int mmd10 = 10 - mod10;
|
||||
return mdv10 * mod10 + div10 * mmd10;
|
||||
}
|
||||
private int FileCount()
|
||||
{
|
||||
int Ticks = Thread.VolatileRead(ref _Ticks);
|
||||
return CountFromTicks(Ticks);
|
||||
}
|
||||
private int FileLookup(String FileName)
|
||||
{
|
||||
FileName = FileName.Substring(1);
|
||||
if ("" == FileName)
|
||||
return 0; /* root */
|
||||
int Count = FileCount();
|
||||
Boolean Valid = int.TryParse(FileName, out int Index);
|
||||
if (!Valid || 0 >= Index || Index > Count)
|
||||
return -1; /* not found */
|
||||
return Index; /* regular file named 1, 2, ..., Count */
|
||||
}
|
||||
private static Byte[] FileContents(int Index)
|
||||
{
|
||||
if (0 == Index)
|
||||
return EmptyByteArray;
|
||||
return Encoding.UTF8.GetBytes(String.Format("{0}\n", Index));
|
||||
}
|
||||
private static void FillFileInfo(int Index, out FileInfo FileInfo)
|
||||
{
|
||||
FileInfo = default(FileInfo);
|
||||
FileInfo.FileAttributes = 0 == Index ? (UInt32)System.IO.FileAttributes.Directory : 0;
|
||||
FileInfo.FileSize = (UInt64)FileContents(Index).Length;
|
||||
FileInfo.AllocationSize = (FileInfo.FileSize + ALLOCATION_UNIT - 1)
|
||||
/ ALLOCATION_UNIT * ALLOCATION_UNIT;
|
||||
FileInfo.CreationTime =
|
||||
FileInfo.LastAccessTime =
|
||||
FileInfo.LastWriteTime =
|
||||
FileInfo.ChangeTime = (UInt64)DateTime.Now.ToFileTimeUtc();
|
||||
}
|
||||
private void Tick(Object Context)
|
||||
{
|
||||
int Ticks = Interlocked.Increment(ref _Ticks);
|
||||
int OldCount = CountFromTicks(Ticks - 1);
|
||||
int NewCount = CountFromTicks(Ticks);
|
||||
NotifyInfo[] NotifyInfo = new NotifyInfo[1];
|
||||
|
||||
if (OldCount < NewCount)
|
||||
{
|
||||
NotifyInfo[0].FileName = String.Format("\\{0}", NewCount);
|
||||
NotifyInfo[0].Action = NotifyAction.Added;
|
||||
NotifyInfo[0].Filter = NotifyFilter.ChangeFileName;
|
||||
Console.Error.WriteLine("CREATE \\{0}", NewCount);
|
||||
}
|
||||
else if (OldCount > NewCount)
|
||||
{
|
||||
NotifyInfo[0].FileName = String.Format("\\{0}", OldCount);
|
||||
NotifyInfo[0].Action = NotifyAction.Removed;
|
||||
NotifyInfo[0].Filter = NotifyFilter.ChangeFileName;
|
||||
Console.Error.WriteLine("REMOVE \\{0}", OldCount);
|
||||
}
|
||||
|
||||
if (OldCount != NewCount)
|
||||
{
|
||||
if (STATUS_SUCCESS == _Host.NotifyBegin(500))
|
||||
{
|
||||
_Host.Notify(NotifyInfo);
|
||||
_Host.NotifyEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Notifyfs()
|
||||
{
|
||||
RawSecurityDescriptor RootSecurityDescriptor = new RawSecurityDescriptor(
|
||||
"O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)");
|
||||
DefaultSecurity = new Byte[RootSecurityDescriptor.BinaryLength];
|
||||
RootSecurityDescriptor.GetBinaryForm(DefaultSecurity, 0);
|
||||
}
|
||||
|
||||
private const int ALLOCATION_UNIT = 4096;
|
||||
private static readonly Byte[] EmptyByteArray = new Byte[0];
|
||||
private static readonly Byte[] DefaultSecurity;
|
||||
private FileSystemHost _Host;
|
||||
private Timer _Timer;
|
||||
private int _Ticks;
|
||||
}
|
||||
|
||||
class NotifyfsService : Service
|
||||
{
|
||||
private class CommandLineUsageException : Exception
|
||||
{
|
||||
public CommandLineUsageException(String Message = null) : base(Message)
|
||||
{
|
||||
HasMessage = null != Message;
|
||||
}
|
||||
|
||||
public bool HasMessage;
|
||||
}
|
||||
|
||||
private const String PROGNAME = "notifyfs-dotnet";
|
||||
|
||||
public NotifyfsService() : base("NotifyfsService")
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnStart(String[] Args)
|
||||
{
|
||||
try
|
||||
{
|
||||
String VolumePrefix = null;
|
||||
String MountPoint = null;
|
||||
FileSystemHost Host = null;
|
||||
Notifyfs Notifyfs = null;
|
||||
int I;
|
||||
|
||||
for (I = 1; Args.Length > I; I++)
|
||||
{
|
||||
String Arg = Args[I];
|
||||
if ('-' != Arg[0])
|
||||
break;
|
||||
switch (Arg[1])
|
||||
{
|
||||
case '?':
|
||||
throw new CommandLineUsageException();
|
||||
case 'm':
|
||||
argtos(Args, ref I, ref MountPoint);
|
||||
break;
|
||||
case 'u':
|
||||
argtos(Args, ref I, ref VolumePrefix);
|
||||
break;
|
||||
default:
|
||||
throw new CommandLineUsageException();
|
||||
}
|
||||
}
|
||||
|
||||
if (Args.Length > I)
|
||||
throw new CommandLineUsageException();
|
||||
|
||||
if (null == MountPoint)
|
||||
throw new CommandLineUsageException();
|
||||
|
||||
FileSystemHost.SetDebugLogFile("-");
|
||||
|
||||
Host = new FileSystemHost(Notifyfs = new Notifyfs());
|
||||
Host.Prefix = VolumePrefix;
|
||||
if (0 > Host.Mount(MountPoint))
|
||||
throw new IOException("cannot mount file system");
|
||||
MountPoint = Host.MountPoint();
|
||||
_Host = Host;
|
||||
|
||||
Log(EVENTLOG_INFORMATION_TYPE, String.Format("{0}{1}{2} -m {3}",
|
||||
PROGNAME,
|
||||
null != VolumePrefix && 0 < VolumePrefix.Length ? " -u " : "",
|
||||
null != VolumePrefix && 0 < VolumePrefix.Length ? VolumePrefix : "",
|
||||
MountPoint));
|
||||
}
|
||||
catch (CommandLineUsageException ex)
|
||||
{
|
||||
Log(EVENTLOG_ERROR_TYPE, String.Format(
|
||||
"{0}" +
|
||||
"usage: {1} OPTIONS\n" +
|
||||
"\n" +
|
||||
"options:\n" +
|
||||
" -u \\Server\\Share [UNC prefix (single backslash)]\n" +
|
||||
" -m MountPoint [X:|*|directory]\n",
|
||||
ex.HasMessage ? ex.Message + "\n" : "",
|
||||
PROGNAME));
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log(EVENTLOG_ERROR_TYPE, String.Format("{0}", ex.Message));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
protected override void OnStop()
|
||||
{
|
||||
_Host.Unmount();
|
||||
_Host = null;
|
||||
}
|
||||
|
||||
private static void argtos(String[] Args, ref int I, ref String V)
|
||||
{
|
||||
if (Args.Length > ++I)
|
||||
V = Args[I];
|
||||
else
|
||||
throw new CommandLineUsageException();
|
||||
}
|
||||
|
||||
private FileSystemHost _Host;
|
||||
}
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Environment.ExitCode = new NotifyfsService().Run();
|
||||
}
|
||||
}
|
||||
}
|
77
tst/notifyfs-dotnet/notifyfs-dotnet.csproj
Normal file
77
tst/notifyfs-dotnet/notifyfs-dotnet.csproj
Normal file
@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>notifyfs</RootNamespace>
|
||||
<AssemblyName>notifyfs-dotnet</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>$(SolutionDir)build\$(Configuration)\</OutputPath>
|
||||
<BaseIntermediateOutputPath>$(SolutionDir)build\$(ProjectName).build\</BaseIntermediateOutputPath>
|
||||
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\</IntermediateOutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>$(SolutionDir)build\$(Configuration)\</OutputPath>
|
||||
<BaseIntermediateOutputPath>$(SolutionDir)build\$(ProjectName).build\</BaseIntermediateOutputPath>
|
||||
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\</IntermediateOutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="winfsp-msil">
|
||||
<HintPath>$(MSBuildProgramFiles32)\WinFsp\bin\winfsp-msil.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.5.2">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Microsoft .NET Framework 4.5.2 %28x86 and x64%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
25
tst/notifyfs-dotnet/notifyfs-dotnet.sln
Normal file
25
tst/notifyfs-dotnet/notifyfs-dotnet.sln
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30717.126
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "notifyfs-dotnet", "notifyfs-dotnet.csproj", "{DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {18D87005-09CA-40CF-9B51-139B486AB8D0}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
8
tst/notifyfs/.gitignore
vendored
Normal file
8
tst/notifyfs/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
build
|
||||
*.ncb
|
||||
*.suo
|
||||
*.vcproj.*
|
||||
*.vcxproj.user
|
||||
*.VC.db
|
||||
*.VC.opendb
|
||||
.vs
|
508
tst/notifyfs/notifyfs.c
Normal file
508
tst/notifyfs/notifyfs.c
Normal file
@ -0,0 +1,508 @@
|
||||
/**
|
||||
* @file notifyfs.c
|
||||
*
|
||||
* @copyright 2015-2020 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 software
|
||||
* in accordance with the commercial license agreement provided in
|
||||
* conjunction with the software. The terms and conditions of any such
|
||||
* commercial license agreement shall govern, supersede, and render
|
||||
* ineffective any application of the GPLv3 license to this software,
|
||||
* notwithstanding of any reference thereto in the software or
|
||||
* associated repository.
|
||||
*/
|
||||
|
||||
#include <winfsp/winfsp.h>
|
||||
#include <sddl.h>
|
||||
|
||||
#define DEBUGFLAGS 0
|
||||
//#define DEBUGFLAGS -1
|
||||
|
||||
#define PROGNAME "notifyfs"
|
||||
#define ALLOCATION_UNIT 4096
|
||||
|
||||
#define info(format, ...) FspServiceLog(EVENTLOG_INFORMATION_TYPE, format, __VA_ARGS__)
|
||||
#define warn(format, ...) FspServiceLog(EVENTLOG_WARNING_TYPE, format, __VA_ARGS__)
|
||||
#define fail(format, ...) FspServiceLog(EVENTLOG_ERROR_TYPE, format, __VA_ARGS__)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FSP_FILE_SYSTEM *FileSystem;
|
||||
PTP_TIMER Timer;
|
||||
UINT32 Ticks;
|
||||
} NOTIFYFS;
|
||||
|
||||
static PSECURITY_DESCRIPTOR DefaultSecurity;
|
||||
static ULONG DefaultSecuritySize;
|
||||
|
||||
static UINT32 CountFromTicks(UINT32 Ticks)
|
||||
{
|
||||
/*
|
||||
* The formula below produces the periodic sequence:
|
||||
* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
||||
* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
||||
* ...
|
||||
*/
|
||||
UINT32 div10 = (Ticks % 20) / 10;
|
||||
UINT32 mod10 = Ticks % 10;
|
||||
UINT32 mdv10 = 1 - div10;
|
||||
UINT32 mmd10 = 10 - mod10;
|
||||
return mdv10 * mod10 + div10 * mmd10;
|
||||
}
|
||||
|
||||
static UINT32 FileCount(NOTIFYFS *Notifyfs)
|
||||
{
|
||||
UINT32 Ticks = InterlockedOr(&Notifyfs->Ticks, 0);
|
||||
return CountFromTicks(Ticks);
|
||||
}
|
||||
|
||||
static UINT32 FileLookup(NOTIFYFS *Notifyfs, PWSTR FileName)
|
||||
{
|
||||
FileName++;
|
||||
PWSTR Endp;
|
||||
UINT32 Count = FileCount(Notifyfs);
|
||||
UINT32 Index = wcstoul(FileName, &Endp, 10);
|
||||
if ('\0' != *Endp || (FileName != Endp && (0 == Index || Index > Count)))
|
||||
return -1; /* not found */
|
||||
if (FileName == Endp)
|
||||
return 0; /* root */
|
||||
return Index; /* regular file named 1, 2, ..., Count */
|
||||
}
|
||||
|
||||
static UINT32 FileContents(UINT32 Index, WCHAR P[32])
|
||||
{
|
||||
WCHAR Buffer[32];
|
||||
if (0 == P)
|
||||
P = Buffer;
|
||||
if (0 == Index)
|
||||
P[0] = '\0';
|
||||
else
|
||||
wsprintfW(P, L"%u\n", (unsigned)Index);
|
||||
return lstrlenW(P);
|
||||
}
|
||||
|
||||
static VOID FillFileInfo(UINT32 Index, FSP_FSCTL_FILE_INFO *FileInfo)
|
||||
{
|
||||
FILETIME SystemTime;
|
||||
|
||||
GetSystemTimeAsFileTime(&SystemTime);
|
||||
|
||||
memset(FileInfo, 0, sizeof FileInfo);
|
||||
FileInfo->FileAttributes = 0 == Index ? FILE_ATTRIBUTE_DIRECTORY : 0;
|
||||
FileInfo->FileSize = FileContents(Index, 0);
|
||||
FileInfo->AllocationSize = (FileInfo->FileSize + ALLOCATION_UNIT - 1)
|
||||
/ ALLOCATION_UNIT * ALLOCATION_UNIT;
|
||||
FileInfo->CreationTime =
|
||||
FileInfo->LastAccessTime =
|
||||
FileInfo->LastWriteTime =
|
||||
FileInfo->ChangeTime = *(PUINT64)&SystemTime;
|
||||
}
|
||||
|
||||
static BOOLEAN AddDirInfo(PWSTR FileName, UINT32 Index,
|
||||
PVOID Buffer, ULONG Length, PULONG PBytesTransferred)
|
||||
{
|
||||
union
|
||||
{
|
||||
UINT8 B[FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) + MAX_PATH * sizeof(WCHAR)];
|
||||
FSP_FSCTL_DIR_INFO D;
|
||||
} DirInfoBuf;
|
||||
FSP_FSCTL_DIR_INFO *DirInfo = &DirInfoBuf.D;
|
||||
|
||||
memset(DirInfo->Padding, 0, sizeof DirInfo->Padding);
|
||||
if (0 != FileName)
|
||||
lstrcpyW(DirInfo->FileNameBuf, FileName);
|
||||
else
|
||||
wsprintfW(DirInfo->FileNameBuf, L"%u", (unsigned)Index);
|
||||
DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + wcslen(DirInfo->FileNameBuf) * sizeof(WCHAR));
|
||||
FillFileInfo(Index, &DirInfo->FileInfo);
|
||||
|
||||
return FspFileSystemAddDirInfo(DirInfo, Buffer, Length, PBytesTransferred);
|
||||
}
|
||||
|
||||
static NTSTATUS GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem,
|
||||
FSP_FSCTL_VOLUME_INFO *VolumeInfo)
|
||||
{
|
||||
memset(VolumeInfo, 0, sizeof *VolumeInfo);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS GetSecurityByName(FSP_FILE_SYSTEM *FileSystem,
|
||||
PWSTR FileName, PUINT32 PFileAttributes,
|
||||
PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize)
|
||||
{
|
||||
NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext;
|
||||
UINT32 Index;
|
||||
|
||||
Index = FileLookup(Notifyfs, FileName);
|
||||
if (-1 == Index)
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
|
||||
if (0 != PFileAttributes)
|
||||
*PFileAttributes = 0 == Index ? FILE_ATTRIBUTE_DIRECTORY : 0;
|
||||
|
||||
if (0 != PSecurityDescriptorSize)
|
||||
{
|
||||
if (DefaultSecuritySize > *PSecurityDescriptorSize)
|
||||
{
|
||||
*PSecurityDescriptorSize = DefaultSecuritySize;
|
||||
return STATUS_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
*PSecurityDescriptorSize = DefaultSecuritySize;
|
||||
if (0 != SecurityDescriptor)
|
||||
memcpy(SecurityDescriptor, DefaultSecurity, DefaultSecuritySize);
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem,
|
||||
PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess,
|
||||
UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize,
|
||||
PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo)
|
||||
{
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
}
|
||||
|
||||
static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem,
|
||||
PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess,
|
||||
PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo)
|
||||
{
|
||||
NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext;
|
||||
FSP_FSCTL_TRANSACT_FULL_CONTEXT *FullContext = (PVOID)PFileContext;
|
||||
UINT32 Index;
|
||||
|
||||
Index = FileLookup(Notifyfs, FileName);
|
||||
if (-1 == Index)
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
|
||||
FullContext->UserContext = Index;
|
||||
FullContext->UserContext2 = 0;
|
||||
|
||||
FillFileInfo(Index, FileInfo);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileContext, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize,
|
||||
FSP_FSCTL_FILE_INFO *FileInfo)
|
||||
{
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
}
|
||||
|
||||
static NTSTATUS Read(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileContext, PVOID Buffer, UINT64 Offset, ULONG Length,
|
||||
PULONG PBytesTransferred)
|
||||
{
|
||||
NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext;
|
||||
FSP_FSCTL_TRANSACT_FULL_CONTEXT *FullContext = FileContext;
|
||||
UINT32 Index = (UINT32)FullContext->UserContext;
|
||||
UINT64 EndOffset;
|
||||
WCHAR ContentBuf[32];
|
||||
UINT32 ContentLen;
|
||||
|
||||
ContentLen = FileContents(Index, ContentBuf);
|
||||
|
||||
if (Offset >= ContentLen)
|
||||
return STATUS_END_OF_FILE;
|
||||
|
||||
EndOffset = Offset + Length;
|
||||
if (EndOffset > ContentLen)
|
||||
EndOffset = ContentLen;
|
||||
|
||||
memcpy(Buffer, (PUINT8)ContentBuf + Offset, (size_t)(EndOffset - Offset));
|
||||
|
||||
*PBytesTransferred = (ULONG)(EndOffset - Offset);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS GetFileInfo(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileContext,
|
||||
FSP_FSCTL_FILE_INFO *FileInfo)
|
||||
{
|
||||
NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext;
|
||||
FSP_FSCTL_TRANSACT_FULL_CONTEXT *FullContext = FileContext;
|
||||
UINT32 Index = (UINT32)FullContext->UserContext;
|
||||
|
||||
FillFileInfo(Index, FileInfo);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS ReadDirectory(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileContext, PWSTR Pattern, PWSTR Marker,
|
||||
PVOID Buffer, ULONG Length, PULONG PBytesTransferred)
|
||||
{
|
||||
NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext;
|
||||
UINT32 Count = FileCount(Notifyfs);
|
||||
UINT32 Index;
|
||||
|
||||
Index = 0 == Marker ? 1 : wcstoul(Marker, 0, 10) + 1;
|
||||
for (; Count >= Index; Index++)
|
||||
if (!AddDirInfo(0, Index, Buffer, Length, PBytesTransferred))
|
||||
break;
|
||||
FspFileSystemAddDirInfo(0, Buffer, Length, PBytesTransferred);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static FSP_FILE_SYSTEM_INTERFACE NotifyfsInterface =
|
||||
{
|
||||
.GetVolumeInfo = GetVolumeInfo,
|
||||
.GetSecurityByName = GetSecurityByName,
|
||||
.Create = Create,
|
||||
.Open = Open,
|
||||
.Overwrite = Overwrite,
|
||||
.Read = Read,
|
||||
.GetFileInfo = GetFileInfo,
|
||||
.ReadDirectory = ReadDirectory,
|
||||
};
|
||||
|
||||
static VOID CALLBACK NotifyfsTick(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_TIMER Timer)
|
||||
{
|
||||
NOTIFYFS *Notifyfs = Context;
|
||||
UINT32 Ticks = InterlockedIncrement(&Notifyfs->Ticks);
|
||||
UINT32 OldCount = CountFromTicks(Ticks - 1);
|
||||
UINT32 NewCount = CountFromTicks(Ticks);
|
||||
union
|
||||
{
|
||||
UINT8 B[FIELD_OFFSET(FSP_FSCTL_NOTIFY_INFO, FileNameBuf) + MAX_PATH * sizeof(WCHAR)];
|
||||
FSP_FSCTL_NOTIFY_INFO V;
|
||||
} NotifyInfoBuf;
|
||||
FSP_FSCTL_NOTIFY_INFO *NotifyInfo = &NotifyInfoBuf.V;
|
||||
|
||||
memset(NotifyInfo, 0, sizeof NotifyInfo);
|
||||
if (OldCount < NewCount)
|
||||
{
|
||||
wsprintfW(NotifyInfo->FileNameBuf, L"\\%u", (unsigned)NewCount);
|
||||
NotifyInfo->Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) +
|
||||
wcslen(NotifyInfo->FileNameBuf) * sizeof(WCHAR));
|
||||
NotifyInfo->Action = FILE_ACTION_ADDED;
|
||||
NotifyInfo->Filter = FILE_NOTIFY_CHANGE_FILE_NAME;
|
||||
FspDebugLog("CREATE \\%u\n", (unsigned)NewCount);
|
||||
}
|
||||
else if (OldCount > NewCount)
|
||||
{
|
||||
wsprintfW(NotifyInfo->FileNameBuf, L"\\%u", (unsigned)OldCount);
|
||||
NotifyInfo->Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) +
|
||||
wcslen(NotifyInfo->FileNameBuf) * sizeof(WCHAR));
|
||||
NotifyInfo->Action = FILE_ACTION_REMOVED;
|
||||
NotifyInfo->Filter = FILE_NOTIFY_CHANGE_FILE_NAME;
|
||||
FspDebugLog("REMOVE \\%u\n", (unsigned)OldCount);
|
||||
}
|
||||
|
||||
if (OldCount != NewCount)
|
||||
{
|
||||
if (STATUS_SUCCESS == FspFileSystemNotifyBegin(Notifyfs->FileSystem, 500))
|
||||
{
|
||||
FspFileSystemNotify(Notifyfs->FileSystem, NotifyInfo, NotifyInfo->Size);
|
||||
FspFileSystemNotifyEnd(Notifyfs->FileSystem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static VOID NotifyfsDelete(NOTIFYFS *Notifyfs);
|
||||
|
||||
static NTSTATUS NotifyfsCreate(PWSTR VolumePrefix, PWSTR MountPoint, NOTIFYFS **PNotifyfs)
|
||||
{
|
||||
FSP_FSCTL_VOLUME_PARAMS VolumeParams;
|
||||
NOTIFYFS *Notifyfs = 0;
|
||||
INT64 TimerDue;
|
||||
NTSTATUS Result;
|
||||
|
||||
*PNotifyfs = 0;
|
||||
|
||||
if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
|
||||
L"O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)", SDDL_REVISION_1,
|
||||
&DefaultSecurity, &DefaultSecuritySize))
|
||||
{
|
||||
Result = FspNtStatusFromWin32(GetLastError());
|
||||
goto exit;
|
||||
}
|
||||
|
||||
Notifyfs = malloc(sizeof *Notifyfs);
|
||||
if (0 == Notifyfs)
|
||||
{
|
||||
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto exit;
|
||||
}
|
||||
memset(Notifyfs, 0, sizeof *Notifyfs);
|
||||
|
||||
memset(&VolumeParams, 0, sizeof VolumeParams);
|
||||
VolumeParams.SectorSize = ALLOCATION_UNIT;
|
||||
VolumeParams.SectorsPerAllocationUnit = 1;
|
||||
VolumeParams.VolumeCreationTime = 0;
|
||||
VolumeParams.VolumeSerialNumber = 0;
|
||||
VolumeParams.FileInfoTimeout = 1000;
|
||||
VolumeParams.CaseSensitiveSearch = 0;
|
||||
VolumeParams.CasePreservedNames = 1;
|
||||
VolumeParams.UnicodeOnDisk = 1;
|
||||
VolumeParams.PersistentAcls = 0;
|
||||
VolumeParams.PostCleanupWhenModifiedOnly = 1;
|
||||
VolumeParams.UmFileContextIsFullContext = 1;
|
||||
if (0 != VolumePrefix)
|
||||
wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix);
|
||||
wcscpy_s(VolumeParams.FileSystemName, sizeof VolumeParams.FileSystemName / sizeof(WCHAR),
|
||||
L"" PROGNAME);
|
||||
|
||||
Result = FspFileSystemCreate(
|
||||
VolumeParams.Prefix[0] ? L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME,
|
||||
&VolumeParams,
|
||||
&NotifyfsInterface,
|
||||
&Notifyfs->FileSystem);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
Notifyfs->FileSystem->UserContext = Notifyfs;
|
||||
|
||||
FspFileSystemSetDebugLog(Notifyfs->FileSystem, DEBUGFLAGS);
|
||||
|
||||
Result = FspFileSystemSetMountPoint(Notifyfs->FileSystem, MountPoint);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
Notifyfs->Timer = CreateThreadpoolTimer(NotifyfsTick, Notifyfs, 0);
|
||||
if (0 == Notifyfs->Timer)
|
||||
{
|
||||
Result = FspNtStatusFromWin32(GetLastError());
|
||||
goto exit;
|
||||
}
|
||||
|
||||
TimerDue = -1000;
|
||||
SetThreadpoolTimer(Notifyfs->Timer, (PVOID)&TimerDue, 1000, 0);
|
||||
|
||||
Result = STATUS_SUCCESS;
|
||||
|
||||
exit:
|
||||
if (NT_SUCCESS(Result))
|
||||
*PNotifyfs = Notifyfs;
|
||||
else if (0 != Notifyfs)
|
||||
NotifyfsDelete(Notifyfs);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static VOID NotifyfsDelete(NOTIFYFS *Notifyfs)
|
||||
{
|
||||
if (0 != Notifyfs->Timer)
|
||||
{
|
||||
SetThreadpoolTimer(Notifyfs->Timer, 0, 0, 0);
|
||||
WaitForThreadpoolTimerCallbacks(Notifyfs->Timer, TRUE);
|
||||
CloseThreadpoolTimer(Notifyfs->Timer);
|
||||
}
|
||||
|
||||
if (0 != Notifyfs->FileSystem)
|
||||
FspFileSystemDelete(Notifyfs->FileSystem);
|
||||
|
||||
free(Notifyfs);
|
||||
}
|
||||
|
||||
static NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv)
|
||||
{
|
||||
#define argtos(v) if (arge > ++argp) v = *argp; else goto usage
|
||||
|
||||
wchar_t **argp, **arge;
|
||||
PWSTR VolumePrefix = 0;
|
||||
PWSTR MountPoint = 0;
|
||||
NOTIFYFS *Notifyfs = 0;
|
||||
NTSTATUS Result;
|
||||
|
||||
for (argp = argv + 1, arge = argv + argc; arge > argp; argp++)
|
||||
{
|
||||
if (L'-' != argp[0][0])
|
||||
break;
|
||||
switch (argp[0][1])
|
||||
{
|
||||
case L'?':
|
||||
goto usage;
|
||||
case L'm':
|
||||
argtos(MountPoint);
|
||||
break;
|
||||
case L'u':
|
||||
argtos(VolumePrefix);
|
||||
break;
|
||||
default:
|
||||
goto usage;
|
||||
}
|
||||
}
|
||||
|
||||
if (arge > argp)
|
||||
goto usage;
|
||||
|
||||
if (0 == MountPoint)
|
||||
goto usage;
|
||||
|
||||
FspDebugLogSetHandle(GetStdHandle(STD_ERROR_HANDLE));
|
||||
|
||||
Result = NotifyfsCreate(VolumePrefix, MountPoint, &Notifyfs);
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
fail(L"cannot create file system");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
Result = FspFileSystemStartDispatcher(Notifyfs->FileSystem, 0);
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
fail(L"cannot start file system");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
MountPoint = FspFileSystemMountPoint(Notifyfs->FileSystem);
|
||||
|
||||
info(L"%s%s%s -m %s",
|
||||
L"" PROGNAME,
|
||||
0 != VolumePrefix && L'\0' != VolumePrefix[0] ? L" -u " : L"",
|
||||
0 != VolumePrefix && L'\0' != VolumePrefix[0] ? VolumePrefix : L"",
|
||||
MountPoint);
|
||||
|
||||
Service->UserContext = Notifyfs;
|
||||
Result = STATUS_SUCCESS;
|
||||
|
||||
exit:
|
||||
if (!NT_SUCCESS(Result) && 0 != Notifyfs)
|
||||
NotifyfsDelete(Notifyfs);
|
||||
|
||||
return Result;
|
||||
|
||||
usage:;
|
||||
static wchar_t usage[] = L""
|
||||
"usage: %s OPTIONS\n"
|
||||
"\n"
|
||||
"options:\n"
|
||||
" -u \\Server\\Share [UNC prefix (single backslash)]\n"
|
||||
" -m MountPoint [X:|*|directory]\n";
|
||||
|
||||
fail(usage, L"" PROGNAME);
|
||||
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
|
||||
#undef argtos
|
||||
}
|
||||
|
||||
static NTSTATUS SvcStop(FSP_SERVICE *Service)
|
||||
{
|
||||
NOTIFYFS *Notifyfs = Service->UserContext;
|
||||
|
||||
FspFileSystemStopDispatcher(Notifyfs->FileSystem);
|
||||
NotifyfsDelete(Notifyfs);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int wmain(int argc, wchar_t **argv)
|
||||
{
|
||||
if (!NT_SUCCESS(FspLoad(0)))
|
||||
return ERROR_DELAY_LOAD_FAILED;
|
||||
|
||||
return FspServiceRun(L"" PROGNAME, SvcStart, SvcStop, 0);
|
||||
}
|
31
tst/notifyfs/notifyfs.sln
Normal file
31
tst/notifyfs/notifyfs.sln
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30717.126
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "notifyfs", "notifyfs.vcxproj", "{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Debug|x64.Build.0 = Debug|x64
|
||||
{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Debug|x86.Build.0 = Debug|Win32
|
||||
{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Release|x64.ActiveCfg = Release|x64
|
||||
{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Release|x64.Build.0 = Release|x64
|
||||
{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Release|x86.ActiveCfg = Release|Win32
|
||||
{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3CD0763B-2BFD-4D73-9539-13273788C41E}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
175
tst/notifyfs/notifyfs.vcxproj
Normal file
175
tst/notifyfs/notifyfs.vcxproj
Normal file
@ -0,0 +1,175 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{4ba1ded0-4268-408a-a4e2-8e1a6d55a99c}</ProjectGuid>
|
||||
<RootNamespace>notifyfs</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>$(LatestTargetPlatformVersion)</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)build\$(Configuration)\</OutDir>
|
||||
<IntDir>$(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\</IntDir>
|
||||
<TargetName>$(ProjectName)-$(PlatformTarget)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)build\$(Configuration)\</OutDir>
|
||||
<IntDir>$(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\</IntDir>
|
||||
<TargetName>$(ProjectName)-$(PlatformTarget)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)build\$(Configuration)\</OutDir>
|
||||
<IntDir>$(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\</IntDir>
|
||||
<TargetName>$(ProjectName)-$(PlatformTarget)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)build\$(Configuration)\</OutDir>
|
||||
<IntDir>$(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\</IntDir>
|
||||
<TargetName>$(ProjectName)-$(PlatformTarget)</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\WinFsp\inc</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>winfsp-$(PlatformTarget).dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\WinFsp\inc</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>winfsp-$(PlatformTarget).dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\WinFsp\inc</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>winfsp-$(PlatformTarget).dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\WinFsp\inc</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>winfsp-$(PlatformTarget).dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="notifyfs.c" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
14
tst/notifyfs/notifyfs.vcxproj.filters
Normal file
14
tst/notifyfs/notifyfs.vcxproj.filters
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="notifyfs.c">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -25,7 +25,7 @@
|
||||
|
||||
#include "winfsp-tests.h"
|
||||
|
||||
void posix_map_sid_test(void)
|
||||
static void posix_map_sid_test(void)
|
||||
{
|
||||
struct
|
||||
{
|
||||
@ -171,7 +171,7 @@ void posix_map_sid_test(void)
|
||||
LocalFree(map[sizeof map / sizeof map[0] - 1].SidStr);
|
||||
}
|
||||
|
||||
void posix_map_sd_test(void)
|
||||
static void posix_map_sd_test(void)
|
||||
{
|
||||
struct
|
||||
{
|
||||
@ -237,7 +237,93 @@ void posix_map_sd_test(void)
|
||||
}
|
||||
}
|
||||
|
||||
void posix_map_path_test(void)
|
||||
static void posix_merge_sd_test(void)
|
||||
{
|
||||
struct
|
||||
{
|
||||
UINT32 Uid, Gid, Mode;
|
||||
PWSTR ExistingSddl;
|
||||
PWSTR Sddl;
|
||||
} map[] =
|
||||
{
|
||||
{
|
||||
18, 544, 00000,
|
||||
0,
|
||||
L"O:SYG:BAD:P(A;;0x1f0198;;;SY)(A;;0x120088;;;BA)(A;;0x120088;;;WD)"
|
||||
},
|
||||
{
|
||||
18, 544, 00000,
|
||||
L"",
|
||||
L"O:SYG:BA"
|
||||
},
|
||||
{
|
||||
18, 544, 00000,
|
||||
L"O:WD",
|
||||
L"O:WDG:BA"
|
||||
},
|
||||
{
|
||||
18, 544, 00000,
|
||||
L"G:WD",
|
||||
L"O:SYG:WD"
|
||||
},
|
||||
{
|
||||
18, 544, 00000,
|
||||
L"O:WDG:WD",
|
||||
L"O:WDG:WD"
|
||||
},
|
||||
{
|
||||
18, 544, 00000,
|
||||
L"D:P",
|
||||
L"O:SYG:BAD:P"
|
||||
},
|
||||
{
|
||||
18, 544, 00000,
|
||||
L"D:P(A;;FA;;;SY)",
|
||||
L"O:SYG:BAD:P(A;;FA;;;SY)"
|
||||
},
|
||||
{
|
||||
18, 544, 00000,
|
||||
L"O:WDG:WDD:P(A;;FA;;;SY)",
|
||||
L"O:WDG:WDD:P(A;;FA;;;SY)"
|
||||
},
|
||||
};
|
||||
NTSTATUS Result;
|
||||
BOOL Success;
|
||||
PSECURITY_DESCRIPTOR ExistingSecurityDescriptor, SecurityDescriptor;
|
||||
PWSTR Sddl;
|
||||
|
||||
for (size_t i = 0; sizeof map / sizeof map[0] > i; i++)
|
||||
{
|
||||
if (0 != map[i].ExistingSddl)
|
||||
{
|
||||
Success = ConvertStringSecurityDescriptorToSecurityDescriptorW(
|
||||
map[i].ExistingSddl, SDDL_REVISION_1,
|
||||
&ExistingSecurityDescriptor, 0);
|
||||
ASSERT(Success);
|
||||
}
|
||||
else
|
||||
ExistingSecurityDescriptor = 0;
|
||||
|
||||
Result = FspPosixMergePermissionsToSecurityDescriptor(
|
||||
map[i].Uid, map[i].Gid, map[i].Mode, ExistingSecurityDescriptor, &SecurityDescriptor);
|
||||
ASSERT(NT_SUCCESS(Result));
|
||||
|
||||
Success = ConvertSecurityDescriptorToStringSecurityDescriptorW(
|
||||
SecurityDescriptor, SDDL_REVISION_1,
|
||||
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
|
||||
&Sddl, 0);
|
||||
ASSERT(Success);
|
||||
ASSERT(0 == wcscmp(map[i].Sddl, Sddl));
|
||||
LocalFree(Sddl);
|
||||
|
||||
FspDeleteSecurityDescriptor(SecurityDescriptor,
|
||||
FspPosixMergePermissionsToSecurityDescriptor);
|
||||
|
||||
LocalFree(ExistingSecurityDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
static void posix_map_path_test(void)
|
||||
{
|
||||
struct
|
||||
{
|
||||
@ -274,5 +360,6 @@ void posix_tests(void)
|
||||
|
||||
TEST(posix_map_sid_test);
|
||||
TEST(posix_map_sd_test);
|
||||
TEST(posix_merge_sd_test);
|
||||
TEST(posix_map_path_test);
|
||||
}
|
||||
|
Reference in New Issue
Block a user