mirror of
https://github.com/winfsp/winfsp.git
synced 2025-07-03 01:12:58 -05:00
Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
b3058a5e3e | |||
3bda3d754e | |||
8109b005be | |||
bd0d6638b0 | |||
912703cd77 | |||
90bc12132e | |||
f1cf020272 | |||
2f65a77d34 | |||
4578414a2c | |||
f0fd53e3f3 | |||
1cc42c9d70 | |||
28ac5a1cfe | |||
e1b1284153 | |||
5014e8bd35 | |||
1b7a78edff | |||
6340811974 | |||
cd21d26b93 | |||
d5ab701e3c | |||
8269f57282 | |||
e59a49992a | |||
10c8c440f9 | |||
f3375fc17f | |||
c1e4b00aa7 | |||
1bb0580a6a | |||
e54c2288f7 | |||
6b4b1dff6c | |||
92b7989999 | |||
f2e2d83b72 | |||
3687df53c6 | |||
a004e4be10 | |||
88edf5723e | |||
7f360827f6 | |||
01f91c771d | |||
844fb7171e | |||
489081b8c2 | |||
c77690e59d | |||
32a5b2bc64 | |||
5045403d85 | |||
13a52c4ab4 | |||
c18d4f1508 | |||
fcfebb968f | |||
10053bc759 | |||
7985827c73 | |||
13146e4854 | |||
84e0744c28 | |||
20e19cb0fc | |||
2326521ef8 | |||
0296502f24 | |||
5d0b10d0b6 |
@ -1,6 +1,79 @@
|
||||
= Changelog
|
||||
|
||||
|
||||
v1.9B1 (2021 Beta1)::
|
||||
|
||||
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`
|
||||
|
||||
* [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.8 (2020.2)::
|
||||
|
||||
Changes since v1.7:
|
||||
|
||||
* [FSD] WinFsp now supports Windows containers. See the link:doc/WinFsp-Container-Support.asciidoc[WinFsp Container Support] document for details.
|
||||
|
||||
* [FSD] The `FSP_FSCTL_QUERY_WINFSP` code provides a simple method to determine if
|
||||
the file system backing a file is a WinFsp file system. To use issue a
|
||||
+
|
||||
----
|
||||
DeviceIoControl(Handle, FSP_FSCTL_QUERY_WINFSP, 0, 0, 0, 0, &Bytes, 0)
|
||||
----
|
||||
+
|
||||
If the return value is TRUE this is a WinFsp file system.
|
||||
|
||||
* [FSD] A fix regarding concurrency of READs on the same file: WinFsp was supposed to allow concurrent READ requests on the same file (e.g. two concurrent overlapped `ReadFile` requests on the same `HANDLE`) to be handled concurrently by the file system; unfortunately due to a problem in recent versions of WinFsp READ requests on the same file were serialized. This problem has now been fixed. See GitHub issue #291 for more details.
|
||||
** *NOTE*: It may be that some file system was inadvertently relying on WinFsp's implicit serialization of READs in this case. Please test your file system thoroughly against this version, especially with regard to READ serialization. Related XKCD: https://imgs.xkcd.com/comics/workflow.png
|
||||
|
||||
* [FSD] When renaming files or directories NTFS allows the target name to contain a backslash at the end (even for files!) whereas WinFsp did not. This problem has been fixed and a test has been added in `winfsp-tests`.
|
||||
|
||||
|
||||
v1.8B3 (2020.2 B2)::
|
||||
|
||||
Changes since v1.7:
|
||||
|
||||
* [FSD] WinFsp now supports Windows containers. See the link:doc/WinFsp-Container-Support.asciidoc[WinFsp Container Support] document for details.
|
||||
|
||||
* [FSD] The `FSP_FSCTL_QUERY_WINFSP` code provides a simple method to determine if
|
||||
the file system backing a file is a WinFsp file system. To use issue a
|
||||
+
|
||||
----
|
||||
DeviceIoControl(Handle, FSP_FSCTL_QUERY_WINFSP, 0, 0, 0, 0, &Bytes, 0)
|
||||
----
|
||||
+
|
||||
If the return value is TRUE this is a WinFsp file system.
|
||||
|
||||
* [FSD] A fix regarding concurrency of READs on the same file: WinFsp was supposed to allow concurrent READ requests on the same file (e.g. two concurrent overlapped `ReadFile` requests on the same `HANDLE`) to be handled concurrently by the file system; unfortunately due to a problem in recent versions of WinFsp READ requests on the same file were serialized. This problem has now been fixed. See GitHub issue #291 for more details.
|
||||
** *NOTE*: It may be that some file system was inadvertently relying on WinFsp's implicit serialization of READs in this case. Please test your file system thoroughly against this version, especially with regard to READ serialization. Related XKCD: https://imgs.xkcd.com/comics/workflow.png
|
||||
|
||||
* [FSD] When renaming files or directories NTFS allows the target name to contain a backslash at the end (even for files!) whereas WinFsp did not. This problem has been fixed and a test has been added in `winfsp-tests`.
|
||||
|
||||
|
||||
v1.8B2 (2020.2 B2)::
|
||||
|
||||
Changes since v1.7:
|
||||
|
||||
* [FSD] WinFsp now supports Windows containers. See the link:doc/WinFsp-Container-Support.asciidoc[WinFsp Container Support] document for details.
|
||||
|
||||
* [FSD] The `FSP_FSCTL_QUERY_WINFSP` code provides a simple method to determine if
|
||||
the file system backing a file is a WinFsp file system. To use issue a
|
||||
+
|
||||
----
|
||||
DeviceIoControl(Handle, FSP_FSCTL_QUERY_WINFSP, 0, 0, 0, 0, &Bytes, 0)
|
||||
----
|
||||
+
|
||||
If the return value is TRUE this is a WinFsp file system.
|
||||
|
||||
* [FSD] A fix regarding concurrency of READs on the same file: WinFsp was supposed to allow concurrent READ requests on the same file (e.g. two concurrent overlapped `ReadFile` requests on the same `HANDLE`) to be handled concurrently by the file system; unfortunately due to a problem in recent versions of WinFsp READ requests on the same file were serialized. This problem has now been fixed. See GitHub issue #291 for more details.
|
||||
** *NOTE*: It may be that some file system was inadvertently relying on WinFsp's implicit serialization of READs in this case. Please test your file system thoroughly against this version, especially with regard to READ serialization. Related XKCD: https://imgs.xkcd.com/comics/workflow.png
|
||||
|
||||
|
||||
v1.8B1 (2020.2 B1)::
|
||||
|
||||
Changes since v1.7:
|
||||
@ -16,6 +89,20 @@ DeviceIoControl(Handle, FSP_FSCTL_QUERY_WINFSP, 0, 0, 0, 0, &Bytes, 0)
|
||||
If the return value is TRUE this is a WinFsp file system.
|
||||
|
||||
|
||||
v1.7 (2020.1)::
|
||||
|
||||
Changes since v1.6:
|
||||
|
||||
* [FUSE] FUSE invalid directory entries no longer break the entire directory listing. Such invalid directory entries are logged. (GitHub PR #292.)
|
||||
* [LAUNCH] The Launcher can now restart file systems that have crashed. Set `Recovery=1` in the file system's registry entry.
|
||||
* [LAUNCH] The Launcher can now redirect file system standard error output. Set `Stderr=PATH` in the file system's registry entry.
|
||||
* [FIX] Work around a problem in CreateProcess/CreateSection that allowed a faulty or malicious file system to bugcheck Windows.
|
||||
* [FIX] Work around an incompatibility with Avast Antivirus.
|
||||
** Native and .NET file systems that experience this problem should set the flag `RejectIrpPriorToTransact0` in `FSP_FSCTL_VOLUME_PARAMS` to `1`. This is only required when mounting on a directory with Avast Antivirus present.
|
||||
** FUSE file systems do not need to do anything special as this flag is always enabled.
|
||||
* [FIX] Fix junction (mount point reparse point) handling. (GitHub issue #269.)
|
||||
|
||||
|
||||
v1.7B2 (2020.1 B2)::
|
||||
|
||||
Changes since v1.6:
|
||||
|
25
appveyor.yml
25
appveyor.yml
@ -1,6 +1,8 @@
|
||||
version: '{build}'
|
||||
|
||||
environment:
|
||||
# Disable the winfsp-tests built-in exception filter to allow WER to collect dumps.
|
||||
WINFSP_TESTS_EXCEPTION_FILTER_DISABLE: 1
|
||||
matrix:
|
||||
- CONFIGURATION: Debug
|
||||
TESTING: Func
|
||||
@ -15,18 +17,25 @@ init:
|
||||
#- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
|
||||
install:
|
||||
# Hack to make WDK 1903 work on VS2015.
|
||||
# See https://github.com/appveyor-tests/WDK-10.0.14393.0/blob/31cf12217fe0c92b218c70d7027dfe145be4f4cb/appveyor.yml#L7
|
||||
- ps: |
|
||||
# Hack to make WDK 1903 work on VS2015.
|
||||
# See https://github.com/appveyor-tests/WDK-10.0.14393.0/blob/31cf12217fe0c92b218c70d7027dfe145be4f4cb/appveyor.yml#L7
|
||||
[xml]$targets = get-content "C:\Program Files (x86)\Windows Kits\10\build\WindowsDriver.Common.targets"
|
||||
$usingTask = $targets.ChildNodes[1].UsingTask | ? {$_.TaskName -eq "ValidateNTTargetVersion"}
|
||||
$usingTask.AssemblyFile = '$(WDKContentRoot)build\bin\Microsoft.DriverKit.Build.Tasks.16.0.dll'
|
||||
$targets.Save("C:\Program Files (x86)\Windows Kits\10\build\WindowsDriver.Common.targets")
|
||||
# Submodules
|
||||
- git submodule update --init --recursive
|
||||
# Kernel and user mode dumps
|
||||
- if exist %SystemRoot%\memory.dmp del %SystemRoot%\memory.dmp
|
||||
- if exist C:\projects\LocalDumps rmdir /s/q C:\projects\LocalDumps
|
||||
- mkdir C:\projects\LocalDumps
|
||||
- reg add "HKLM\Software\Microsoft\Windows\Windows Error Reporting\LocalDumps" /v DumpFolder /t REG_EXPAND_SZ /d C:\projects\LocalDumps /f
|
||||
- reg add "HKLM\Software\Microsoft\Windows\Windows Error Reporting\LocalDumps" /v DumpType /t REG_DWORD /d 2 /f
|
||||
# Boot configuration
|
||||
- appveyor AddMessage "Change boot configuration and reboot" -Category Information
|
||||
- bcdedit /set testsigning on
|
||||
- if %TESTING%==Func verifier /standard /driver winfsp-x64.sys
|
||||
- if exist %SystemRoot%\memory.dmp del %SystemRoot%\memory.dmp
|
||||
- ps: Restart-Computer -Force
|
||||
- ps: Start-Sleep -s 60
|
||||
|
||||
@ -35,8 +44,8 @@ build_script:
|
||||
# build cygfuse
|
||||
- C:\cygwin64\setup-x86_64.exe -qnNd -P cygport
|
||||
- C:\cygwin64\bin\bash --login -c "make -C '%CD%\opt\cygfuse' dist"
|
||||
- C:\cygwin\setup-x86.exe -qnNd -P cygport
|
||||
- C:\cygwin\bin\bash --login -c "make -C '%CD%\opt\cygfuse' dist"
|
||||
#- C:\cygwin\setup-x86.exe -qnNd -P cygport
|
||||
#- C:\cygwin\bin\bash --login -c "make -C '%CD%\opt\cygfuse' dist"
|
||||
# build winfsp
|
||||
- tools\build.bat %CONFIGURATION%
|
||||
|
||||
@ -53,9 +62,11 @@ test_script:
|
||||
- if %TESTING%==Avast tools\run-tests.bat %CONFIGURATION% avast-tests
|
||||
- if %TESTING%==Perf tools\run-perf-tests.bat %CONFIGURATION% baseline > perf-tests.csv && type perf-tests.csv & appveyor PushArtifact perf-tests.csv
|
||||
- choco uninstall winfsp -y
|
||||
- if exist %SystemRoot%\memory.dmp exit 1
|
||||
|
||||
on_finish:
|
||||
- if exist %SystemRoot%\memory.dmp (7z a memory.dmp.zip %SystemRoot%\memory.dmp && appveyor PushArtifact memory.dmp.zip)
|
||||
- verifier /query
|
||||
- if exist %SystemRoot%\memory.dmp (7z a km.dmp.zip %SystemRoot%\memory.dmp && appveyor PushArtifact km.dmp.zip)
|
||||
- dir /a/b C:\projects\LocalDumps | findstr "^" && (7z a um.dmp.zip C:\projects\LocalDumps && appveyor PushArtifact um.dmp.zip) || ver>nul
|
||||
- if exist *.dmp.zip (7z a sym.pdb.zip build\VStudio\build\%CONFIGURATION%\*.pdb && appveyor PushArtifact sym.pdb.zip)
|
||||
- if exist *.dmp.zip exit 1
|
||||
#- ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
|
@ -107,7 +107,7 @@
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>ntdll.lib;netapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>ntdll.lib;netapi32.lib;dbghelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
@ -123,7 +123,7 @@
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<AdditionalDependencies>ntdll.lib;netapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>ntdll.lib;netapi32.lib;dbghelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
@ -143,7 +143,7 @@
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>ntdll.lib;netapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>ntdll.lib;netapi32.lib;dbghelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
@ -163,7 +163,7 @@
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>ntdll.lib;netapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>ntdll.lib;netapi32.lib;dbghelp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
@ -195,6 +195,7 @@
|
||||
<ClCompile Include="..\..\..\tst\winfsp-tests\lock-test.c" />
|
||||
<ClCompile Include="..\..\..\tst\winfsp-tests\memfs-test.c" />
|
||||
<ClCompile Include="..\..\..\tst\winfsp-tests\mount-test.c" />
|
||||
<ClCompile Include="..\..\..\tst\winfsp-tests\notify-test.c" />
|
||||
<ClCompile Include="..\..\..\tst\winfsp-tests\oplock-test.c" />
|
||||
<ClCompile Include="..\..\..\tst\winfsp-tests\path-test.c" />
|
||||
<ClCompile Include="..\..\..\tst\winfsp-tests\posix-test.c" />
|
||||
|
@ -109,6 +109,9 @@
|
||||
<ClCompile Include="..\..\..\tst\winfsp-tests\uuid5-test.c">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\tst\winfsp-tests\notify-test.c">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\ext\tlib\testsuite.h">
|
||||
|
@ -16,9 +16,9 @@
|
||||
<MyCompanyName>Navimatics LLC</MyCompanyName>
|
||||
<MyCopyright>2015-$([System.DateTime]::Now.ToString(`yyyy`)) Bill Zissimopoulos</MyCopyright>
|
||||
|
||||
<MyCanonicalVersion>1.8</MyCanonicalVersion>
|
||||
<MyCanonicalVersion>1.9</MyCanonicalVersion>
|
||||
|
||||
<MyProductVersion>2020.2 Beta1</MyProductVersion>
|
||||
<MyProductVersion>2021 Beta1</MyProductVersion>
|
||||
<MyProductStage>Beta</MyProductStage>
|
||||
|
||||
<MyVersion>$(MyCanonicalVersion).$(MyBuildNumber)</MyVersion>
|
||||
|
@ -52,6 +52,7 @@
|
||||
<ClCompile Include="..\..\src\dll\fuse\fuse_main.c" />
|
||||
<ClCompile Include="..\..\src\dll\fuse\fuse_opt.c" />
|
||||
<ClCompile Include="..\..\src\dll\launch.c" />
|
||||
<ClCompile Include="..\..\src\dll\ldap.c" />
|
||||
<ClCompile Include="..\..\src\dll\mount.c" />
|
||||
<ClCompile Include="..\..\src\dll\np.c" />
|
||||
<ClCompile Include="..\..\src\dll\security.c" />
|
||||
@ -224,7 +225,7 @@ copy /b $(OutDir)fuse3-$(PlatformTarget).pc + %(FullPath) $(OutDir)fuse3-$(Platf
|
||||
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<ModuleDefinitionFile>..\..\src\dll\library.def</ModuleDefinitionFile>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);rpcrt4.lib;credui.lib;secur32.lib;version.lib</AdditionalDependencies>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);rpcrt4.lib;credui.lib;secur32.lib;version.lib;netapi32.lib;wldap32.lib</AdditionalDependencies>
|
||||
<StripPrivateSymbols>$(OutDir)$(TargetFileName).public.pdb</StripPrivateSymbols>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
@ -251,7 +252,7 @@ copy /b $(OutDir)fuse3-$(PlatformTarget).pc + %(FullPath) $(OutDir)fuse3-$(Platf
|
||||
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
|
||||
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
|
||||
<ModuleDefinitionFile>..\..\src\dll\library.def</ModuleDefinitionFile>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);rpcrt4.lib;credui.lib;secur32.lib;version.lib</AdditionalDependencies>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);rpcrt4.lib;credui.lib;secur32.lib;version.lib;netapi32.lib;wldap32.lib</AdditionalDependencies>
|
||||
<StripPrivateSymbols>$(OutDir)$(TargetFileName).public.pdb</StripPrivateSymbols>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
@ -281,7 +282,7 @@ copy /b $(OutDir)fuse3-$(PlatformTarget).pc + %(FullPath) $(OutDir)fuse3-$(Platf
|
||||
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
|
||||
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
|
||||
<ModuleDefinitionFile>..\..\src\dll\library.def</ModuleDefinitionFile>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);rpcrt4.lib;credui.lib;secur32.lib;version.lib</AdditionalDependencies>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);rpcrt4.lib;credui.lib;secur32.lib;version.lib;netapi32.lib;wldap32.lib</AdditionalDependencies>
|
||||
<StripPrivateSymbols>$(OutDir)$(TargetFileName).public.pdb</StripPrivateSymbols>
|
||||
<AdditionalOptions>/PDBALTPATH:$(TargetFileName).pdb %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
@ -312,7 +313,7 @@ copy /b $(OutDir)fuse3-$(PlatformTarget).pc + %(FullPath) $(OutDir)fuse3-$(Platf
|
||||
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
|
||||
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
|
||||
<ModuleDefinitionFile>..\..\src\dll\library.def</ModuleDefinitionFile>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);rpcrt4.lib;credui.lib;secur32.lib;version.lib</AdditionalDependencies>
|
||||
<AdditionalDependencies>%(AdditionalDependencies);rpcrt4.lib;credui.lib;secur32.lib;version.lib;netapi32.lib;wldap32.lib</AdditionalDependencies>
|
||||
<StripPrivateSymbols>$(OutDir)$(TargetFileName).public.pdb</StripPrivateSymbols>
|
||||
<AdditionalOptions>/PDBALTPATH:$(TargetFileName).pdb %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
|
@ -169,6 +169,9 @@
|
||||
<ClCompile Include="..\..\src\shared\ku\posix.c">
|
||||
<Filter>Source\shared\ku</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\dll\ldap.c">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\src\dll\library.def">
|
||||
|
@ -152,6 +152,8 @@ FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_exit)(struct fsp_fuse_env *env,
|
||||
struct fuse *f);
|
||||
FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_exited)(struct fsp_fuse_env *env,
|
||||
struct fuse *f);
|
||||
FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_notify)(struct fsp_fuse_env *env,
|
||||
struct fuse *f, const char *path, uint32_t action);
|
||||
FSP_FUSE_API struct fuse_context *FSP_FUSE_API_NAME(fsp_fuse_get_context)(struct fsp_fuse_env *env);
|
||||
|
||||
FSP_FUSE_SYM(
|
||||
@ -212,6 +214,13 @@ int fuse_exited(struct fuse *f),
|
||||
(fsp_fuse_env(), f);
|
||||
})
|
||||
|
||||
FSP_FUSE_SYM(
|
||||
int fuse_notify(struct fuse *f, const char *path, uint32_t action),
|
||||
{
|
||||
return FSP_FUSE_API_CALL(fsp_fuse_notify)
|
||||
(fsp_fuse_env(), f, path, action);
|
||||
})
|
||||
|
||||
FSP_FUSE_SYM(
|
||||
struct fuse_context *fuse_get_context(void),
|
||||
{
|
||||
@ -236,9 +245,8 @@ int fuse_interrupted(void),
|
||||
FSP_FUSE_SYM(
|
||||
int fuse_invalidate(struct fuse *f, const char *path),
|
||||
{
|
||||
(void)f;
|
||||
(void)path;
|
||||
return -EINVAL;
|
||||
return FSP_FUSE_API_CALL(fsp_fuse_notify)
|
||||
(fsp_fuse_env(), f, path, 0);
|
||||
})
|
||||
|
||||
FSP_FUSE_SYM(
|
||||
|
@ -79,6 +79,17 @@ extern "C" {
|
||||
#define UF_ARCHIVE FSP_FUSE_UF_ARCHIVE
|
||||
#endif
|
||||
|
||||
/* notify extension */
|
||||
#define FSP_FUSE_NOTIFY_MKDIR 0x0001
|
||||
#define FSP_FUSE_NOTIFY_RMDIR 0x0002
|
||||
#define FSP_FUSE_NOTIFY_CREATE 0x0004
|
||||
#define FSP_FUSE_NOTIFY_UNLINK 0x0008
|
||||
#define FSP_FUSE_NOTIFY_CHMOD 0x0010
|
||||
#define FSP_FUSE_NOTIFY_CHOWN 0x0020
|
||||
#define FSP_FUSE_NOTIFY_UTIME 0x0040
|
||||
#define FSP_FUSE_NOTIFY_CHFLAGS 0x0080
|
||||
#define FSP_FUSE_NOTIFY_TRUNCATE 0x0100
|
||||
|
||||
struct fuse_file_info
|
||||
{
|
||||
int flags;
|
||||
|
@ -66,6 +66,8 @@ extern const __declspec(selectany) GUID FspFsvrtDeviceClassGuid =
|
||||
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 't', METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
|
||||
#define FSP_FSCTL_STOP \
|
||||
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'S', METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define FSP_FSCTL_NOTIFY \
|
||||
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'n', METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
|
||||
/* fsctl internal device codes (usable only in-kernel) */
|
||||
#define FSP_FSCTL_TRANSACT_INTERNAL \
|
||||
@ -268,6 +270,15 @@ typedef struct
|
||||
FSP_FSCTL_STATIC_ASSERT(24 == sizeof(FSP_FSCTL_STREAM_INFO),
|
||||
"sizeof(FSP_FSCTL_STREAM_INFO) must be exactly 24.");
|
||||
typedef struct
|
||||
{
|
||||
UINT16 Size;
|
||||
UINT32 Filter;
|
||||
UINT32 Action;
|
||||
WCHAR FileNameBuf[];
|
||||
} FSP_FSCTL_NOTIFY_INFO;
|
||||
FSP_FSCTL_STATIC_ASSERT(12 == sizeof(FSP_FSCTL_NOTIFY_INFO),
|
||||
"sizeof(FSP_FSCTL_NOTIFY_INFO) must be exactly 12.");
|
||||
typedef struct
|
||||
{
|
||||
UINT64 UserContext;
|
||||
UINT64 UserContext2;
|
||||
@ -617,6 +628,8 @@ FSP_API NTSTATUS FspFsctlTransact(HANDLE VolumeHandle,
|
||||
PVOID RequestBuf, SIZE_T *PRequestBufSize,
|
||||
BOOLEAN Batch);
|
||||
FSP_API NTSTATUS FspFsctlStop(HANDLE VolumeHandle);
|
||||
FSP_API NTSTATUS FspFsctlNotify(HANDLE VolumeHandle,
|
||||
FSP_FSCTL_NOTIFY_INFO *NotifyInfo, SIZE_T Size);
|
||||
FSP_API NTSTATUS FspFsctlGetVolumeList(PWSTR DevicePath,
|
||||
PWCHAR VolumeListBuf, PSIZE_T PVolumeListSize);
|
||||
FSP_API NTSTATUS FspFsctlPreflight(PWSTR DevicePath);
|
||||
|
@ -1186,6 +1186,72 @@ FSP_API VOID FspFileSystemStopDispatcher(FSP_FILE_SYSTEM *FileSystem);
|
||||
*/
|
||||
FSP_API VOID FspFileSystemSendResponse(FSP_FILE_SYSTEM *FileSystem,
|
||||
FSP_FSCTL_TRANSACT_RSP *Response);
|
||||
/**
|
||||
* Begin notifying Windows that the file system has file changes.
|
||||
*
|
||||
* A file system that wishes to notify Windows about file changes must
|
||||
* first issue an FspFileSystemBegin call, followed by 0 or more
|
||||
* FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call.
|
||||
*
|
||||
* This operation blocks concurrent file rename operations. File rename
|
||||
* operations may interfere with file notification, because a file being
|
||||
* notified may also be concurrently renamed. After all file change
|
||||
* notifications have been issued, you must make sure to call
|
||||
* FspFileSystemNotifyEnd to allow file rename operations to proceed.
|
||||
*
|
||||
* @param FileSystem
|
||||
* The file system object.
|
||||
* @return
|
||||
* STATUS_SUCCESS or error code. The error code STATUS_CANT_WAIT means that
|
||||
* a file rename operation is currently in progress and the operation must be
|
||||
* retried at a later time.
|
||||
*/
|
||||
FSP_API NTSTATUS FspFileSystemNotifyBegin(FSP_FILE_SYSTEM *FileSystem, ULONG Timeout);
|
||||
/**
|
||||
* End notifying Windows that the file system has file changes.
|
||||
*
|
||||
* A file system that wishes to notify Windows about file changes must
|
||||
* first issue an FspFileSystemBegin call, followed by 0 or more
|
||||
* FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call.
|
||||
*
|
||||
* This operation allows any blocked file rename operations to proceed.
|
||||
*
|
||||
* @param FileSystem
|
||||
* The file system object.
|
||||
* @return
|
||||
* STATUS_SUCCESS or error code.
|
||||
*/
|
||||
FSP_API NTSTATUS FspFileSystemNotifyEnd(FSP_FILE_SYSTEM *FileSystem);
|
||||
/**
|
||||
* Notify Windows that the file system has file changes.
|
||||
*
|
||||
* A file system that wishes to notify Windows about file changes must
|
||||
* first issue an FspFileSystemBegin call, followed by 0 or more
|
||||
* FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call.
|
||||
*
|
||||
* Note that FspFileSystemNotify requires file names to be normalized. A
|
||||
* normalized file name is one that contains the correct case of all characters
|
||||
* in the file name.
|
||||
*
|
||||
* For case-sensitive file systems all file names are normalized by definition.
|
||||
* For case-insensitive file systems that implement file name normalization,
|
||||
* a normalized file name is the one that the file system specifies in the
|
||||
* response to Create or Open (see also FspFileSystemGetOpenFileInfo). For
|
||||
* case-insensitive file systems that do not implement file name normalization
|
||||
* a normalized file name is the upper case version of the file name used
|
||||
* to open the file.
|
||||
*
|
||||
* @param FileSystem
|
||||
* The file system object.
|
||||
* @param NotifyInfo
|
||||
* Buffer containing information about file changes.
|
||||
* @param Size
|
||||
* Size of buffer.
|
||||
* @return
|
||||
* STATUS_SUCCESS or error code.
|
||||
*/
|
||||
FSP_API NTSTATUS FspFileSystemNotify(FSP_FILE_SYSTEM *FileSystem,
|
||||
FSP_FSCTL_NOTIFY_INFO *NotifyInfo, SIZE_T Size);
|
||||
/**
|
||||
* Get the current operation context.
|
||||
*
|
||||
@ -1645,6 +1711,28 @@ UINT32 FspFileSystemGetEaPackedSize(PFILE_FULL_EA_INFORMATION SingleEa)
|
||||
/* magic computations are courtesy of NTFS */
|
||||
return 5 + SingleEa->EaNameLength + SingleEa->EaValueLength;
|
||||
}
|
||||
/**
|
||||
* Add notify information to a buffer.
|
||||
*
|
||||
* This is a helper for filling a buffer to use with FspFileSystemNotify.
|
||||
*
|
||||
* @param NotifyInfo
|
||||
* The notify information to add.
|
||||
* @param Buffer
|
||||
* Pointer to a buffer that will receive the notify information.
|
||||
* @param Length
|
||||
* Length of buffer.
|
||||
* @param PBytesTransferred [out]
|
||||
* Pointer to a memory location that will receive the actual number of bytes stored. This should
|
||||
* be initialized to 0 prior to the first call to FspFileSystemAddNotifyInfo for a particular
|
||||
* buffer.
|
||||
* @return
|
||||
* TRUE if the notify information was added, FALSE if there was not enough space to add it.
|
||||
* @see
|
||||
* FspFileSystemNotify
|
||||
*/
|
||||
FSP_API BOOLEAN FspFileSystemAddNotifyInfo(FSP_FSCTL_NOTIFY_INFO *NotifyInfo,
|
||||
PVOID Buffer, ULONG Length, PULONG PBytesTransferred);
|
||||
|
||||
/*
|
||||
* Directory buffering
|
||||
|
52
src/dll/fs.c
52
src/dll/fs.c
@ -432,6 +432,58 @@ FSP_API FSP_FILE_SYSTEM_OPERATION_CONTEXT *FspFileSystemGetOperationContext(VOID
|
||||
return (FSP_FILE_SYSTEM_OPERATION_CONTEXT *)TlsGetValue(FspFileSystemTlsKey);
|
||||
}
|
||||
|
||||
FSP_API NTSTATUS FspFileSystemNotifyBegin(FSP_FILE_SYSTEM *FileSystem, ULONG Timeout)
|
||||
{
|
||||
static const ULONG Delays[] =
|
||||
{
|
||||
10/*ms*/,
|
||||
10/*ms*/,
|
||||
50/*ms*/,
|
||||
50/*ms*/,
|
||||
100/*ms*/,
|
||||
100/*ms*/,
|
||||
300/*ms*/,
|
||||
};
|
||||
ULONG Total = 0, Delay;
|
||||
NTSTATUS Result;
|
||||
|
||||
for (ULONG i = 0, n = sizeof(Delays) / sizeof(Delays[0]);; i++)
|
||||
{
|
||||
Result = FspFsctlNotify(FileSystem->VolumeHandle, 0, 0);
|
||||
if (STATUS_CANT_WAIT != Result)
|
||||
return Result;
|
||||
|
||||
Delay = n > i ? Delays[i] : Delays[n - 1];
|
||||
if (INFINITE == Timeout)
|
||||
Sleep(Delay);
|
||||
else
|
||||
{
|
||||
if (Total >= Timeout)
|
||||
break;
|
||||
if (Total + Delay > Timeout)
|
||||
Delay = Timeout - Total;
|
||||
Total += Delay;
|
||||
Sleep(Delay);
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
FSP_API NTSTATUS FspFileSystemNotifyEnd(FSP_FILE_SYSTEM *FileSystem)
|
||||
{
|
||||
FSP_FSCTL_NOTIFY_INFO NotifyInfo;
|
||||
|
||||
memset(&NotifyInfo, 0, sizeof NotifyInfo);
|
||||
return FspFsctlNotify(FileSystem->VolumeHandle, &NotifyInfo, sizeof NotifyInfo.Size);
|
||||
}
|
||||
|
||||
FSP_API NTSTATUS FspFileSystemNotify(FSP_FILE_SYSTEM *FileSystem,
|
||||
FSP_FSCTL_NOTIFY_INFO *NotifyInfo, SIZE_T Size)
|
||||
{
|
||||
return FspFsctlNotify(FileSystem->VolumeHandle, NotifyInfo, Size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Out-of-Line
|
||||
*/
|
||||
|
@ -161,6 +161,25 @@ FSP_API NTSTATUS FspFsctlStop(HANDLE VolumeHandle)
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
FSP_API NTSTATUS FspFsctlNotify(HANDLE VolumeHandle,
|
||||
FSP_FSCTL_NOTIFY_INFO *NotifyInfo, SIZE_T Size)
|
||||
{
|
||||
NTSTATUS Result = STATUS_SUCCESS;
|
||||
DWORD Bytes = 0;
|
||||
|
||||
if (!DeviceIoControl(VolumeHandle,
|
||||
FSP_FSCTL_NOTIFY,
|
||||
NotifyInfo, (DWORD)Size, 0, 0,
|
||||
&Bytes, 0))
|
||||
{
|
||||
Result = FspNtStatusFromWin32(GetLastError());
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
return Result;
|
||||
}
|
||||
|
||||
FSP_API NTSTATUS FspFsctlGetVolumeList(PWSTR DevicePath,
|
||||
PWCHAR VolumeListBuf, PSIZE_T PVolumeListSize)
|
||||
{
|
||||
|
@ -1866,3 +1866,9 @@ FSP_API BOOLEAN FspFileSystemAddEa(PFILE_FULL_EA_INFORMATION SingleEa,
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
FSP_API BOOLEAN FspFileSystemAddNotifyInfo(FSP_FSCTL_NOTIFY_INFO *NotifyInfo,
|
||||
PVOID Buffer, ULONG Length, PULONG PBytesTransferred)
|
||||
{
|
||||
return FspFileSystemAddXxxInfo(NotifyInfo, Buffer, Length, PBytesTransferred);
|
||||
}
|
||||
|
@ -184,6 +184,28 @@ FSP_FUSE_API struct fuse_chan *fsp_fuse_mount(struct fsp_fuse_env *env,
|
||||
MountPointBuf[1] = L'\0';
|
||||
Size = 2 * sizeof(WCHAR);
|
||||
}
|
||||
else if (
|
||||
(
|
||||
'\\' == mountpoint[0] &&
|
||||
'\\' == mountpoint[1] &&
|
||||
('?' == mountpoint[2] || '.' == mountpoint[2]) &&
|
||||
'\\' == mountpoint[3]
|
||||
) &&
|
||||
(
|
||||
('A' <= mountpoint[4] && mountpoint[4] <= 'Z') ||
|
||||
('a' <= mountpoint[4] && mountpoint[4] <= 'z')
|
||||
) &&
|
||||
':' == mountpoint[5] && '\0' == mountpoint[6])
|
||||
{
|
||||
MountPointBuf[0] = '\\';
|
||||
MountPointBuf[1] = '\\';
|
||||
MountPointBuf[2] = mountpoint[2];
|
||||
MountPointBuf[3] = '\\';
|
||||
MountPointBuf[4] = mountpoint[4];
|
||||
MountPointBuf[5] = ':';
|
||||
MountPointBuf[6] = '\0';
|
||||
Size = 7 * sizeof(WCHAR);
|
||||
}
|
||||
else if (
|
||||
(
|
||||
('A' <= mountpoint[0] && mountpoint[0] <= 'Z') ||
|
||||
@ -610,6 +632,115 @@ FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_exited)(struct fsp_fuse_env *env,
|
||||
return f->exited;
|
||||
}
|
||||
|
||||
FSP_FUSE_API int fsp_fuse_notify(struct fsp_fuse_env *env,
|
||||
struct fuse *f, const char *path, uint32_t action)
|
||||
{
|
||||
PWSTR Path = 0;
|
||||
int PathLength;
|
||||
union
|
||||
{
|
||||
FSP_FSCTL_NOTIFY_INFO V;
|
||||
UINT8 B[sizeof(FSP_FSCTL_NOTIFY_INFO) + FSP_FSCTL_TRANSACT_PATH_SIZEMAX];
|
||||
} NotifyInfo;
|
||||
NTSTATUS Result;
|
||||
int result;
|
||||
|
||||
Result = FspPosixMapPosixToWindowsPath(path, &Path);
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
result = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
PathLength = lstrlenW(Path);
|
||||
if (FSP_FSCTL_TRANSACT_PATH_SIZEMAX / sizeof(WCHAR) <= PathLength)
|
||||
{
|
||||
result = -ENAMETOOLONG;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + PathLength * sizeof(WCHAR));
|
||||
NotifyInfo.V.Filter = 0;
|
||||
NotifyInfo.V.Action = 0;
|
||||
memcpy(NotifyInfo.V.FileNameBuf, Path, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO));
|
||||
|
||||
if (!f->VolumeParams.CaseSensitiveSearch)
|
||||
{
|
||||
/*
|
||||
* Case-insensitive FUSE file systems do not normalize open file names, which means
|
||||
* that the FSD automatically normalizes file names internally by uppercasing them.
|
||||
*
|
||||
* The FspFileSystemNotify API requires normalized names, so upper case the file name
|
||||
* here in the case of case-insensitive file systems.
|
||||
*/
|
||||
CharUpperBuffW(NotifyInfo.V.FileNameBuf, PathLength);
|
||||
}
|
||||
|
||||
if (action & FSP_FUSE_NOTIFY_MKDIR)
|
||||
{
|
||||
NotifyInfo.V.Filter = FILE_NOTIFY_CHANGE_DIR_NAME;
|
||||
NotifyInfo.V.Action = FILE_ACTION_ADDED;
|
||||
}
|
||||
else if (action & FSP_FUSE_NOTIFY_RMDIR)
|
||||
{
|
||||
NotifyInfo.V.Filter = FILE_NOTIFY_CHANGE_DIR_NAME;
|
||||
NotifyInfo.V.Action = FILE_ACTION_REMOVED;
|
||||
}
|
||||
else if (action & FSP_FUSE_NOTIFY_CREATE)
|
||||
{
|
||||
NotifyInfo.V.Filter = FILE_NOTIFY_CHANGE_FILE_NAME;
|
||||
NotifyInfo.V.Action = FILE_ACTION_ADDED;
|
||||
}
|
||||
else if (action & FSP_FUSE_NOTIFY_UNLINK)
|
||||
{
|
||||
NotifyInfo.V.Filter = FILE_NOTIFY_CHANGE_FILE_NAME;
|
||||
NotifyInfo.V.Action = FILE_ACTION_REMOVED;
|
||||
}
|
||||
|
||||
if (action & (FSP_FUSE_NOTIFY_CHMOD | FSP_FUSE_NOTIFY_CHOWN))
|
||||
{
|
||||
NotifyInfo.V.Filter |= FILE_NOTIFY_CHANGE_SECURITY;
|
||||
if (0 == NotifyInfo.V.Action)
|
||||
NotifyInfo.V.Action = FILE_ACTION_MODIFIED;
|
||||
}
|
||||
|
||||
if (action & FSP_FUSE_NOTIFY_UTIME)
|
||||
{
|
||||
NotifyInfo.V.Filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_LAST_WRITE;
|
||||
if (0 == NotifyInfo.V.Action)
|
||||
NotifyInfo.V.Action = FILE_ACTION_MODIFIED;
|
||||
}
|
||||
|
||||
if (action & FSP_FUSE_NOTIFY_CHFLAGS)
|
||||
{
|
||||
NotifyInfo.V.Filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
|
||||
if (0 == NotifyInfo.V.Action)
|
||||
NotifyInfo.V.Action = FILE_ACTION_MODIFIED;
|
||||
}
|
||||
|
||||
if (action & FSP_FUSE_NOTIFY_TRUNCATE)
|
||||
{
|
||||
NotifyInfo.V.Filter |= FILE_NOTIFY_CHANGE_SIZE;
|
||||
if (0 == NotifyInfo.V.Action)
|
||||
NotifyInfo.V.Action = FILE_ACTION_MODIFIED;
|
||||
}
|
||||
|
||||
Result = FspFileSystemNotify(f->FileSystem, &NotifyInfo.V, NotifyInfo.V.Size);
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
result = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
result = 0;
|
||||
|
||||
exit:
|
||||
if (0 != Path)
|
||||
FspPosixDeletePath(Path);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
FSP_FUSE_API struct fuse_context *fsp_fuse_get_context(struct fsp_fuse_env *env)
|
||||
{
|
||||
struct fuse_context *context;
|
||||
|
@ -30,60 +30,6 @@
|
||||
#define fsp_fuse_opt_match_exact ((const char *)1) /* exact option match */
|
||||
#define fsp_fuse_opt_match_next ((const char *)2) /* option match, value is next arg */
|
||||
|
||||
static long long strtoint(const char *p, int base, int is_signed)
|
||||
{
|
||||
long long v;
|
||||
int maxdig, maxalp, sign = +1;
|
||||
|
||||
if (is_signed)
|
||||
{
|
||||
if ('+' == *p)
|
||||
p++;
|
||||
else if ('-' == *p)
|
||||
p++, sign = -1;
|
||||
}
|
||||
|
||||
if (0 == base)
|
||||
{
|
||||
if ('0' == *p)
|
||||
{
|
||||
p++;
|
||||
if ('x' == *p || 'X' == *p)
|
||||
{
|
||||
p++;
|
||||
base = 16;
|
||||
}
|
||||
else
|
||||
base = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
base = 10;
|
||||
}
|
||||
}
|
||||
|
||||
maxdig = 10 < base ? '9' : (base - 1) + '0';
|
||||
maxalp = 10 < base ? (base - 1 - 10) + 'a' : 0;
|
||||
|
||||
for (v = 0; *p; p++)
|
||||
{
|
||||
int c = *p;
|
||||
|
||||
if ('0' <= c && c <= maxdig)
|
||||
v = base * v + (c - '0');
|
||||
else
|
||||
{
|
||||
c |= 0x20;
|
||||
if ('a' <= c && c <= maxalp)
|
||||
v = base * v + (c - 'a') + 10;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sign * v;
|
||||
}
|
||||
|
||||
static void fsp_fuse_opt_match_templ(
|
||||
const char *templ, const char **pspec,
|
||||
const char **parg)
|
||||
@ -281,19 +227,19 @@ static int fsp_fuse_opt_process_arg(struct fsp_fuse_env *env,
|
||||
z++;
|
||||
break;
|
||||
case 'd':
|
||||
llv = strtoint(argl, 10, 1);
|
||||
llv = strtollint(argl, 0, 10, 1);
|
||||
goto ivar;
|
||||
case 'i':
|
||||
llv = strtoint(argl, 0, 1);
|
||||
llv = strtollint(argl, 0, 0, 1);
|
||||
goto ivar;
|
||||
case 'o':
|
||||
llv = strtoint(argl, 8, 0);
|
||||
llv = strtollint(argl, 0, 8, 0);
|
||||
goto ivar;
|
||||
case 'u':
|
||||
llv = strtoint(argl, 10, 0);
|
||||
llv = strtollint(argl, 0, 10, 0);
|
||||
goto ivar;
|
||||
case 'x': case 'X':
|
||||
llv = strtoint(argl, 16, 0);
|
||||
llv = strtollint(argl, 0, 16, 0);
|
||||
ivar:
|
||||
if (z)
|
||||
VAR(data, opt, size_t) = (size_t)llv;
|
||||
|
157
src/dll/ldap.c
Normal file
157
src/dll/ldap.c
Normal file
@ -0,0 +1,157 @@
|
||||
/**
|
||||
* @file dll/ldap.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 <dll/library.h>
|
||||
#include <winldap.h>
|
||||
|
||||
ULONG FspLdapConnect(PWSTR HostName, PVOID *PLdap)
|
||||
{
|
||||
LDAP *Ldap = 0;
|
||||
ULONG LdapResult;
|
||||
|
||||
*PLdap = 0;
|
||||
|
||||
Ldap = ldap_initW(HostName, LDAP_PORT);
|
||||
if (0 == Ldap)
|
||||
{
|
||||
LdapResult = LdapGetLastError();
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* enable signing and encryption */
|
||||
ldap_set_optionW(Ldap, LDAP_OPT_SIGN, LDAP_OPT_ON);
|
||||
ldap_set_optionW(Ldap, LDAP_OPT_ENCRYPT, LDAP_OPT_ON);
|
||||
|
||||
LdapResult = ldap_bind_sW(Ldap, 0, 0, LDAP_AUTH_NEGOTIATE);
|
||||
if (LDAP_SUCCESS != LdapResult)
|
||||
goto exit;
|
||||
|
||||
*PLdap = Ldap;
|
||||
LdapResult = LDAP_SUCCESS;
|
||||
|
||||
exit:
|
||||
if (LDAP_SUCCESS != LdapResult)
|
||||
{
|
||||
if (0 != Ldap)
|
||||
ldap_unbind(Ldap);
|
||||
}
|
||||
|
||||
return LdapResult;
|
||||
}
|
||||
|
||||
VOID FspLdapClose(PVOID Ldap0)
|
||||
{
|
||||
LDAP *Ldap = Ldap0;
|
||||
|
||||
ldap_unbind(Ldap);
|
||||
}
|
||||
|
||||
ULONG FspLdapGetValue(PVOID Ldap0, PWSTR Base, ULONG Scope, PWSTR Filter, PWSTR Attribute,
|
||||
PWSTR *PValue)
|
||||
{
|
||||
LDAP *Ldap = Ldap0;
|
||||
PWSTR Attributes[2];
|
||||
LDAPMessage *Message = 0, *Entry;
|
||||
PWSTR *Values = 0;
|
||||
int Size;
|
||||
PWSTR Value;
|
||||
ULONG LdapResult;
|
||||
|
||||
*PValue = 0;
|
||||
|
||||
Attributes[0] = Attribute;
|
||||
Attributes[1] = 0;
|
||||
LdapResult = ldap_search_sW(Ldap, Base, Scope, Filter, Attributes, 0, &Message);
|
||||
if (LDAP_SUCCESS != LdapResult)
|
||||
goto exit;
|
||||
|
||||
Entry = ldap_first_entry(Ldap, Message);
|
||||
if (0 == Entry)
|
||||
{
|
||||
LdapResult = LDAP_OTHER;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
Values = ldap_get_valuesW(Ldap, Entry, Attributes[0]);
|
||||
if (0 == Values || 0 == ldap_count_valuesW(Values))
|
||||
{
|
||||
LdapResult = LDAP_OTHER;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
Size = (lstrlenW(Values[0]) + 1) * sizeof(WCHAR);
|
||||
Value = MemAlloc(Size);
|
||||
if (0 == Value)
|
||||
{
|
||||
LdapResult = LDAP_NO_MEMORY;
|
||||
goto exit;
|
||||
}
|
||||
memcpy(Value, Values[0], Size);
|
||||
|
||||
*PValue = Value;
|
||||
LdapResult = LDAP_SUCCESS;
|
||||
|
||||
exit:
|
||||
if (0 != Values)
|
||||
ldap_value_freeW(Values);
|
||||
if (0 != Message)
|
||||
ldap_msgfree(Message);
|
||||
|
||||
return LdapResult;
|
||||
}
|
||||
|
||||
ULONG FspLdapGetDefaultNamingContext(PVOID Ldap, PWSTR *PValue)
|
||||
{
|
||||
return FspLdapGetValue(Ldap, 0, LDAP_SCOPE_BASE, L"(objectClass=*)", L"defaultNamingContext",
|
||||
PValue);
|
||||
}
|
||||
|
||||
ULONG FspLdapGetTrustPosixOffset(PVOID Ldap, PWSTR Context, PWSTR Domain, PWSTR *PValue)
|
||||
{
|
||||
WCHAR Base[1024];
|
||||
WCHAR Filter[512];
|
||||
BOOLEAN IsFlatName;
|
||||
|
||||
*PValue = 0;
|
||||
|
||||
if (sizeof Base / sizeof Base[0] - 64 < lstrlenW(Context) ||
|
||||
sizeof Filter / sizeof Filter[0] - 64 < lstrlenW(Domain))
|
||||
return LDAP_OTHER;
|
||||
|
||||
IsFlatName = TRUE;
|
||||
for (PWSTR P = Domain; *P; P++)
|
||||
if (L'.' == *P)
|
||||
{
|
||||
IsFlatName = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
wsprintfW(Base,
|
||||
L"CN=System,%s",
|
||||
Context);
|
||||
wsprintfW(Filter,
|
||||
IsFlatName ?
|
||||
L"(&(objectClass=trustedDomain)(flatName=%s))" :
|
||||
L"(&(objectClass=trustedDomain)(name=%s))",
|
||||
Domain);
|
||||
|
||||
return FspLdapGetValue(Ldap, Base, LDAP_SCOPE_ONELEVEL, Filter, L"trustPosixOffset", PValue);
|
||||
}
|
@ -64,6 +64,13 @@ NTSTATUS FspEventLogUnregister(VOID);
|
||||
PSID FspWksidNew(WELL_KNOWN_SID_TYPE WellKnownSidType, PNTSTATUS PResult);
|
||||
PSID FspWksidGet(WELL_KNOWN_SID_TYPE WellKnownSidType);
|
||||
|
||||
ULONG FspLdapConnect(PWSTR HostName, PVOID *PLdap);
|
||||
VOID FspLdapClose(PVOID Ldap);
|
||||
ULONG FspLdapGetValue(PVOID Ldap, PWSTR Base, ULONG Scope, PWSTR Filter, PWSTR Attribute,
|
||||
PWSTR *PValue);
|
||||
ULONG FspLdapGetDefaultNamingContext(PVOID Ldap, PWSTR *PValue);
|
||||
ULONG FspLdapGetTrustPosixOffset(PVOID Ldap, PWSTR Context, PWSTR Domain, PWSTR *PValue);
|
||||
|
||||
PWSTR FspDiagIdent(VOID);
|
||||
|
||||
#define FspFileSystemDirectoryBufferEntryInvalid ((ULONG)-1)
|
||||
|
@ -46,6 +46,7 @@ case ERROR_CANT_ACCESS_FILE: return STATUS_IO_REPARSE_TAG_NOT_H
|
||||
case ERROR_CANT_DISABLE_MANDATORY: return STATUS_CANT_DISABLE_MANDATORY;
|
||||
case ERROR_CANT_OPEN_ANONYMOUS: return STATUS_CANT_OPEN_ANONYMOUS;
|
||||
case ERROR_CANT_RESOLVE_FILENAME: return STATUS_REPARSE_POINT_NOT_RESOLVED;
|
||||
case ERROR_CANT_WAIT: return STATUS_CANT_WAIT;
|
||||
case ERROR_CHILD_MUST_BE_VOLATILE: return STATUS_CHILD_MUST_BE_VOLATILE;
|
||||
case ERROR_CLEANER_CARTRIDGE_INSTALLED: return STATUS_CLEANER_CARTRIDGE_INSTALLED;
|
||||
case ERROR_CLUSTER_INVALID_NETWORK: return STATUS_CLUSTER_INVALID_NETWORK;
|
||||
|
@ -582,6 +582,75 @@ namespace Fsp
|
||||
Response.IoStatus.Status = (UInt32)Status;
|
||||
Api.FspFileSystemSendResponse(_FileSystemPtr, ref Response);
|
||||
}
|
||||
/// <summary>
|
||||
/// Begin notifying Windows that the file system has file changes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// A file system that wishes to notify Windows about file changes must
|
||||
/// first issue an FspFileSystemBegin call, followed by 0 or more
|
||||
/// FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call.
|
||||
/// </para><para>
|
||||
/// This operation blocks concurrent file rename operations. File rename
|
||||
/// operations may interfere with file notification, because a file being
|
||||
/// notified may also be concurrently renamed. After all file change
|
||||
/// notifications have been issued, you must make sure to call
|
||||
/// FspFileSystemNotifyEnd to allow file rename operations to proceed.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// STATUS_SUCCESS or error code. The error code STATUS_CANT_WAIT means that
|
||||
/// a file rename operation is currently in progress and the operation must be
|
||||
/// retried at a later time.
|
||||
/// </returns>
|
||||
public Int32 NotifyBegin(UInt32 Timeout)
|
||||
{
|
||||
return Api.FspFileSystemNotifyBegin(_FileSystemPtr, Timeout);
|
||||
}
|
||||
/// <summary>
|
||||
/// End notifying Windows that the file system has file changes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// A file system that wishes to notify Windows about file changes must
|
||||
/// first issue an FspFileSystemBegin call, followed by 0 or more
|
||||
/// FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call.
|
||||
/// </para><para>
|
||||
/// This operation allows any blocked file rename operations to proceed.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <returns>STATUS_SUCCESS or error code.</returns>
|
||||
public Int32 NotifyEnd()
|
||||
{
|
||||
return Api.FspFileSystemNotifyEnd(_FileSystemPtr);
|
||||
}
|
||||
/// <summary>
|
||||
/// Notify Windows that the file system has file changes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// A file system that wishes to notify Windows about file changes must
|
||||
/// first issue an FspFileSystemBegin call, followed by 0 or more
|
||||
/// FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call.
|
||||
/// </para><para>
|
||||
/// Note that FspFileSystemNotify requires file names to be normalized. A
|
||||
/// normalized file name is one that contains the correct case of all characters
|
||||
/// in the file name.
|
||||
/// </para><para>
|
||||
/// For case-sensitive file systems all file names are normalized by definition.
|
||||
/// For case-insensitive file systems that implement file name normalization,
|
||||
/// a normalized file name is the one that the file system specifies in the
|
||||
/// response to Create or Open (see also FspFileSystemGetOpenFileInfo). For
|
||||
/// case-insensitive file systems that do not implement file name normalization
|
||||
/// a normalized file name is the upper case version of the file name used
|
||||
/// to open the file.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <returns>STATUS_SUCCESS or error code.</returns>
|
||||
public Int32 Notify(NotifyInfo[] NotifyInfoArray)
|
||||
{
|
||||
return Api.FspFileSystemNotify(_FileSystemPtr, NotifyInfoArray);
|
||||
}
|
||||
|
||||
/* FSP_FILE_SYSTEM_INTERFACE */
|
||||
private static Byte[] ByteBufferNotNull = new Byte[0];
|
||||
|
@ -310,6 +310,44 @@ namespace Fsp.Interop
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct NotifyInfoInternal
|
||||
{
|
||||
internal const int FileNameBufSize = 1024 * 2/*FSP_FSCTL_TRANSACT_PATH_SIZEMAX*/;
|
||||
internal static int FileNameBufOffset =
|
||||
(int)Marshal.OffsetOf(typeof(DirInfo), "FileNameBuf");
|
||||
|
||||
internal UInt16 Size;
|
||||
internal UInt32 Filter;
|
||||
internal UInt32 Action;
|
||||
//internal unsafe fixed UInt16 FileNameBuf[];
|
||||
internal unsafe fixed UInt16 FileNameBuf[FileNameBufSize];
|
||||
|
||||
internal unsafe void SetFileNameBuf(String Value)
|
||||
{
|
||||
fixed (UInt16 *P = FileNameBuf)
|
||||
{
|
||||
int Size = null != Value ? Value.Length : 0;
|
||||
if (Size > FileNameBufSize)
|
||||
Size = FileNameBufSize;
|
||||
for (int I = 0; Size > I; I++)
|
||||
P[I] = Value[I];
|
||||
this.Size = (UInt16)(FileNameBufOffset + Size * 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains file change notification information.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct NotifyInfo
|
||||
{
|
||||
public String FileName;
|
||||
public UInt32 Action;
|
||||
public UInt32 Filter;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct FullEaInformation
|
||||
{
|
||||
@ -743,6 +781,18 @@ namespace Fsp.Interop
|
||||
IntPtr FileSystem,
|
||||
ref FspFsctlTransactRsp Response);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate Int32 FspFileSystemNotifyBegin(
|
||||
IntPtr FileSystem,
|
||||
UInt32 Timeout);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate Int32 FspFileSystemNotifyEnd(
|
||||
IntPtr FileSystem);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate Int32 FspFileSystemNotify(
|
||||
IntPtr FileSystem,
|
||||
IntPtr NotifyInfo,
|
||||
UIntPtr Size);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal unsafe delegate FspFileSystemOperationContext *FspFileSystemGetOperationContext();
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
internal delegate IntPtr FspFileSystemMountPointF(
|
||||
@ -805,6 +855,13 @@ namespace Fsp.Interop
|
||||
out UInt32 PBytesTransferred);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal delegate Boolean FspFileSystemAddNotifyInfo(
|
||||
IntPtr NotifyInfo,
|
||||
IntPtr Buffer,
|
||||
UInt32 Length,
|
||||
out UInt32 PBytesTransferred);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal delegate Boolean FspFileSystemAcquireDirectoryBuffer(
|
||||
ref IntPtr PDirBuffer,
|
||||
[MarshalAs(UnmanagedType.U1)] Boolean Reset,
|
||||
@ -930,6 +987,9 @@ namespace Fsp.Interop
|
||||
internal static Proto.FspFileSystemStartDispatcher FspFileSystemStartDispatcher;
|
||||
internal static Proto.FspFileSystemStopDispatcher FspFileSystemStopDispatcher;
|
||||
internal static Proto.FspFileSystemSendResponse FspFileSystemSendResponse;
|
||||
internal static Proto.FspFileSystemNotifyBegin FspFileSystemNotifyBegin;
|
||||
internal static Proto.FspFileSystemNotifyEnd FspFileSystemNotifyEnd;
|
||||
internal static Proto.FspFileSystemNotify _FspFileSystemNotify;
|
||||
internal static Proto.FspFileSystemGetOperationContext FspFileSystemGetOperationContext;
|
||||
internal static Proto.FspFileSystemMountPointF FspFileSystemMountPoint;
|
||||
internal static Proto.FspFileSystemSetOperationGuardStrategyF FspFileSystemSetOperationGuardStrategy;
|
||||
@ -941,6 +1001,7 @@ namespace Fsp.Interop
|
||||
internal static Proto.FspFileSystemCanReplaceReparsePoint _FspFileSystemCanReplaceReparsePoint;
|
||||
internal static Proto.FspFileSystemAddStreamInfo _FspFileSystemAddStreamInfo;
|
||||
internal static Proto.FspFileSystemAddEa _FspFileSystemAddEa;
|
||||
internal static Proto.FspFileSystemAddNotifyInfo _FspFileSystemAddNotifyInfo;
|
||||
internal static Proto.FspFileSystemAcquireDirectoryBuffer FspFileSystemAcquireDirectoryBuffer;
|
||||
internal static Proto.FspFileSystemFillDirectoryBuffer FspFileSystemFillDirectoryBuffer;
|
||||
internal static Proto.FspFileSystemReleaseDirectoryBuffer FspFileSystemReleaseDirectoryBuffer;
|
||||
@ -1013,6 +1074,15 @@ namespace Fsp.Interop
|
||||
{
|
||||
return _FspFileSystemAddStreamInfo(IntPtr.Zero, Buffer, Length, out PBytesTransferred);
|
||||
}
|
||||
internal static unsafe Boolean FspFileSystemAddNotifyInfo(
|
||||
ref NotifyInfoInternal NotifyInfo,
|
||||
IntPtr Buffer,
|
||||
UInt32 Length,
|
||||
out UInt32 PBytesTransferred)
|
||||
{
|
||||
fixed (NotifyInfoInternal *P = &NotifyInfo)
|
||||
return _FspFileSystemAddNotifyInfo((IntPtr)P, Buffer, Length, out PBytesTransferred);
|
||||
}
|
||||
|
||||
internal delegate Int32 EnumerateEa(
|
||||
Object FileNode,
|
||||
@ -1070,6 +1140,34 @@ namespace Fsp.Interop
|
||||
return _FspFileSystemAddEa(IntPtr.Zero, Buffer, Length, out PBytesTransferred);
|
||||
}
|
||||
|
||||
internal static unsafe Int32 FspFileSystemNotify(
|
||||
IntPtr FileSystem,
|
||||
NotifyInfo[] NotifyInfoArray)
|
||||
{
|
||||
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;
|
||||
}
|
||||
Byte[] Buffer = new Byte[Length];
|
||||
UInt32 BytesTransferred = default(UInt32);
|
||||
fixed (Byte *P = Buffer)
|
||||
{
|
||||
for (int I = 0; NotifyInfoArray.Length > I; I++)
|
||||
{
|
||||
NotifyInfoInternal Internal = default(NotifyInfoInternal);
|
||||
Internal.Action = NotifyInfoArray[I].Action;
|
||||
Internal.Filter = NotifyInfoArray[I].Filter;
|
||||
Internal.SetFileNameBuf(NotifyInfoArray[I].FileName);
|
||||
FspFileSystemAddNotifyInfo(
|
||||
ref Internal, (IntPtr)P, (UInt32)Length, out BytesTransferred);
|
||||
}
|
||||
return _FspFileSystemNotify(FileSystem, (IntPtr)P, (UIntPtr)BytesTransferred);
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe static Object GetUserContext(
|
||||
IntPtr NativePtr)
|
||||
{
|
||||
@ -1330,6 +1428,9 @@ namespace Fsp.Interop
|
||||
FspFileSystemStartDispatcher = GetEntryPoint<Proto.FspFileSystemStartDispatcher>(Module);
|
||||
FspFileSystemStopDispatcher = GetEntryPoint<Proto.FspFileSystemStopDispatcher>(Module);
|
||||
FspFileSystemSendResponse = GetEntryPoint<Proto.FspFileSystemSendResponse>(Module);
|
||||
FspFileSystemNotifyBegin = GetEntryPoint<Proto.FspFileSystemNotifyBegin>(Module);
|
||||
FspFileSystemNotifyEnd = GetEntryPoint<Proto.FspFileSystemNotifyEnd>(Module);
|
||||
_FspFileSystemNotify = GetEntryPoint<Proto.FspFileSystemNotify>(Module);
|
||||
FspFileSystemGetOperationContext = GetEntryPoint<Proto.FspFileSystemGetOperationContext>(Module);
|
||||
FspFileSystemMountPoint = GetEntryPoint<Proto.FspFileSystemMountPointF>(Module);
|
||||
FspFileSystemSetOperationGuardStrategy = GetEntryPoint<Proto.FspFileSystemSetOperationGuardStrategyF>(Module);
|
||||
@ -1341,6 +1442,7 @@ namespace Fsp.Interop
|
||||
_FspFileSystemCanReplaceReparsePoint = GetEntryPoint<Proto.FspFileSystemCanReplaceReparsePoint>(Module);
|
||||
_FspFileSystemAddStreamInfo = GetEntryPoint<Proto.FspFileSystemAddStreamInfo>(Module);
|
||||
_FspFileSystemAddEa = GetEntryPoint<Proto.FspFileSystemAddEa>(Module);
|
||||
_FspFileSystemAddNotifyInfo = GetEntryPoint<Proto.FspFileSystemAddNotifyInfo>(Module);
|
||||
FspFileSystemAcquireDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemAcquireDirectoryBuffer>(Module);
|
||||
FspFileSystemFillDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemFillDirectoryBuffer>(Module);
|
||||
FspFileSystemReleaseDirectoryBuffer = GetEntryPoint<Proto.FspFileSystemReleaseDirectoryBuffer>(Module);
|
||||
|
@ -55,36 +55,6 @@ static void printlog(HANDLE h, const char *format, ...)
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static unsigned wcstoint(const wchar_t *p, const wchar_t **endp, int base)
|
||||
{
|
||||
unsigned v;
|
||||
int maxdig, maxalp;
|
||||
|
||||
maxdig = 10 < base ? '9' : (base - 1) + '0';
|
||||
maxalp = 10 < base ? (base - 1 - 10) + 'a' : 0;
|
||||
|
||||
for (v = 0; *p; p++)
|
||||
{
|
||||
int c = *p;
|
||||
|
||||
if ('0' <= c && c <= maxdig)
|
||||
v = base * v + (c - '0');
|
||||
else
|
||||
{
|
||||
c |= 0x20;
|
||||
if ('a' <= c && c <= maxalp)
|
||||
v = base * v + (c - 'a') + 10;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != endp)
|
||||
*endp = (wchar_t *)p;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
fatal(ERROR_INVALID_PARAMETER,
|
||||
@ -380,7 +350,7 @@ static NTSTATUS id_uid(PWSTR UidStr)
|
||||
UINT32 Uid;
|
||||
NTSTATUS Result;
|
||||
|
||||
Uid = wcstoint(UidStr, &UidStr, 10);
|
||||
Uid = wcstouint(UidStr, &UidStr, 10, 0);
|
||||
if (L'\0' != *UidStr)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
@ -525,13 +495,13 @@ static NTSTATUS perm_mode(PWSTR PermStr)
|
||||
UINT32 Uid, Gid, Mode;
|
||||
NTSTATUS Result;
|
||||
|
||||
Uid = wcstoint(PermStr, &PermStr, 10);
|
||||
Uid = wcstouint(PermStr, &PermStr, 10, 0);
|
||||
if (L':' != *PermStr)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
Gid = wcstoint(PermStr + 1, &PermStr, 10);
|
||||
Gid = wcstouint(PermStr + 1, &PermStr, 10, 0);
|
||||
if (L':' != *PermStr)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
Mode = wcstoint(PermStr + 1, &PermStr, 8);
|
||||
Mode = wcstouint(PermStr + 1, &PermStr, 8, 0);
|
||||
if (L'\0' != *PermStr)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
|
@ -26,6 +26,8 @@
|
||||
|
||||
#include <dll/library.h>
|
||||
#include <aclapi.h>
|
||||
#include <dsgetdc.h>
|
||||
#include <lm.h>
|
||||
#define _NTDEF_
|
||||
#include <ntsecapi.h>
|
||||
|
||||
|
@ -111,8 +111,59 @@ static union
|
||||
#define FspUnmappedUid (65534)
|
||||
|
||||
static PISID FspAccountDomainSid, FspPrimaryDomainSid;
|
||||
static struct
|
||||
{
|
||||
PSID DomainSid;
|
||||
PWSTR NetbiosDomainName;
|
||||
PWSTR DnsDomainName;
|
||||
ULONG TrustPosixOffset;
|
||||
} *FspTrustedDomains;
|
||||
ULONG FspTrustedDomainCount;
|
||||
static INIT_ONCE FspPosixInitOnce = INIT_ONCE_STATIC_INIT;
|
||||
#if !defined(_KERNEL_MODE)
|
||||
static ULONG FspPosixInitializeTrustPosixOffsets(VOID)
|
||||
{
|
||||
PVOID Ldap = 0;
|
||||
PWSTR DefaultNamingContext = 0;
|
||||
PWSTR TrustPosixOffsetString = 0;
|
||||
ULONG LdapResult;
|
||||
|
||||
LdapResult = FspLdapConnect(0/* default LDAP server */, &Ldap);
|
||||
if (0 != LdapResult)
|
||||
goto exit;
|
||||
|
||||
LdapResult = FspLdapGetDefaultNamingContext(Ldap, &DefaultNamingContext);
|
||||
if (0 != LdapResult)
|
||||
goto exit;
|
||||
|
||||
/* get the "trustPosixOffset" for each trusted domain */
|
||||
for (ULONG I = 0; FspTrustedDomainCount > I; I++)
|
||||
{
|
||||
MemFree(TrustPosixOffsetString);
|
||||
LdapResult = FspLdapGetTrustPosixOffset(Ldap,
|
||||
DefaultNamingContext, FspTrustedDomains[I].DnsDomainName, &TrustPosixOffsetString);
|
||||
if (0 == LdapResult)
|
||||
FspTrustedDomains[I].TrustPosixOffset = wcstouint(TrustPosixOffsetString, 0, 10, 1);
|
||||
}
|
||||
|
||||
LdapResult = 0;
|
||||
|
||||
exit:
|
||||
MemFree(TrustPosixOffsetString);
|
||||
MemFree(DefaultNamingContext);
|
||||
if (0 != Ldap)
|
||||
FspLdapClose(Ldap);
|
||||
|
||||
/* if the "trustPosixOffset" looks wrong, fix it up using Cygwin magic value 0xfe500000 */
|
||||
for (ULONG I = 0; FspTrustedDomainCount > I; I++)
|
||||
{
|
||||
if (0x100000 > FspTrustedDomains[I].TrustPosixOffset)
|
||||
FspTrustedDomains[I].TrustPosixOffset = 0xfe500000;
|
||||
}
|
||||
|
||||
return LdapResult;
|
||||
}
|
||||
|
||||
static BOOL WINAPI FspPosixInitialize(
|
||||
PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
|
||||
{
|
||||
@ -120,8 +171,10 @@ static BOOL WINAPI FspPosixInitialize(
|
||||
LSA_HANDLE PolicyHandle = 0;
|
||||
PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo = 0;
|
||||
PPOLICY_DNS_DOMAIN_INFO PrimaryDomainInfo = 0;
|
||||
PDS_DOMAIN_TRUSTSW TrustedDomains = 0;
|
||||
ULONG TrustedDomainCount, RealTrustedDomainCount;
|
||||
BYTE Count;
|
||||
ULONG Size;
|
||||
ULONG Size, Temp;
|
||||
NTSTATUS Result;
|
||||
|
||||
Result = LsaOpenPolicy(0, &Obja, POLICY_VIEW_LOCAL_INFORMATION, &PolicyHandle);
|
||||
@ -148,9 +201,104 @@ static BOOL WINAPI FspPosixInitialize(
|
||||
FspPrimaryDomainSid = MemAlloc(Size);
|
||||
if (0 != FspPrimaryDomainSid)
|
||||
memcpy(FspPrimaryDomainSid, PrimaryDomainInfo->Sid, Size);
|
||||
|
||||
if (ERROR_SUCCESS == DsEnumerateDomainTrustsW(
|
||||
0, DS_DOMAIN_DIRECT_INBOUND | DS_DOMAIN_DIRECT_OUTBOUND | DS_DOMAIN_IN_FOREST,
|
||||
&TrustedDomains, &TrustedDomainCount))
|
||||
{
|
||||
Size = 0;
|
||||
RealTrustedDomainCount = 0;
|
||||
for (ULONG I = 0; TrustedDomainCount > I; I++)
|
||||
{
|
||||
if (0 == TrustedDomains[I].DomainSid ||
|
||||
(0 == TrustedDomains[I].NetbiosDomainName &&
|
||||
0 == TrustedDomains[I].DnsDomainName) ||
|
||||
EqualSid(TrustedDomains[I].DomainSid, FspPrimaryDomainSid))
|
||||
continue;
|
||||
if (0 != TrustedDomains[I].DomainSid)
|
||||
{
|
||||
Size = FSP_FSCTL_DEFAULT_ALIGN_UP(Size);
|
||||
Size += GetLengthSid(TrustedDomains[I].DomainSid);
|
||||
}
|
||||
if (0 != TrustedDomains[I].NetbiosDomainName)
|
||||
{
|
||||
Size = FSP_FSCTL_ALIGN_UP(Size, sizeof(WCHAR));
|
||||
Size += (lstrlenW(TrustedDomains[I].NetbiosDomainName) + 1) * sizeof(WCHAR);
|
||||
}
|
||||
if (0 != TrustedDomains[I].DnsDomainName)
|
||||
{
|
||||
Size = FSP_FSCTL_ALIGN_UP(Size, sizeof(WCHAR));
|
||||
Size += (lstrlenW(TrustedDomains[I].DnsDomainName) + 1) * sizeof(WCHAR);
|
||||
}
|
||||
RealTrustedDomainCount++;
|
||||
}
|
||||
Size = FSP_FSCTL_DEFAULT_ALIGN_UP(sizeof FspTrustedDomains[0] * RealTrustedDomainCount) + Size;
|
||||
if (0 < RealTrustedDomainCount)
|
||||
{
|
||||
FspTrustedDomains = MemAlloc(Size);
|
||||
if (0 != FspTrustedDomains)
|
||||
{
|
||||
Size = FSP_FSCTL_DEFAULT_ALIGN_UP(sizeof FspTrustedDomains[0] * RealTrustedDomainCount);
|
||||
for (ULONG I = 0, J = 0; TrustedDomainCount > I; I++)
|
||||
{
|
||||
if (0 == TrustedDomains[I].DomainSid ||
|
||||
(0 == TrustedDomains[I].NetbiosDomainName &&
|
||||
0 == TrustedDomains[I].DnsDomainName) ||
|
||||
EqualSid(TrustedDomains[I].DomainSid, FspPrimaryDomainSid))
|
||||
continue;
|
||||
FspTrustedDomains[J].DomainSid = 0;
|
||||
FspTrustedDomains[J].NetbiosDomainName = 0;
|
||||
FspTrustedDomains[J].DnsDomainName = 0;
|
||||
FspTrustedDomains[J].TrustPosixOffset = 0;
|
||||
if (0 != TrustedDomains[I].DomainSid)
|
||||
{
|
||||
Size = FSP_FSCTL_DEFAULT_ALIGN_UP(Size);
|
||||
FspTrustedDomains[J].DomainSid =
|
||||
(PVOID)((PUINT8)FspTrustedDomains + Size);
|
||||
Size += (Temp = GetLengthSid(TrustedDomains[I].DomainSid));
|
||||
memcpy(FspTrustedDomains[J].DomainSid,
|
||||
TrustedDomains[I].DomainSid, Temp);
|
||||
}
|
||||
if (0 != TrustedDomains[I].NetbiosDomainName)
|
||||
{
|
||||
Size = FSP_FSCTL_ALIGN_UP(Size, sizeof(WCHAR));
|
||||
FspTrustedDomains[J].NetbiosDomainName =
|
||||
(PVOID)((PUINT8)FspTrustedDomains + Size);
|
||||
Size += (Temp = (lstrlenW(TrustedDomains[I].NetbiosDomainName) + 1) * sizeof(WCHAR));
|
||||
memcpy(FspTrustedDomains[J].NetbiosDomainName,
|
||||
TrustedDomains[I].NetbiosDomainName, Temp);
|
||||
}
|
||||
if (0 != TrustedDomains[I].DnsDomainName)
|
||||
{
|
||||
Size = FSP_FSCTL_ALIGN_UP(Size, sizeof(WCHAR));
|
||||
FspTrustedDomains[J].DnsDomainName =
|
||||
(PVOID)((PUINT8)FspTrustedDomains + Size);
|
||||
Size += (Temp = (lstrlenW(TrustedDomains[I].DnsDomainName) + 1) * sizeof(WCHAR));
|
||||
memcpy(FspTrustedDomains[J].DnsDomainName,
|
||||
TrustedDomains[I].DnsDomainName, Temp);
|
||||
}
|
||||
if (0 == FspTrustedDomains[J].NetbiosDomainName)
|
||||
FspTrustedDomains[J].NetbiosDomainName =
|
||||
FspTrustedDomains[J].DnsDomainName;
|
||||
else
|
||||
if (0 == FspTrustedDomains[J].DnsDomainName)
|
||||
FspTrustedDomains[J].DnsDomainName =
|
||||
FspTrustedDomains[J].NetbiosDomainName;
|
||||
J++;
|
||||
}
|
||||
FspTrustedDomainCount = RealTrustedDomainCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 < FspTrustedDomainCount)
|
||||
FspPosixInitializeTrustPosixOffsets();
|
||||
|
||||
exit:
|
||||
if (0 != TrustedDomains)
|
||||
NetApiBufferFree(TrustedDomains);
|
||||
|
||||
if (0 != PrimaryDomainInfo)
|
||||
LsaFreeMemory(PrimaryDomainInfo);
|
||||
|
||||
@ -172,6 +320,7 @@ VOID FspPosixFinalize(BOOLEAN Dynamic)
|
||||
|
||||
if (Dynamic)
|
||||
{
|
||||
MemFree(FspTrustedDomains);
|
||||
MemFree(FspAccountDomainSid);
|
||||
MemFree(FspPrimaryDomainSid);
|
||||
}
|
||||
@ -318,7 +467,8 @@ FSP_API NTSTATUS FspPosixMapUidToSid(UINT32 Uid, PSID *PSid)
|
||||
}
|
||||
else if (0x100000 <= Uid && Uid < 0xff000000)
|
||||
{
|
||||
if (0 != FspPrimaryDomainSid &&
|
||||
if ((Uid < 0x300000 || 0 == FspTrustedDomainCount) &&
|
||||
0 != FspPrimaryDomainSid &&
|
||||
5 == FspPrimaryDomainSid->IdentifierAuthority.Value[5] &&
|
||||
4 == FspPrimaryDomainSid->SubAuthorityCount)
|
||||
{
|
||||
@ -329,11 +479,30 @@ FSP_API NTSTATUS FspPosixMapUidToSid(UINT32 Uid, PSID *PSid)
|
||||
FspPrimaryDomainSid->SubAuthority[3],
|
||||
Uid - 0x100000);
|
||||
}
|
||||
else
|
||||
{
|
||||
PISID DomainSid = 0;
|
||||
ULONG TrustPosixOffset = 0;
|
||||
for (ULONG I = 0; FspTrustedDomainCount > I; I++)
|
||||
{
|
||||
if (FspTrustedDomains[I].TrustPosixOffset <= Uid &&
|
||||
FspTrustedDomains[I].TrustPosixOffset > TrustPosixOffset)
|
||||
{
|
||||
DomainSid = FspTrustedDomains[I].DomainSid;
|
||||
TrustPosixOffset = FspTrustedDomains[I].TrustPosixOffset;
|
||||
}
|
||||
}
|
||||
if (0 != DomainSid)
|
||||
{
|
||||
*PSid = FspPosixCreateSid(5, 5,
|
||||
21,
|
||||
DomainSid->SubAuthority[1],
|
||||
DomainSid->SubAuthority[2],
|
||||
DomainSid->SubAuthority[3],
|
||||
Uid - TrustPosixOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* I am sorry, I am not going to bother with all that trustPosixOffset stuff.
|
||||
* But if you need it, I accept patches :)
|
||||
*/
|
||||
|
||||
/* [IDMAP]
|
||||
* Mandatory Labels:
|
||||
@ -432,11 +601,15 @@ FSP_API NTSTATUS FspPosixMapSidToUid(PSID Sid, PUINT32 PUid)
|
||||
else if (0 != FspAccountDomainSid &&
|
||||
FspPosixIsRelativeSid(FspAccountDomainSid, Sid))
|
||||
*PUid = 0x30000 + Rid;
|
||||
|
||||
/*
|
||||
* I am sorry, I am not going to bother with all that trustPosixOffset stuff.
|
||||
* But if you need it, I accept patches :)
|
||||
*/
|
||||
else
|
||||
for (ULONG I = 0; FspTrustedDomainCount > I; I++)
|
||||
{
|
||||
if (FspPosixIsRelativeSid(FspTrustedDomains[I].DomainSid, Sid))
|
||||
{
|
||||
*PUid = FspTrustedDomains[I].TrustPosixOffset + Rid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* [IDMAP]
|
||||
|
@ -130,6 +130,62 @@ WINFSP_SHARED_MINIMAL_STRNCMP(invariant_wcsnicmp, wchar_t, invariant_toupper)
|
||||
#undef WINFSP_SHARED_MINIMAL_STRCMP
|
||||
#undef WINFSP_SHARED_MINIMAL_STRNCMP
|
||||
|
||||
#define WINFSP_SHARED_MINIMAL_STRTOINT(NAME, CTYPE, ITYPE)\
|
||||
static inline\
|
||||
ITYPE NAME(const CTYPE *p, const CTYPE **endp, int base, int is_signed)\
|
||||
{\
|
||||
ITYPE v;\
|
||||
int maxdig, maxalp, sign = +1;\
|
||||
if (is_signed)\
|
||||
{\
|
||||
if ('+' == *p)\
|
||||
p++;\
|
||||
else if ('-' == *p)\
|
||||
p++, sign = -1;\
|
||||
}\
|
||||
if (0 == base)\
|
||||
{\
|
||||
if ('0' == *p)\
|
||||
{\
|
||||
p++;\
|
||||
if ('x' == *p || 'X' == *p)\
|
||||
{\
|
||||
p++;\
|
||||
base = 16;\
|
||||
}\
|
||||
else\
|
||||
base = 8;\
|
||||
}\
|
||||
else\
|
||||
{\
|
||||
base = 10;\
|
||||
}\
|
||||
}\
|
||||
maxdig = 10 < base ? '9' : (base - 1) + '0';\
|
||||
maxalp = 10 < base ? (base - 1 - 10) + 'a' : 0;\
|
||||
for (v = 0; *p; p++)\
|
||||
{\
|
||||
int c = *p;\
|
||||
if ('0' <= c && c <= maxdig)\
|
||||
v = base * v + (c - '0');\
|
||||
else\
|
||||
{\
|
||||
c |= 0x20;\
|
||||
if ('a' <= c && c <= maxalp)\
|
||||
v = base * v + (c - 'a') + 10;\
|
||||
else\
|
||||
break;\
|
||||
}\
|
||||
}\
|
||||
if (0 != endp)\
|
||||
*endp = (CTYPE *)p;\
|
||||
return sign * v;\
|
||||
}
|
||||
WINFSP_SHARED_MINIMAL_STRTOINT(strtouint, char, unsigned int)
|
||||
WINFSP_SHARED_MINIMAL_STRTOINT(strtollint, char, long long int)
|
||||
WINFSP_SHARED_MINIMAL_STRTOINT(wcstouint, wchar_t, unsigned int)
|
||||
WINFSP_SHARED_MINIMAL_STRTOINT(wcstollint, wchar_t, long long int)
|
||||
|
||||
static inline void *MemAlloc(size_t Size)
|
||||
{
|
||||
return HeapAlloc(GetProcessHeap(), 0, Size);
|
||||
|
@ -41,7 +41,9 @@ static VOID FspFsvolDeviceFini(PDEVICE_OBJECT DeviceObject);
|
||||
static IO_TIMER_ROUTINE FspFsvolDeviceTimerRoutine;
|
||||
static WORKER_THREAD_ROUTINE FspFsvolDeviceExpirationRoutine;
|
||||
VOID FspFsvolDeviceFileRenameAcquireShared(PDEVICE_OBJECT DeviceObject);
|
||||
BOOLEAN FspFsvolDeviceFileRenameTryAcquireShared(PDEVICE_OBJECT DeviceObject);
|
||||
VOID FspFsvolDeviceFileRenameAcquireExclusive(PDEVICE_OBJECT DeviceObject);
|
||||
BOOLEAN FspFsvolDeviceFileRenameTryAcquireExclusive(PDEVICE_OBJECT DeviceObject);
|
||||
VOID FspFsvolDeviceFileRenameSetOwner(PDEVICE_OBJECT DeviceObject, PVOID Owner);
|
||||
VOID FspFsvolDeviceFileRenameRelease(PDEVICE_OBJECT DeviceObject);
|
||||
VOID FspFsvolDeviceFileRenameReleaseOwner(PDEVICE_OBJECT DeviceObject, PVOID Owner);
|
||||
@ -83,7 +85,9 @@ VOID FspDeviceDeleteAll(VOID);
|
||||
#pragma alloc_text(PAGE, FspFsvolDeviceInit)
|
||||
#pragma alloc_text(PAGE, FspFsvolDeviceFini)
|
||||
#pragma alloc_text(PAGE, FspFsvolDeviceFileRenameAcquireShared)
|
||||
#pragma alloc_text(PAGE, FspFsvolDeviceFileRenameTryAcquireShared)
|
||||
#pragma alloc_text(PAGE, FspFsvolDeviceFileRenameAcquireExclusive)
|
||||
#pragma alloc_text(PAGE, FspFsvolDeviceFileRenameTryAcquireExclusive)
|
||||
#pragma alloc_text(PAGE, FspFsvolDeviceFileRenameSetOwner)
|
||||
#pragma alloc_text(PAGE, FspFsvolDeviceFileRenameRelease)
|
||||
#pragma alloc_text(PAGE, FspFsvolDeviceFileRenameReleaseOwner)
|
||||
@ -411,10 +415,11 @@ static NTSTATUS FspFsvolDeviceInit(PDEVICE_OBJECT DeviceObject)
|
||||
return Result;
|
||||
FsvolDeviceExtension->InitDoneEa = 1;
|
||||
|
||||
/* initialize the FSRTL Notify mechanism */
|
||||
/* initialize the Volume Notify and FSRTL Notify mechanisms */
|
||||
Result = FspNotifyInitializeSync(&FsvolDeviceExtension->NotifySync);
|
||||
if (!NT_SUCCESS(Result))
|
||||
return Result;
|
||||
FspWgroupInitialize(&FsvolDeviceExtension->VolumeNotifyWgroup);
|
||||
InitializeListHead(&FsvolDeviceExtension->NotifyList);
|
||||
FsvolDeviceExtension->InitDoneNotify = 1;
|
||||
|
||||
@ -473,7 +478,7 @@ static VOID FspFsvolDeviceFini(PDEVICE_OBJECT DeviceObject)
|
||||
if (FsvolDeviceExtension->InitDoneStat)
|
||||
FspStatisticsDelete(FsvolDeviceExtension->Statistics);
|
||||
|
||||
/* uninitialize the FSRTL Notify mechanism */
|
||||
/* uninitialize the Volume Notify and FSRTL Notify mechanisms */
|
||||
if (FsvolDeviceExtension->InitDoneNotify)
|
||||
{
|
||||
FspNotifyCleanupAll(
|
||||
@ -596,6 +601,15 @@ VOID FspFsvolDeviceFileRenameAcquireShared(PDEVICE_OBJECT DeviceObject)
|
||||
ExAcquireResourceSharedLite(&FsvolDeviceExtension->FileRenameResource, TRUE);
|
||||
}
|
||||
|
||||
BOOLEAN FspFsvolDeviceFileRenameTryAcquireShared(PDEVICE_OBJECT DeviceObject)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject);
|
||||
|
||||
return ExAcquireResourceSharedLite(&FsvolDeviceExtension->FileRenameResource, FALSE);
|
||||
}
|
||||
|
||||
VOID FspFsvolDeviceFileRenameAcquireExclusive(PDEVICE_OBJECT DeviceObject)
|
||||
{
|
||||
PAGED_CODE();
|
||||
@ -605,6 +619,15 @@ VOID FspFsvolDeviceFileRenameAcquireExclusive(PDEVICE_OBJECT DeviceObject)
|
||||
ExAcquireResourceExclusiveLite(&FsvolDeviceExtension->FileRenameResource, TRUE);
|
||||
}
|
||||
|
||||
BOOLEAN FspFsvolDeviceFileRenameTryAcquireExclusive(PDEVICE_OBJECT DeviceObject)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject);
|
||||
|
||||
return ExAcquireResourceExclusiveLite(&FsvolDeviceExtension->FileRenameResource, FALSE);
|
||||
}
|
||||
|
||||
VOID FspFsvolDeviceFileRenameSetOwner(PDEVICE_OBJECT DeviceObject, PVOID Owner)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
@ -652,6 +652,28 @@ VOID FspIrpHookReset(PIRP Irp);
|
||||
PVOID FspIrpHookContext(PVOID Context);
|
||||
NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context);
|
||||
|
||||
/* utility: wait groups
|
||||
*
|
||||
* A Wait Group is a synchronization primitive that encapsulates a counter.
|
||||
* A Wait Group is considered signaled when the counter is 0 and non-signaled
|
||||
* when the counter is non-0. (Wait Group functionality is similar to Golang's
|
||||
* sync.WaitGroup.)
|
||||
*
|
||||
* Wait Groups must always be allocated in non-paged storage.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
KEVENT Event;
|
||||
LONG Count;
|
||||
KSPIN_LOCK SpinLock;
|
||||
} FSP_WGROUP;
|
||||
VOID FspWgroupInitialize(FSP_WGROUP *Wgroup);
|
||||
VOID FspWgroupIncrement(FSP_WGROUP *Wgroup);
|
||||
VOID FspWgroupDecrement(FSP_WGROUP *Wgroup);
|
||||
VOID FspWgroupSignalPermanently(FSP_WGROUP *Wgroup);
|
||||
NTSTATUS FspWgroupWait(FSP_WGROUP *Wgroup,
|
||||
KPROCESSOR_MODE WaitMode, BOOLEAN Alertable, PLARGE_INTEGER PTimeout);
|
||||
|
||||
/* silos */
|
||||
typedef struct
|
||||
{
|
||||
@ -1124,6 +1146,8 @@ typedef struct
|
||||
KSPIN_LOCK InfoSpinLock;
|
||||
UINT64 InfoExpirationTime;
|
||||
FSP_FSCTL_VOLUME_INFO VolumeInfo;
|
||||
LONG VolumeNotifyLock;
|
||||
FSP_WGROUP VolumeNotifyWgroup;
|
||||
PNOTIFY_SYNC NotifySync;
|
||||
LIST_ENTRY NotifyList;
|
||||
FSP_STATISTICS *Statistics;
|
||||
@ -1182,7 +1206,9 @@ VOID FspDeviceDelete(PDEVICE_OBJECT DeviceObject);
|
||||
BOOLEAN FspDeviceReference(PDEVICE_OBJECT DeviceObject);
|
||||
VOID FspDeviceDereference(PDEVICE_OBJECT DeviceObject);
|
||||
VOID FspFsvolDeviceFileRenameAcquireShared(PDEVICE_OBJECT DeviceObject);
|
||||
BOOLEAN FspFsvolDeviceFileRenameTryAcquireShared(PDEVICE_OBJECT DeviceObject);
|
||||
VOID FspFsvolDeviceFileRenameAcquireExclusive(PDEVICE_OBJECT DeviceObject);
|
||||
BOOLEAN FspFsvolDeviceFileRenameTryAcquireExclusive(PDEVICE_OBJECT DeviceObject);
|
||||
VOID FspFsvolDeviceFileRenameSetOwner(PDEVICE_OBJECT DeviceObject, PVOID Owner);
|
||||
VOID FspFsvolDeviceFileRenameRelease(PDEVICE_OBJECT DeviceObject);
|
||||
VOID FspFsvolDeviceFileRenameReleaseOwner(PDEVICE_OBJECT DeviceObject, PVOID Owner);
|
||||
@ -1328,6 +1354,8 @@ NTSTATUS FspVolumeTransactFsext(
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||
NTSTATUS FspVolumeStop(
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||
NTSTATUS FspVolumeNotify(
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||
NTSTATUS FspVolumeWork(
|
||||
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||
|
||||
@ -1359,8 +1387,10 @@ typedef struct
|
||||
FAST_MUTEX HeaderFastMutex;
|
||||
SECTION_OBJECT_POINTERS SectionObjectPointers;
|
||||
KSPIN_LOCK NpInfoSpinLock; /* allows to invalidate non-page Info w/o resources acquired */
|
||||
UINT64 Security;
|
||||
UINT64 DirInfo;
|
||||
UINT64 StreamInfo;
|
||||
UINT64 Ea;
|
||||
} FSP_FILE_NODE_NONPAGED;
|
||||
typedef struct FSP_FILE_NODE
|
||||
{
|
||||
@ -1391,11 +1421,9 @@ typedef struct FSP_FILE_NODE
|
||||
UINT64 ChangeTime;
|
||||
UINT32 EaSize;
|
||||
ULONG FileInfoChangeNumber;
|
||||
UINT64 Security;
|
||||
ULONG SecurityChangeNumber;
|
||||
ULONG DirInfoChangeNumber;
|
||||
ULONG StreamInfoChangeNumber;
|
||||
UINT64 Ea;
|
||||
ULONG EaChangeNumber;
|
||||
ULONG EaChangeCount;
|
||||
BOOLEAN TruncateOnClose;
|
||||
@ -1540,6 +1568,7 @@ BOOLEAN FspFileNodeReferenceSecurity(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, P
|
||||
VOID FspFileNodeSetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size);
|
||||
BOOLEAN FspFileNodeTrySetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
|
||||
ULONG SecurityChangeNumber);
|
||||
VOID FspFileNodeInvalidateSecurity(FSP_FILE_NODE *FileNode);
|
||||
static inline
|
||||
ULONG FspFileNodeSecurityChangeNumber(FSP_FILE_NODE *FileNode)
|
||||
{
|
||||
@ -1573,6 +1602,7 @@ BOOLEAN FspFileNodeReferenceEa(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG
|
||||
VOID FspFileNodeSetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size);
|
||||
BOOLEAN FspFileNodeTrySetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
|
||||
ULONG EaChangeNumber);
|
||||
VOID FspFileNodeInvalidateEa(FSP_FILE_NODE *FileNode);
|
||||
static inline
|
||||
ULONG FspFileNodeEaChangeNumber(FSP_FILE_NODE *FileNode)
|
||||
{
|
||||
@ -1582,6 +1612,9 @@ ULONG FspFileNodeEaChangeNumber(FSP_FILE_NODE *FileNode)
|
||||
}
|
||||
VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action,
|
||||
BOOLEAN InvalidateCaches);
|
||||
VOID FspFileNodeInvalidateCachesAndNotifyChangeByName(PDEVICE_OBJECT FsvolDeviceObject,
|
||||
PUNICODE_STRING FileName, ULONG Filter, ULONG Action,
|
||||
BOOLEAN InvalidateParentCaches);
|
||||
NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp);
|
||||
NTSTATUS FspFileDescCreate(FSP_FILE_DESC **PFileDesc);
|
||||
VOID FspFileDescDelete(FSP_FILE_DESC *FileDesc);
|
||||
|
224
src/sys/file.c
224
src/sys/file.c
@ -76,6 +76,7 @@ BOOLEAN FspFileNodeReferenceSecurity(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, P
|
||||
VOID FspFileNodeSetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size);
|
||||
BOOLEAN FspFileNodeTrySetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
|
||||
ULONG SecurityChangeNumber);
|
||||
VOID FspFileNodeInvalidateSecurity(FSP_FILE_NODE *FileNode);
|
||||
BOOLEAN FspFileNodeReferenceDirInfo(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize);
|
||||
VOID FspFileNodeSetDirInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size);
|
||||
BOOLEAN FspFileNodeTrySetDirInfo(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
|
||||
@ -93,8 +94,12 @@ BOOLEAN FspFileNodeReferenceEa(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG
|
||||
VOID FspFileNodeSetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size);
|
||||
BOOLEAN FspFileNodeTrySetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
|
||||
ULONG EaChangeNumber);
|
||||
VOID FspFileNodeInvalidateEa(FSP_FILE_NODE *FileNode);
|
||||
VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action,
|
||||
BOOLEAN InvalidateCaches);
|
||||
VOID FspFileNodeInvalidateCachesAndNotifyChangeByName(PDEVICE_OBJECT FsvolDeviceObject,
|
||||
PUNICODE_STRING FileName, ULONG Filter, ULONG Action,
|
||||
BOOLEAN InvalidateParentCaches);
|
||||
NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp);
|
||||
static NTSTATUS FspFileNodeCompleteLockIrp(PVOID Context, PIRP Irp);
|
||||
NTSTATUS FspFileDescCreate(FSP_FILE_DESC **PFileDesc);
|
||||
@ -149,9 +154,10 @@ VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp);
|
||||
#pragma alloc_text(PAGE, FspFileNodeTrySetFileInfoAndSecurityOnOpen)
|
||||
#pragma alloc_text(PAGE, FspFileNodeTrySetFileInfo)
|
||||
#pragma alloc_text(PAGE, FspFileNodeInvalidateFileInfo)
|
||||
#pragma alloc_text(PAGE, FspFileNodeReferenceSecurity)
|
||||
#pragma alloc_text(PAGE, FspFileNodeSetSecurity)
|
||||
#pragma alloc_text(PAGE, FspFileNodeTrySetSecurity)
|
||||
// !#pragma alloc_text(PAGE, FspFileNodeReferenceSecurity)
|
||||
// !#pragma alloc_text(PAGE, FspFileNodeSetSecurity)
|
||||
// !#pragma alloc_text(PAGE, FspFileNodeTrySetSecurity)
|
||||
// !#pragma alloc_text(PAGE, FspFileNodeInvalidateSecurity)
|
||||
// !#pragma alloc_text(PAGE, FspFileNodeReferenceDirInfo)
|
||||
// !#pragma alloc_text(PAGE, FspFileNodeSetDirInfo)
|
||||
// !#pragma alloc_text(PAGE, FspFileNodeTrySetDirInfo)
|
||||
@ -162,10 +168,12 @@ VOID FspFileNodeOplockComplete(PVOID Context, PIRP Irp);
|
||||
// !#pragma alloc_text(PAGE, FspFileNodeSetStreamInfo)
|
||||
// !#pragma alloc_text(PAGE, FspFileNodeTrySetStreamInfo)
|
||||
// !#pragma alloc_text(PAGE, FspFileNodeInvalidateStreamInfo)
|
||||
#pragma alloc_text(PAGE, FspFileNodeReferenceEa)
|
||||
#pragma alloc_text(PAGE, FspFileNodeSetEa)
|
||||
#pragma alloc_text(PAGE, FspFileNodeTrySetEa)
|
||||
// !#pragma alloc_text(PAGE, FspFileNodeReferenceEa)
|
||||
// !#pragma alloc_text(PAGE, FspFileNodeSetEa)
|
||||
// !#pragma alloc_text(PAGE, FspFileNodeTrySetEa)
|
||||
// !#pragma alloc_text(PAGE, FspFileNodeInvalidateEa)
|
||||
#pragma alloc_text(PAGE, FspFileNodeNotifyChange)
|
||||
#pragma alloc_text(PAGE, FspFileNodeInvalidateCachesAndNotifyChangeByName)
|
||||
#pragma alloc_text(PAGE, FspFileNodeProcessLockIrp)
|
||||
#pragma alloc_text(PAGE, FspFileNodeCompleteLockIrp)
|
||||
#pragma alloc_text(PAGE, FspFileDescCreate)
|
||||
@ -368,10 +376,10 @@ VOID FspFileNodeDelete(FSP_FILE_NODE *FileNode)
|
||||
|
||||
FsRtlTeardownPerStreamContexts(&FileNode->Header);
|
||||
|
||||
FspMetaCacheInvalidateItem(FsvolDeviceExtension->EaCache, FileNode->Ea);
|
||||
FspMetaCacheInvalidateItem(FsvolDeviceExtension->EaCache, FileNode->NonPaged->Ea);
|
||||
FspMetaCacheInvalidateItem(FsvolDeviceExtension->StreamInfoCache, FileNode->NonPaged->StreamInfo);
|
||||
FspMetaCacheInvalidateItem(FsvolDeviceExtension->DirInfoCache, FileNode->NonPaged->DirInfo);
|
||||
FspMetaCacheInvalidateItem(FsvolDeviceExtension->SecurityCache, FileNode->Security);
|
||||
FspMetaCacheInvalidateItem(FsvolDeviceExtension->SecurityCache, FileNode->NonPaged->Security);
|
||||
|
||||
FspDeviceDereference(FileNode->FsvolDeviceObject);
|
||||
|
||||
@ -1905,38 +1913,54 @@ VOID FspFileNodeInvalidateFileInfo(FSP_FILE_NODE *FileNode)
|
||||
|
||||
BOOLEAN FspFileNodeReferenceSecurity(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize)
|
||||
{
|
||||
PAGED_CODE();
|
||||
// !PAGED_CODE();
|
||||
|
||||
if (0 != FileNode->MainFileNode)
|
||||
FileNode = FileNode->MainFileNode;
|
||||
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
||||
FspFsvolDeviceExtension(FileNode->FsvolDeviceObject);
|
||||
FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged;
|
||||
UINT64 Security;
|
||||
|
||||
/* no need to acquire the NpInfoSpinLock as the FileNode is acquired */
|
||||
Security = NonPaged->Security;
|
||||
|
||||
return FspMetaCacheReferenceItemBuffer(FsvolDeviceExtension->SecurityCache,
|
||||
FileNode->Security, PBuffer, PSize);
|
||||
Security, PBuffer, PSize);
|
||||
}
|
||||
|
||||
VOID FspFileNodeSetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size)
|
||||
{
|
||||
PAGED_CODE();
|
||||
// !PAGED_CODE();
|
||||
|
||||
if (0 != FileNode->MainFileNode)
|
||||
FileNode = FileNode->MainFileNode;
|
||||
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
||||
FspFsvolDeviceExtension(FileNode->FsvolDeviceObject);
|
||||
FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged;
|
||||
KIRQL Irql;
|
||||
UINT64 Security;
|
||||
|
||||
FspMetaCacheInvalidateItem(FsvolDeviceExtension->SecurityCache, FileNode->Security);
|
||||
FileNode->Security = 0 != Buffer ?
|
||||
/* no need to acquire the NpInfoSpinLock as the FileNode is acquired */
|
||||
Security = NonPaged->Security;
|
||||
|
||||
FspMetaCacheInvalidateItem(FsvolDeviceExtension->SecurityCache, Security);
|
||||
Security = 0 != Buffer ?
|
||||
FspMetaCacheAddItem(FsvolDeviceExtension->SecurityCache, Buffer, Size) : 0;
|
||||
FileNode->SecurityChangeNumber++;
|
||||
|
||||
/* acquire the NpInfoSpinLock to protect against concurrent FspFileNodeInvalidateSecurity */
|
||||
KeAcquireSpinLock(&NonPaged->NpInfoSpinLock, &Irql);
|
||||
NonPaged->Security = Security;
|
||||
KeReleaseSpinLock(&NonPaged->NpInfoSpinLock, Irql);
|
||||
}
|
||||
|
||||
BOOLEAN FspFileNodeTrySetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
|
||||
ULONG SecurityChangeNumber)
|
||||
{
|
||||
PAGED_CODE();
|
||||
// !PAGED_CODE();
|
||||
|
||||
if (FspFileNodeSecurityChangeNumber(FileNode) != SecurityChangeNumber)
|
||||
return FALSE;
|
||||
@ -1945,6 +1969,27 @@ BOOLEAN FspFileNodeTrySetSecurity(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
VOID FspFileNodeInvalidateSecurity(FSP_FILE_NODE *FileNode)
|
||||
{
|
||||
// !PAGED_CODE();
|
||||
|
||||
if (0 != FileNode->MainFileNode)
|
||||
FileNode = FileNode->MainFileNode;
|
||||
|
||||
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||
FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged;
|
||||
KIRQL Irql;
|
||||
UINT64 Security;
|
||||
|
||||
/* acquire the NpInfoSpinLock to protect against concurrent FspFileNodeSetSecurity */
|
||||
KeAcquireSpinLock(&NonPaged->NpInfoSpinLock, &Irql);
|
||||
Security = NonPaged->Security;
|
||||
KeReleaseSpinLock(&NonPaged->NpInfoSpinLock, Irql);
|
||||
|
||||
FspMetaCacheInvalidateItem(FsvolDeviceExtension->SecurityCache, Security);
|
||||
}
|
||||
|
||||
BOOLEAN FspFileNodeReferenceDirInfo(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize)
|
||||
{
|
||||
// !PAGED_CODE();
|
||||
@ -2130,38 +2175,54 @@ VOID FspFileNodeInvalidateStreamInfo(FSP_FILE_NODE *FileNode)
|
||||
|
||||
BOOLEAN FspFileNodeReferenceEa(FSP_FILE_NODE *FileNode, PCVOID *PBuffer, PULONG PSize)
|
||||
{
|
||||
PAGED_CODE();
|
||||
// !PAGED_CODE();
|
||||
|
||||
if (0 != FileNode->MainFileNode)
|
||||
FileNode = FileNode->MainFileNode;
|
||||
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
||||
FspFsvolDeviceExtension(FileNode->FsvolDeviceObject);
|
||||
FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged;
|
||||
UINT64 Ea;
|
||||
|
||||
/* no need to acquire the NpInfoSpinLock as the FileNode is acquired */
|
||||
Ea = NonPaged->Ea;
|
||||
|
||||
return FspMetaCacheReferenceItemBuffer(FsvolDeviceExtension->EaCache,
|
||||
FileNode->Ea, PBuffer, PSize);
|
||||
Ea, PBuffer, PSize);
|
||||
}
|
||||
|
||||
VOID FspFileNodeSetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size)
|
||||
{
|
||||
PAGED_CODE();
|
||||
// !PAGED_CODE();
|
||||
|
||||
if (0 != FileNode->MainFileNode)
|
||||
FileNode = FileNode->MainFileNode;
|
||||
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension =
|
||||
FspFsvolDeviceExtension(FileNode->FsvolDeviceObject);
|
||||
FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged;
|
||||
KIRQL Irql;
|
||||
UINT64 Ea;
|
||||
|
||||
FspMetaCacheInvalidateItem(FsvolDeviceExtension->EaCache, FileNode->Ea);
|
||||
FileNode->Ea = 0 != Buffer ?
|
||||
/* no need to acquire the NpInfoSpinLock as the FileNode is acquired */
|
||||
Ea = NonPaged->Ea;
|
||||
|
||||
FspMetaCacheInvalidateItem(FsvolDeviceExtension->EaCache, Ea);
|
||||
Ea = 0 != Buffer ?
|
||||
FspMetaCacheAddItem(FsvolDeviceExtension->EaCache, Buffer, Size) : 0;
|
||||
FileNode->EaChangeNumber++;
|
||||
|
||||
/* acquire the NpInfoSpinLock to protect against concurrent FspFileNodeInvalidateEa */
|
||||
KeAcquireSpinLock(&NonPaged->NpInfoSpinLock, &Irql);
|
||||
NonPaged->Ea = Ea;
|
||||
KeReleaseSpinLock(&NonPaged->NpInfoSpinLock, Irql);
|
||||
}
|
||||
|
||||
BOOLEAN FspFileNodeTrySetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
|
||||
ULONG EaChangeNumber)
|
||||
{
|
||||
PAGED_CODE();
|
||||
// !PAGED_CODE();
|
||||
|
||||
if (FspFileNodeEaChangeNumber(FileNode) != EaChangeNumber)
|
||||
return FALSE;
|
||||
@ -2170,6 +2231,27 @@ BOOLEAN FspFileNodeTrySetEa(FSP_FILE_NODE *FileNode, PCVOID Buffer, ULONG Size,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
VOID FspFileNodeInvalidateEa(FSP_FILE_NODE *FileNode)
|
||||
{
|
||||
// !PAGED_CODE();
|
||||
|
||||
if (0 != FileNode->MainFileNode)
|
||||
FileNode = FileNode->MainFileNode;
|
||||
|
||||
PDEVICE_OBJECT FsvolDeviceObject = FileNode->FsvolDeviceObject;
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||
FSP_FILE_NODE_NONPAGED *NonPaged = FileNode->NonPaged;
|
||||
KIRQL Irql;
|
||||
UINT64 Ea;
|
||||
|
||||
/* acquire the NpInfoSpinLock to protect against concurrent FspFileNodeSetEa */
|
||||
KeAcquireSpinLock(&NonPaged->NpInfoSpinLock, &Irql);
|
||||
Ea = NonPaged->Ea;
|
||||
KeReleaseSpinLock(&NonPaged->NpInfoSpinLock, Irql);
|
||||
|
||||
FspMetaCacheInvalidateItem(FsvolDeviceExtension->EaCache, Ea);
|
||||
}
|
||||
|
||||
VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action,
|
||||
BOOLEAN InvalidateCaches)
|
||||
{
|
||||
@ -2232,6 +2314,108 @@ VOID FspFileNodeNotifyChange(FSP_FILE_NODE *FileNode, ULONG Filter, ULONG Action
|
||||
}
|
||||
}
|
||||
|
||||
VOID FspFileNodeInvalidateCachesAndNotifyChangeByName(PDEVICE_OBJECT FsvolDeviceObject,
|
||||
PUNICODE_STRING FileName, ULONG Filter, ULONG Action,
|
||||
BOOLEAN InvalidateParentCaches)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
FSP_FILE_NODE *FileNode;
|
||||
|
||||
FspFsvolDeviceLockContextTable(FsvolDeviceObject);
|
||||
FileNode = FspFsvolDeviceLookupContextByName(FsvolDeviceObject, FileName);
|
||||
if (0 != FileNode)
|
||||
FspFileNodeReference(FileNode);
|
||||
FspFsvolDeviceUnlockContextTable(FsvolDeviceObject);
|
||||
|
||||
if (0 != FileNode)
|
||||
{
|
||||
FspFileNodeAcquireExclusive(FileNode, Full);
|
||||
|
||||
if (0 != FileNode->NonPaged->SectionObjectPointers.DataSectionObject)
|
||||
{
|
||||
IO_STATUS_BLOCK IoStatus;
|
||||
FspCcFlushCache(&FileNode->NonPaged->SectionObjectPointers, 0, 0, &IoStatus);
|
||||
if (NT_SUCCESS(IoStatus.Status))
|
||||
CcPurgeCacheSection(&FileNode->NonPaged->SectionObjectPointers, 0, 0, FALSE);
|
||||
}
|
||||
|
||||
FspFileNodeInvalidateFileInfo(FileNode);
|
||||
FspFileNodeInvalidateSecurity(FileNode);
|
||||
FspFileNodeInvalidateDirInfo(FileNode);
|
||||
FspFileNodeInvalidateStreamInfo(FileNode);
|
||||
FspFileNodeInvalidateEa(FileNode);
|
||||
|
||||
FspFileNodeNotifyChange(FileNode, Filter, Action, InvalidateParentCaches);
|
||||
|
||||
FspFileNodeRelease(FileNode, Full);
|
||||
|
||||
FspFileNodeDereference(FileNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||
UNICODE_STRING Parent, Suffix;
|
||||
BOOLEAN IsStream;
|
||||
|
||||
IsStream = FALSE;
|
||||
for (PWSTR P = FileName->Buffer, EndP = P + FileName->Length / sizeof(WCHAR); EndP > P; P++)
|
||||
if (L':' == *P)
|
||||
{
|
||||
IsStream = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (IsStream)
|
||||
{
|
||||
if (FlagOn(Filter, FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME))
|
||||
SetFlag(Filter, FILE_NOTIFY_CHANGE_STREAM_NAME);
|
||||
if (FlagOn(Filter, FILE_NOTIFY_CHANGE_SIZE))
|
||||
SetFlag(Filter, FILE_NOTIFY_CHANGE_STREAM_SIZE);
|
||||
if (FlagOn(Filter, FILE_NOTIFY_CHANGE_LAST_WRITE))
|
||||
SetFlag(Filter, FILE_NOTIFY_CHANGE_STREAM_WRITE);
|
||||
ClearFlag(Filter, ~(FILE_NOTIFY_CHANGE_STREAM_NAME | FILE_NOTIFY_CHANGE_STREAM_SIZE |
|
||||
FILE_NOTIFY_CHANGE_STREAM_WRITE));
|
||||
|
||||
switch (Action)
|
||||
{
|
||||
case FILE_ACTION_ADDED:
|
||||
Action = FILE_ACTION_ADDED_STREAM;
|
||||
break;
|
||||
case FILE_ACTION_REMOVED:
|
||||
Action = FILE_ACTION_REMOVED_STREAM;
|
||||
break;
|
||||
case FILE_ACTION_MODIFIED:
|
||||
Action = FILE_ACTION_MODIFIED_STREAM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != Filter)
|
||||
{
|
||||
FspFileNameSuffix(FileName, &Parent, &Suffix);
|
||||
|
||||
if (InvalidateParentCaches)
|
||||
{
|
||||
FspFsvolDeviceInvalidateVolumeInfo(FsvolDeviceObject);
|
||||
if (!IsStream)
|
||||
{
|
||||
if (sizeof(WCHAR) == FileName->Length && L'\\' == FileName->Buffer[0])
|
||||
; /* root does not have a parent */
|
||||
else
|
||||
FspFileNodeInvalidateDirInfoByName(FsvolDeviceObject, &Parent);
|
||||
}
|
||||
}
|
||||
|
||||
FspNotifyReportChange(
|
||||
FsvolDeviceExtension->NotifySync, &FsvolDeviceExtension->NotifyList,
|
||||
FileName,
|
||||
(USHORT)((PUINT8)Suffix.Buffer - (PUINT8)FileName->Buffer),
|
||||
0, Filter, Action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NTSTATUS FspFileNodeProcessLockIrp(FSP_FILE_NODE *FileNode, PIRP Irp)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
@ -1589,6 +1589,10 @@ retry:
|
||||
|
||||
Suffix.Length = (USHORT)Info->FileNameLength;
|
||||
Suffix.Buffer = Info->FileName;
|
||||
/* remove any trailing backslash; NTFS allows it for both directories AND files! */
|
||||
if (sizeof(WCHAR) * 2/* not empty or root */ <= Suffix.Length &&
|
||||
L'\\' == Suffix.Buffer[Suffix.Length / sizeof(WCHAR) - 1])
|
||||
Suffix.Length -= sizeof(WCHAR);
|
||||
/* if there is a backslash anywhere in the NewFileName get its suffix */
|
||||
for (PWSTR P = Suffix.Buffer, EndP = P + Suffix.Length / sizeof(WCHAR); EndP > P; P++)
|
||||
if (L'\\' == *P)
|
||||
|
@ -98,6 +98,10 @@ static NTSTATUS FspFsctlFileSystemControl(
|
||||
if (0 != IrpSp->FileObject->FsContext2)
|
||||
Result = FspVolumeStop(FsctlDeviceObject, Irp, IrpSp);
|
||||
break;
|
||||
case FSP_FSCTL_NOTIFY:
|
||||
if (0 != IrpSp->FileObject->FsContext2)
|
||||
Result = FspVolumeNotify(FsctlDeviceObject, Irp, IrpSp);
|
||||
break;
|
||||
default:
|
||||
if (CTL_CODE(0, 0xC00, 0, 0) ==
|
||||
(IrpSp->Parameters.FileSystemControl.FsControlCode & CTL_CODE(0, 0xC00, 0, 0)))
|
||||
|
@ -249,9 +249,9 @@ static NTSTATUS FspFsvolReadNonCached(
|
||||
if (!NT_SUCCESS(Result))
|
||||
return Result;
|
||||
|
||||
/* acquire FileNode exclusive Full */
|
||||
/* acquire FileNode shared Full */
|
||||
Success = DEBUGTEST(90) &&
|
||||
FspFileNodeTryAcquireExclusiveF(FileNode, FspFileNodeAcquireFull, CanWait);
|
||||
FspFileNodeTryAcquireSharedF(FileNode, FspFileNodeAcquireFull, CanWait);
|
||||
if (!Success)
|
||||
return FspWqRepostIrpWorkItem(Irp, FspFsvolReadNonCached, 0);
|
||||
|
||||
@ -280,21 +280,21 @@ static NTSTATUS FspFsvolReadNonCached(
|
||||
/* if this is a non-cached transfer on a cached file then flush the file */
|
||||
if (!PagingIo && 0 != FileObject->SectionObjectPointer->DataSectionObject)
|
||||
{
|
||||
FspFileNodeRelease(FileNode, Full);
|
||||
if (!CanWait)
|
||||
{
|
||||
FspFileNodeRelease(FileNode, Full);
|
||||
return FspWqRepostIrpWorkItem(Irp, FspFsvolReadNonCached, 0);
|
||||
}
|
||||
|
||||
/* need to acquire exclusive for flushing */
|
||||
FspFileNodeAcquireExclusive(FileNode, Full);
|
||||
Result = FspFileNodeFlushAndPurgeCache(FileNode,
|
||||
IrpSp->Parameters.Read.ByteOffset.QuadPart,
|
||||
IrpSp->Parameters.Read.Length,
|
||||
FALSE);
|
||||
FspFileNodeRelease(FileNode, Full);
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
FspFileNodeRelease(FileNode, Full);
|
||||
return Result;
|
||||
}
|
||||
|
||||
FspFileNodeAcquireShared(FileNode, Full);
|
||||
}
|
||||
|
||||
/* trim ReadLength during CreateProcess; resolve bugcheck for filesystem that reports incorrect size */
|
||||
@ -310,9 +310,6 @@ static NTSTATUS FspFsvolReadNonCached(
|
||||
ReadLength = (ULONG)(FileInfo.FileSize - ReadOffset.QuadPart);
|
||||
}
|
||||
|
||||
/* convert FileNode to shared */
|
||||
FspFileNodeConvertExclusiveToShared(FileNode, Full);
|
||||
|
||||
Request = FspIrpRequest(Irp);
|
||||
if (0 == Request)
|
||||
{
|
||||
|
@ -131,6 +131,12 @@ NTSTATUS FspIrpHook(PIRP Irp, PIO_COMPLETION_ROUTINE CompletionRoutine, PVOID Ow
|
||||
VOID FspIrpHookReset(PIRP Irp);
|
||||
PVOID FspIrpHookContext(PVOID Context);
|
||||
NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context);
|
||||
VOID FspWgroupInitialize(FSP_WGROUP *Wgroup);
|
||||
VOID FspWgroupIncrement(FSP_WGROUP *Wgroup);
|
||||
VOID FspWgroupDecrement(FSP_WGROUP *Wgroup);
|
||||
VOID FspWgroupSignalPermanently(FSP_WGROUP *Wgroup);
|
||||
NTSTATUS FspWgroupWait(FSP_WGROUP *Wgroup,
|
||||
KPROCESSOR_MODE WaitMode, BOOLEAN Alertable, PLARGE_INTEGER PTimeout);
|
||||
|
||||
#ifdef ALLOC_PRAGMA
|
||||
#pragma alloc_text(PAGE, FspIsNtDdiVersionAvailable)
|
||||
@ -176,6 +182,11 @@ NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context);
|
||||
#pragma alloc_text(PAGE, FspIrpHookReset)
|
||||
// !#pragma alloc_text(PAGE, FspIrpHookContext)
|
||||
// !#pragma alloc_text(PAGE, FspIrpHookNext)
|
||||
// !#pragma alloc_text(PAGE, FspWgroupInitialize)
|
||||
// !#pragma alloc_text(PAGE, FspWgroupIncrement)
|
||||
// !#pragma alloc_text(PAGE, FspWgroupDecrement)
|
||||
// !#pragma alloc_text(PAGE, FspWgroupSignalPermanently)
|
||||
// !#pragma alloc_text(PAGE, FspWgroupWait)
|
||||
#endif
|
||||
|
||||
static const LONG Delays[] =
|
||||
@ -597,6 +608,13 @@ NTSTATUS FspBufferUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation)
|
||||
if (0 == Length || 0 != Irp->AssociatedIrp.SystemBuffer)
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
if (KernelMode == Irp->RequestorMode &&
|
||||
(PUINT8)MM_SYSTEM_RANGE_START <= (PUINT8)Irp->UserBuffer)
|
||||
{
|
||||
Irp->AssociatedIrp.SystemBuffer = Irp->UserBuffer;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
PVOID SystemBuffer = FspAllocNonPagedExternal(Length);
|
||||
if (0 == SystemBuffer)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
@ -1488,3 +1506,56 @@ NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context)
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
VOID FspWgroupInitialize(FSP_WGROUP *Wgroup)
|
||||
{
|
||||
// !PAGED_CODE();
|
||||
|
||||
KeInitializeEvent(&Wgroup->Event, NotificationEvent, TRUE);
|
||||
Wgroup->Count = 0;
|
||||
KeInitializeSpinLock(&Wgroup->SpinLock);
|
||||
}
|
||||
|
||||
VOID FspWgroupIncrement(FSP_WGROUP *Wgroup)
|
||||
{
|
||||
// !PAGED_CODE();
|
||||
|
||||
KIRQL Irql;
|
||||
|
||||
KeAcquireSpinLock(&Wgroup->SpinLock, &Irql);
|
||||
if (0 <= Wgroup->Count && 1 == ++Wgroup->Count)
|
||||
KeClearEvent(&Wgroup->Event);
|
||||
KeReleaseSpinLock(&Wgroup->SpinLock, Irql);
|
||||
}
|
||||
|
||||
VOID FspWgroupDecrement(FSP_WGROUP *Wgroup)
|
||||
{
|
||||
// !PAGED_CODE();
|
||||
|
||||
KIRQL Irql;
|
||||
|
||||
KeAcquireSpinLock(&Wgroup->SpinLock, &Irql);
|
||||
if (0 < Wgroup->Count && 0 == --Wgroup->Count)
|
||||
KeSetEvent(&Wgroup->Event, 1, FALSE);
|
||||
KeReleaseSpinLock(&Wgroup->SpinLock, Irql);
|
||||
}
|
||||
|
||||
VOID FspWgroupSignalPermanently(FSP_WGROUP *Wgroup)
|
||||
{
|
||||
// !PAGED_CODE();
|
||||
|
||||
KIRQL Irql;
|
||||
|
||||
KeAcquireSpinLock(&Wgroup->SpinLock, &Irql);
|
||||
Wgroup->Count = -1;
|
||||
KeSetEvent(&Wgroup->Event, 1, FALSE);
|
||||
KeReleaseSpinLock(&Wgroup->SpinLock, Irql);
|
||||
}
|
||||
|
||||
NTSTATUS FspWgroupWait(FSP_WGROUP *Wgroup,
|
||||
KPROCESSOR_MODE WaitMode, BOOLEAN Alertable, PLARGE_INTEGER PTimeout)
|
||||
{
|
||||
// !PAGED_CODE();
|
||||
|
||||
return KeWaitForSingleObject(&Wgroup->Event, Executive, WaitMode, Alertable, PTimeout);
|
||||
}
|
||||
|
230
src/sys/volume.c
230
src/sys/volume.c
@ -50,6 +50,11 @@ NTSTATUS FspVolumeTransactFsext(
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||
NTSTATUS FspVolumeStop(
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||
NTSTATUS FspVolumeNotify(
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||
static NTSTATUS FspVolumeNotifyLock(
|
||||
PDEVICE_OBJECT FsvolDeviceObject);
|
||||
static WORKER_THREAD_ROUTINE FspVolumeNotifyWork;
|
||||
NTSTATUS FspVolumeWork(
|
||||
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||
|
||||
@ -68,6 +73,9 @@ NTSTATUS FspVolumeWork(
|
||||
#pragma alloc_text(PAGE, FspVolumeTransact)
|
||||
#pragma alloc_text(PAGE, FspVolumeTransactFsext)
|
||||
#pragma alloc_text(PAGE, FspVolumeStop)
|
||||
#pragma alloc_text(PAGE, FspVolumeNotify)
|
||||
#pragma alloc_text(PAGE, FspVolumeNotifyLock)
|
||||
#pragma alloc_text(PAGE, FspVolumeNotifyWork)
|
||||
#pragma alloc_text(PAGE, FspVolumeWork)
|
||||
#endif
|
||||
|
||||
@ -468,6 +476,11 @@ static VOID FspVolumeDeleteNoLock(
|
||||
FspMupUnregister(Globals->FsmupDeviceObject, FsvolDeviceObject);
|
||||
}
|
||||
|
||||
/* release the volume notify lock if held (so that any pending rename will abort) */
|
||||
FspWgroupSignalPermanently(&FsvolDeviceExtension->VolumeNotifyWgroup);
|
||||
if (1 == InterlockedCompareExchange(&FsvolDeviceExtension->VolumeNotifyLock, 0, 1))
|
||||
FspFsvolDeviceFileRenameReleaseOwner(FsvolDeviceObject, FsvolDeviceObject);
|
||||
|
||||
/* release the volume device object */
|
||||
FspDeviceDereference(FsvolDeviceObject);
|
||||
}
|
||||
@ -965,6 +978,7 @@ NTSTATUS FspVolumeTransact(
|
||||
if (0 != InternalBuffer)
|
||||
{
|
||||
ASSERT(FSP_FSCTL_TRANSACT_INTERNAL == ControlCode);
|
||||
*(PVOID *)OutputBuffer = 0;
|
||||
FspFree(InternalBuffer);
|
||||
}
|
||||
FspIopCompleteCanceledIrp(PendingIrp);
|
||||
@ -1053,6 +1067,222 @@ NTSTATUS FspVolumeStop(
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
WORK_QUEUE_ITEM WorkItem;
|
||||
PDEVICE_OBJECT FsvolDeviceObject;
|
||||
ULONG InputBufferLength;
|
||||
FSP_FSCTL_DECLSPEC_ALIGN UINT8 InputBuffer[];
|
||||
} FSP_VOLUME_NOTIFY_WORK_ITEM;
|
||||
|
||||
NTSTATUS FspVolumeNotify(
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
/*
|
||||
* FspVolumeNotify processing requires multiple locks that cannot be acquired
|
||||
* synchronously or deadlocks are possible. (The reason is that FspVolumeNotify
|
||||
* may be called by the user mode file system while servicing a request that
|
||||
* has already acquired one of the required locks.)
|
||||
*
|
||||
* For this reason FspVolumeNotify does its processing asynchronously; it ships
|
||||
* its payload as a work item to a system worker thread, which will perform the
|
||||
* actual processing. See FspVolumeNotifyWork.
|
||||
*/
|
||||
|
||||
ASSERT(IRP_MJ_FILE_SYSTEM_CONTROL == IrpSp->MajorFunction);
|
||||
ASSERT(IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction);
|
||||
ASSERT(FSP_FSCTL_NOTIFY == IrpSp->Parameters.FileSystemControl.FsControlCode);
|
||||
ASSERT(METHOD_NEITHER == (IrpSp->Parameters.FileSystemControl.FsControlCode & 3));
|
||||
ASSERT(0 != IrpSp->FileObject->FsContext2);
|
||||
|
||||
PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2;
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||
PVOID InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
|
||||
ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
|
||||
FSP_VOLUME_NOTIFY_WORK_ITEM *NotifyWorkItem = 0;
|
||||
NTSTATUS Result;
|
||||
|
||||
if (0 == InputBufferLength)
|
||||
return FspVolumeNotifyLock(FsvolDeviceObject);
|
||||
|
||||
if (!FspDeviceReference(FsvolDeviceObject))
|
||||
return STATUS_CANCELLED;
|
||||
|
||||
NotifyWorkItem = FspAllocNonPaged(
|
||||
FIELD_OFFSET(FSP_VOLUME_NOTIFY_WORK_ITEM, InputBuffer) + InputBufferLength);
|
||||
if (0 == NotifyWorkItem)
|
||||
{
|
||||
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ProbeForRead(InputBuffer, InputBufferLength, 1);
|
||||
RtlCopyMemory(NotifyWorkItem->InputBuffer, InputBuffer, InputBufferLength);
|
||||
NotifyWorkItem->InputBufferLength = InputBufferLength;
|
||||
}
|
||||
except (EXCEPTION_EXECUTE_HANDLER)
|
||||
{
|
||||
Result = GetExceptionCode();
|
||||
Result = FsRtlIsNtstatusExpected(Result) ? STATUS_INVALID_USER_BUFFER : Result;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ExInitializeWorkItem(&NotifyWorkItem->WorkItem, FspVolumeNotifyWork, NotifyWorkItem);
|
||||
NotifyWorkItem->FsvolDeviceObject = FsvolDeviceObject;
|
||||
|
||||
FspWgroupIncrement(&FsvolDeviceExtension->VolumeNotifyWgroup);
|
||||
ExQueueWorkItem(&NotifyWorkItem->WorkItem, DelayedWorkQueue);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
fail:
|
||||
if (0 != NotifyWorkItem)
|
||||
FspFree(NotifyWorkItem);
|
||||
|
||||
FspDeviceDereference(FsvolDeviceObject);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static NTSTATUS FspVolumeNotifyLock(
|
||||
PDEVICE_OBJECT FsvolDeviceObject)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
NTSTATUS Result;
|
||||
|
||||
if (!FspDeviceReference(FsvolDeviceObject))
|
||||
return STATUS_CANCELLED;
|
||||
|
||||
/*
|
||||
* Acquire the rename lock shared to disallow concurrent RENAME's.
|
||||
*
|
||||
* This guards against the race where a file that we want to invalidate
|
||||
* is being concurrently renamed to a different name. Thus we may think
|
||||
* that the file is not open and not invalidate its caches, whereas the
|
||||
* file has simply changed name.
|
||||
*/
|
||||
Result = STATUS_CANT_WAIT;
|
||||
if (FspFsvolDeviceFileRenameTryAcquireShared(FsvolDeviceObject))
|
||||
{
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||
|
||||
if (0 == InterlockedCompareExchange(&FsvolDeviceExtension->VolumeNotifyLock, 1, 0))
|
||||
{
|
||||
FspFsvolDeviceFileRenameSetOwner(FsvolDeviceObject, FsvolDeviceObject);
|
||||
Result = STATUS_SUCCESS;
|
||||
}
|
||||
else
|
||||
FspFsvolDeviceFileRenameRelease(FsvolDeviceObject);
|
||||
}
|
||||
|
||||
FspDeviceDereference(FsvolDeviceObject);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static VOID FspVolumeNotifyWork(PVOID NotifyWorkItem0)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
FsRtlEnterFileSystem();
|
||||
IoSetTopLevelIrp(0);
|
||||
|
||||
FSP_VOLUME_NOTIFY_WORK_ITEM *NotifyWorkItem = NotifyWorkItem0;
|
||||
PDEVICE_OBJECT FsvolDeviceObject = NotifyWorkItem->FsvolDeviceObject;
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||
FSP_FSCTL_NOTIFY_INFO *NotifyInfo = (PVOID)NotifyWorkItem->InputBuffer;
|
||||
PUINT8 NotifyInfoEnd = (PUINT8)NotifyInfo + NotifyWorkItem->InputBufferLength;
|
||||
ULONG NotifyInfoSize;
|
||||
UNICODE_STRING FileName = { 0 }, StreamPart = { 0 }, AbsFileName = { 0 }, FullFileName = { 0 };
|
||||
ULONG StreamType = FspFileNameStreamTypeNone;
|
||||
BOOLEAN Unlock = FALSE;
|
||||
NTSTATUS Result;
|
||||
|
||||
/* iterate over notify information and invalidate/notify each file */
|
||||
for (; (PUINT8)NotifyInfo + sizeof(NotifyInfo->Size) <= NotifyInfoEnd;
|
||||
NotifyInfo = (PVOID)((PUINT8)NotifyInfo + FSP_FSCTL_DEFAULT_ALIGN_UP(NotifyInfoSize)))
|
||||
{
|
||||
NotifyInfoSize = NotifyInfo->Size;
|
||||
|
||||
if (sizeof(FSP_FSCTL_NOTIFY_INFO) > NotifyInfoSize)
|
||||
{
|
||||
Unlock = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
FileName.Length =
|
||||
FileName.MaximumLength = (USHORT)(NotifyInfoSize - sizeof(FSP_FSCTL_NOTIFY_INFO));
|
||||
FileName.Buffer = NotifyInfo->FileNameBuf;
|
||||
if (sizeof(WCHAR) * 2/* not empty or root */ <= FileName.Length &&
|
||||
L'\\' == FileName.Buffer[FileName.Length / sizeof(WCHAR) - 1])
|
||||
FileName.Length -= sizeof(WCHAR);
|
||||
|
||||
if (!FspFileNameIsValid(&FileName, FsvolDeviceExtension->VolumeParams.MaxComponentLength,
|
||||
FsvolDeviceExtension->VolumeParams.NamedStreams ? &StreamPart : 0,
|
||||
&StreamType))
|
||||
continue;
|
||||
|
||||
if (sizeof(WCHAR) <= FileName.Length && L'\\' == FileName.Buffer[0])
|
||||
{
|
||||
/* absolute file names are used as-is */
|
||||
|
||||
AbsFileName = FileName;
|
||||
|
||||
FspFileNodeInvalidateCachesAndNotifyChangeByName(FsvolDeviceObject,
|
||||
&FileName, NotifyInfo->Filter, NotifyInfo->Action,
|
||||
TRUE);
|
||||
}
|
||||
else if (0 != AbsFileName.Length)
|
||||
{
|
||||
/* relative file names are considered relative to the last absolute file name */
|
||||
|
||||
if (0 == FullFileName.Buffer)
|
||||
{
|
||||
FullFileName.Buffer = FspAllocatePoolMustSucceed(
|
||||
NonPagedPool, FSP_FSCTL_TRANSACT_PATH_SIZEMAX, FSP_ALLOC_INTERNAL_TAG);
|
||||
FullFileName.MaximumLength = FSP_FSCTL_TRANSACT_PATH_SIZEMAX;
|
||||
}
|
||||
|
||||
FullFileName.Length = 0;
|
||||
Result = RtlAppendUnicodeStringToString(&FullFileName, &AbsFileName);
|
||||
if (NT_SUCCESS(Result))
|
||||
{
|
||||
if (sizeof(WCHAR) * 2/* not empty or root */ <= AbsFileName.Length)
|
||||
Result = RtlAppendUnicodeToString(&FullFileName, L"\\");
|
||||
}
|
||||
if (NT_SUCCESS(Result))
|
||||
Result = RtlAppendUnicodeStringToString(&FullFileName, &FileName);
|
||||
|
||||
if (NT_SUCCESS(Result))
|
||||
FspFileNodeInvalidateCachesAndNotifyChangeByName(FsvolDeviceObject,
|
||||
&FullFileName, NotifyInfo->Filter, NotifyInfo->Action,
|
||||
FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != FullFileName.Buffer)
|
||||
FspFree(FullFileName.Buffer);
|
||||
|
||||
FspFree(NotifyWorkItem);
|
||||
|
||||
FspWgroupDecrement(&FsvolDeviceExtension->VolumeNotifyWgroup);
|
||||
if (Unlock)
|
||||
{
|
||||
FspWgroupWait(&FsvolDeviceExtension->VolumeNotifyWgroup, KernelMode, FALSE, 0);
|
||||
if (1 == InterlockedCompareExchange(&FsvolDeviceExtension->VolumeNotifyLock, 0, 1))
|
||||
FspFsvolDeviceFileRenameReleaseOwner(FsvolDeviceObject, FsvolDeviceObject);
|
||||
}
|
||||
|
||||
FspDeviceDereference(FsvolDeviceObject);
|
||||
|
||||
FsRtlExitFileSystem();
|
||||
}
|
||||
|
||||
NTSTATUS FspVolumeWork(
|
||||
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
||||
{
|
||||
|
@ -347,6 +347,7 @@ STATUS_BAD_VALIDATION_CLASS ERROR_BAD_VALIDATION_CLASS
|
||||
STATUS_BAD_TOKEN_TYPE ERROR_BAD_TOKEN_TYPE
|
||||
STATUS_BAD_MASTER_BOOT_RECORD ERROR_INVALID_PARAMETER
|
||||
STATUS_NO_SECURITY_ON_OBJECT ERROR_NO_SECURITY_ON_OBJECT
|
||||
STATUS_CANT_WAIT ERROR_CANT_WAIT
|
||||
STATUS_CANT_ACCESS_DOMAIN_INFO ERROR_CANT_ACCESS_DOMAIN_INFO
|
||||
STATUS_INVALID_SERVER_STATE ERROR_INVALID_SERVER_STATE
|
||||
STATUS_INVALID_DOMAIN_STATE ERROR_INVALID_DOMAIN_STATE
|
||||
|
345
tools/parselog.nim
Normal file
345
tools/parselog.nim
Normal file
@ -0,0 +1,345 @@
|
||||
# @file parselog.nim
|
||||
#
|
||||
# parse WinFsp debug logs
|
||||
#
|
||||
# @copyright 2015-2020 Bill Zissimopoulos
|
||||
|
||||
import algorithm
|
||||
import macros
|
||||
import parseopt
|
||||
import parseutils
|
||||
import strformat
|
||||
import strscans
|
||||
import strutils
|
||||
import tables
|
||||
|
||||
type
|
||||
Req = ref object
|
||||
fsname: string
|
||||
tid: uint32
|
||||
irp: uint64
|
||||
op: string
|
||||
inform: string
|
||||
context: string
|
||||
args: seq[(string, string)]
|
||||
Rsp = ref object
|
||||
fsname: string
|
||||
tid: uint32
|
||||
irp: uint64
|
||||
op: string
|
||||
status: uint32
|
||||
inform: uint
|
||||
context: string
|
||||
args: seq[(string, string)]
|
||||
|
||||
proc parseAndAddArg(prefix: string, rest: var string, args: var seq[(string, string)]) =
|
||||
discard scanf(rest, ",$*", rest)
|
||||
discard scanf(rest, "$s$*", rest)
|
||||
var n, v: string
|
||||
if scanf(rest, "\"$*\"$*", v, rest):
|
||||
args.add((prefix & "", v))
|
||||
elif scanf(rest, "$w=\"$*\"$*", n, v, rest):
|
||||
args.add((prefix & n, v))
|
||||
elif scanf(rest, "$w={$*}$*", n, v, rest):
|
||||
while "" != v:
|
||||
parseAndAddArg(n & ".", v, args)
|
||||
elif scanf(rest, "$w=$*,$*", n, v, rest):
|
||||
args.add((prefix & n, v))
|
||||
elif scanf(rest, "$w=$*$.", n, v):
|
||||
rest = ""
|
||||
args.add((prefix & n, v))
|
||||
elif scanf(rest, "$*,$*", v, rest):
|
||||
args.add((prefix & "", v))
|
||||
else:
|
||||
v = rest
|
||||
rest = ""
|
||||
args.add((prefix & "", v))
|
||||
|
||||
proc parseArgs(rest: var string): seq[(string, string)] =
|
||||
while "" != rest:
|
||||
parseAndAddArg("", rest, result)
|
||||
|
||||
proc parseReq(op, rest: string): Req =
|
||||
result = Req(op: op)
|
||||
var rest = rest
|
||||
var inform: string
|
||||
if scanf(rest, "[$+]$s$*", inform, rest):
|
||||
result.inform = inform
|
||||
var c0, c1: uint64
|
||||
if scanf(rest, "${parseHex[uint64]}:${parseHex[uint64]}$*", c0, c1, rest):
|
||||
result.context = toHex(c0) & ":" & toHex(c1)
|
||||
result.args = parseArgs(rest)
|
||||
|
||||
proc parseRsp(op, rest: string): Rsp =
|
||||
result = Rsp(op: op)
|
||||
var rest = rest
|
||||
var status: uint32
|
||||
var inform: uint
|
||||
if scanf(rest, "IoStatus=${parseHex[uint32]}[${parseUint}]$s$*", status, inform, rest):
|
||||
result.status = status
|
||||
result.inform = inform
|
||||
var c0, c1: uint64
|
||||
if scanf(rest, "UserContext=${parseHex[uint64]}:${parseHex[uint64]}$*", c0, c1, rest):
|
||||
result.context = toHex(c0) & ":" & toHex(c1)
|
||||
result.args = parseArgs(rest)
|
||||
|
||||
proc parseLog(path: string, processReq: proc(req: Req), processRsp: proc(rsp: Rsp)) =
|
||||
let file = open(path)
|
||||
defer: file.close()
|
||||
var lineno = 0
|
||||
try:
|
||||
for line in lines file:
|
||||
inc lineno
|
||||
var fsname, dir, op, rest: string
|
||||
var tid: uint32
|
||||
var irp: uint64
|
||||
var req: Req
|
||||
var rsp: Rsp
|
||||
if scanf(line, "$+[TID=${parseHex[uint32]}]:$s${parseHex[uint64]}:$s$+ $*",
|
||||
fsname, tid, irp, op, rest):
|
||||
dir = op[0..1]
|
||||
op = op[2..^1]
|
||||
case dir
|
||||
of ">>":
|
||||
req = parseReq(op, rest)
|
||||
req.fsname = fsname
|
||||
req.tid = tid
|
||||
req.irp = irp
|
||||
processReq(req)
|
||||
of "<<":
|
||||
rsp = parseRsp(op, rest)
|
||||
rsp.fsname = fsname
|
||||
rsp.tid = tid
|
||||
rsp.irp = irp
|
||||
processRsp(rsp)
|
||||
else:
|
||||
continue
|
||||
except:
|
||||
echo &"An exception has occurred while parsing file {path} line {lineno}"
|
||||
raise
|
||||
|
||||
type
|
||||
Stat = ref object
|
||||
ototal: int # open total
|
||||
omulti: int # multiplicate open total
|
||||
oerror: int # open error total
|
||||
rtotal: int # read total
|
||||
rnoaln: int # non-aligned read total
|
||||
rbytes: uint64 # read bytes
|
||||
rerror: int # read error total
|
||||
wtotal: int # write total
|
||||
wnoaln: int # non-aligned write total
|
||||
wbytes: uint64 # write bytes
|
||||
werror: int # write error total
|
||||
dtotal: int # query directory total
|
||||
dbytes: uint64 # query directory bytes
|
||||
derror: int # query directory error total
|
||||
ptotal: int # query directory w/ pattern total
|
||||
pbytes: uint64 # query directory w/ pattern bytes
|
||||
perror: int # query directory w/ pattern error total
|
||||
ocount: int # current open count
|
||||
var
|
||||
reqtab = newTable[uint64, Req]()
|
||||
filetab = newTable[string, string]()
|
||||
stattab = newOrderedTable[string, Stat]()
|
||||
aggr = Stat()
|
||||
|
||||
proc getArg(args: seq[(string, string)], name: string): string =
|
||||
for n, v in items(args):
|
||||
if name == n:
|
||||
return v
|
||||
|
||||
proc processReq(req: Req) =
|
||||
reqtab[req.irp] = req
|
||||
case req.op
|
||||
of "Close":
|
||||
var filename: string
|
||||
if filetab.pop(req.context, filename):
|
||||
var stat = stattab.mgetOrPut(filename, Stat())
|
||||
stat.ocount -= 1
|
||||
|
||||
proc processRsp(rsp: Rsp) =
|
||||
var req: Req
|
||||
if reqtab.pop(rsp.irp, req):
|
||||
doAssert req.op == rsp.op
|
||||
doAssert req.irp == rsp.irp
|
||||
case req.op
|
||||
of "Create":
|
||||
var filename = getArg(req.args, "")
|
||||
if "" != filename:
|
||||
if 0 == rsp.status:
|
||||
filetab[rsp.context] = filename
|
||||
var stat = stattab.mgetOrPut(filename, Stat())
|
||||
stat.ototal += 1
|
||||
aggr.ototal += 1
|
||||
stat.ocount += 1
|
||||
if 2 == stat.ocount:
|
||||
stat.omulti += 1
|
||||
aggr.omulti += 1
|
||||
else:
|
||||
var stat = stattab.mgetOrPut(filename, Stat())
|
||||
stat.oerror += 1
|
||||
aggr.oerror += 1
|
||||
of "Read":
|
||||
var filename = filetab[req.context]
|
||||
var stat = stattab.mgetOrPut(filename, Stat())
|
||||
if 0 == rsp.status or 0xC0000011u32 == rsp.status:
|
||||
var oarg = getArg(req.args, "Offset")
|
||||
var larg = getArg(req.args, "Length")
|
||||
var hi, lo: uint32
|
||||
var offset: uint64
|
||||
var length: uint
|
||||
if scanf(oarg, "${parseHex[uint32]}:${parseHex[uint32]}", hi, lo):
|
||||
offset = uint64(hi) * 0x100000000u64 + uint64(lo)
|
||||
discard scanf(larg, "${parseUint}", length)
|
||||
stat.rtotal += 1
|
||||
stat.rbytes += rsp.inform
|
||||
aggr.rtotal += 1
|
||||
aggr.rbytes += rsp.inform
|
||||
if 0 != offset mod 4096 or 0 != length mod 4096:
|
||||
stat.rnoaln += 1
|
||||
aggr.rnoaln += 1
|
||||
else:
|
||||
stat.rerror += 1
|
||||
aggr.rerror += 1
|
||||
of "Write":
|
||||
var filename = filetab[req.context]
|
||||
var stat = stattab.mgetOrPut(filename, Stat())
|
||||
if 0 == rsp.status:
|
||||
var oarg = getArg(req.args, "Offset")
|
||||
var larg = getArg(req.args, "Length")
|
||||
var hi, lo: uint32
|
||||
var offset: uint64
|
||||
var length: uint
|
||||
if scanf(oarg, "${parseHex[uint32]}:${parseHex[uint32]}", hi, lo):
|
||||
offset = uint64(hi) * 0x100000000u64 + uint64(lo)
|
||||
discard scanf(larg, "${parseUint}", length)
|
||||
stat.wtotal += 1
|
||||
stat.wbytes += rsp.inform
|
||||
aggr.wtotal += 1
|
||||
aggr.wbytes += rsp.inform
|
||||
if 0 != offset mod 4096 or 0 != length mod 4096:
|
||||
stat.wnoaln += 1
|
||||
aggr.wnoaln += 1
|
||||
else:
|
||||
stat.werror += 1
|
||||
aggr.werror += 1
|
||||
of "QueryDirectory":
|
||||
var filename = filetab[req.context]
|
||||
var stat = stattab.mgetOrPut(filename, Stat())
|
||||
var pattern = getArg(req.args, "Pattern")
|
||||
if "NULL" == pattern:
|
||||
if 0 == rsp.status:
|
||||
stat.dtotal += 1
|
||||
stat.dbytes += rsp.inform
|
||||
aggr.dtotal += 1
|
||||
aggr.dbytes += rsp.inform
|
||||
else:
|
||||
stat.derror += 1
|
||||
aggr.derror += 1
|
||||
else:
|
||||
if 0 == rsp.status:
|
||||
stat.ptotal += 1
|
||||
stat.pbytes += rsp.inform
|
||||
aggr.ptotal += 1
|
||||
aggr.pbytes += rsp.inform
|
||||
else:
|
||||
stat.perror += 1
|
||||
aggr.perror += 1
|
||||
|
||||
macro identName(n: untyped): untyped =
|
||||
result = n.strVal.newLit
|
||||
|
||||
template dumpstat(F: untyped) =
|
||||
stattab.sort(proc (x, y: (string, Stat)): int =
|
||||
cmp(x[1].F, y[1].F), SortOrder.Descending)
|
||||
var width, rows = 0
|
||||
for filename, stat in stattab.pairs:
|
||||
if 0 == width:
|
||||
var s = identName(F).toUpperAscii()
|
||||
width = len($aggr.F)
|
||||
if width < len(s):
|
||||
width = len(s)
|
||||
var f: string
|
||||
formatValue(f, s, ">" & $width)
|
||||
echo f, " PER% FILENAME"
|
||||
var c0, c1: string
|
||||
formatValue(c0, stat.F, $width)
|
||||
if 0 != aggr.F:
|
||||
formatValue(c1, 100.0 * float(stat.F) / float(aggr.F), "5.1f")
|
||||
else:
|
||||
c1 = " "
|
||||
echo c0, " ", c1, " ", filename
|
||||
inc rows
|
||||
if opttop == rows:
|
||||
break
|
||||
var c0: string
|
||||
formatValue(c0, aggr.F, $width)
|
||||
echo c0, " 100.0 TOTAL"
|
||||
|
||||
proc main =
|
||||
var filenames: seq[string]
|
||||
var optstat: seq[string]
|
||||
var opttop = 0
|
||||
for kind, key, val in getopt(shortNoVal = {'Z'}, longNoVal = @["Zoo"]):
|
||||
case kind
|
||||
of cmdShortOption, cmdLongOption:
|
||||
case key
|
||||
of "stat":
|
||||
optstat.add(val)
|
||||
of "n":
|
||||
opttop = parseInt(val)
|
||||
of cmdArgument:
|
||||
filenames.add(key)
|
||||
else:
|
||||
discard
|
||||
if 0 == len(optstat):
|
||||
optstat.add("ototal")
|
||||
|
||||
if 0 == len(filenames):
|
||||
stderr.writeLine("usage: parselog [-nNN] [--stat ototal|rtotal|wtotal|dtotal|...] file...")
|
||||
quit(2)
|
||||
|
||||
for filename in filenames:
|
||||
parseLog filename, processReq, processRsp
|
||||
|
||||
for s in optstat:
|
||||
case s
|
||||
of "ototal":
|
||||
dumpstat ototal
|
||||
of "omulti":
|
||||
dumpstat omulti
|
||||
of "oerror":
|
||||
dumpstat oerror
|
||||
of "rtotal":
|
||||
dumpstat rtotal
|
||||
of "rnoaln":
|
||||
dumpstat rnoaln
|
||||
of "rbytes":
|
||||
dumpstat rbytes
|
||||
of "rerror":
|
||||
dumpstat rerror
|
||||
of "wtotal":
|
||||
dumpstat wtotal
|
||||
of "wnoaln":
|
||||
dumpstat wnoaln
|
||||
of "wbytes":
|
||||
dumpstat wbytes
|
||||
of "werror":
|
||||
dumpstat werror
|
||||
of "dtotal":
|
||||
dumpstat dtotal
|
||||
of "dbytes":
|
||||
dumpstat dbytes
|
||||
of "derror":
|
||||
dumpstat derror
|
||||
of "ptotal":
|
||||
dumpstat ptotal
|
||||
of "pbytes":
|
||||
dumpstat pbytes
|
||||
of "perror":
|
||||
dumpstat perror
|
||||
echo ""
|
||||
|
||||
when isMainModule:
|
||||
main()
|
@ -37,6 +37,7 @@ set dfl_tests=^
|
||||
winfsp-tests-x64-mountpoint-dir-case-sensitive ^
|
||||
winfsp-tests-x64-no-traverse ^
|
||||
winfsp-tests-x64-oplock ^
|
||||
winfsp-tests-x64-notify ^
|
||||
winfsp-tests-x64-external ^
|
||||
winfsp-tests-x64-external-share ^
|
||||
fsx-memfs-x64-disk ^
|
||||
@ -56,6 +57,7 @@ set dfl_tests=^
|
||||
winfsp-tests-x86-mountpoint-dir-case-sensitive ^
|
||||
winfsp-tests-x86-no-traverse ^
|
||||
winfsp-tests-x86-oplock ^
|
||||
winfsp-tests-x86-notify ^
|
||||
winfsp-tests-x86-external ^
|
||||
winfsp-tests-x86-external-share ^
|
||||
fsx-memfs-x86-disk ^
|
||||
@ -214,6 +216,11 @@ winfsp-tests-x64 --oplock=filter --resilient * +ea*
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
:winfsp-tests-x64-notify
|
||||
winfsp-tests-x64 --notify --resilient * +ea*
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
:winfsp-tests-x86
|
||||
winfsp-tests-x86 +*
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
@ -254,6 +261,11 @@ winfsp-tests-x86 --oplock=filter --resilient * +ea*
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
:winfsp-tests-x86-notify
|
||||
winfsp-tests-x86 --notify --resilient * +ea*
|
||||
if !ERRORLEVEL! neq 0 goto fail
|
||||
exit /b 0
|
||||
|
||||
:winfsp-tests-x64-external
|
||||
M:
|
||||
fltmc instances -v M: | findstr aswSnx >nul
|
||||
|
@ -10,9 +10,9 @@ cygfuse3: memfs-cygfuse3
|
||||
winfsp-fuse3: memfs-winfsp-fuse3
|
||||
|
||||
memfs-cygfuse3: memfs-fuse3.cpp
|
||||
g++ $^ -o $@ -g -Wall `pkg-config fuse3 --cflags --libs`
|
||||
g++ $^ -o $@ -g -Wall -std=gnu++17 `pkg-config fuse3 --cflags --libs`
|
||||
|
||||
memfs-winfsp-fuse3: export PKG_CONFIG_PATH=$(PWD)/winfsp.install/lib
|
||||
memfs-winfsp-fuse3: memfs-fuse3.cpp
|
||||
ln -nsf "`regtool --wow32 get '/HKLM/Software/WinFsp/InstallDir' | cygpath -au -f -`" winfsp.install
|
||||
g++ $^ -o $@ -g -Wall `pkg-config fuse3 --cflags --libs`
|
||||
g++ $^ -o $@ -g -Wall -std=gnu++17 `pkg-config fuse3 --cflags --libs`
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#define WINFSP_TESTS_NO_HOOKS
|
||||
#include "winfsp-tests.h"
|
||||
#include <winfsp/winfsp.h>
|
||||
|
||||
#define FILENAMEBUF_SIZE 1024
|
||||
|
||||
@ -107,6 +108,75 @@ static VOID PrepareFileName(PCWSTR FileName, PWSTR FileNameBuf)
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
HANDLE VolumeHandle;
|
||||
FSP_FSCTL_NOTIFY_INFO *NotifyInfo;
|
||||
} MAYBE_NOTIFY_DATA;
|
||||
|
||||
static DWORD WINAPI MaybeNotifyRoutine(PVOID Context)
|
||||
{
|
||||
MAYBE_NOTIFY_DATA *NotifyData = Context;
|
||||
|
||||
/*
|
||||
* The supplied VolumeHandle may be invalid or refer to the wrong object.
|
||||
* This is ok because:
|
||||
*
|
||||
* - If the VolumeHandle is invalid, Windows will catch it and will fail the operation.
|
||||
* - If the VolumeHandle refers to the wrong object, the FspFsctlNotify "should" fail
|
||||
* because of an unknown DeviceIoControl code.
|
||||
* - If the VolumeHandle refers to the wrong file system, it is still ok if we send an
|
||||
* extraneous notify.
|
||||
*/
|
||||
|
||||
FspFsctlNotify(NotifyData->VolumeHandle,
|
||||
NotifyData->NotifyInfo, NotifyData->NotifyInfo->Size);
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, NotifyData->NotifyInfo);
|
||||
HeapFree(GetProcessHeap(), 0, NotifyData);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static VOID MaybeNotify(PWSTR FileName, ULONG Filter, ULONG Action)
|
||||
{
|
||||
static WCHAR DevicePrefix[] =
|
||||
L"\\\\?\\GLOBALROOT\\Device\\Volume{01234567-0123-0123-0101-010101010101}";
|
||||
static WCHAR MemfsSharePrefix[] =
|
||||
L"\\\\memfs\\share";
|
||||
MAYBE_NOTIFY_DATA *NotifyData;
|
||||
FSP_FSCTL_NOTIFY_INFO *NotifyInfo;
|
||||
size_t L;
|
||||
|
||||
if (!OptNotify || OptExternal || 0 == memfs_handle)
|
||||
return;
|
||||
|
||||
if (0 == wcsncmp(FileName, DevicePrefix, wcschr(DevicePrefix, L'{') - DevicePrefix))
|
||||
FileName += wcslen(DevicePrefix);
|
||||
else if (0 == mywcscmp(
|
||||
FileName, (int)wcslen(MemfsSharePrefix), MemfsSharePrefix, (int)wcslen(MemfsSharePrefix)))
|
||||
FileName += wcslen(MemfsSharePrefix);
|
||||
else
|
||||
return;
|
||||
|
||||
L = wcslen(FileName);
|
||||
NotifyData = HeapAlloc(GetProcessHeap(), 0, sizeof *NotifyData);
|
||||
NotifyInfo = HeapAlloc(GetProcessHeap(), 0, sizeof *NotifyInfo + L * sizeof(WCHAR));
|
||||
if (0 == NotifyData || 0 == NotifyInfo)
|
||||
ABORT("cannot malloc notify data");
|
||||
|
||||
NotifyInfo->Size = (UINT16)(sizeof *NotifyInfo + L * sizeof(WCHAR));
|
||||
NotifyInfo->Filter = Filter;
|
||||
NotifyInfo->Action = Action;
|
||||
memcpy(NotifyInfo->FileNameBuf, FileName, L * sizeof(WCHAR));
|
||||
|
||||
NotifyData->VolumeHandle = memfs_handle;
|
||||
NotifyData->NotifyInfo = NotifyInfo;
|
||||
|
||||
if (!QueueUserWorkItem(MaybeNotifyRoutine, NotifyData, 0))
|
||||
ABORT("cannot queue notify data");
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
HANDLE File;
|
||||
@ -210,6 +280,9 @@ HANDLE WINAPI HookCreateFileW(
|
||||
|
||||
PrepareFileName(lpFileName, FileNameBuf);
|
||||
|
||||
MaybeNotify(FileNameBuf,
|
||||
FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
|
||||
|
||||
MaybeRequestOplock(FileNameBuf);
|
||||
|
||||
MaybeAdjustTraversePrivilege(FALSE);
|
||||
@ -241,6 +314,9 @@ BOOL WINAPI HookSetFileAttributesW(
|
||||
|
||||
PrepareFileName(lpFileName, FileNameBuf);
|
||||
|
||||
MaybeNotify(FileNameBuf,
|
||||
FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
|
||||
|
||||
MaybeAdjustTraversePrivilege(FALSE);
|
||||
Success = SetFileAttributesW(FileNameBuf, dwFileAttributes);
|
||||
MaybeAdjustTraversePrivilege(TRUE);
|
||||
@ -256,6 +332,9 @@ BOOL WINAPI HookCreateDirectoryW(
|
||||
|
||||
PrepareFileName(lpPathName, FileNameBuf);
|
||||
|
||||
MaybeNotify(FileNameBuf,
|
||||
FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
|
||||
|
||||
MaybeAdjustTraversePrivilege(FALSE);
|
||||
Success = CreateDirectoryW(FileNameBuf, lpSecurityAttributes);
|
||||
MaybeAdjustTraversePrivilege(TRUE);
|
||||
@ -270,6 +349,9 @@ BOOL WINAPI HookDeleteFileW(
|
||||
|
||||
PrepareFileName(lpFileName, FileNameBuf);
|
||||
|
||||
MaybeNotify(FileNameBuf,
|
||||
FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
|
||||
|
||||
MaybeRequestOplock(FileNameBuf);
|
||||
|
||||
MaybeAdjustTraversePrivilege(FALSE);
|
||||
@ -287,6 +369,9 @@ BOOL WINAPI HookRemoveDirectoryW(
|
||||
|
||||
PrepareFileName(lpPathName, FileNameBuf);
|
||||
|
||||
MaybeNotify(FileNameBuf,
|
||||
FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
|
||||
|
||||
MaybeAdjustTraversePrivilege(FALSE);
|
||||
Success = (OptResilient ? ResilientRemoveDirectoryW : RemoveDirectoryW)(
|
||||
FileNameBuf);
|
||||
@ -306,6 +391,13 @@ BOOL WINAPI HookMoveFileExW(
|
||||
PrepareFileName(lpExistingFileName, OldFileNameBuf);
|
||||
PrepareFileName(lpNewFileName, NewFileNameBuf);
|
||||
|
||||
MaybeNotify(OldFileNameBuf,
|
||||
FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
|
||||
if (OptCaseInsensitive ?
|
||||
_wcsicmp(OldFileNameBuf, NewFileNameBuf) : wcscmp(OldFileNameBuf, NewFileNameBuf))
|
||||
MaybeNotify(NewFileNameBuf,
|
||||
FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
|
||||
|
||||
MaybeRequestOplock(OldFileNameBuf);
|
||||
if (OptCaseInsensitive ?
|
||||
_wcsicmp(OldFileNameBuf, NewFileNameBuf) : wcscmp(OldFileNameBuf, NewFileNameBuf))
|
||||
@ -326,6 +418,9 @@ HANDLE WINAPI HookFindFirstFileW(
|
||||
|
||||
PrepareFileName(lpFileName, FileNameBuf);
|
||||
|
||||
MaybeNotify(FileNameBuf,
|
||||
FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
|
||||
|
||||
MaybeAdjustTraversePrivilege(FALSE);
|
||||
Handle = FindFirstFileW(FileNameBuf, lpFindFileData);
|
||||
MaybeAdjustTraversePrivilege(TRUE);
|
||||
@ -343,6 +438,9 @@ HANDLE WINAPI HookFindFirstStreamW(
|
||||
|
||||
PrepareFileName(lpFileName, FileNameBuf);
|
||||
|
||||
MaybeNotify(FileNameBuf,
|
||||
FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
|
||||
|
||||
MaybeAdjustTraversePrivilege(FALSE);
|
||||
Handle = FindFirstStreamW(FileNameBuf, InfoLevel, lpFindStreamData, dwFlags);
|
||||
MaybeAdjustTraversePrivilege(TRUE);
|
||||
@ -426,6 +524,9 @@ BOOL WINAPI HookSetCurrentDirectoryW(
|
||||
|
||||
PrepareFileName(lpPathName, FileNameBuf);
|
||||
|
||||
MaybeNotify(FileNameBuf,
|
||||
FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
|
||||
|
||||
MaybeAdjustTraversePrivilege(FALSE);
|
||||
Success = SetCurrentDirectoryW(FileNameBuf);
|
||||
MaybeAdjustTraversePrivilege(TRUE);
|
||||
@ -449,6 +550,9 @@ BOOL WINAPI HookCreateProcessW(
|
||||
|
||||
PrepareFileName(lpApplicationName, FileNameBuf);
|
||||
|
||||
MaybeNotify(FileNameBuf,
|
||||
FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE, FILE_ACTION_MODIFIED);
|
||||
|
||||
MaybeAdjustTraversePrivilege(FALSE);
|
||||
Success = CreateProcessW(FileNameBuf,
|
||||
lpCommandLine, /* we should probably change this as well */
|
||||
|
@ -1025,6 +1025,76 @@ void rename_test(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void rename_backslash_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
|
||||
{
|
||||
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
|
||||
|
||||
HANDLE Handle;
|
||||
BOOL Success;
|
||||
WCHAR Dir1Path[MAX_PATH];
|
||||
WCHAR Dir2Path[MAX_PATH];
|
||||
WCHAR File0Path[MAX_PATH];
|
||||
WCHAR File1Path[MAX_PATH];
|
||||
|
||||
StringCbPrintfW(Dir1Path, sizeof Dir1Path, L"%s%s\\dir1",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||
|
||||
StringCbPrintfW(Dir2Path, sizeof Dir2Path, L"%s%s\\dir2\\",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||
|
||||
StringCbPrintfW(File0Path, sizeof File0Path, L"%s%s\\dir1\\file0",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||
|
||||
StringCbPrintfW(File1Path, sizeof File1Path, L"%s%s\\dir1\\file1\\",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||
|
||||
Success = CreateDirectoryW(Dir1Path, 0);
|
||||
ASSERT(Success);
|
||||
|
||||
Handle = CreateFileW(File0Path,
|
||||
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != Handle);
|
||||
CloseHandle(Handle);
|
||||
|
||||
Success = MoveFileExW(File0Path, File1Path, 0);
|
||||
ASSERT(Success);
|
||||
|
||||
Success = MoveFileExW(Dir1Path, Dir2Path, 0);
|
||||
ASSERT(Success);
|
||||
|
||||
StringCbPrintfW(File1Path, sizeof File1Path, L"%s%s\\dir2\\file1",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||
|
||||
Success = DeleteFileW(File1Path);
|
||||
ASSERT(Success);
|
||||
|
||||
Success = RemoveDirectoryW(Dir2Path);
|
||||
ASSERT(Success);
|
||||
|
||||
memfs_stop(memfs);
|
||||
}
|
||||
|
||||
void rename_backslash_test(void)
|
||||
{
|
||||
if (NtfsTests)
|
||||
{
|
||||
WCHAR DirBuf[MAX_PATH];
|
||||
GetTestDirectory(DirBuf);
|
||||
rename_backslash_dotest(-1, DirBuf, 0);
|
||||
}
|
||||
if (WinFspDiskTests)
|
||||
{
|
||||
rename_backslash_dotest(MemfsDisk, 0, 0);
|
||||
rename_backslash_dotest(MemfsDisk, 0, 1000);
|
||||
}
|
||||
if (WinFspNetTests)
|
||||
{
|
||||
rename_backslash_dotest(MemfsNet, L"\\\\memfs\\share", 0);
|
||||
rename_backslash_dotest(MemfsNet, L"\\\\memfs\\share", 1000);
|
||||
}
|
||||
}
|
||||
|
||||
static void rename_open_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
|
||||
{
|
||||
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
|
||||
@ -1982,6 +2052,7 @@ void info_tests(void)
|
||||
TEST(delete_mmap_test);
|
||||
TEST(delete_standby_test);
|
||||
TEST(rename_test);
|
||||
TEST(rename_backslash_test);
|
||||
TEST(rename_open_test);
|
||||
TEST(rename_caseins_test);
|
||||
if (!OptShareName)
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "winfsp-tests.h"
|
||||
|
||||
int memfs_running;
|
||||
HANDLE memfs_handle;
|
||||
|
||||
void *memfs_start_ex(ULONG Flags, ULONG FileInfoTimeout)
|
||||
{
|
||||
@ -66,6 +67,7 @@ void *memfs_start_ex(ULONG Flags, ULONG FileInfoTimeout)
|
||||
ASSERT(NT_SUCCESS(Result));
|
||||
|
||||
memfs_running = 1;
|
||||
memfs_handle = MemfsFileSystem(Memfs)->VolumeHandle;
|
||||
|
||||
return Memfs;
|
||||
}
|
||||
@ -77,6 +79,7 @@ void *memfs_start(ULONG Flags)
|
||||
|
||||
void memfs_stop(void *data)
|
||||
{
|
||||
memfs_handle = 0;
|
||||
memfs_running = 0;
|
||||
|
||||
if (0 == data)
|
||||
|
460
tst/winfsp-tests/notify-test.c
Normal file
460
tst/winfsp-tests/notify-test.c
Normal file
@ -0,0 +1,460 @@
|
||||
/**
|
||||
* @file notify-test.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 <tlib/testsuite.h>
|
||||
#include <process.h>
|
||||
#include <strsafe.h>
|
||||
#include "memfs.h"
|
||||
|
||||
#include "winfsp-tests.h"
|
||||
|
||||
static
|
||||
void notify_abandon_dotest(ULONG Flags)
|
||||
{
|
||||
void *memfs = memfs_start(Flags);
|
||||
FSP_FILE_SYSTEM *FileSystem = MemfsFileSystem(memfs);
|
||||
NTSTATUS Result;
|
||||
|
||||
Result = FspFsctlNotify(FileSystem->VolumeHandle, 0, 0);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
|
||||
Result = FspFsctlNotify(FileSystem->VolumeHandle, 0, 0);
|
||||
ASSERT(STATUS_CANT_WAIT == Result);
|
||||
|
||||
memfs_stop(memfs);
|
||||
}
|
||||
|
||||
static
|
||||
void notify_abandon_test(void)
|
||||
{
|
||||
if (WinFspDiskTests)
|
||||
notify_abandon_dotest(MemfsDisk);
|
||||
if (WinFspNetTests)
|
||||
notify_abandon_dotest(MemfsNet);
|
||||
}
|
||||
|
||||
static
|
||||
unsigned __stdcall notify_abandon_rename_dotest_thread(void *FilePath)
|
||||
{
|
||||
FspDebugLog(__FUNCTION__ ": \"%S\"\n", FilePath);
|
||||
|
||||
WCHAR NewFilePath[MAX_PATH];
|
||||
HANDLE Handle;
|
||||
BOOL Success;
|
||||
|
||||
Handle = CreateFileW(FilePath,
|
||||
GENERIC_READ | GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
if (INVALID_HANDLE_VALUE == Handle)
|
||||
return GetLastError();
|
||||
CloseHandle(Handle);
|
||||
|
||||
StringCbPrintfW(NewFilePath, sizeof NewFilePath, L"%s.new", FilePath);
|
||||
Success = MoveFileExW(FilePath, NewFilePath, 0);
|
||||
|
||||
return Success ? 0 : GetLastError();
|
||||
}
|
||||
|
||||
static
|
||||
void notify_abandon_rename_dotest(ULONG Flags, PWSTR Prefix)
|
||||
{
|
||||
void *memfs = memfs_start(Flags);
|
||||
FSP_FILE_SYSTEM *FileSystem = MemfsFileSystem(memfs);
|
||||
WCHAR FilePath[MAX_PATH];
|
||||
HANDLE Thread;
|
||||
DWORD ExitCode;
|
||||
NTSTATUS Result;
|
||||
|
||||
Result = FspFsctlNotify(FileSystem->VolumeHandle, 0, 0);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
|
||||
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\file0",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||
|
||||
Thread = (HANDLE)_beginthreadex(0, 0, notify_abandon_rename_dotest_thread, FilePath, 0, 0);
|
||||
ASSERT(0 != Thread);
|
||||
|
||||
Sleep(1000);
|
||||
|
||||
memfs_stop(memfs);
|
||||
|
||||
WaitForSingleObject(Thread, INFINITE);
|
||||
GetExitCodeThread(Thread, &ExitCode);
|
||||
CloseHandle(Thread);
|
||||
ASSERT(ERROR_OPERATION_ABORTED == ExitCode);
|
||||
}
|
||||
|
||||
static
|
||||
void notify_abandon_rename_test(void)
|
||||
{
|
||||
if (WinFspDiskTests)
|
||||
notify_abandon_rename_dotest(MemfsDisk, 0);
|
||||
if (WinFspNetTests)
|
||||
notify_abandon_rename_dotest(MemfsNet, L"\\\\memfs\\share");
|
||||
}
|
||||
|
||||
static
|
||||
void notify_timeout_dotest(ULONG Flags)
|
||||
{
|
||||
void *memfs = memfs_start(Flags);
|
||||
FSP_FILE_SYSTEM *FileSystem = MemfsFileSystem(memfs);
|
||||
NTSTATUS Result;
|
||||
|
||||
Result = FspFsctlNotify(FileSystem->VolumeHandle, 0, 0);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
|
||||
Result = FspFileSystemNotifyBegin(FileSystem, 0);
|
||||
ASSERT(STATUS_CANT_WAIT == Result);
|
||||
|
||||
Result = FspFileSystemNotifyBegin(FileSystem, 9);
|
||||
ASSERT(STATUS_CANT_WAIT == Result);
|
||||
|
||||
Result = FspFileSystemNotifyBegin(FileSystem, 10);
|
||||
ASSERT(STATUS_CANT_WAIT == Result);
|
||||
|
||||
Result = FspFileSystemNotifyBegin(FileSystem, 11);
|
||||
ASSERT(STATUS_CANT_WAIT == Result);
|
||||
|
||||
Result = FspFileSystemNotifyBegin(FileSystem, 20);
|
||||
ASSERT(STATUS_CANT_WAIT == Result);
|
||||
|
||||
Result = FspFileSystemNotifyBegin(FileSystem, 1000);
|
||||
ASSERT(STATUS_CANT_WAIT == Result);
|
||||
|
||||
memfs_stop(memfs);
|
||||
}
|
||||
|
||||
static
|
||||
void notify_timeout_test(void)
|
||||
{
|
||||
if (WinFspDiskTests)
|
||||
notify_timeout_dotest(MemfsDisk);
|
||||
if (WinFspNetTests)
|
||||
notify_timeout_dotest(MemfsNet);
|
||||
}
|
||||
|
||||
static
|
||||
void notify_change_dotest(ULONG Flags)
|
||||
{
|
||||
void *memfs = memfs_start(Flags);
|
||||
FSP_FILE_SYSTEM *FileSystem = MemfsFileSystem(memfs);
|
||||
union
|
||||
{
|
||||
FSP_FSCTL_NOTIFY_INFO V;
|
||||
UINT8 B[1024];
|
||||
} Buffer;
|
||||
ULONG Length = 0;
|
||||
union
|
||||
{
|
||||
FSP_FSCTL_NOTIFY_INFO V;
|
||||
UINT8 B[sizeof(FSP_FSCTL_NOTIFY_INFO) + MAX_PATH * sizeof(WCHAR)];
|
||||
} NotifyInfo;
|
||||
PWSTR FileName;
|
||||
NTSTATUS Result;
|
||||
|
||||
Result = FspFileSystemNotifyBegin(FileSystem, 0);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
|
||||
FileName = L"\\";
|
||||
NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR));
|
||||
NotifyInfo.V.Filter = FILE_NOTIFY_CHANGE_LAST_WRITE;
|
||||
NotifyInfo.V.Action = FILE_ACTION_MODIFIED;
|
||||
memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO));
|
||||
FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length);
|
||||
|
||||
FileName = L"bar";
|
||||
NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR));
|
||||
NotifyInfo.V.Filter = FILE_NOTIFY_CHANGE_LAST_WRITE;
|
||||
NotifyInfo.V.Action = FILE_ACTION_MODIFIED;
|
||||
memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO));
|
||||
FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length);
|
||||
|
||||
FileName = L"baz";
|
||||
NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR));
|
||||
NotifyInfo.V.Filter = FILE_NOTIFY_CHANGE_LAST_WRITE;
|
||||
NotifyInfo.V.Action = FILE_ACTION_MODIFIED;
|
||||
memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO));
|
||||
FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length);
|
||||
|
||||
FileName = L"\\foo";
|
||||
NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR));
|
||||
NotifyInfo.V.Filter = FILE_NOTIFY_CHANGE_LAST_WRITE;
|
||||
NotifyInfo.V.Action = FILE_ACTION_MODIFIED;
|
||||
memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO));
|
||||
FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length);
|
||||
|
||||
FileName = L"bar";
|
||||
NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR));
|
||||
NotifyInfo.V.Filter = FILE_NOTIFY_CHANGE_LAST_WRITE;
|
||||
NotifyInfo.V.Action = FILE_ACTION_MODIFIED;
|
||||
memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO));
|
||||
FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length);
|
||||
|
||||
FileName = L"baz";
|
||||
NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR));
|
||||
NotifyInfo.V.Filter = FILE_NOTIFY_CHANGE_LAST_WRITE;
|
||||
NotifyInfo.V.Action = FILE_ACTION_MODIFIED;
|
||||
memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO));
|
||||
FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length);
|
||||
|
||||
FileName = L"\\foo\\";
|
||||
NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR));
|
||||
NotifyInfo.V.Filter = FILE_NOTIFY_CHANGE_LAST_WRITE;
|
||||
NotifyInfo.V.Action = FILE_ACTION_MODIFIED;
|
||||
memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO));
|
||||
FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length);
|
||||
|
||||
FileName = L"bar";
|
||||
NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR));
|
||||
NotifyInfo.V.Filter = FILE_NOTIFY_CHANGE_LAST_WRITE;
|
||||
NotifyInfo.V.Action = FILE_ACTION_MODIFIED;
|
||||
memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO));
|
||||
FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length);
|
||||
|
||||
FileName = L"baz";
|
||||
NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR));
|
||||
NotifyInfo.V.Filter = FILE_NOTIFY_CHANGE_LAST_WRITE;
|
||||
NotifyInfo.V.Action = FILE_ACTION_MODIFIED;
|
||||
memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO));
|
||||
FspFileSystemAddNotifyInfo(&NotifyInfo.V, &Buffer, sizeof Buffer, &Length);
|
||||
|
||||
Result = FspFileSystemNotify(FileSystem, &Buffer.V, Length);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
|
||||
Result = FspFileSystemNotifyEnd(FileSystem);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
|
||||
memfs_stop(memfs);
|
||||
}
|
||||
|
||||
static
|
||||
void notify_change_test(void)
|
||||
{
|
||||
if (WinFspDiskTests)
|
||||
notify_change_dotest(MemfsDisk);
|
||||
if (WinFspNetTests)
|
||||
notify_change_dotest(MemfsNet);
|
||||
}
|
||||
|
||||
static
|
||||
void notify_open_change_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
|
||||
{
|
||||
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
|
||||
FSP_FILE_SYSTEM *FileSystem = MemfsFileSystem(memfs);
|
||||
|
||||
HANDLE DirHandle, FileHandle;
|
||||
WCHAR FilePath[MAX_PATH];
|
||||
union
|
||||
{
|
||||
FSP_FSCTL_NOTIFY_INFO V;
|
||||
UINT8 B[sizeof(FSP_FSCTL_NOTIFY_INFO) + MAX_PATH * sizeof(WCHAR)];
|
||||
} NotifyInfo;
|
||||
PWSTR FileName;
|
||||
BOOL Success;
|
||||
NTSTATUS Result;
|
||||
|
||||
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||
|
||||
Success = CreateDirectoryW(FilePath, 0);
|
||||
ASSERT(Success);
|
||||
|
||||
DirHandle = CreateFileW(FilePath,
|
||||
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != DirHandle);
|
||||
|
||||
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\file0",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||
|
||||
FileHandle = CreateFileW(FilePath,
|
||||
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != FileHandle);
|
||||
|
||||
//DWORD Bytes;
|
||||
//Success = WriteFile(FileHandle, "foobar", 6, &Bytes, 0);
|
||||
//ASSERT(Success);
|
||||
|
||||
Result = FspFileSystemNotifyBegin(FileSystem, 1000);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
|
||||
FileName = L"\\dir1";
|
||||
NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR));
|
||||
NotifyInfo.V.Filter = FILE_NOTIFY_CHANGE_LAST_WRITE;
|
||||
NotifyInfo.V.Action = FILE_ACTION_MODIFIED;
|
||||
memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO));
|
||||
Result = FspFileSystemNotify(FileSystem, &NotifyInfo.V, NotifyInfo.V.Size);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
|
||||
FileName = L"\\dir1\\file0";
|
||||
NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR));
|
||||
NotifyInfo.V.Filter = FILE_NOTIFY_CHANGE_LAST_WRITE;
|
||||
NotifyInfo.V.Action = FILE_ACTION_MODIFIED;
|
||||
memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO));
|
||||
Result = FspFileSystemNotify(FileSystem, &NotifyInfo.V, NotifyInfo.V.Size);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
|
||||
Result = FspFileSystemNotifyEnd(FileSystem);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
|
||||
CloseHandle(FileHandle);
|
||||
|
||||
CloseHandle(DirHandle);
|
||||
|
||||
memfs_stop(memfs);
|
||||
}
|
||||
|
||||
static
|
||||
void notify_open_change_test(void)
|
||||
{
|
||||
if (WinFspDiskTests)
|
||||
{
|
||||
notify_open_change_dotest(MemfsDisk, 0, 0);
|
||||
notify_open_change_dotest(MemfsDisk, 0, 1000);
|
||||
notify_open_change_dotest(MemfsDisk, 0, INFINITE);
|
||||
}
|
||||
if (WinFspNetTests)
|
||||
{
|
||||
notify_open_change_dotest(MemfsNet, L"\\\\memfs\\share", 0);
|
||||
notify_open_change_dotest(MemfsNet, L"\\\\memfs\\share", 1000);
|
||||
notify_open_change_dotest(MemfsNet, L"\\\\memfs\\share", INFINITE);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
unsigned __stdcall notify_dirnotify_dotest_thread(void *FileSystem0)
|
||||
{
|
||||
FspDebugLog(__FUNCTION__ "\n");
|
||||
|
||||
FSP_FILE_SYSTEM *FileSystem = FileSystem0;
|
||||
union
|
||||
{
|
||||
FSP_FSCTL_NOTIFY_INFO V;
|
||||
UINT8 B[sizeof(FSP_FSCTL_NOTIFY_INFO) + MAX_PATH * sizeof(WCHAR)];
|
||||
} NotifyInfo;
|
||||
PWSTR FileName;
|
||||
NTSTATUS Result;
|
||||
|
||||
Sleep(1000); /* wait for ReadDirectoryChangesW */
|
||||
|
||||
FileName = L"\\Directory\\Subdirectory\\file0";
|
||||
NotifyInfo.V.Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + wcslen(FileName) * sizeof(WCHAR));
|
||||
NotifyInfo.V.Filter = FILE_NOTIFY_CHANGE_FILE_NAME;
|
||||
NotifyInfo.V.Action = FILE_ACTION_ADDED;
|
||||
memcpy(NotifyInfo.V.FileNameBuf, FileName, NotifyInfo.V.Size - sizeof(FSP_FSCTL_NOTIFY_INFO));
|
||||
Result = FspFileSystemNotify(FileSystem, &NotifyInfo.V, NotifyInfo.V.Size);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static
|
||||
void notify_dirnotify_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
|
||||
{
|
||||
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
|
||||
FSP_FILE_SYSTEM *FileSystem = MemfsFileSystem(memfs);
|
||||
|
||||
WCHAR FilePath[MAX_PATH];
|
||||
HANDLE Handle;
|
||||
BOOL Success;
|
||||
HANDLE Thread;
|
||||
DWORD ExitCode;
|
||||
DWORD BytesTransferred;
|
||||
PFILE_NOTIFY_INFORMATION NotifyInfo;
|
||||
|
||||
NotifyInfo = malloc(4096);
|
||||
ASSERT(0 != NotifyInfo);
|
||||
|
||||
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\Directory",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||
|
||||
Success = CreateDirectoryW(FilePath, 0);
|
||||
ASSERT(Success);
|
||||
|
||||
Handle = CreateFileW(FilePath,
|
||||
FILE_LIST_DIRECTORY, FILE_SHARE_READ, 0, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != Handle);
|
||||
|
||||
Thread = (HANDLE)_beginthreadex(0, 0, notify_dirnotify_dotest_thread, FileSystem, 0, 0);
|
||||
ASSERT(0 != Thread);
|
||||
|
||||
Success = ReadDirectoryChangesW(Handle,
|
||||
NotifyInfo, 4096, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME, &BytesTransferred, 0, 0);
|
||||
ASSERT(Success);
|
||||
ASSERT(0 < BytesTransferred);
|
||||
|
||||
ASSERT(FILE_ACTION_ADDED == NotifyInfo->Action);
|
||||
ASSERT(wcslen(L"Subdirectory\\file0") * sizeof(WCHAR) == NotifyInfo->FileNameLength);
|
||||
ASSERT(0 == mywcscmp(L"Subdirectory\\file0", -1,
|
||||
NotifyInfo->FileName, NotifyInfo->FileNameLength / sizeof(WCHAR)));
|
||||
|
||||
ASSERT(0 == NotifyInfo->NextEntryOffset);
|
||||
|
||||
WaitForSingleObject(Thread, INFINITE);
|
||||
GetExitCodeThread(Thread, &ExitCode);
|
||||
CloseHandle(Thread);
|
||||
ASSERT(STATUS_SUCCESS == ExitCode);
|
||||
|
||||
Success = CloseHandle(Handle);
|
||||
ASSERT(Success);
|
||||
|
||||
Success = RemoveDirectoryW(FilePath);
|
||||
ASSERT(Success);
|
||||
|
||||
free(NotifyInfo);
|
||||
|
||||
memfs_stop(memfs);
|
||||
}
|
||||
|
||||
static
|
||||
void notify_dirnotify_test(void)
|
||||
{
|
||||
if (WinFspDiskTests &&
|
||||
!OptNoTraverseToken /* WinFsp does not support change notifications w/o traverse privilege */ &&
|
||||
!OptCaseRandomize)
|
||||
{
|
||||
notify_dirnotify_dotest(MemfsDisk, 0, 0);
|
||||
notify_dirnotify_dotest(MemfsDisk, 0, 1000);
|
||||
notify_dirnotify_dotest(MemfsDisk, 0, INFINITE);
|
||||
}
|
||||
if (WinFspNetTests &&
|
||||
!OptNoTraverseToken /* WinFsp does not support change notifications w/o traverse privilege */ &&
|
||||
!OptCaseRandomize)
|
||||
{
|
||||
notify_dirnotify_dotest(MemfsNet, L"\\\\memfs\\share", 0);
|
||||
notify_dirnotify_dotest(MemfsNet, L"\\\\memfs\\share", 1000);
|
||||
notify_dirnotify_dotest(MemfsNet, L"\\\\memfs\\share", INFINITE);
|
||||
}
|
||||
}
|
||||
|
||||
void notify_tests(void)
|
||||
{
|
||||
if (OptExternal || OptNotify)
|
||||
return;
|
||||
|
||||
TEST(notify_abandon_test);
|
||||
TEST(notify_abandon_rename_test);
|
||||
TEST(notify_timeout_test);
|
||||
TEST(notify_change_test);
|
||||
TEST(notify_open_change_test);
|
||||
TEST(notify_dirnotify_test);
|
||||
}
|
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#include <lm.h>
|
||||
#include <signal.h>
|
||||
#include <tlib/testsuite.h>
|
||||
@ -38,6 +39,7 @@ BOOLEAN OptCaseInsensitiveCmp = FALSE;
|
||||
BOOLEAN OptCaseInsensitive = FALSE;
|
||||
BOOLEAN OptCaseRandomize = FALSE;
|
||||
BOOLEAN OptFlushAndPurgeOnCleanup = FALSE;
|
||||
BOOLEAN OptNotify = FALSE;
|
||||
WCHAR OptOplock = 0;
|
||||
WCHAR OptMountPointBuf[MAX_PATH], *OptMountPoint;
|
||||
WCHAR OptShareNameBuf[MAX_PATH], *OptShareName, *OptShareTarget;
|
||||
@ -174,6 +176,37 @@ static void abort_handler(int sig)
|
||||
|
||||
LONG WINAPI UnhandledExceptionHandler(struct _EXCEPTION_POINTERS *ExceptionInfo)
|
||||
{
|
||||
if (0 != ExceptionInfo && 0 != ExceptionInfo->ExceptionRecord)
|
||||
{
|
||||
static CHAR OutBuf[128];
|
||||
static union
|
||||
{
|
||||
SYMBOL_INFO V;
|
||||
UINT8 Buf[sizeof(SYMBOL_INFO) + 64];
|
||||
} Info;
|
||||
LARGE_INTEGER Large;
|
||||
Info.V.SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
Info.V.MaxNameLen = 64;
|
||||
if (SymFromAddr(GetCurrentProcess(),
|
||||
(DWORD64)ExceptionInfo->ExceptionRecord->ExceptionAddress,
|
||||
&Large.QuadPart,
|
||||
&Info.V))
|
||||
{
|
||||
wsprintfA(OutBuf, "\nEXCEPTION 0x%lX at %s+0x%lX(0x%p)\n",
|
||||
ExceptionInfo->ExceptionRecord->ExceptionCode,
|
||||
Info.V.Name,
|
||||
Large.LowPart,
|
||||
ExceptionInfo->ExceptionRecord->ExceptionAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
wsprintfA(OutBuf, "\nEXCEPTION 0x%lX at 0x%p\n",
|
||||
ExceptionInfo->ExceptionRecord->ExceptionCode,
|
||||
ExceptionInfo->ExceptionRecord->ExceptionAddress);
|
||||
}
|
||||
WriteFile(GetStdHandle(STD_ERROR_HANDLE), OutBuf, lstrlenA(OutBuf), &Large.LowPart, 0);
|
||||
}
|
||||
|
||||
exiting();
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
@ -211,12 +244,17 @@ int main(int argc, char *argv[])
|
||||
TESTSUITE(ea_tests);
|
||||
TESTSUITE(stream_tests);
|
||||
TESTSUITE(oplock_tests);
|
||||
TESTSUITE(notify_tests);
|
||||
TESTSUITE(wsl_tests);
|
||||
TESTSUITE(volpath_tests);
|
||||
|
||||
SymInitialize(GetCurrentProcess(), 0, TRUE);
|
||||
|
||||
atexit(exiting);
|
||||
signal(SIGABRT, abort_handler);
|
||||
SetUnhandledExceptionFilter(UnhandledExceptionHandler);
|
||||
#pragma warning(suppress: 4996)
|
||||
if (0 == getenv("WINFSP_TESTS_EXCEPTION_FILTER_DISABLE"))
|
||||
SetUnhandledExceptionFilter(UnhandledExceptionHandler);
|
||||
|
||||
for (int argi = 1; argc > argi; argi++)
|
||||
{
|
||||
@ -258,6 +296,11 @@ int main(int argc, char *argv[])
|
||||
OptFlushAndPurgeOnCleanup = TRUE;
|
||||
rmarg(argv, argc, argi);
|
||||
}
|
||||
else if (0 == strcmp("--notify", a))
|
||||
{
|
||||
OptNotify = TRUE;
|
||||
rmarg(argv, argc, argi);
|
||||
}
|
||||
else if (0 == strcmp("--oplock=batch", a))
|
||||
{
|
||||
OptOplock = 'B';
|
||||
|
@ -159,6 +159,7 @@ extern BOOLEAN OptCaseInsensitiveCmp;
|
||||
extern BOOLEAN OptCaseInsensitive;
|
||||
extern BOOLEAN OptCaseRandomize;
|
||||
extern BOOLEAN OptFlushAndPurgeOnCleanup;
|
||||
extern BOOLEAN OptNotify;
|
||||
extern WCHAR OptOplock;
|
||||
extern WCHAR OptMountPointBuf[], *OptMountPoint;
|
||||
extern WCHAR OptShareNameBuf[], *OptShareName, *OptShareTarget;
|
||||
@ -168,3 +169,4 @@ extern HANDLE OptNoTraverseToken;
|
||||
extern LUID OptNoTraverseLuid;
|
||||
|
||||
extern int memfs_running;
|
||||
extern HANDLE memfs_handle;
|
||||
|
Reference in New Issue
Block a user