mirror of
https://github.com/winfsp/winfsp.git
synced 2025-07-02 17:02:57 -05:00
Compare commits
95 Commits
Author | SHA1 | Date | |
---|---|---|---|
9d76495340 | |||
41c604b0fd | |||
28a9534a65 | |||
b87c907af8 | |||
9d5efe5f98 | |||
c5b850be35 | |||
0af0bfbe7c | |||
1dbcae3985 | |||
a5726c820b | |||
870c54253a | |||
f9cbbea386 | |||
a939d9997b | |||
c803ef24f8 | |||
2d5d058d2f | |||
a372c6ee40 | |||
c8a5f52fb1 | |||
40052b143e | |||
8006763367 | |||
7ed9c56005 | |||
12fd8942f7 | |||
3eb1f48174 | |||
ac26bde9ee | |||
6421dd92a9 | |||
b05d5e286e | |||
2d0df701e3 | |||
2f1a5b98e2 | |||
21a636aaae | |||
3b90908e01 | |||
90e86d4592 | |||
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 | |||
5fac25d200 | |||
b82aeeadbd | |||
dcaa24bc52 | |||
7e37fc57f9 | |||
8efe1f3a1f | |||
aa3beba928 | |||
899cd5595d | |||
6bcbfd5380 | |||
a197b99960 | |||
2ffb8a1c97 | |||
fc18b70a00 | |||
191c98bd41 | |||
5360f5ca6e | |||
89aaf33b62 | |||
675ecf2e51 | |||
b663cfdca5 | |||
dcf83b6d64 |
1
.github/workflows/avm.yml
vendored
1
.github/workflows/avm.yml
vendored
@ -13,3 +13,4 @@ jobs:
|
||||
files: |
|
||||
https://github.com/billziss-gh/winfsp/releases/download/v1.6/winfsp-1.6.20027.msi
|
||||
https://github.com/billziss-gh/winfsp/releases/download/v1.7B1/winfsp-1.7.20038.msi
|
||||
https://github.com/billziss-gh/winfsp/releases/download/v1.8/winfsp-1.8.20304.msi
|
||||
|
@ -1,5 +1,156 @@
|
||||
= Changelog
|
||||
|
||||
|
||||
v1.9 (2021)::
|
||||
|
||||
Changes since v1.8:
|
||||
|
||||
* [NEW] WinFsp now supports file change notifications and cache invalidations. This functionality is offered via the following new API's:
|
||||
** Native API: `FspFileSystemNotify`
|
||||
** FUSE API: `fuse_notify`
|
||||
** .NET API: `FileSystemHost.Notify`
|
||||
|
||||
* [NEW] A native file system (notifyfs) and a .NET file system (notifyfs-dotnet) have been added to showcase the new file change notification API.
|
||||
|
||||
* [NEW] A new WinFsp-FUSE option `FileSecurity` has been added that allows the complete specification of file security descriptors using SDDL. (See GitHub issue https://github.com/rclone/rclone/issues/4717#issuecomment-767723287.)
|
||||
|
||||
* [FIX] The FSD now correctly handles a rare situation where receiving too many CLOSE calls from the OS (e.g. when caching thousands of files) could result in a bogus "insufficient resources" error. (See commit 0af0bfbe7c45e353d693a2a45965d99fa81f2163.)
|
||||
|
||||
* [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.
|
||||
|
||||
* [FIX] WinFsp-FUSE options such as `volname`, `VolumePrefix`, `FileSystemName`, `ExactFileSystemName` are now truncated when too long. (See GitHub issue #311.)
|
||||
|
||||
* [FIX] The WinFsp driver is now started in a thread-safe manner if it is not already running. (See GitHub issue billziss-gh/cgofuse#51.)
|
||||
|
||||
* [FIX] Credentials entered in the Credentials dialog (when mapping network drives) are no longer saved by default. (See GitHub PR #342. Thanks @gaotxg.)
|
||||
|
||||
|
||||
v1.9B2 (2021 Beta2)::
|
||||
|
||||
Changes since v1.8:
|
||||
|
||||
* [NEW] WinFsp now supports file change notifications and cache invalidations. This functionality is offered via the following new API's:
|
||||
** Native API: `FspFileSystemNotify`
|
||||
** FUSE API: `fuse_notify`
|
||||
** .NET API: `FileSystemHost.Notify`
|
||||
|
||||
* [NEW] A native file system (notifyfs) and a .NET file system (notifyfs-dotnet) have been added to showcase the new file change notification API.
|
||||
|
||||
* [NEW] A new WinFsp-FUSE option `FileSecurity` has been added that allows the complete specification of file security descriptors using SDDL. (See GitHub issue https://github.com/rclone/rclone/issues/4717#issuecomment-767723287.)
|
||||
|
||||
* [FIX] The FSD now correctly handles a rare situation where receiving too many CLOSE calls from the OS (e.g. when caching thousands of files) could result in a bogus "insufficient resources" error. (See commit 0af0bfbe7c45e353d693a2a45965d99fa81f2163.)
|
||||
|
||||
* [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.
|
||||
|
||||
* [FIX] WinFsp-FUSE options such as `volname`, `VolumePrefix`, `FileSystemName`, `ExactFileSystemName` are now truncated when too long. (See GitHub issue #311.)
|
||||
|
||||
* [FIX] The WinFsp driver is now started in a thread-safe manner if it is not already running. (See GitHub issue billziss-gh/cgofuse#51.)
|
||||
|
||||
* [FIX] Credentials entered in the Credentials dialog (when mapping network drives) are no longer saved by default. (See GitHub PR #342. Thanks @gaotxg.)
|
||||
|
||||
|
||||
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:
|
||||
|
||||
* [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.
|
||||
|
||||
|
||||
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:
|
||||
|
@ -54,6 +54,7 @@ This CONTRIBUTOR AGREEMENT applies to any contribution that you make to the WinF
|
||||
CONTRIBUTOR LIST
|
||||
----------------
|
||||
|===
|
||||
|Alberto Alonso |alberto at alonso.xyz
|
||||
|Ben Rubson |ben.rubson at gmail.com
|
||||
|Bill Zissimopoulos |billziss at navimatics.com
|
||||
|Brett Dutro |brett.dutro at gmail.com
|
||||
@ -68,4 +69,5 @@ CONTRIBUTOR LIST
|
||||
|Sam Kelly (DuroSoft Technologies LLC, https://durosoft.com) |sam at durosoft.com
|
||||
|Santiago Ganis |sganis at gmail.com
|
||||
|Tobias Urlaub |saibotu at outlook.de
|
||||
|Victor Gao |victgm at outlook.com
|
||||
|===
|
||||
|
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'))
|
||||
|
@ -538,6 +538,31 @@
|
||||
<File Name="passthrough-dotnet.csproj" KeyPath="yes" />
|
||||
</Component>
|
||||
</Directory>
|
||||
<Directory Id="SMPDIR.notifyfs" Name="notifyfs">
|
||||
<Component Id="C.notifyfs.c">
|
||||
<File Name="notifyfs.c" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.notifyfs.sln">
|
||||
<File Name="notifyfs.sln" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.notifyfs.vcxproj">
|
||||
<File Name="notifyfs.vcxproj" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.notifyfs.vcxproj.filters">
|
||||
<File Name="notifyfs.vcxproj.filters" KeyPath="yes" />
|
||||
</Component>
|
||||
</Directory>
|
||||
<Directory Id="SMPDIR.notifyfs_dotnet" Name="notifyfs-dotnet">
|
||||
<Component Id="C.notifyfs_dotnet.Program.cs">
|
||||
<File Id="FILE.notifyfs_dotnet.Program.cs" Name="Program.cs" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.notifyfs_dotnet.sln">
|
||||
<File Name="notifyfs-dotnet.sln" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="C.notifyfs_dotnet.csproj">
|
||||
<File Name="notifyfs-dotnet.csproj" KeyPath="yes" />
|
||||
</Component>
|
||||
</Directory>
|
||||
</DirectoryRef>
|
||||
<DirectoryRef Id="SYMDIR">
|
||||
<Component Id="C.winfsp_x64.sys.pdb">
|
||||
@ -674,6 +699,10 @@
|
||||
<ComponentRef Id="C.passthrough_fuse3.vcxproj.filters" />
|
||||
<ComponentRef Id="C.passthrough_fuse3.Makefile" />
|
||||
<ComponentRef Id="C.passthrough_fuse3.README.md" />
|
||||
<ComponentRef Id="C.notifyfs.c" />
|
||||
<ComponentRef Id="C.notifyfs.sln" />
|
||||
<ComponentRef Id="C.notifyfs.vcxproj" />
|
||||
<ComponentRef Id="C.notifyfs.vcxproj.filters" />
|
||||
</ComponentGroup>
|
||||
<ComponentGroup Id="C.WinFsp.sym">
|
||||
<ComponentRef Id="C.winfsp_x64.sys.pdb" />
|
||||
@ -703,6 +732,9 @@
|
||||
<ComponentRef Id="C.passthrough_dotnet.Program.cs" />
|
||||
<ComponentRef Id="C.passthrough_dotnet.sln" />
|
||||
<ComponentRef Id="C.passthrough_dotnet.csproj" />
|
||||
<ComponentRef Id="C.notifyfs_dotnet.Program.cs" />
|
||||
<ComponentRef Id="C.notifyfs_dotnet.sln" />
|
||||
<ComponentRef Id="C.notifyfs_dotnet.csproj" />
|
||||
</ComponentGroup>
|
||||
|
||||
<Feature
|
||||
|
@ -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.7</MyCanonicalVersion>
|
||||
<MyCanonicalVersion>1.9</MyCanonicalVersion>
|
||||
|
||||
<MyProductVersion>2020.1</MyProductVersion>
|
||||
<MyProductVersion>2021</MyProductVersion>
|
||||
<MyProductStage>Gold</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">
|
||||
|
@ -185,7 +185,9 @@
|
||||
<ClCompile Include="..\..\src\sys\read.c" />
|
||||
<ClCompile Include="..\..\src\sys\security.c" />
|
||||
<ClCompile Include="..\..\src\sys\shutdown.c" />
|
||||
<ClCompile Include="..\..\src\sys\silo.c" />
|
||||
<ClCompile Include="..\..\src\sys\statistics.c" />
|
||||
<ClCompile Include="..\..\src\sys\trace.c" />
|
||||
<ClCompile Include="..\..\src\sys\util.c" />
|
||||
<ClCompile Include="..\..\src\sys\volinfo.c" />
|
||||
<ClCompile Include="..\..\src\sys\volume.c" />
|
||||
|
@ -122,6 +122,12 @@
|
||||
<ClCompile Include="..\..\src\shared\ku\uuid5.c">
|
||||
<Filter>Source\shared\ku</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\sys\silo.c">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\sys\trace.c">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\src\sys\driver.h">
|
||||
|
73
doc/WinFsp-Container-Support.asciidoc
Normal file
73
doc/WinFsp-Container-Support.asciidoc
Normal file
@ -0,0 +1,73 @@
|
||||
= WinFsp Container Support
|
||||
|
||||
WinFsp gained support for Windows Containers in release 2020.2 B1. This document discusses this new functionality and explains how user mode file systems can take advantage of it.
|
||||
|
||||
NOTE: WinFsp support for Windows Containers is at this time highly experimental. It may change substantially in the future or eliminated altogether.
|
||||
|
||||
== Windows Containers
|
||||
|
||||
Containers are a technology for deployment and execution of applications in an isolated environment. Applications deployed inside a container cannot be affected by or affect applications in other containers or in the host operating system.
|
||||
|
||||
Containers on Windows provide isolation via one of two modes:
|
||||
|
||||
- The _Hyper-V_ isolation mode where a container runs in its own virtual machine and has its own kernel.
|
||||
|
||||
- The _process_ isolation mode where a container runs in a "silo" inside the host operating system's kernel. This is the same model used in containers for Linux.
|
||||
|
||||
WinFsp supports containers that use the process isolation mode in versions of Windows 10 1809 and later.
|
||||
|
||||
=== Server Silos
|
||||
|
||||
Containers using the process isolation mode are called _server silos_. Server silos are built on top of kernel job objects that have been extended with additional capabities useful for containers. Most importantly each server silo has its own object namespace root directory that is distinct from the host's object namespace root directory. This allows for all named kernel objects inside a server silo to be isolated from named objects in other server silos or the host.
|
||||
|
||||
The Windows kernel provides a number of DDI's (Device Driver Interfaces) to enable kernel drivers to work with server silos. The WinFsp FSD uses these DDI's to monitor silo creation and termination and manage its own devices within the silo's object namespace.
|
||||
|
||||
=== Docker
|
||||
|
||||
In order to manage containers on Windows a version of Docker for Windows is needed. In order to use server silos and process isolation with Docker the switch `--isolation=process` must be used when creating a container:
|
||||
|
||||
----
|
||||
docker run --isolation=process ...
|
||||
----
|
||||
|
||||
== Containerized User Mode File Systems
|
||||
|
||||
The WinFsp 2020.2 B1 release gained the ability for user mode file systems to run inside a server silo container and expose a file system either within the container or to the host operating system. This allows for user mode file systems to be containerized.
|
||||
|
||||
There are a few considerations that must be taken into account when deploying a user mode file system in a container:
|
||||
|
||||
* When using WinFsp with containers the process isolation mode must be used.
|
||||
|
||||
* WinFsp currently supports the Windows Server Core and Windows base images. Work is underway for Nano Server compatibility.
|
||||
|
||||
* User mode file systems that run in a container cannot start the FSD, because containers disallow driver loading. (Usually the WinFsp DLL automatically starts the WinFsp FSD when it is not already running, but this is not possible within a container.)
|
||||
+
|
||||
To work around this problem start the FSD outside the container. For example, the FSD can be started in the host using the `sc.exe` utility.
|
||||
+
|
||||
----
|
||||
sc.exe start winfsp
|
||||
----
|
||||
+
|
||||
Once the FSD has been started it will monitor server silo creation and termination and will set things up so that containerized user mode file systems will be able to interface with it.
|
||||
|
||||
* WinFsp file systems typically use the registry to locate and load the WinFsp DLL (see `FspLoad` in `winfsp/winfsp.h`). However the registry inside a container does not contain any WinFsp entries. The easiest workaround is to hardcode the location of the WinFsp installation within the container, which will also hardcode the location of the WinFsp DLL.
|
||||
|
||||
* During development it is sometimes useful to bind mount the WinFsp installation directory into the container. For example:
|
||||
+
|
||||
----
|
||||
docker run -it --rm --isolation=process "-vC:\Program Files (x86)\WinFsp:C:\Program Files (x86)\WinFsp:RO" mcr.microsoft.com/windows/servercore:2004 cmd.exe
|
||||
----
|
||||
|
||||
* When a WinFsp based file system is mounted as a disk file system within a container, it is only visible within that container. For example, the drive `Z:` below and associated file system volume will be accessible only within the container:
|
||||
+
|
||||
----
|
||||
memfs-x64 -i -F NTFS -m Z:
|
||||
----
|
||||
|
||||
* When a WinFsp based file system is mounted as a network file system within a container, the file system becomes accessible **outside** the container via its UNC prefix. For example, while the drive `Y:` below will be accessible only within the container, the file system volume will be accessible from both the container and the host via the UNC prefix `\\memfs\share`.
|
||||
+
|
||||
----
|
||||
memfs-x64 -i -F NTFS -m Y: -u \memfs\share
|
||||
----
|
||||
+
|
||||
The reason that this happens is that server silo containers share the same MUP device object with the host. The MUP (Multiple UNC Provider) is the Windows kernel component responsible for resolving UNC prefixes. It is unclear at this time whether this "leak" from the container to the host via the shared MUP is intentional or whether it is an unintended consequence. I note that the server silo MUP entry in the object namespace is a symbolic link, which suggests that the shared MUP device object was a conscious decision by Microsoft.
|
@ -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,11 +66,17 @@ 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 \
|
||||
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'I', METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
|
||||
/* fsvol device codes */
|
||||
#define FSP_FSCTL_QUERY_WINFSP \
|
||||
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + '?', METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define FSP_FSCTL_VOLUME_PARAMS_PREFIX "\\VolumeParams="
|
||||
|
||||
#define FSP_FSCTL_VOLUME_NAME_SIZE (64 * sizeof(WCHAR))
|
||||
@ -264,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;
|
||||
@ -613,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
|
||||
@ -1738,6 +1826,10 @@ FSP_API VOID FspDeleteSid(PSID Sid, NTSTATUS (*CreateFunc)());
|
||||
FSP_API NTSTATUS FspPosixMapPermissionsToSecurityDescriptor(
|
||||
UINT32 Uid, UINT32 Gid, UINT32 Mode,
|
||||
PSECURITY_DESCRIPTOR *PSecurityDescriptor);
|
||||
FSP_API NTSTATUS FspPosixMergePermissionsToSecurityDescriptor(
|
||||
UINT32 Uid, UINT32 Gid, UINT32 Mode,
|
||||
PSECURITY_DESCRIPTOR ExistingSecurityDescriptor,
|
||||
PSECURITY_DESCRIPTOR *PSecurityDescriptor);
|
||||
FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions(
|
||||
PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||||
PUINT32 PUid, PUINT32 PGid, PUINT32 PMode);
|
||||
|
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)
|
||||
{
|
||||
@ -235,6 +254,7 @@ FSP_API NTSTATUS FspFsctlPreflight(PWSTR DevicePath)
|
||||
|
||||
static NTSTATUS FspFsctlStartService(VOID)
|
||||
{
|
||||
static SRWLOCK Lock = SRWLOCK_INIT;
|
||||
PWSTR DriverName = L"" FSP_FSCTL_DRIVER_NAME;
|
||||
SC_HANDLE ScmHandle = 0;
|
||||
SC_HANDLE SvcHandle = 0;
|
||||
@ -242,6 +262,24 @@ static NTSTATUS FspFsctlStartService(VOID)
|
||||
DWORD LastError;
|
||||
NTSTATUS Result;
|
||||
|
||||
AcquireSRWLockExclusive(&Lock);
|
||||
|
||||
/* Determine if we are running inside container.
|
||||
*
|
||||
* See https://github.com/microsoft/perfview/blob/V1.9.65/src/TraceEvent/TraceEventSession.cs#L525
|
||||
* See https://stackoverflow.com/a/50748300
|
||||
*/
|
||||
LastError = RegGetValueW(
|
||||
HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control",
|
||||
L"ContainerType",
|
||||
RRF_RT_REG_DWORD, 0,
|
||||
0, 0);
|
||||
if (ERROR_SUCCESS == LastError)
|
||||
{
|
||||
Result = STATUS_SUCCESS;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ScmHandle = OpenSCManagerW(0, 0, 0);
|
||||
if (0 == ScmHandle)
|
||||
{
|
||||
@ -296,6 +334,8 @@ exit:
|
||||
if (0 != ScmHandle)
|
||||
CloseServiceHandle(ScmHandle);
|
||||
|
||||
ReleaseSRWLockExclusive(&Lock);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include <dll/fuse/library.h>
|
||||
#include <sddl.h>
|
||||
|
||||
struct fuse_chan
|
||||
{
|
||||
@ -105,6 +106,8 @@ static struct fuse_opt fsp_fuse_core_opts[] =
|
||||
FUSE_OPT_KEY("ExactFileSystemName=", 'E'),
|
||||
FUSE_OPT_KEY("--ExactFileSystemName=", 'E'),
|
||||
|
||||
FUSE_OPT_KEY("FileSecurity=", 's'),
|
||||
FUSE_OPT_KEY("--FileSecurity=", 's'),
|
||||
FSP_FUSE_CORE_OPT("UserName=", set_uid, 1),
|
||||
FUSE_OPT_KEY("UserName=", 'u'),
|
||||
FSP_FUSE_CORE_OPT("--UserName=", set_uid, 1),
|
||||
@ -184,6 +187,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') ||
|
||||
@ -244,6 +269,29 @@ FSP_FUSE_API int fsp_fuse_is_lib_option(struct fsp_fuse_env *env,
|
||||
return fsp_fuse_opt_match(env, fsp_fuse_core_opts, opt);
|
||||
}
|
||||
|
||||
static int fsp_fuse_sddl_to_security(const char *Sddl, PUINT8 Security, PULONG PSecuritySize)
|
||||
{
|
||||
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
||||
ULONG SecurityDescriptorSize;
|
||||
int res = -1;
|
||||
|
||||
if (ConvertStringSecurityDescriptorToSecurityDescriptorA(
|
||||
Sddl, SDDL_REVISION_1, &SecurityDescriptor, &SecurityDescriptorSize))
|
||||
{
|
||||
if (*PSecuritySize >= SecurityDescriptorSize)
|
||||
{
|
||||
memcpy(Security, SecurityDescriptor, SecurityDescriptorSize);
|
||||
*PSecuritySize = SecurityDescriptorSize;
|
||||
|
||||
res = 0;
|
||||
}
|
||||
|
||||
LocalFree(SecurityDescriptor);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int fsp_fuse_username_to_uid(const char *username, int *puid)
|
||||
{
|
||||
union
|
||||
@ -281,6 +329,63 @@ static int fsp_fuse_username_to_uid(const char *username, int *puid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsp_fuse_utf8towcs_trunc(
|
||||
const char *Str, int StrLen,
|
||||
PWSTR Wcs, int WcsLen)
|
||||
{
|
||||
if (0 == StrLen)
|
||||
return 0;
|
||||
|
||||
int Size = MultiByteToWideChar(CP_UTF8, 0, Str, StrLen, Wcs, WcsLen);
|
||||
if (0 != Size)
|
||||
return Size;
|
||||
|
||||
if (0 == WcsLen)
|
||||
return 0;
|
||||
|
||||
PWSTR Buf = 0;
|
||||
|
||||
Size = MultiByteToWideChar(CP_UTF8, 0, Str, StrLen, 0, 0);
|
||||
if (0 == Size)
|
||||
goto exit;
|
||||
|
||||
Buf = MemAlloc(Size * sizeof(WCHAR));
|
||||
if (0 == Buf)
|
||||
{
|
||||
SetLastError(ERROR_NO_SYSTEM_RESOURCES);
|
||||
Size = 0;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
Size = MultiByteToWideChar(CP_UTF8, 0, Str, StrLen, Buf, Size);
|
||||
if (0 == Size)
|
||||
goto exit;
|
||||
|
||||
if (-1 == StrLen)
|
||||
{
|
||||
if (Size >= WcsLen)
|
||||
{
|
||||
Size = WcsLen - 1;
|
||||
memcpy(Wcs, Buf, Size * sizeof(WCHAR));
|
||||
Wcs[Size] = L'\0';
|
||||
Size++;
|
||||
}
|
||||
else
|
||||
memcpy(Wcs, Buf, Size * sizeof(WCHAR));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Size >= WcsLen)
|
||||
Size = WcsLen;
|
||||
memcpy(Wcs, Buf, Size * sizeof(WCHAR));
|
||||
}
|
||||
|
||||
exit:
|
||||
MemFree(Buf);
|
||||
|
||||
return Size;
|
||||
}
|
||||
|
||||
static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
|
||||
struct fuse_args *outargs)
|
||||
{
|
||||
@ -295,6 +400,7 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
|
||||
FspServiceLog(EVENTLOG_ERROR_TYPE, L""
|
||||
FSP_FUSE_LIBRARY_NAME " options:\n"
|
||||
" -o umask=MASK set file permissions (octal)\n"
|
||||
" -o FileSecurity=SDDL set file DACL (SDDL format)\n"
|
||||
" -o create_umask=MASK set newly created file permissions (octal)\n"
|
||||
" -o create_file_umask=MASK for files only\n"
|
||||
" -o create_dir_umask=MASK for directories only\n"
|
||||
@ -345,7 +451,7 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
|
||||
arg += sizeof "VolumePrefix=" - 1;
|
||||
else if ('V' == arg[2])
|
||||
arg += sizeof "--VolumePrefix=" - 1;
|
||||
if (0 == MultiByteToWideChar(CP_UTF8, 0, arg, -1,
|
||||
if (0 == fsp_fuse_utf8towcs_trunc(arg, -1,
|
||||
opt_data->VolumeParams.Prefix, sizeof opt_data->VolumeParams.Prefix / sizeof(WCHAR)))
|
||||
return -1;
|
||||
opt_data->VolumeParams.Prefix
|
||||
@ -361,7 +467,7 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
|
||||
arg += sizeof "FileSystemName=" - 1;
|
||||
else if ('F' == arg[2])
|
||||
arg += sizeof "--FileSystemName=" - 1;
|
||||
if (0 == MultiByteToWideChar(CP_UTF8, 0, arg, -1,
|
||||
if (0 == fsp_fuse_utf8towcs_trunc(arg, -1,
|
||||
opt_data->VolumeParams.FileSystemName + 5,
|
||||
sizeof opt_data->VolumeParams.FileSystemName / sizeof(WCHAR) - 5))
|
||||
return -1;
|
||||
@ -374,13 +480,25 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
|
||||
arg += sizeof "ExactFileSystemName=" - 1;
|
||||
else if ('E' == arg[2])
|
||||
arg += sizeof "--ExactFileSystemName=" - 1;
|
||||
if (0 == MultiByteToWideChar(CP_UTF8, 0, arg, -1,
|
||||
if (0 == fsp_fuse_utf8towcs_trunc(arg, -1,
|
||||
opt_data->VolumeParams.FileSystemName,
|
||||
sizeof opt_data->VolumeParams.FileSystemName / sizeof(WCHAR)))
|
||||
return -1;
|
||||
opt_data->VolumeParams.FileSystemName
|
||||
[sizeof opt_data->VolumeParams.FileSystemName / sizeof(WCHAR) - 1] = L'\0';
|
||||
return 0;
|
||||
case 's':
|
||||
if ('F' == arg[0])
|
||||
arg += sizeof "FileSecurity=" - 1;
|
||||
else if ('F' == arg[2])
|
||||
arg += sizeof "--FileSecurity=" - 1;
|
||||
opt_data->FileSecuritySize = sizeof opt_data->FileSecurityBuf;
|
||||
if (-1 == fsp_fuse_sddl_to_security(arg, opt_data->FileSecurityBuf, &opt_data->FileSecuritySize))
|
||||
{
|
||||
opt_data->FileSecuritySize = (ULONG)-1;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
case 'u':
|
||||
if ('U' == arg[0])
|
||||
arg += sizeof "UserName=" - 1;
|
||||
@ -406,7 +524,7 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
|
||||
case 'v':
|
||||
arg += sizeof "volname=" - 1;
|
||||
opt_data->VolumeLabelLength = (UINT16)(sizeof(WCHAR) *
|
||||
MultiByteToWideChar(CP_UTF8, 0, arg, lstrlenA(arg),
|
||||
fsp_fuse_utf8towcs_trunc(arg, lstrlenA(arg),
|
||||
opt_data->VolumeLabel, sizeof opt_data->VolumeLabel / sizeof(WCHAR)));
|
||||
if (0 == opt_data->VolumeLabelLength)
|
||||
return -1;
|
||||
@ -448,7 +566,12 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env,
|
||||
|
||||
if (-1 == fsp_fuse_core_opt_parse(env, args, &opt_data, /*help=*/1))
|
||||
{
|
||||
if (-1 == opt_data.username_to_uid_result)
|
||||
if ((ULONG)-1 == opt_data.FileSecuritySize)
|
||||
{
|
||||
ErrorMessage = L": invalid file security.";
|
||||
goto fail;
|
||||
}
|
||||
else if (-1 == opt_data.username_to_uid_result)
|
||||
{
|
||||
ErrorMessage = L": invalid user or group name.";
|
||||
goto fail;
|
||||
@ -517,7 +640,7 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env,
|
||||
if (L'\0' == opt_data.VolumeParams.FileSystemName[0])
|
||||
memcpy(opt_data.VolumeParams.FileSystemName, L"FUSE", 5 * sizeof(WCHAR));
|
||||
|
||||
f = fsp_fuse_obj_alloc(env, sizeof *f);
|
||||
f = fsp_fuse_obj_alloc(env, sizeof *f + opt_data.FileSecuritySize);
|
||||
if (0 == f)
|
||||
goto fail;
|
||||
|
||||
@ -537,6 +660,11 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env,
|
||||
memcpy(&f->VolumeParams, &opt_data.VolumeParams, sizeof opt_data.VolumeParams);
|
||||
f->VolumeLabelLength = opt_data.VolumeLabelLength;
|
||||
memcpy(&f->VolumeLabel, &opt_data.VolumeLabel, opt_data.VolumeLabelLength);
|
||||
if (0 != opt_data.FileSecuritySize)
|
||||
{
|
||||
memcpy(f->FileSecurityBuf, opt_data.FileSecurityBuf, opt_data.FileSecuritySize);
|
||||
f->FileSecurity = f->FileSecurityBuf;
|
||||
}
|
||||
|
||||
Size = (lstrlenW(ch->MountPoint) + 1) * sizeof(WCHAR);
|
||||
f->MountPoint = fsp_fuse_obj_alloc(env, Size);
|
||||
@ -610,6 +738,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;
|
||||
|
@ -472,7 +472,8 @@ static NTSTATUS fsp_fuse_intf_GetSecurityEx(FSP_FILE_SYSTEM *FileSystem,
|
||||
|
||||
if (0 != PSecurityDescriptorSize)
|
||||
{
|
||||
Result = FspPosixMapPermissionsToSecurityDescriptor(Uid, Gid, Mode, &SecurityDescriptor);
|
||||
Result = FspPosixMergePermissionsToSecurityDescriptor(Uid, Gid, Mode, f->FileSecurity,
|
||||
&SecurityDescriptor);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
@ -498,7 +499,7 @@ static NTSTATUS fsp_fuse_intf_GetSecurityEx(FSP_FILE_SYSTEM *FileSystem,
|
||||
exit:
|
||||
if (0 != SecurityDescriptor)
|
||||
FspDeleteSecurityDescriptor(SecurityDescriptor,
|
||||
FspPosixMapPermissionsToSecurityDescriptor);
|
||||
FspPosixMergePermissionsToSecurityDescriptor);
|
||||
|
||||
return Result;
|
||||
}
|
||||
@ -1682,7 +1683,8 @@ static NTSTATUS fsp_fuse_intf_SetSecurity(FSP_FILE_SYSTEM *FileSystem,
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
Result = FspPosixMapPermissionsToSecurityDescriptor(Uid, Gid, Mode, &SecurityDescriptor);
|
||||
Result = FspPosixMergePermissionsToSecurityDescriptor(Uid, Gid, Mode, f->FileSecurity,
|
||||
&SecurityDescriptor);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
@ -1729,7 +1731,7 @@ exit:
|
||||
|
||||
if (0 != SecurityDescriptor)
|
||||
FspDeleteSecurityDescriptor(SecurityDescriptor,
|
||||
FspPosixMapPermissionsToSecurityDescriptor);
|
||||
FspPosixMergePermissionsToSecurityDescriptor);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -72,6 +72,8 @@ struct fuse
|
||||
FSP_FILE_SYSTEM *FileSystem;
|
||||
volatile int exited;
|
||||
struct fuse3 *fuse3;
|
||||
PSECURITY_DESCRIPTOR FileSecurity;
|
||||
FSP_FSCTL_DECLSPEC_ALIGN UINT8 FileSecurityBuf[];
|
||||
};
|
||||
struct fsp_fuse_context_header
|
||||
{
|
||||
@ -156,6 +158,8 @@ struct fsp_fuse_core_opt_data
|
||||
FSP_FSCTL_VOLUME_PARAMS VolumeParams;
|
||||
UINT16 VolumeLabelLength;
|
||||
WCHAR VolumeLabel[sizeof ((FSP_FSCTL_VOLUME_INFO *)0)->VolumeLabel / sizeof(WCHAR)];
|
||||
ULONG FileSecuritySize;
|
||||
FSP_FSCTL_DECLSPEC_ALIGN UINT8 FileSecurityBuf[1024];
|
||||
};
|
||||
FSP_FSCTL_STATIC_ASSERT(
|
||||
sizeof ((struct fuse *)0)->VolumeLabel == sizeof ((struct fsp_fuse_core_opt_data *)0)->VolumeLabel,
|
||||
|
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)
|
||||
|
@ -742,7 +742,7 @@ DWORD APIENTRY NPAddConnection3(HWND hwndOwner,
|
||||
DWORD AuthPackage, CredentialsKind;
|
||||
WCHAR UserName[CREDUI_MAX_USERNAME_LENGTH + 1], Password[CREDUI_MAX_PASSWORD_LENGTH + 1];
|
||||
#if defined(FSP_NP_CREDENTIAL_MANAGER)
|
||||
BOOL Save = TRUE;
|
||||
BOOL Save = FALSE;
|
||||
#endif
|
||||
|
||||
//dwFlags |= CONNECT_INTERACTIVE | CONNECT_PROMPT; /* TESTING ONLY! */
|
||||
|
@ -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;
|
||||
|
@ -488,7 +488,8 @@ FSP_API VOID FspDeleteSecurityDescriptor(PSECURITY_DESCRIPTOR SecurityDescriptor
|
||||
return;
|
||||
|
||||
if ((NTSTATUS (*)())FspAccessCheckEx == CreateFunc ||
|
||||
(NTSTATUS (*)())FspPosixMapPermissionsToSecurityDescriptor == CreateFunc)
|
||||
(NTSTATUS (*)())FspPosixMapPermissionsToSecurityDescriptor == CreateFunc ||
|
||||
(NTSTATUS (*)())FspPosixMergePermissionsToSecurityDescriptor == CreateFunc)
|
||||
MemFree(SecurityDescriptor);
|
||||
else
|
||||
if ((NTSTATUS (*)())FspCreateSecurityDescriptor == CreateFunc ||
|
||||
|
@ -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,85 @@ 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(NotifyInfoInternal), "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>
|
||||
/// Enumeration of all the possible values for NotifyInfo.Action
|
||||
/// </summary>
|
||||
public enum NotifyAction : UInt32
|
||||
{
|
||||
Added = 1,
|
||||
Removed = 2,
|
||||
Modified = 3,
|
||||
RenamedOldName = 4,
|
||||
RenamedNewName = 5,
|
||||
AddedStream = 6,
|
||||
RemovedStream = 7,
|
||||
ModifiedStream = 8,
|
||||
RemovedByDelete = 9,
|
||||
IdNotTunnelled = 10,
|
||||
TunnelledIdCollision = 11,
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Enumeration of all the possible values for NotifyInfo.Filter
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum NotifyFilter : UInt32
|
||||
{
|
||||
None = 0x00000,
|
||||
ChangeFileName = 0x00001,
|
||||
ChangeDirName = 0x00002,
|
||||
ChangeName = ChangeFileName | ChangeDirName,
|
||||
ChangeAttributes = 0x00004,
|
||||
ChangeSize = 0x00008,
|
||||
ChangeLastWrite = 0x00010,
|
||||
ChangeLastAccess = 0x00020,
|
||||
ChangeCreation = 0x00040,
|
||||
ChangeEa = 0x00080,
|
||||
ChangeSecurity = 0x00100,
|
||||
ChangeStreamName = 0x00200,
|
||||
ChangeStreamSize = 0x00400,
|
||||
ChangeStreamWrite = 0x00800,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains file change notification information.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct NotifyInfo
|
||||
{
|
||||
public String FileName;
|
||||
public NotifyAction Action;
|
||||
public NotifyFilter Filter;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct FullEaInformation
|
||||
{
|
||||
@ -743,6 +822,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 +896,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 +1028,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 +1042,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 +1115,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 +1181,35 @@ 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 += NotifyInfoInternal.FileNameBufOffset +
|
||||
NotifyInfoArray[I].FileName.Length * 2;
|
||||
Length = (Length + 7) & ~7; // align to next qword boundary
|
||||
}
|
||||
|
||||
Byte[] Buffer = new Byte[Length];
|
||||
UInt32 BytesTransferred = default(UInt32);
|
||||
fixed (Byte *P = Buffer)
|
||||
{
|
||||
for (int I = 0; NotifyInfoArray.Length > I; I++)
|
||||
{
|
||||
NotifyInfoInternal Internal = default(NotifyInfoInternal);
|
||||
Internal.Action = (UInt32)NotifyInfoArray[I].Action;
|
||||
Internal.Filter = (UInt32)NotifyInfoArray[I].Filter;
|
||||
Internal.SetFileNameBuf(NotifyInfoArray[I].FileName);
|
||||
FspFileSystemAddNotifyInfo(
|
||||
ref Internal, (IntPtr)P, (UInt32)Length, out BytesTransferred);
|
||||
}
|
||||
return _FspFileSystemNotify(FileSystem, (IntPtr)P, (UIntPtr)BytesTransferred);
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe static Object GetUserContext(
|
||||
IntPtr NativePtr)
|
||||
{
|
||||
@ -1330,6 +1470,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 +1484,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>
|
||||
|
||||
|
@ -41,6 +41,10 @@ FSP_API VOID FspDeleteSid(PSID Sid, NTSTATUS (*CreateFunc)());
|
||||
FSP_API NTSTATUS FspPosixMapPermissionsToSecurityDescriptor(
|
||||
UINT32 Uid, UINT32 Gid, UINT32 Mode,
|
||||
PSECURITY_DESCRIPTOR *PSecurityDescriptor);
|
||||
FSP_API NTSTATUS FspPosixMergePermissionsToSecurityDescriptor(
|
||||
UINT32 Uid, UINT32 Gid, UINT32 Mode,
|
||||
PSECURITY_DESCRIPTOR ExistingSecurityDescriptor,
|
||||
PSECURITY_DESCRIPTOR *PSecurityDescriptor);
|
||||
FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions(
|
||||
PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||||
PUINT32 PUid, PUINT32 PGid, PUINT32 PMode);
|
||||
@ -59,6 +63,7 @@ FSP_API VOID FspPosixDecodeWindowsPath(PWSTR WindowsPath, ULONG Size);
|
||||
#pragma alloc_text(PAGE, FspPosixCreateSid)
|
||||
#pragma alloc_text(PAGE, FspDeleteSid)
|
||||
#pragma alloc_text(PAGE, FspPosixMapPermissionsToSecurityDescriptor)
|
||||
#pragma alloc_text(PAGE, FspPosixMergePermissionsToSecurityDescriptor)
|
||||
#pragma alloc_text(PAGE, FspPosixMapSecurityDescriptorToPermissions)
|
||||
#pragma alloc_text(PAGE, FspPosixMapWindowsToPosixPathEx)
|
||||
#pragma alloc_text(PAGE, FspPosixMapPosixToWindowsPathEx)
|
||||
@ -111,8 +116,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 +176,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 +206,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 +325,7 @@ VOID FspPosixFinalize(BOOLEAN Dynamic)
|
||||
|
||||
if (Dynamic)
|
||||
{
|
||||
MemFree(FspTrustedDomains);
|
||||
MemFree(FspAccountDomainSid);
|
||||
MemFree(FspPrimaryDomainSid);
|
||||
}
|
||||
@ -318,7 +472,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 +484,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 +606,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]
|
||||
@ -725,6 +903,101 @@ lasterror:
|
||||
goto exit;
|
||||
}
|
||||
|
||||
FSP_API NTSTATUS FspPosixMergePermissionsToSecurityDescriptor(
|
||||
UINT32 Uid, UINT32 Gid, UINT32 Mode,
|
||||
PSECURITY_DESCRIPTOR ExistingSecurityDescriptor,
|
||||
PSECURITY_DESCRIPTOR *PSecurityDescriptor)
|
||||
{
|
||||
FSP_KU_CODE;
|
||||
|
||||
if (0 == ExistingSecurityDescriptor)
|
||||
return FspPosixMapPermissionsToSecurityDescriptor(Uid, Gid, Mode, PSecurityDescriptor);
|
||||
|
||||
PSID ExistingOwnerSid = 0, ExistingGroupSid = 0;
|
||||
BOOL Defaulted, ExistingDaclPresent;
|
||||
PACL ExistingAcl = 0;
|
||||
PSID OwnerSid = 0, GroupSid = 0;
|
||||
SECURITY_DESCRIPTOR SecurityDescriptor;
|
||||
PSECURITY_DESCRIPTOR RelativeSecurityDescriptor = 0;
|
||||
ULONG Size;
|
||||
NTSTATUS Result;
|
||||
|
||||
*PSecurityDescriptor = 0;
|
||||
|
||||
if (!GetSecurityDescriptorOwner(ExistingSecurityDescriptor, &ExistingOwnerSid, &Defaulted))
|
||||
goto lasterror;
|
||||
if (!GetSecurityDescriptorGroup(ExistingSecurityDescriptor, &ExistingGroupSid, &Defaulted))
|
||||
goto lasterror;
|
||||
if (!GetSecurityDescriptorDacl(ExistingSecurityDescriptor, &ExistingDaclPresent, &ExistingAcl, &Defaulted))
|
||||
goto lasterror;
|
||||
|
||||
if (0 == ExistingOwnerSid)
|
||||
{
|
||||
Result = FspPosixMapUidToSid(Uid, &OwnerSid);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
ExistingOwnerSid = OwnerSid;
|
||||
}
|
||||
|
||||
if (0 == ExistingGroupSid)
|
||||
{
|
||||
Result = FspPosixMapUidToSid(Gid, &GroupSid);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
ExistingGroupSid = GroupSid;
|
||||
}
|
||||
|
||||
if (!InitializeSecurityDescriptor(&SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION))
|
||||
goto lasterror;
|
||||
|
||||
if (!SetSecurityDescriptorOwner(&SecurityDescriptor, ExistingOwnerSid, FALSE))
|
||||
goto lasterror;
|
||||
if (!SetSecurityDescriptorGroup(&SecurityDescriptor, ExistingGroupSid, FALSE))
|
||||
goto lasterror;
|
||||
if (0 != ExistingAcl)
|
||||
{
|
||||
if (!SetSecurityDescriptorControl(&SecurityDescriptor, SE_DACL_PROTECTED, SE_DACL_PROTECTED))
|
||||
goto lasterror;
|
||||
if (!SetSecurityDescriptorDacl(&SecurityDescriptor, TRUE, ExistingAcl, FALSE))
|
||||
goto lasterror;
|
||||
}
|
||||
|
||||
Size = 0;
|
||||
if (!MakeSelfRelativeSD(&SecurityDescriptor, 0, &Size) &&
|
||||
ERROR_INSUFFICIENT_BUFFER != GetLastError())
|
||||
goto lasterror;
|
||||
|
||||
RelativeSecurityDescriptor = MemAlloc(Size);
|
||||
if (0 == RelativeSecurityDescriptor)
|
||||
{
|
||||
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!MakeSelfRelativeSD(&SecurityDescriptor, RelativeSecurityDescriptor, &Size))
|
||||
goto lasterror;
|
||||
|
||||
*PSecurityDescriptor = RelativeSecurityDescriptor;
|
||||
|
||||
Result = STATUS_SUCCESS;
|
||||
|
||||
exit:
|
||||
if (!NT_SUCCESS(Result))
|
||||
MemFree(RelativeSecurityDescriptor);
|
||||
|
||||
if (0 != GroupSid)
|
||||
FspDeleteSid(GroupSid, FspPosixMapUidToSid);
|
||||
|
||||
if (0 != OwnerSid)
|
||||
FspDeleteSid(OwnerSid, FspPosixMapUidToSid);
|
||||
|
||||
return Result;
|
||||
|
||||
lasterror:
|
||||
Result = FspNtStatusFromWin32(GetLastError());
|
||||
goto exit;
|
||||
}
|
||||
|
||||
static inline ACCESS_MASK FspPosixCanonicalizeAccessMask(ACCESS_MASK AccessMask)
|
||||
{
|
||||
PGENERIC_MAPPING Mapping = FspGetFileGenericMapping();
|
||||
|
@ -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);
|
||||
|
@ -84,8 +84,9 @@ static NTSTATUS FspFsvolClose(
|
||||
FspFileDescDelete(FileDesc); /* this will also close the MainFileObject if any */
|
||||
FspFileNodeDereference(FileNode);
|
||||
|
||||
/* if we are closing files in the context of a rename make it synchronous */
|
||||
if (FspFsvolDeviceFileRenameIsAcquiredExclusive(FsvolDeviceObject))
|
||||
/* if closing in the context of a rename or IOQ is above the watermark make it synchronous */
|
||||
if (FspFsvolDeviceFileRenameIsAcquiredExclusive(FsvolDeviceObject) ||
|
||||
FspIoqPendingAboveWatermark(FspFsvolDeviceExtension(FsvolDeviceObject)->Ioq, 50))
|
||||
{
|
||||
/* acquire ownership of the Request */
|
||||
Request->Hint = (UINT_PTR)Irp;
|
||||
|
@ -22,6 +22,10 @@
|
||||
#include <sys/driver.h>
|
||||
|
||||
#if DBG
|
||||
|
||||
#undef STATUS_INSUFFICIENT_RESOURCES
|
||||
#define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC000009AL)
|
||||
|
||||
#define SYM(x) case x: return #x;
|
||||
#define SYMBRC(x) case x: return "[" #x "]";
|
||||
|
||||
@ -330,4 +334,5 @@ VOID FspDebugLogIrp(const char *func, PIRP Irp, NTSTATUS Result)
|
||||
NtStatusSym(Result),
|
||||
(LONGLONG)Irp->IoStatus.Information);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -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();
|
||||
|
276
src/sys/driver.c
276
src/sys/driver.c
@ -21,26 +21,16 @@
|
||||
|
||||
#include <sys/driver.h>
|
||||
|
||||
/*
|
||||
* Define the following macro to include FspUnload.
|
||||
*
|
||||
* Note that this driver is no longer unloadable.
|
||||
* See the comments in DriverEntry as to why!
|
||||
*/
|
||||
//#define FSP_UNLOAD
|
||||
|
||||
DRIVER_INITIALIZE DriverEntry;
|
||||
static VOID FspDriverMultiVersionInitialize(VOID);
|
||||
#if defined(FSP_UNLOAD)
|
||||
DRIVER_UNLOAD FspUnload;
|
||||
#endif
|
||||
static NTSTATUS FspDriverInitializeDevices(VOID);
|
||||
static VOID FspDriverFinalizeDevices(VOID);
|
||||
|
||||
#ifdef ALLOC_PRAGMA
|
||||
#pragma alloc_text(INIT, DriverEntry)
|
||||
#pragma alloc_text(INIT, FspDriverMultiVersionInitialize)
|
||||
#if defined(FSP_UNLOAD)
|
||||
#pragma alloc_text(PAGE, FspUnload)
|
||||
#endif
|
||||
#pragma alloc_text(PAGE, FspDriverInitializeDevices)
|
||||
#pragma alloc_text(PAGE, FspDriverFinalizeDevices)
|
||||
#endif
|
||||
|
||||
NTSTATUS DriverEntry(
|
||||
@ -48,10 +38,9 @@ NTSTATUS DriverEntry(
|
||||
{
|
||||
FSP_ENTER_DRV();
|
||||
|
||||
FSP_TRACE_INIT();
|
||||
|
||||
/* setup the driver object */
|
||||
#if defined(FSP_UNLOAD)
|
||||
DriverObject->DriverUnload = FspUnload;
|
||||
#endif
|
||||
DriverObject->MajorFunction[IRP_MJ_CREATE] = FspCreate;
|
||||
DriverObject->MajorFunction[IRP_MJ_CLOSE] = FspClose;
|
||||
DriverObject->MajorFunction[IRP_MJ_READ] = FspRead;
|
||||
@ -133,96 +122,45 @@ NTSTATUS DriverEntry(
|
||||
#pragma prefast(suppress:28175, "We are a filesystem: ok to access FastIoDispatch")
|
||||
DriverObject->FastIoDispatch = &FspFastIoDispatch;
|
||||
|
||||
BOOLEAN InitDoneGRes = FALSE, InitDonePsBuf = FALSE;
|
||||
UNICODE_STRING DeviceSddl;
|
||||
UNICODE_STRING DeviceName;
|
||||
BOOLEAN InitDoneGRes = FALSE, InitDoneSilo = FALSE, InitDonePsBuf = FALSE,
|
||||
InitDoneDevices = FALSE;
|
||||
|
||||
FspDriverObject = DriverObject;
|
||||
FspDriverMultiVersionInitialize();
|
||||
|
||||
ExInitializeResourceLite(&FspDeviceGlobalResource);
|
||||
InitDoneGRes = TRUE;
|
||||
|
||||
FspDriverMultiVersionInitialize();
|
||||
Result = FspSiloInitialize(FspDriverInitializeDevices, FspDriverFinalizeDevices);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
InitDoneSilo = TRUE;
|
||||
|
||||
Result = FspProcessBufferInitialize();
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
InitDonePsBuf = TRUE;
|
||||
|
||||
/* create the file system control device objects */
|
||||
RtlInitUnicodeString(&DeviceSddl, L"" FSP_FSCTL_DEVICE_SDDL);
|
||||
RtlInitUnicodeString(&DeviceName, L"\\Device\\" FSP_FSCTL_DISK_DEVICE_NAME);
|
||||
Result = FspDeviceCreateSecure(FspFsctlDeviceExtensionKind, 0,
|
||||
&DeviceName, FILE_DEVICE_DISK_FILE_SYSTEM, FILE_DEVICE_SECURE_OPEN,
|
||||
&DeviceSddl, &FspFsctlDeviceClassGuid,
|
||||
&FspFsctlDiskDeviceObject);
|
||||
Result = FspDriverInitializeDevices();
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
RtlInitUnicodeString(&DeviceName, L"\\Device\\" FSP_FSCTL_NET_DEVICE_NAME);
|
||||
Result = FspDeviceCreateSecure(FspFsctlDeviceExtensionKind, 0,
|
||||
&DeviceName, FILE_DEVICE_NETWORK_FILE_SYSTEM, FILE_DEVICE_SECURE_OPEN,
|
||||
&DeviceSddl, &FspFsctlDeviceClassGuid,
|
||||
&FspFsctlNetDeviceObject);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
Result = FspDeviceCreate(FspFsmupDeviceExtensionKind, 0,
|
||||
FILE_DEVICE_NETWORK_FILE_SYSTEM, FILE_REMOTE_DEVICE,
|
||||
&FspFsmupDeviceObject);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
#if DBG
|
||||
/*
|
||||
* Fix GitHub issue #177. All credit for the investigation of this issue
|
||||
* and the suggested steps to reproduce and work around the problem goes
|
||||
* to GitHub user @thinkport.
|
||||
*
|
||||
* On debug builds set DO_LOW_PRIORITY_FILESYSTEM to place the file system
|
||||
* at the end of the file system list during IoRegisterFileSystem below.
|
||||
* This allows us to test the behavior of our Fsvrt devices when foreign
|
||||
* file systems attempt to use them for mounting.
|
||||
*/
|
||||
SetFlag(FspFsctlDiskDeviceObject->Flags, DO_LOW_PRIORITY_FILESYSTEM);
|
||||
#endif
|
||||
|
||||
Result = FspDeviceInitialize(FspFsctlDiskDeviceObject);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
Result = FspDeviceInitialize(FspFsctlNetDeviceObject);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
Result = FspDeviceInitialize(FspFsmupDeviceObject);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
|
||||
RtlInitUnicodeString(&DeviceName, L"\\Device\\" FSP_FSCTL_MUP_DEVICE_NAME);
|
||||
Result = FsRtlRegisterUncProviderEx(&FspMupHandle,
|
||||
&DeviceName, FspFsmupDeviceObject, 0);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
/*
|
||||
* Register our "disk" device as a file system. We do not register our "net" device
|
||||
* as a file system; we register with the MUP instead.
|
||||
*
|
||||
* Please note that the call below makes our driver unloadable. In fact the driver
|
||||
* remains unloadable even if we issue an IoUnregisterFileSystem() call immediately
|
||||
* after our IoRegisterFileSystem() call! Some system component appears to keep an
|
||||
* extra reference to our device somewhere.
|
||||
*/
|
||||
IoRegisterFileSystem(FspFsctlDiskDeviceObject);
|
||||
InitDoneDevices = TRUE;
|
||||
|
||||
Result = STATUS_SUCCESS;
|
||||
|
||||
exit:
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
if (0 != FspFsmupDeviceObject)
|
||||
FspDeviceDelete(FspFsmupDeviceObject);
|
||||
if (0 != FspFsctlNetDeviceObject)
|
||||
FspDeviceDelete(FspFsctlNetDeviceObject);
|
||||
if (0 != FspFsctlDiskDeviceObject)
|
||||
FspDeviceDelete(FspFsctlDiskDeviceObject);
|
||||
if (InitDoneDevices)
|
||||
FspDriverFinalizeDevices();
|
||||
if (InitDonePsBuf)
|
||||
FspProcessBufferFinalize();
|
||||
if (InitDoneSilo)
|
||||
FspSiloFinalize();
|
||||
if (InitDoneGRes)
|
||||
ExDeleteResourceLite(&FspDeviceGlobalResource);
|
||||
|
||||
FSP_TRACE_FINI();
|
||||
}
|
||||
|
||||
#pragma prefast(suppress:28175, "We are in DriverEntry: ok to access DriverName")
|
||||
@ -237,7 +175,7 @@ static VOID FspDriverMultiVersionInitialize(VOID)
|
||||
#pragma prefast(suppress:30035, "FspDriverMultiVersionInitialize is called from DriverEntry")
|
||||
ExInitializeDriverRuntime(DrvRtPoolNxOptIn);
|
||||
|
||||
if (RtlIsNtDdiVersionAvailable(NTDDI_WIN7))
|
||||
if (FspIsNtDdiVersionAvailable(NTDDI_WIN7))
|
||||
{
|
||||
UNICODE_STRING Name;
|
||||
|
||||
@ -246,42 +184,164 @@ static VOID FspDriverMultiVersionInitialize(VOID)
|
||||
(FSP_MV_CcCoherencyFlushAndPurgeCache *)(UINT_PTR)MmGetSystemRoutineAddress(&Name);
|
||||
}
|
||||
|
||||
if (RtlIsNtDdiVersionAvailable(NTDDI_WIN8))
|
||||
if (FspIsNtDdiVersionAvailable(NTDDI_WIN8))
|
||||
FspMvMdlMappingNoWrite = MdlMappingNoWrite;
|
||||
|
||||
if (RtlIsNtDdiVersionAvailable(0x0A000005/*NTDDI_WIN10_RS4*/))
|
||||
if (FspIsNtDdiVersionAvailable(NTDDI_WIN10_RS4))
|
||||
FspHasReparsePointCaseSensitivityFix = TRUE;
|
||||
}
|
||||
|
||||
#if defined(FSP_UNLOAD)
|
||||
VOID FspUnload(
|
||||
PDRIVER_OBJECT DriverObject)
|
||||
static NTSTATUS FspDriverInitializeDevices(VOID)
|
||||
{
|
||||
FSP_ENTER_VOID(PAGED_CODE());
|
||||
PAGED_CODE();
|
||||
|
||||
FsRtlDeregisterUncProvider(FspMupHandle);
|
||||
FSP_SILO_GLOBALS *Globals;
|
||||
UNICODE_STRING DeviceSddl;
|
||||
UNICODE_STRING DeviceName;
|
||||
GUID Guid;
|
||||
NTSTATUS Result;
|
||||
|
||||
FspFsctlDiskDeviceObject = 0;
|
||||
FspFsctlNetDeviceObject = 0;
|
||||
FspFsmupDeviceObject = 0;
|
||||
//FspDeviceDeleteAll();
|
||||
FspSiloGetGlobals(&Globals);
|
||||
ASSERT(0 != Globals);
|
||||
|
||||
ExDeleteResourceLite(&FspDeviceGlobalResource);
|
||||
FspDriverObject = 0;
|
||||
/* create the file system control device objects */
|
||||
RtlInitUnicodeString(&DeviceSddl, L"" FSP_FSCTL_DEVICE_SDDL);
|
||||
RtlInitUnicodeString(&DeviceName, L"\\Device\\" FSP_FSCTL_DISK_DEVICE_NAME);
|
||||
Result = FspDeviceCreateSecure(FspFsctlDeviceExtensionKind, 0,
|
||||
&DeviceName, FILE_DEVICE_DISK_FILE_SYSTEM, FILE_DEVICE_SECURE_OPEN,
|
||||
&DeviceSddl, &FspFsctlDeviceClassGuid,
|
||||
&Globals->FsctlDiskDeviceObject);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
RtlInitUnicodeString(&DeviceName, L"\\Device\\" FSP_FSCTL_NET_DEVICE_NAME);
|
||||
Result = FspDeviceCreateSecure(FspFsctlDeviceExtensionKind, 0,
|
||||
&DeviceName, FILE_DEVICE_NETWORK_FILE_SYSTEM, FILE_DEVICE_SECURE_OPEN,
|
||||
&DeviceSddl, &FspFsctlDeviceClassGuid,
|
||||
&Globals->FsctlNetDeviceObject);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
Result = FspDeviceCreate(FspFsmupDeviceExtensionKind, 0,
|
||||
FILE_DEVICE_NETWORK_FILE_SYSTEM, FILE_REMOTE_DEVICE,
|
||||
&Globals->FsmupDeviceObject);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
FspProcessBufferFinalize();
|
||||
|
||||
#pragma prefast(suppress:28175, "We are in DriverUnload: ok to access DriverName")
|
||||
FSP_LEAVE_VOID("DriverName=\"%wZ\"",
|
||||
&DriverObject->DriverName);
|
||||
}
|
||||
#if DBG
|
||||
/*
|
||||
* Fix GitHub issue #177. All credit for the investigation of this issue
|
||||
* and the suggested steps to reproduce and work around the problem goes
|
||||
* to GitHub user @thinkport.
|
||||
*
|
||||
* On debug builds set DO_LOW_PRIORITY_FILESYSTEM to place the file system
|
||||
* at the end of the file system list during IoRegisterFileSystem below.
|
||||
* This allows us to test the behavior of our Fsvrt devices when foreign
|
||||
* file systems attempt to use them for mounting.
|
||||
*/
|
||||
SetFlag(Globals->FsctlDiskDeviceObject->Flags, DO_LOW_PRIORITY_FILESYSTEM);
|
||||
#endif
|
||||
|
||||
Result = FspDeviceInitialize(Globals->FsctlDiskDeviceObject);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
Result = FspDeviceInitialize(Globals->FsctlNetDeviceObject);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
Result = FspDeviceInitialize(Globals->FsmupDeviceObject);
|
||||
ASSERT(STATUS_SUCCESS == Result);
|
||||
|
||||
FspSiloGetContainerId(&Guid);
|
||||
RtlInitEmptyUnicodeString(&DeviceName,
|
||||
Globals->FsmupDeviceNameBuf, sizeof Globals->FsmupDeviceNameBuf);
|
||||
Result = RtlUnicodeStringPrintf(&DeviceName,
|
||||
0 == ((PULONG)&Guid)[0] && 0 == ((PULONG)&Guid)[1] &&
|
||||
0 == ((PULONG)&Guid)[2] && 0 == ((PULONG)&Guid)[3] ?
|
||||
L"\\Device\\" FSP_FSCTL_MUP_DEVICE_NAME :
|
||||
L"\\Device\\" FSP_FSCTL_MUP_DEVICE_NAME "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
|
||||
Guid.Data1, Guid.Data2, Guid.Data3,
|
||||
Guid.Data4[0], Guid.Data4[1], Guid.Data4[2], Guid.Data4[3],
|
||||
Guid.Data4[4], Guid.Data4[5], Guid.Data4[6], Guid.Data4[7]);
|
||||
ASSERT(NT_SUCCESS(Result));
|
||||
DeviceName.MaximumLength = DeviceName.Length;
|
||||
Result = FsRtlRegisterUncProviderEx(&Globals->MupHandle,
|
||||
&DeviceName, Globals->FsmupDeviceObject, 0);
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
Globals->MupHandle = 0;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register our "disk" device as a file system. We do not register our "net" device
|
||||
* as a file system; we register with the MUP instead.
|
||||
*/
|
||||
IoRegisterFileSystem(Globals->FsctlDiskDeviceObject);
|
||||
|
||||
Result = STATUS_SUCCESS;
|
||||
|
||||
exit:
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
if (0 != Globals->MupHandle)
|
||||
{
|
||||
FsRtlDeregisterUncProvider(Globals->MupHandle);
|
||||
Globals->MupHandle = 0;
|
||||
}
|
||||
if (0 != Globals->FsmupDeviceObject)
|
||||
{
|
||||
FspDeviceDelete(Globals->FsmupDeviceObject);
|
||||
Globals->FsmupDeviceObject = 0;
|
||||
}
|
||||
if (0 != Globals->FsctlNetDeviceObject)
|
||||
{
|
||||
FspDeviceDelete(Globals->FsctlNetDeviceObject);
|
||||
Globals->FsctlNetDeviceObject = 0;
|
||||
}
|
||||
if (0 != Globals->FsctlDiskDeviceObject)
|
||||
{
|
||||
FspDeviceDelete(Globals->FsctlDiskDeviceObject);
|
||||
Globals->FsctlDiskDeviceObject = 0;
|
||||
}
|
||||
}
|
||||
|
||||
FspSiloDereferenceGlobals(Globals);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static VOID FspDriverFinalizeDevices(VOID)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
FSP_SILO_GLOBALS *Globals;
|
||||
|
||||
FspSiloGetGlobals(&Globals);
|
||||
ASSERT(0 != Globals);
|
||||
|
||||
IoUnregisterFileSystem(Globals->FsctlDiskDeviceObject);
|
||||
|
||||
if (0 != Globals->MupHandle)
|
||||
{
|
||||
FsRtlDeregisterUncProvider(Globals->MupHandle);
|
||||
Globals->MupHandle = 0;
|
||||
}
|
||||
if (0 != Globals->FsmupDeviceObject)
|
||||
{
|
||||
FspDeviceDelete(Globals->FsmupDeviceObject);
|
||||
Globals->FsmupDeviceObject = 0;
|
||||
}
|
||||
if (0 != Globals->FsctlNetDeviceObject)
|
||||
{
|
||||
FspDeviceDelete(Globals->FsctlNetDeviceObject);
|
||||
Globals->FsctlNetDeviceObject = 0;
|
||||
}
|
||||
if (0 != Globals->FsctlDiskDeviceObject)
|
||||
{
|
||||
FspDeviceDelete(Globals->FsctlDiskDeviceObject);
|
||||
Globals->FsctlDiskDeviceObject = 0;
|
||||
}
|
||||
|
||||
FspSiloDereferenceGlobals(Globals);
|
||||
}
|
||||
|
||||
PDRIVER_OBJECT FspDriverObject;
|
||||
PDEVICE_OBJECT FspFsctlDiskDeviceObject;
|
||||
PDEVICE_OBJECT FspFsctlNetDeviceObject;
|
||||
PDEVICE_OBJECT FspFsmupDeviceObject;
|
||||
HANDLE FspMupHandle;
|
||||
FAST_IO_DISPATCH FspFastIoDispatch;
|
||||
CACHE_MANAGER_CALLBACKS FspCacheManagerCallbacks;
|
||||
|
||||
|
@ -147,6 +147,40 @@ VOID FspDebugLogIrp(const char *func, PIRP Irp, NTSTATUS Result);
|
||||
#define DEBUGTEST_EX(C, Percent, Deflt) (Deflt)
|
||||
#endif
|
||||
|
||||
/* trace */
|
||||
#if FSP_TRACE_ENABLED
|
||||
VOID FspTraceInitialize(VOID);
|
||||
VOID FspTraceFinalize(VOID);
|
||||
VOID FspTrace(const char *file, int line, const char *func);
|
||||
VOID FspTraceNtStatus(const char *file, int line, const char *func, NTSTATUS Status);
|
||||
#define FSP_TRACE_INIT() \
|
||||
FspTraceInitialize()
|
||||
#define FSP_TRACE_FINI() \
|
||||
FspTraceFinalize()
|
||||
#define FSP_TRACE() \
|
||||
FspTrace( \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
__FUNCTION__)
|
||||
#define FSP_TRACE_NTSTATUS(Status) \
|
||||
FspTraceNtStatus( \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
__FUNCTION__, \
|
||||
Status)
|
||||
#else
|
||||
#define FSP_TRACE_INIT() \
|
||||
((VOID)0)
|
||||
#define FSP_TRACE_FINI() \
|
||||
((VOID)0)
|
||||
#define FSP_TRACE() \
|
||||
((VOID)0)
|
||||
#define FSP_TRACE_NTSTATUS(Result) \
|
||||
((VOID)0)
|
||||
#endif
|
||||
#undef STATUS_INSUFFICIENT_RESOURCES
|
||||
#define STATUS_INSUFFICIENT_RESOURCES (FSP_TRACE_NTSTATUS(0xC000009AL), (NTSTATUS)0xC000009AL)
|
||||
|
||||
/* FSP_ENTER/FSP_LEAVE */
|
||||
#if DBG
|
||||
#define FSP_DEBUGLOG_(fmt, rfmt, ...) \
|
||||
@ -503,6 +537,7 @@ NTSTATUS FspUuid5Make(const UUID *Namespace, const VOID *Buffer, ULONG Size, UUI
|
||||
/* utility */
|
||||
PVOID FspAllocatePoolMustSucceed(POOL_TYPE PoolType, SIZE_T Size, ULONG Tag);
|
||||
PVOID FspAllocateIrpMustSucceed(CCHAR StackSize);
|
||||
BOOLEAN FspIsNtDdiVersionAvailable(ULONG RequestedVersion);
|
||||
NTSTATUS FspCreateGuid(GUID *Guid);
|
||||
NTSTATUS FspGetDeviceObjectPointer(PUNICODE_STRING ObjectName, ACCESS_MASK DesiredAccess,
|
||||
PULONG PFileNameIndex, PFILE_OBJECT *PFileObject, PDEVICE_OBJECT *PDeviceObject);
|
||||
@ -651,6 +686,45 @@ 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
|
||||
{
|
||||
PDEVICE_OBJECT FsctlDiskDeviceObject;
|
||||
PDEVICE_OBJECT FsctlNetDeviceObject;
|
||||
PDEVICE_OBJECT FsmupDeviceObject;
|
||||
HANDLE MupHandle;
|
||||
WCHAR FsmupDeviceNameBuf[64];
|
||||
} FSP_SILO_GLOBALS;
|
||||
typedef NTSTATUS (*FSP_SILO_INIT_CALLBACK)(VOID);
|
||||
typedef VOID (*FSP_SILO_FINI_CALLBACK)(VOID);
|
||||
NTSTATUS FspSiloGetGlobals(FSP_SILO_GLOBALS **PGlobals);
|
||||
VOID FspSiloDereferenceGlobals(FSP_SILO_GLOBALS *Globals);
|
||||
VOID FspSiloGetContainerId(GUID *ContainerId);
|
||||
NTSTATUS FspSiloInitialize(FSP_SILO_INIT_CALLBACK Init, FSP_SILO_FINI_CALLBACK Fini);
|
||||
VOID FspSiloFinalize(VOID);
|
||||
|
||||
/* process buffers */
|
||||
#define FspProcessBufferSizeMax (64 * 1024)
|
||||
NTSTATUS FspProcessBufferInitialize(VOID);
|
||||
@ -879,6 +953,7 @@ BOOLEAN FspIoqPostIrpEx(FSP_IOQ *Ioq, PIRP Irp, BOOLEAN BestEffort, NTSTATUS *PR
|
||||
PIRP FspIoqNextPendingIrp(FSP_IOQ *Ioq, PIRP BoundaryIrp, PLARGE_INTEGER Timeout,
|
||||
PIRP CancellableIrp);
|
||||
ULONG FspIoqPendingIrpCount(FSP_IOQ *Ioq);
|
||||
BOOLEAN FspIoqPendingAboveWatermark(FSP_IOQ *Ioq, ULONG Watermark);
|
||||
BOOLEAN FspIoqStartProcessingIrp(FSP_IOQ *Ioq, PIRP Irp);
|
||||
PIRP FspIoqEndProcessingIrp(FSP_IOQ *Ioq, UINT_PTR IrpHint);
|
||||
ULONG FspIoqProcessIrpCount(FSP_IOQ *Ioq);
|
||||
@ -1068,6 +1143,7 @@ typedef struct
|
||||
KSPIN_LOCK SpinLock;
|
||||
LONG RefCount;
|
||||
UINT32 Kind;
|
||||
GUID SiloContainerId;
|
||||
} FSP_DEVICE_EXTENSION;
|
||||
typedef struct
|
||||
{
|
||||
@ -1105,6 +1181,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;
|
||||
@ -1163,7 +1241,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);
|
||||
@ -1309,6 +1389,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);
|
||||
|
||||
@ -1340,8 +1422,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
|
||||
{
|
||||
@ -1372,11 +1456,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;
|
||||
@ -1521,6 +1603,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)
|
||||
{
|
||||
@ -1554,6 +1637,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)
|
||||
{
|
||||
@ -1563,6 +1647,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);
|
||||
@ -1694,10 +1781,6 @@ FSP_MV_CcCoherencyFlushAndPurgeCache(
|
||||
|
||||
/* extern */
|
||||
extern PDRIVER_OBJECT FspDriverObject;
|
||||
extern PDEVICE_OBJECT FspFsctlDiskDeviceObject;
|
||||
extern PDEVICE_OBJECT FspFsctlNetDeviceObject;
|
||||
extern PDEVICE_OBJECT FspFsmupDeviceObject;
|
||||
extern HANDLE FspMupHandle;
|
||||
extern FAST_IO_DISPATCH FspFastIoDispatch;
|
||||
extern CACHE_MANAGER_CALLBACKS FspCacheManagerCallbacks;
|
||||
extern FSP_IOPREP_DISPATCH *FspIopPrepareFunction[];
|
||||
|
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)))
|
||||
@ -698,6 +702,10 @@ static NTSTATUS FspFsvolFileSystemControl(
|
||||
case FSP_FSCTL_WORK_BEST_EFFORT:
|
||||
Result = FspVolumeWork(FsvolDeviceObject, Irp, IrpSp);
|
||||
break;
|
||||
case FSP_FSCTL_QUERY_WINFSP:
|
||||
Irp->IoStatus.Information = 0;
|
||||
Result = STATUS_SUCCESS;
|
||||
break;
|
||||
case FSCTL_GET_REPARSE_POINT:
|
||||
Result = FspFsvolFileSystemControlReparsePoint(FsvolDeviceObject, Irp, IrpSp, FALSE);
|
||||
break;
|
||||
|
@ -643,6 +643,16 @@ ULONG FspIoqPendingIrpCount(FSP_IOQ *Ioq)
|
||||
return Result;
|
||||
}
|
||||
|
||||
BOOLEAN FspIoqPendingAboveWatermark(FSP_IOQ *Ioq, ULONG Watermark)
|
||||
{
|
||||
BOOLEAN Result;
|
||||
KIRQL Irql;
|
||||
KeAcquireSpinLock(&Ioq->SpinLock, &Irql);
|
||||
Result = Watermark < 100 * Ioq->PendingIrpCount / Ioq->PendingIrpCapacity;
|
||||
KeReleaseSpinLock(&Ioq->SpinLock, Irql);
|
||||
return Result;
|
||||
}
|
||||
|
||||
BOOLEAN FspIoqStartProcessingIrp(FSP_IOQ *Ioq, PIRP Irp)
|
||||
{
|
||||
NTSTATUS Result;
|
||||
|
@ -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)
|
||||
{
|
||||
|
325
src/sys/silo.c
Normal file
325
src/sys/silo.c
Normal file
@ -0,0 +1,325 @@
|
||||
/**
|
||||
* @file sys/silo.c
|
||||
*
|
||||
* @copyright 2015-2020 Bill Zissimopoulos
|
||||
*/
|
||||
/*
|
||||
* This file is part of WinFsp.
|
||||
*
|
||||
* You can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License version 3 as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Licensees holding a valid commercial license may use this software
|
||||
* in accordance with the commercial license agreement provided in
|
||||
* conjunction with the software. The terms and conditions of any such
|
||||
* commercial license agreement shall govern, supersede, and render
|
||||
* ineffective any application of the GPLv3 license to this software,
|
||||
* notwithstanding of any reference thereto in the software or
|
||||
* associated repository.
|
||||
*/
|
||||
|
||||
#include <sys/driver.h>
|
||||
|
||||
NTSTATUS FspSiloInitialize(FSP_SILO_INIT_CALLBACK Init, FSP_SILO_FINI_CALLBACK Fini);
|
||||
|
||||
#ifdef ALLOC_PRAGMA
|
||||
#pragma alloc_text(INIT, FspSiloInitialize)
|
||||
#endif
|
||||
|
||||
typedef PEJOB FSP_PESILO;
|
||||
typedef PVOID FSP_PSILO_MONITOR;
|
||||
|
||||
typedef NTSTATUS (NTAPI *FSP_SILO_MONITOR_CREATE_CALLBACK)(FSP_PESILO Silo);
|
||||
typedef VOID (NTAPI *FSP_SILO_MONITOR_TERMINATE_CALLBACK)(FSP_PESILO Silo);
|
||||
typedef VOID (NTAPI *FSP_SILO_CONTEXT_CLEANUP_CALLBACK)(PVOID SiloContext);
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4201) /* nameless struct/union */
|
||||
typedef struct _FSP_SILO_MONITOR_REGISTRATION
|
||||
{
|
||||
UCHAR Version;
|
||||
BOOLEAN MonitorHost;
|
||||
BOOLEAN MonitorExistingSilos;
|
||||
UCHAR Reserved[5];
|
||||
union
|
||||
{
|
||||
PUNICODE_STRING DriverObjectName;
|
||||
PUNICODE_STRING ComponentName;
|
||||
};
|
||||
FSP_SILO_MONITOR_CREATE_CALLBACK CreateCallback;
|
||||
FSP_SILO_MONITOR_TERMINATE_CALLBACK TerminateCallback;
|
||||
} FSP_SILO_MONITOR_REGISTRATION;
|
||||
#pragma warning(pop)
|
||||
|
||||
typedef NTSTATUS FSP_SILO_PsRegisterSiloMonitor(
|
||||
FSP_SILO_MONITOR_REGISTRATION *Registration,
|
||||
FSP_PSILO_MONITOR *ReturnedMonitor);
|
||||
typedef NTSTATUS FSP_SILO_PsStartSiloMonitor(
|
||||
FSP_PSILO_MONITOR Monitor);
|
||||
typedef VOID FSP_SILO_PsUnregisterSiloMonitor(
|
||||
FSP_PSILO_MONITOR Monitor);
|
||||
typedef ULONG FSP_SILO_PsGetSiloMonitorContextSlot(
|
||||
FSP_PSILO_MONITOR Monitor);
|
||||
typedef NTSTATUS FSP_SILO_PsCreateSiloContext(
|
||||
FSP_PESILO Silo,
|
||||
ULONG Size,
|
||||
POOL_TYPE PoolType,
|
||||
FSP_SILO_CONTEXT_CLEANUP_CALLBACK ContextCleanupCallback,
|
||||
PVOID *ReturnedSiloContext);
|
||||
typedef VOID FSP_SILO_PsDereferenceSiloContext(
|
||||
PVOID SiloContext);
|
||||
typedef NTSTATUS FSP_SILO_PsInsertSiloContext(
|
||||
FSP_PESILO Silo,
|
||||
ULONG ContextSlot,
|
||||
PVOID SiloContext);
|
||||
typedef NTSTATUS FSP_SILO_PsRemoveSiloContext(
|
||||
FSP_PESILO Silo,
|
||||
ULONG ContextSlot,
|
||||
PVOID *RemovedSiloContext);
|
||||
typedef NTSTATUS FSP_SILO_PsGetSiloContext(
|
||||
FSP_PESILO Silo,
|
||||
ULONG ContextSlot,
|
||||
PVOID *ReturnedSiloContext);
|
||||
typedef FSP_PESILO FSP_SILO_PsAttachSiloToCurrentThread(
|
||||
FSP_PESILO Silo);
|
||||
typedef VOID FSP_SILO_PsDetachSiloFromCurrentThread(
|
||||
FSP_PESILO PreviousSilo);
|
||||
typedef FSP_PESILO FSP_SILO_PsGetCurrentServerSilo(
|
||||
VOID);
|
||||
typedef GUID* FSP_SILO_PsGetSiloContainerId(
|
||||
FSP_PESILO Silo);
|
||||
|
||||
static FSP_SILO_PsRegisterSiloMonitor *FspSiloPsRegisterSiloMonitor;
|
||||
static FSP_SILO_PsStartSiloMonitor *FspSiloPsStartSiloMonitor;
|
||||
static FSP_SILO_PsUnregisterSiloMonitor *FspSiloPsUnregisterSiloMonitor;
|
||||
static FSP_SILO_PsGetSiloMonitorContextSlot *FspSiloPsGetSiloMonitorContextSlot;
|
||||
static FSP_SILO_PsCreateSiloContext *FspSiloPsCreateSiloContext;
|
||||
static FSP_SILO_PsDereferenceSiloContext *FspSiloPsDereferenceSiloContext;
|
||||
static FSP_SILO_PsInsertSiloContext *FspSiloPsInsertSiloContext;
|
||||
static FSP_SILO_PsRemoveSiloContext *FspSiloPsRemoveSiloContext;
|
||||
static FSP_SILO_PsGetSiloContext *FspSiloPsGetSiloContext;
|
||||
static FSP_SILO_PsAttachSiloToCurrentThread *FspSiloPsAttachSiloToCurrentThread;
|
||||
static FSP_SILO_PsDetachSiloFromCurrentThread *FspSiloPsDetachSiloFromCurrentThread;
|
||||
static FSP_SILO_PsGetCurrentServerSilo *FspSiloPsGetCurrentServerSilo;
|
||||
static FSP_SILO_PsGetSiloContainerId *FspSiloPsGetSiloContainerId;
|
||||
static FSP_PSILO_MONITOR FspSiloMonitor;
|
||||
static FSP_SILO_INIT_CALLBACK FspSiloInitCallback;
|
||||
static FSP_SILO_FINI_CALLBACK FspSiloFiniCallback;
|
||||
static BOOLEAN FspSiloInitDone = FALSE;
|
||||
|
||||
static FSP_SILO_GLOBALS FspSiloHostGlobals;
|
||||
|
||||
#define FSP_SILO_MONITOR_REGISTRATION_VERSION 1
|
||||
|
||||
#define LOAD(n) \
|
||||
{ \
|
||||
UNICODE_STRING Name; \
|
||||
RtlInitUnicodeString(&Name, L"" #n);\
|
||||
FspSilo ## n = \
|
||||
(FSP_SILO_ ## n *)(UINT_PTR)MmGetSystemRoutineAddress(&Name);\
|
||||
if (0 == FspSilo ## n) \
|
||||
Fail++; \
|
||||
}
|
||||
#define CALL(n) (FspSilo ## n)
|
||||
|
||||
NTSTATUS FspSiloGetGlobals(FSP_SILO_GLOBALS **PGlobals)
|
||||
{
|
||||
FSP_PESILO Silo;
|
||||
ULONG ContextSlot;
|
||||
FSP_SILO_GLOBALS *Globals;
|
||||
NTSTATUS Result;
|
||||
|
||||
if (!FspSiloInitDone ||
|
||||
0 == (Silo = CALL(PsGetCurrentServerSilo)()))
|
||||
{
|
||||
*PGlobals = &FspSiloHostGlobals;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
ContextSlot = CALL(PsGetSiloMonitorContextSlot)(FspSiloMonitor);
|
||||
|
||||
Result = CALL(PsGetSiloContext)(Silo, ContextSlot, &Globals);
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
*PGlobals = 0;
|
||||
return Result;
|
||||
}
|
||||
|
||||
*PGlobals = Globals;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
VOID FspSiloDereferenceGlobals(FSP_SILO_GLOBALS *Globals)
|
||||
{
|
||||
if (&FspSiloHostGlobals == Globals)
|
||||
return;
|
||||
|
||||
CALL(PsDereferenceSiloContext)(Globals);
|
||||
}
|
||||
|
||||
VOID FspSiloGetContainerId(GUID *ContainerId)
|
||||
{
|
||||
FSP_PESILO Silo;
|
||||
GUID *Guid;
|
||||
|
||||
if (!FspSiloInitDone ||
|
||||
0 == (Silo = CALL(PsGetCurrentServerSilo)()) ||
|
||||
0 == (Guid = CALL(PsGetSiloContainerId)(Silo)))
|
||||
{
|
||||
RtlZeroMemory(ContainerId, sizeof *ContainerId);
|
||||
return;
|
||||
}
|
||||
|
||||
RtlCopyMemory(ContainerId, Guid, sizeof *ContainerId);
|
||||
}
|
||||
|
||||
static NTSTATUS NTAPI FspSiloMonitorCreateCallback(FSP_PESILO Silo)
|
||||
{
|
||||
ULONG ContextSlot;
|
||||
FSP_SILO_GLOBALS *Globals = 0;
|
||||
BOOLEAN Inserted = FALSE;
|
||||
NTSTATUS Result;
|
||||
|
||||
ASSERT(0 != Silo);
|
||||
|
||||
ContextSlot = CALL(PsGetSiloMonitorContextSlot)(FspSiloMonitor);
|
||||
|
||||
Result = CALL(PsCreateSiloContext)(Silo, sizeof(FSP_SILO_GLOBALS), NonPagedPoolNx, 0, &Globals);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
RtlZeroMemory(Globals, sizeof(FSP_SILO_GLOBALS));
|
||||
|
||||
/* PsInsertSiloContext adds reference to Globals */
|
||||
Result = CALL(PsInsertSiloContext)(Silo, ContextSlot, Globals);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
Inserted = TRUE;
|
||||
|
||||
if (0 != FspSiloInitCallback)
|
||||
{
|
||||
FSP_PESILO PreviousSilo = CALL(PsAttachSiloToCurrentThread)(Silo);
|
||||
Result = FspSiloInitCallback();
|
||||
CALL(PsDetachSiloFromCurrentThread)(PreviousSilo);
|
||||
}
|
||||
|
||||
exit:
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
if (Inserted)
|
||||
CALL(PsRemoveSiloContext)(Silo, ContextSlot, 0);
|
||||
}
|
||||
|
||||
if (0 != Globals)
|
||||
CALL(PsDereferenceSiloContext)(Globals);
|
||||
|
||||
/*
|
||||
* Ignore errors and return success. There are two reasons for this:
|
||||
*
|
||||
* - Returning an error will disallow container creation.
|
||||
* - In some cases returning an error will crash Windows with an
|
||||
* unexpected page fault in wcifs.sys.
|
||||
*/
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static VOID NTAPI FspSiloMonitorTerminateCallback(FSP_PESILO Silo)
|
||||
{
|
||||
ULONG ContextSlot;
|
||||
FSP_SILO_GLOBALS *Globals;
|
||||
NTSTATUS Result;
|
||||
|
||||
ASSERT(0 != Silo);
|
||||
|
||||
ContextSlot = CALL(PsGetSiloMonitorContextSlot)(FspSiloMonitor);
|
||||
|
||||
/* if we cannot get the Globals, it must mean that we failed in Create callback */
|
||||
Result = CALL(PsGetSiloContext)(Silo, ContextSlot, &Globals);
|
||||
if (!NT_SUCCESS(Result))
|
||||
return;
|
||||
CALL(PsDereferenceSiloContext)(Globals);
|
||||
Globals = 0;
|
||||
|
||||
if (0 != FspSiloFiniCallback)
|
||||
{
|
||||
FSP_PESILO PreviousSilo = CALL(PsAttachSiloToCurrentThread)(Silo);
|
||||
FspSiloFiniCallback();
|
||||
CALL(PsDetachSiloFromCurrentThread)(PreviousSilo);
|
||||
}
|
||||
|
||||
/* PsRemoveSiloContext removes reference to Globals (possibly freeing it) */
|
||||
CALL(PsRemoveSiloContext)(Silo, ContextSlot, 0);
|
||||
}
|
||||
|
||||
NTSTATUS FspSiloInitialize(FSP_SILO_INIT_CALLBACK Init, FSP_SILO_FINI_CALLBACK Fini)
|
||||
{
|
||||
NTSTATUS Result = STATUS_SUCCESS;
|
||||
|
||||
if (FspIsNtDdiVersionAvailable(NTDDI_WIN10_RS5))
|
||||
{
|
||||
ULONG Fail = 0;
|
||||
|
||||
LOAD(PsRegisterSiloMonitor);
|
||||
LOAD(PsStartSiloMonitor);
|
||||
LOAD(PsUnregisterSiloMonitor);
|
||||
LOAD(PsGetSiloMonitorContextSlot);
|
||||
LOAD(PsCreateSiloContext);
|
||||
LOAD(PsDereferenceSiloContext);
|
||||
LOAD(PsInsertSiloContext);
|
||||
LOAD(PsRemoveSiloContext);
|
||||
LOAD(PsGetSiloContext);
|
||||
LOAD(PsAttachSiloToCurrentThread);
|
||||
LOAD(PsDetachSiloFromCurrentThread);
|
||||
LOAD(PsGetCurrentServerSilo);
|
||||
LOAD(PsGetSiloContainerId);
|
||||
|
||||
if (0 == Fail)
|
||||
{
|
||||
FSP_SILO_MONITOR_REGISTRATION Registration =
|
||||
{
|
||||
.Version = FSP_SILO_MONITOR_REGISTRATION_VERSION,
|
||||
.MonitorHost = FALSE,
|
||||
.MonitorExistingSilos = TRUE,
|
||||
.DriverObjectName = 0,
|
||||
.CreateCallback = FspSiloMonitorCreateCallback,
|
||||
.TerminateCallback = FspSiloMonitorTerminateCallback,
|
||||
};
|
||||
FSP_PSILO_MONITOR Monitor = 0;
|
||||
UNICODE_STRING DriverName;
|
||||
|
||||
RtlInitUnicodeString(&DriverName, L"" DRIVER_NAME);
|
||||
Registration.DriverObjectName = &DriverName;
|
||||
Result = CALL(PsRegisterSiloMonitor)(&Registration, &Monitor);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
Result = CALL(PsStartSiloMonitor)(Monitor);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
FspSiloMonitor = Monitor;
|
||||
FspSiloInitCallback = Init;
|
||||
FspSiloFiniCallback = Fini;
|
||||
|
||||
FspSiloInitDone = TRUE;
|
||||
Result = STATUS_SUCCESS;
|
||||
|
||||
exit:
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
if (0 != Monitor)
|
||||
CALL(PsUnregisterSiloMonitor)(Monitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
VOID FspSiloFinalize(VOID)
|
||||
{
|
||||
if (!FspSiloInitDone)
|
||||
return;
|
||||
|
||||
CALL(PsUnregisterSiloMonitor)(FspSiloMonitor);
|
||||
}
|
146
src/sys/trace.c
Normal file
146
src/sys/trace.c
Normal file
@ -0,0 +1,146 @@
|
||||
/**
|
||||
* @file sys/trace.c
|
||||
*
|
||||
* @copyright 2015-2020 Bill Zissimopoulos
|
||||
*/
|
||||
/*
|
||||
* This file is part of WinFsp.
|
||||
*
|
||||
* You can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License version 3 as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Licensees holding a valid commercial license may use this software
|
||||
* in accordance with the commercial license agreement provided in
|
||||
* conjunction with the software. The terms and conditions of any such
|
||||
* commercial license agreement shall govern, supersede, and render
|
||||
* ineffective any application of the GPLv3 license to this software,
|
||||
* notwithstanding of any reference thereto in the software or
|
||||
* associated repository.
|
||||
*/
|
||||
|
||||
#include <sys/driver.h>
|
||||
|
||||
#if FSP_TRACE_ENABLED
|
||||
|
||||
#undef STATUS_INSUFFICIENT_RESOURCES
|
||||
#define STATUS_INSUFFICIENT_RESOURCES ((NTSTATUS)0xC000009AL)
|
||||
|
||||
static struct
|
||||
{
|
||||
HANDLE Handle;
|
||||
PKEVENT Event;
|
||||
} FspLowMemoryCondition, FspLowNonPagedPoolCondition, FspLowPagedPoolCondition;
|
||||
|
||||
VOID FspTrace(const char *file, int line, const char *func)
|
||||
{
|
||||
for (const char *p = file; '\0' != *p; p++)
|
||||
if ('\\' == *p)
|
||||
file = p + 1;
|
||||
|
||||
ASSERT(DISPATCH_LEVEL >= KeGetCurrentIrql());
|
||||
DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_TRACE_LEVEL,
|
||||
DRIVER_NAME ": %s[%s:%d]\n", func, file, line);
|
||||
}
|
||||
|
||||
VOID FspTraceNtStatus(const char *file, int line, const char *func, NTSTATUS Status)
|
||||
{
|
||||
for (const char *p = file; '\0' != *p; p++)
|
||||
if ('\\' == *p)
|
||||
file = p + 1;
|
||||
|
||||
ASSERT(DISPATCH_LEVEL >= KeGetCurrentIrql());
|
||||
BOOLEAN LowMemoryCondition, LowNonPagedPoolCondition, LowPagedPoolCondition;
|
||||
switch (Status)
|
||||
{
|
||||
case STATUS_INSUFFICIENT_RESOURCES:
|
||||
LowMemoryCondition = 0 != FspLowMemoryCondition.Event &&
|
||||
!!KeReadStateEvent(FspLowMemoryCondition.Event);
|
||||
LowNonPagedPoolCondition = 0 != FspLowNonPagedPoolCondition.Event &&
|
||||
!!KeReadStateEvent(FspLowNonPagedPoolCondition.Event);
|
||||
LowPagedPoolCondition = 0 != FspLowPagedPoolCondition.Event &&
|
||||
!!KeReadStateEvent(FspLowPagedPoolCondition.Event);
|
||||
DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_TRACE_LEVEL,
|
||||
DRIVER_NAME ": %s[%s:%d]: STATUS_INSUFFICIENT_RESOURCES (Memory=%c%c%c)\n",
|
||||
func, file, line,
|
||||
LowMemoryCondition ? 'M' : '-',
|
||||
LowNonPagedPoolCondition ? 'N' : '-',
|
||||
LowPagedPoolCondition ? 'P' : '-');
|
||||
break;
|
||||
default:
|
||||
DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_TRACE_LEVEL,
|
||||
DRIVER_NAME ": %s[%s:%d]: Status=%lx\n",
|
||||
func, file, line,
|
||||
Status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static PKEVENT FspOpenEvent(PWSTR Name, PHANDLE PHandle)
|
||||
{
|
||||
UNICODE_STRING ObjectName;
|
||||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
HANDLE Handle;
|
||||
PKEVENT Event;
|
||||
NTSTATUS Result;
|
||||
|
||||
*PHandle = 0;
|
||||
|
||||
RtlInitUnicodeString(&ObjectName, Name);
|
||||
InitializeObjectAttributes(&ObjectAttributes,
|
||||
&ObjectName, OBJ_KERNEL_HANDLE, 0, 0);
|
||||
Result = ZwOpenEvent(&Handle,
|
||||
EVENT_ALL_ACCESS, &ObjectAttributes);
|
||||
if (!NT_SUCCESS(Result))
|
||||
return 0;
|
||||
|
||||
Result = ObReferenceObjectByHandle(Handle,
|
||||
0, *ExEventObjectType, KernelMode, &Event, 0);
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
ZwClose(Handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*PHandle = Handle;
|
||||
|
||||
return Event;
|
||||
}
|
||||
|
||||
static VOID FspCloseEvent(PKEVENT Event, HANDLE Handle)
|
||||
{
|
||||
if (0 != Event)
|
||||
ObDereferenceObject(Event);
|
||||
|
||||
if (0 != Handle)
|
||||
ZwClose(Handle);
|
||||
}
|
||||
|
||||
VOID FspTraceInitialize(VOID)
|
||||
{
|
||||
#define OPEN_EVENT(NAME) \
|
||||
(Fsp ## NAME.Event = FspOpenEvent(L"\\KernelObjects\\" #NAME, &Fsp ## NAME.Handle))
|
||||
|
||||
OPEN_EVENT(LowMemoryCondition);
|
||||
OPEN_EVENT(LowNonPagedPoolCondition);
|
||||
OPEN_EVENT(LowPagedPoolCondition);
|
||||
|
||||
#undef OPEN_EVENT
|
||||
|
||||
FSP_TRACE();
|
||||
}
|
||||
|
||||
VOID FspTraceFinalize(VOID)
|
||||
{
|
||||
FSP_TRACE();
|
||||
|
||||
#define CLOSE_EVENT(NAME) \
|
||||
(FspCloseEvent(Fsp ## NAME.Event, Fsp ## NAME.Handle), Fsp ## NAME.Event = 0, Fsp ## NAME.Handle = 0)
|
||||
|
||||
CLOSE_EVENT(LowMemoryCondition);
|
||||
CLOSE_EVENT(LowNonPagedPoolCondition);
|
||||
CLOSE_EVENT(LowPagedPoolCondition);
|
||||
|
||||
#undef CLOSE_EVENT
|
||||
}
|
||||
#endif
|
142
src/sys/util.c
142
src/sys/util.c
@ -21,6 +21,7 @@
|
||||
|
||||
#include <sys/driver.h>
|
||||
|
||||
BOOLEAN FspIsNtDdiVersionAvailable(ULONG Version);
|
||||
NTSTATUS FspCreateGuid(GUID *Guid);
|
||||
NTSTATUS FspGetDeviceObjectPointer(PUNICODE_STRING ObjectName, ACCESS_MASK DesiredAccess,
|
||||
PULONG PFileNameIndex, PFILE_OBJECT *PFileObject, PDEVICE_OBJECT *PDeviceObject);
|
||||
@ -130,8 +131,15 @@ 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)
|
||||
#pragma alloc_text(PAGE, FspCreateGuid)
|
||||
#pragma alloc_text(PAGE, FspGetDeviceObjectPointer)
|
||||
#pragma alloc_text(PAGE, FspRegistryGetValue)
|
||||
@ -174,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[] =
|
||||
@ -223,6 +236,75 @@ PVOID FspAllocateIrpMustSucceed(CCHAR StackSize)
|
||||
}
|
||||
}
|
||||
|
||||
BOOLEAN FspIsNtDdiVersionAvailable(ULONG RequestedVersion)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
static ULONG Version;
|
||||
|
||||
if (0 == Version)
|
||||
{
|
||||
RTL_OSVERSIONINFOEXW VersionInfo;
|
||||
ULONG TempVersion;
|
||||
|
||||
RtlZeroMemory(&VersionInfo, sizeof VersionInfo);
|
||||
VersionInfo.dwOSVersionInfoSize = sizeof VersionInfo;
|
||||
RtlGetVersion((PVOID)&VersionInfo);
|
||||
|
||||
TempVersion =
|
||||
((UINT8)VersionInfo.dwMajorVersion << 24) |
|
||||
((UINT8)VersionInfo.dwMinorVersion << 16) |
|
||||
((UINT8)VersionInfo.wServicePackMajor << 8);
|
||||
|
||||
if (10 <= VersionInfo.dwMajorVersion)
|
||||
{
|
||||
/* see https://docs.microsoft.com/en-us/windows/release-information/ */
|
||||
static struct
|
||||
{
|
||||
ULONG BuildNumber;
|
||||
ULONG Subver;
|
||||
} Builds[] =
|
||||
{
|
||||
{ 10240, SUBVER(NTDDI_WIN10) },
|
||||
{ 10586, SUBVER(NTDDI_WIN10_TH2) },
|
||||
{ 14393, SUBVER(NTDDI_WIN10_RS1) },
|
||||
{ 15063, SUBVER(NTDDI_WIN10_RS2) },
|
||||
{ 16299, SUBVER(NTDDI_WIN10_RS3) },
|
||||
{ 17134, SUBVER(NTDDI_WIN10_RS4) },
|
||||
{ 17763, SUBVER(NTDDI_WIN10_RS5) },
|
||||
{ 18362, SUBVER(NTDDI_WIN10_19H1) },
|
||||
{ 18363, SUBVER(NTDDI_WIN10_19H1) },
|
||||
{ 19041, 9 },
|
||||
{ (ULONG)-1, 10 },
|
||||
};
|
||||
int Lo = 0, Hi = sizeof Builds / sizeof Builds[0] - 1, Mi;
|
||||
|
||||
while (Lo <= Hi)
|
||||
{
|
||||
Mi = (unsigned)(Lo + Hi) >> 1;
|
||||
|
||||
if (Builds[Mi].BuildNumber < VersionInfo.dwBuildNumber)
|
||||
Lo = Mi + 1;
|
||||
else if (Builds[Mi].BuildNumber > VersionInfo.dwBuildNumber)
|
||||
Hi = Mi - 1;
|
||||
else
|
||||
{
|
||||
Lo = Mi;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Mi = Lo;
|
||||
|
||||
TempVersion |= (UINT8)Builds[Mi].Subver;
|
||||
}
|
||||
|
||||
/* thread-safe because multiple threads will compute same value */
|
||||
InterlockedExchange((PVOID)&Version, TempVersion);
|
||||
}
|
||||
|
||||
return RequestedVersion <= Version;
|
||||
}
|
||||
|
||||
NTSTATUS FspCreateGuid(GUID *Guid)
|
||||
{
|
||||
PAGED_CODE();
|
||||
@ -526,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;
|
||||
@ -1417,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);
|
||||
}
|
||||
|
270
src/sys/volume.c
270
src/sys/volume.c
@ -24,11 +24,13 @@
|
||||
NTSTATUS FspVolumeCreate(
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||
static NTSTATUS FspVolumeCreateNoLock(
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
|
||||
FSP_SILO_GLOBALS *Globals);
|
||||
VOID FspVolumeDelete(
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||
static VOID FspVolumeDeleteNoLock(
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
|
||||
FSP_SILO_GLOBALS *Globals);
|
||||
static WORKER_THREAD_ROUTINE FspVolumeDeleteDelayed;
|
||||
NTSTATUS FspVolumeMount(
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||
@ -48,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);
|
||||
|
||||
@ -66,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
|
||||
|
||||
@ -77,15 +87,25 @@ NTSTATUS FspVolumeCreate(
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
FSP_SILO_GLOBALS *Globals;
|
||||
NTSTATUS Result;
|
||||
|
||||
FspSiloGetGlobals(&Globals);
|
||||
ASSERT(0 != Globals);
|
||||
|
||||
FspDeviceGlobalLock();
|
||||
Result = FspVolumeCreateNoLock(FsctlDeviceObject, Irp, IrpSp);
|
||||
Result = FspVolumeCreateNoLock(FsctlDeviceObject, Irp, IrpSp,
|
||||
Globals);
|
||||
FspDeviceGlobalUnlock();
|
||||
|
||||
FspSiloDereferenceGlobals(Globals);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static NTSTATUS FspVolumeCreateNoLock(
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
|
||||
FSP_SILO_GLOBALS *Globals)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
@ -302,18 +322,18 @@ static NTSTATUS FspVolumeCreateNoLock(
|
||||
/* do we need to register with fsmup? */
|
||||
if (0 == FsvrtDeviceObject)
|
||||
{
|
||||
Result = FspMupRegister(FspFsmupDeviceObject, FsvolDeviceObject);
|
||||
Result = FspMupRegister(Globals->FsmupDeviceObject, FsvolDeviceObject);
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
FspDeviceDereference(FsvolDeviceObject);
|
||||
return Result;
|
||||
}
|
||||
|
||||
RtlInitUnicodeString(&FsmupDeviceName, L"\\Device\\" FSP_FSCTL_MUP_DEVICE_NAME);
|
||||
RtlInitUnicodeString(&FsmupDeviceName, Globals->FsmupDeviceNameBuf);
|
||||
Result = IoCreateSymbolicLink(&FsvolDeviceExtension->VolumeName, &FsmupDeviceName);
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
FspMupUnregister(FspFsmupDeviceObject, FsvolDeviceObject);
|
||||
FspMupUnregister(Globals->FsmupDeviceObject, FsvolDeviceObject);
|
||||
FspDeviceDereference(FsvolDeviceObject);
|
||||
return Result;
|
||||
}
|
||||
@ -331,6 +351,7 @@ VOID FspVolumeDelete(
|
||||
{
|
||||
// !PAGED_CODE();
|
||||
|
||||
FSP_SILO_GLOBALS *Globals;
|
||||
PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2;
|
||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||
PDEVICE_OBJECT FsvrtDeviceObject = FsvolDeviceExtension->FsvrtDeviceObject;
|
||||
@ -350,10 +371,16 @@ VOID FspVolumeDelete(
|
||||
|
||||
FspDeviceReference(FsvolDeviceObject);
|
||||
|
||||
FspSiloGetGlobals(&Globals);
|
||||
ASSERT(0 != Globals);
|
||||
|
||||
FspDeviceGlobalLock();
|
||||
FspVolumeDeleteNoLock(FsctlDeviceObject, Irp, IrpSp);
|
||||
FspVolumeDeleteNoLock(FsctlDeviceObject, Irp, IrpSp,
|
||||
Globals);
|
||||
FspDeviceGlobalUnlock();
|
||||
|
||||
FspSiloDereferenceGlobals(Globals);
|
||||
|
||||
/*
|
||||
* Call MmForceSectionClosed on active files to ensure that Mm removes them from Standby List.
|
||||
*/
|
||||
@ -370,7 +397,8 @@ VOID FspVolumeDelete(
|
||||
}
|
||||
|
||||
static VOID FspVolumeDeleteNoLock(
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
|
||||
FSP_SILO_GLOBALS *Globals)
|
||||
{
|
||||
// !PAGED_CODE();
|
||||
|
||||
@ -445,9 +473,14 @@ static VOID FspVolumeDeleteNoLock(
|
||||
else
|
||||
{
|
||||
IoDeleteSymbolicLink(&FsvolDeviceExtension->VolumeName);
|
||||
FspMupUnregister(FspFsmupDeviceObject, FsvolDeviceObject);
|
||||
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);
|
||||
}
|
||||
@ -945,6 +978,7 @@ NTSTATUS FspVolumeTransact(
|
||||
if (0 != InternalBuffer)
|
||||
{
|
||||
ASSERT(FSP_FSCTL_TRANSACT_INTERNAL == ControlCode);
|
||||
*(PVOID *)OutputBuffer = 0;
|
||||
FspFree(InternalBuffer);
|
||||
}
|
||||
FspIopCompleteCanceledIrp(PendingIrp);
|
||||
@ -1033,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)
|
||||
{
|
||||
|
2
tools/build-choco.bat
Normal file → Executable file
2
tools/build-choco.bat
Normal file → Executable file
@ -19,7 +19,7 @@ if X%~nx0==Xbuild-choco.bat (
|
||||
goto :choco
|
||||
)
|
||||
|
||||
call "%VS140COMNTOOLS%\..\..\VC\vcvarsall.bat" x64
|
||||
call "%~dp0vcvarsall.bat" x64
|
||||
|
||||
if not X%SignedPackage%==X (
|
||||
if not exist "%~dp0..\build\VStudio\build\%Configuration%\winfsp-*.msi" (echo previous build not found >&2 & exit /b 1)
|
||||
|
@ -10,7 +10,7 @@ if not X%4==X set ProjDir=%~4
|
||||
|
||||
if X!ProjDir!==X (echo usage: build-sample Config Arch Sample ProjDir >&2 & goto fail)
|
||||
|
||||
call "%VS140COMNTOOLS%\..\..\VC\vcvarsall.bat" x64
|
||||
call "%~dp0vcvarsall.bat" x64
|
||||
|
||||
if X!FSP_SAMPLE_DIR!==X (
|
||||
set RegKey="HKLM\SOFTWARE\WinFsp"
|
||||
|
@ -19,7 +19,7 @@ if X%~nx0==Xbuild-choco.bat (
|
||||
goto :choco
|
||||
)
|
||||
|
||||
call "%VS140COMNTOOLS%\..\..\VC\vcvarsall.bat" x64
|
||||
call "%~dp0vcvarsall.bat" x64
|
||||
|
||||
if not X%SignedPackage%==X (
|
||||
if not exist "%~dp0..\build\VStudio\build\%Configuration%\winfsp-*.msi" (echo previous build not found >&2 & exit /b 1)
|
||||
|
@ -12,4 +12,4 @@ reg query %RegKey% /v %RegVal% >nul 2>&1 || (echo Cannot find Windows Kit >&2 &
|
||||
for /f "tokens=2,*" %%i in ('reg query %RegKey% /v %RegVal% ^| findstr %RegVal%') do (
|
||||
set KitRoot="%%j"
|
||||
)
|
||||
start "winfsp" %KitRoot%\Debuggers\x64\windbg -W %DebugWorkspace% -k net:port=%DebugPort%,key=%DebugKey%
|
||||
start "%DebugWorkspace%" %KitRoot%\Debuggers\x64\windbg -W %DebugWorkspace% -k net:port=%DebugPort%,key=%DebugKey%
|
||||
|
@ -1,21 +1,36 @@
|
||||
@echo off
|
||||
|
||||
setlocal
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
set CONFIG=Debug
|
||||
set SUFFIX=x64
|
||||
set TARGET_MACHINE=WIN8DBG
|
||||
if not X%1==X set TARGET_MACHINE=%1
|
||||
set TARGET_ACCOUNT=\Users\%USERNAME%\Downloads\winfsp\
|
||||
set TARGET=\\%TARGET_MACHINE%%TARGET_ACCOUNT%
|
||||
set Config=Debug
|
||||
set Suffix=x64
|
||||
set Deploy=C:\Deploy\winfsp
|
||||
set Target=Win10DBG
|
||||
set Chkpnt=winfsp
|
||||
if not X%1==X set Target=%1
|
||||
if not X%2==X set Chkpnt=%2
|
||||
|
||||
cd %~dp0..
|
||||
mkdir %TARGET% 2>nul
|
||||
for %%f in (winfsp-%SUFFIX%.sys winfsp-%SUFFIX%.dll winfsp-tests-%SUFFIX%.exe fsbench-%SUFFIX%.exe fscrash-%SUFFIX%.exe memfs-%SUFFIX%.exe) do (
|
||||
copy build\VStudio\build\%CONFIG%\%%f %TARGET% >nul
|
||||
(
|
||||
echo regsvr32 /s winfsp-x64.dll
|
||||
) > %~dp0..\build\VStudio\build\%Config%\deploy-setup.bat
|
||||
|
||||
set Files=
|
||||
for %%f in (
|
||||
%~dp0..\build\VStudio\build\%Config%\
|
||||
winfsp-%Suffix%.sys
|
||||
winfsp-%Suffix%.dll
|
||||
winfsp-tests-%Suffix%.exe
|
||||
memfs-%Suffix%.exe
|
||||
deploy-setup.bat
|
||||
) do (
|
||||
set File=%%~f
|
||||
if [!File:~-1!] == [\] (
|
||||
set Dir=!File!
|
||||
) else (
|
||||
if not [!Files!] == [] set Files=!Files!,
|
||||
set Files=!Files!'!Dir!!File!'
|
||||
)
|
||||
)
|
||||
copy tools\ifstest.bat %TARGET% >nul
|
||||
echo sc create WinFsp type=filesys binPath=%%~dp0winfsp-%SUFFIX%.sys >%TARGET%sc-create.bat
|
||||
echo sc start WinFsp >%TARGET%sc-start.bat
|
||||
echo sc stop WinFsp >%TARGET%sc-stop.bat
|
||||
echo sc delete WinFsp >%TARGET%sc-delete.bat
|
||||
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -Command "& '%~dp0deploy.ps1' -Name '%Target%' -CheckpointName '%Chkpnt%' -Files !Files! -Destination '%Deploy%'"
|
||||
|
35
tools/deploy.ps1
Normal file
35
tools/deploy.ps1
Normal file
@ -0,0 +1,35 @@
|
||||
param (
|
||||
[Parameter(Mandatory)][string]$Name,
|
||||
[string]$CheckpointName,
|
||||
[Parameter(Mandatory)][string[]]$Files,
|
||||
[Parameter(Mandatory)][string]$Destination
|
||||
)
|
||||
|
||||
function Restore-VM ($Name, $CheckpointName) {
|
||||
$VM = Get-VM -Name $Name
|
||||
if ($VM.State -eq "Running") {
|
||||
Stop-VM -Name $Name -TurnOff
|
||||
}
|
||||
|
||||
if (-not $CheckpointName) {
|
||||
$Checkpoint = Get-VMCheckpoint -VMName $Name |
|
||||
Sort-Object -Property CreationTime -Descending |
|
||||
select -First 1
|
||||
} else {
|
||||
$Checkpoint = Get-VMCheckpoint -VMName $Name -Name $CheckpointName
|
||||
}
|
||||
Restore-VMCheckpoint -VMCheckpoint $Checkpoint -Confirm:$false
|
||||
|
||||
Start-VM -Name $Name
|
||||
}
|
||||
|
||||
function Deploy-VMFiles ($Name, $Files, $Destination) {
|
||||
foreach ($File in $Files) {
|
||||
$Leaf = Split-Path -Path $File -Leaf
|
||||
$Dest = Join-Path $Destination $Leaf
|
||||
Copy-VMFile -Name $Name -SourcePath $File -DestinationPath $Dest -FileSource Host -CreateFullPath -Force
|
||||
}
|
||||
}
|
||||
|
||||
Restore-VM -Name $Name -CheckpointName $CheckpointName
|
||||
Deploy-VMFiles -Name $Name -Files $Files -Destination $Destination
|
@ -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
|
||||
|
@ -19,7 +19,7 @@ set cdef=/D_AMD64_
|
||||
if /i X%outarch%==Xx86 set arch=x86
|
||||
if /i X%outarch%==Xx86 set cdef=/D_X86_
|
||||
|
||||
call "%VS140COMNTOOLS%\..\..\VC\vcvarsall.bat" !arch!
|
||||
call "%~dp0vcvarsall.bat" !arch!
|
||||
set INCLUDE=%~dp0..\opt\fsext\inc;%~dp0..\inc;!WindowsSdkDir!Include\!WindowsSDKVersion!km\crt;!WindowsSdkDir!Include\!WindowsSDKVersion!km;!WindowsSdkDir!Include\!WindowsSDKVersion!km\shared;!INCLUDE!
|
||||
|
||||
if exist !workdir! rmdir /s/q !workdir!
|
||||
|
@ -5,7 +5,7 @@ setlocal
|
||||
set Configuration=Release
|
||||
if not X%1==X set Configuration=%1
|
||||
|
||||
call "%VS140COMNTOOLS%\..\..\VC\vcvarsall.bat" x64
|
||||
call "%~dp0vcvarsall.bat" x64
|
||||
|
||||
cd %~dp0..\ext\test
|
||||
nmake /f Nmakefile
|
||||
|
@ -11,4 +11,9 @@ for /f "tokens=2,*" %%i in ('reg query %RegKey% /v %RegVal% ^| findstr %RegVal%'
|
||||
set KitRoot=%%j
|
||||
)
|
||||
|
||||
findstr /R /I "\<0*[Xx]*%1[Ll]*\>" "%KitRoot%Include\10.0.10586.0\shared\%~n0.h"
|
||||
set KitVer=10.0.10586.0
|
||||
for /f "tokens=*" %%i in ('dir /ad/b "%KitRoot%Include\10.*"') do (
|
||||
set KitVer=%%i
|
||||
)
|
||||
|
||||
findstr /R /I "\<0*[Xx]*%1[Ll]*\>" "%KitRoot%Include\%KitVer%\shared\%~n0.h"
|
||||
|
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
tools/vcvarsall.bat
Executable file
10
tools/vcvarsall.bat
Executable file
@ -0,0 +1,10 @@
|
||||
@echo off
|
||||
|
||||
set vcvarsall="%VS140COMNTOOLS%\..\..\VC\vcvarsall.bat"
|
||||
set vswhere="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||
if exist %vswhere% (
|
||||
for /f "usebackq tokens=*" %%i in (`%vswhere% -find VC\**\vcvarsall.bat`) do (
|
||||
set vcvarsall="%%i"
|
||||
)
|
||||
)
|
||||
call %vcvarsall% %*
|
@ -11,4 +11,9 @@ for /f "tokens=2,*" %%i in ('reg query %RegKey% /v %RegVal% ^| findstr %RegVal%'
|
||||
set KitRoot=%%j
|
||||
)
|
||||
|
||||
findstr /R /I "\<0*[Xx]*%1[Ll]*\>" "%KitRoot%Include\10.0.10586.0\shared\%~n0.h"
|
||||
set KitVer=10.0.10586.0
|
||||
for /f "tokens=*" %%i in ('dir /ad/b "%KitRoot%Include\10.*"') do (
|
||||
set KitVer=%%i
|
||||
)
|
||||
|
||||
findstr /R /I "\<0*[Xx]*%1[Ll]*\>" "%KitRoot%Include\%KitVer%\shared\%~n0.h"
|
||||
|
@ -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`
|
||||
|
9
tst/notifyfs-dotnet/.gitignore
vendored
Normal file
9
tst/notifyfs-dotnet/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
build
|
||||
*.ncb
|
||||
*.suo
|
||||
*.vcproj.*
|
||||
*.vcxproj.user
|
||||
*.csproj.user
|
||||
*.VC.db
|
||||
*.VC.opendb
|
||||
.vs
|
394
tst/notifyfs-dotnet/Program.cs
Normal file
394
tst/notifyfs-dotnet/Program.cs
Normal file
@ -0,0 +1,394 @@
|
||||
/**
|
||||
* @file Program.cs
|
||||
*
|
||||
* @copyright 2015-2020 Bill Zissimopoulos
|
||||
*/
|
||||
/*
|
||||
* This file is part of WinFsp.
|
||||
*
|
||||
* You can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License version 3 as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Licensees holding a valid commercial license may use this software
|
||||
* in accordance with the commercial license agreement provided in
|
||||
* conjunction with the software. The terms and conditions of any such
|
||||
* commercial license agreement shall govern, supersede, and render
|
||||
* ineffective any application of the GPLv3 license to this software,
|
||||
* notwithstanding of any reference thereto in the software or
|
||||
* associated repository.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.AccessControl;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
using Fsp;
|
||||
using VolumeInfo = Fsp.Interop.VolumeInfo;
|
||||
using FileInfo = Fsp.Interop.FileInfo;
|
||||
using NotifyInfo = Fsp.Interop.NotifyInfo;
|
||||
using NotifyAction = Fsp.Interop.NotifyAction;
|
||||
using NotifyFilter = Fsp.Interop.NotifyFilter;
|
||||
|
||||
namespace notifyfs
|
||||
{
|
||||
class Notifyfs : FileSystemBase
|
||||
{
|
||||
public override Int32 Init(Object Host)
|
||||
{
|
||||
_Host = (FileSystemHost)Host;
|
||||
_Host.SectorSize = ALLOCATION_UNIT;
|
||||
_Host.SectorsPerAllocationUnit = 1;
|
||||
_Host.FileInfoTimeout = 1000;
|
||||
_Host.CaseSensitiveSearch = false;
|
||||
_Host.CasePreservedNames = true;
|
||||
_Host.UnicodeOnDisk = true;
|
||||
_Host.PersistentAcls = false;
|
||||
_Host.PostCleanupWhenModifiedOnly = true;
|
||||
_Host.VolumeCreationTime = 0;
|
||||
_Host.VolumeSerialNumber = 0;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
public override Int32 Mounted(Object Host)
|
||||
{
|
||||
_Timer = new Timer(this.Tick, null, 0, 1000);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
public override void Unmounted(Object Host)
|
||||
{
|
||||
WaitHandle Event = new ManualResetEvent(false);
|
||||
_Timer.Dispose(Event);
|
||||
Event.WaitOne();
|
||||
}
|
||||
public override Int32 GetVolumeInfo(
|
||||
out VolumeInfo VolumeInfo)
|
||||
{
|
||||
VolumeInfo = default(VolumeInfo);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
public override Int32 GetSecurityByName(
|
||||
String FileName,
|
||||
out UInt32 FileAttributes/* or ReparsePointIndex */,
|
||||
ref Byte[] SecurityDescriptor)
|
||||
{
|
||||
int Index = FileLookup(FileName);
|
||||
if (-1 == Index)
|
||||
{
|
||||
FileAttributes = default(UInt32);
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
FileAttributes = 0 == Index ? (UInt32)System.IO.FileAttributes.Directory : 0;
|
||||
if (null != SecurityDescriptor)
|
||||
SecurityDescriptor = DefaultSecurity;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
public override Int32 Open(
|
||||
String FileName,
|
||||
UInt32 CreateOptions,
|
||||
UInt32 GrantedAccess,
|
||||
out Object FileNode,
|
||||
out Object FileDesc,
|
||||
out FileInfo FileInfo,
|
||||
out String NormalizedName)
|
||||
{
|
||||
FileNode = default(Object);
|
||||
FileDesc = default(Object);
|
||||
FileInfo = default(FileInfo);
|
||||
NormalizedName = default(String);
|
||||
|
||||
int Index = FileLookup(FileName);
|
||||
if (-1 == Index)
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
|
||||
FileNode = Index;
|
||||
FillFileInfo(Index, out FileInfo);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
public override Int32 Read(
|
||||
Object FileNode,
|
||||
Object FileDesc,
|
||||
IntPtr Buffer,
|
||||
UInt64 Offset,
|
||||
UInt32 Length,
|
||||
out UInt32 BytesTransferred)
|
||||
{
|
||||
int Index = (int)FileNode;
|
||||
UInt64 EndOffset;
|
||||
Byte[] Contents = FileContents(Index);
|
||||
|
||||
if (Offset >= (UInt64)Contents.Length)
|
||||
{
|
||||
BytesTransferred = 0;
|
||||
return STATUS_END_OF_FILE;
|
||||
}
|
||||
|
||||
EndOffset = Offset + Length;
|
||||
if (EndOffset > (UInt64)Contents.Length)
|
||||
EndOffset = (UInt64)Contents.Length;
|
||||
|
||||
BytesTransferred = (UInt32)(EndOffset - Offset);
|
||||
Marshal.Copy(Contents, (int)Offset, Buffer, (int)BytesTransferred);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
public override Int32 GetFileInfo(
|
||||
Object FileNode,
|
||||
Object FileDesc,
|
||||
out FileInfo FileInfo)
|
||||
{
|
||||
int Index = (int)FileNode;
|
||||
|
||||
FillFileInfo(Index, out FileInfo);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
public override Boolean ReadDirectoryEntry(
|
||||
Object FileNode,
|
||||
Object FileDesc,
|
||||
String Pattern,
|
||||
String Marker,
|
||||
ref Object Context,
|
||||
out String FileName,
|
||||
out FileInfo FileInfo)
|
||||
{
|
||||
IEnumerator<String> Enumerator = (IEnumerator<String>)Context;
|
||||
|
||||
if (null == Enumerator)
|
||||
{
|
||||
List<String> ChildrenFileNames = new List<String>();
|
||||
for (int Index = 1, Count = FileCount(); Count >= Index; Index++)
|
||||
ChildrenFileNames.Add(String.Format("{0}", Index));
|
||||
Context = Enumerator = ChildrenFileNames.GetEnumerator();
|
||||
}
|
||||
|
||||
while (Enumerator.MoveNext())
|
||||
{
|
||||
FileName = Enumerator.Current;
|
||||
FillFileInfo(int.Parse(FileName), out FileInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
FileName = default(String);
|
||||
FileInfo = default(FileInfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int CountFromTicks(int Ticks)
|
||||
{
|
||||
/*
|
||||
* The formula below produces the periodic sequence:
|
||||
* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
||||
* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
||||
* ...
|
||||
*/
|
||||
int div10 = (Ticks % 20) / 10;
|
||||
int mod10 = Ticks % 10;
|
||||
int mdv10 = 1 - div10;
|
||||
int mmd10 = 10 - mod10;
|
||||
return mdv10 * mod10 + div10 * mmd10;
|
||||
}
|
||||
private int FileCount()
|
||||
{
|
||||
int Ticks = Thread.VolatileRead(ref _Ticks);
|
||||
return CountFromTicks(Ticks);
|
||||
}
|
||||
private int FileLookup(String FileName)
|
||||
{
|
||||
FileName = FileName.Substring(1);
|
||||
if ("" == FileName)
|
||||
return 0; /* root */
|
||||
int Count = FileCount();
|
||||
Boolean Valid = int.TryParse(FileName, out int Index);
|
||||
if (!Valid || 0 >= Index || Index > Count)
|
||||
return -1; /* not found */
|
||||
return Index; /* regular file named 1, 2, ..., Count */
|
||||
}
|
||||
private static Byte[] FileContents(int Index)
|
||||
{
|
||||
if (0 == Index)
|
||||
return EmptyByteArray;
|
||||
return Encoding.UTF8.GetBytes(String.Format("{0}\n", Index));
|
||||
}
|
||||
private static void FillFileInfo(int Index, out FileInfo FileInfo)
|
||||
{
|
||||
FileInfo = default(FileInfo);
|
||||
FileInfo.FileAttributes = 0 == Index ? (UInt32)System.IO.FileAttributes.Directory : 0;
|
||||
FileInfo.FileSize = (UInt64)FileContents(Index).Length;
|
||||
FileInfo.AllocationSize = (FileInfo.FileSize + ALLOCATION_UNIT - 1)
|
||||
/ ALLOCATION_UNIT * ALLOCATION_UNIT;
|
||||
FileInfo.CreationTime =
|
||||
FileInfo.LastAccessTime =
|
||||
FileInfo.LastWriteTime =
|
||||
FileInfo.ChangeTime = (UInt64)DateTime.Now.ToFileTimeUtc();
|
||||
}
|
||||
private void Tick(Object Context)
|
||||
{
|
||||
int Ticks = Interlocked.Increment(ref _Ticks);
|
||||
int OldCount = CountFromTicks(Ticks - 1);
|
||||
int NewCount = CountFromTicks(Ticks);
|
||||
NotifyInfo[] NotifyInfo = new NotifyInfo[1];
|
||||
|
||||
if (OldCount < NewCount)
|
||||
{
|
||||
NotifyInfo[0].FileName = String.Format("\\{0}", NewCount);
|
||||
NotifyInfo[0].Action = NotifyAction.Added;
|
||||
NotifyInfo[0].Filter = NotifyFilter.ChangeFileName;
|
||||
Console.Error.WriteLine("CREATE \\{0}", NewCount);
|
||||
}
|
||||
else if (OldCount > NewCount)
|
||||
{
|
||||
NotifyInfo[0].FileName = String.Format("\\{0}", OldCount);
|
||||
NotifyInfo[0].Action = NotifyAction.Removed;
|
||||
NotifyInfo[0].Filter = NotifyFilter.ChangeFileName;
|
||||
Console.Error.WriteLine("REMOVE \\{0}", OldCount);
|
||||
}
|
||||
|
||||
if (OldCount != NewCount)
|
||||
{
|
||||
if (STATUS_SUCCESS == _Host.NotifyBegin(500))
|
||||
{
|
||||
_Host.Notify(NotifyInfo);
|
||||
_Host.NotifyEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Notifyfs()
|
||||
{
|
||||
RawSecurityDescriptor RootSecurityDescriptor = new RawSecurityDescriptor(
|
||||
"O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)");
|
||||
DefaultSecurity = new Byte[RootSecurityDescriptor.BinaryLength];
|
||||
RootSecurityDescriptor.GetBinaryForm(DefaultSecurity, 0);
|
||||
}
|
||||
|
||||
private const int ALLOCATION_UNIT = 4096;
|
||||
private static readonly Byte[] EmptyByteArray = new Byte[0];
|
||||
private static readonly Byte[] DefaultSecurity;
|
||||
private FileSystemHost _Host;
|
||||
private Timer _Timer;
|
||||
private int _Ticks;
|
||||
}
|
||||
|
||||
class NotifyfsService : Service
|
||||
{
|
||||
private class CommandLineUsageException : Exception
|
||||
{
|
||||
public CommandLineUsageException(String Message = null) : base(Message)
|
||||
{
|
||||
HasMessage = null != Message;
|
||||
}
|
||||
|
||||
public bool HasMessage;
|
||||
}
|
||||
|
||||
private const String PROGNAME = "notifyfs-dotnet";
|
||||
|
||||
public NotifyfsService() : base("NotifyfsService")
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnStart(String[] Args)
|
||||
{
|
||||
try
|
||||
{
|
||||
String VolumePrefix = null;
|
||||
String MountPoint = null;
|
||||
FileSystemHost Host = null;
|
||||
Notifyfs Notifyfs = null;
|
||||
int I;
|
||||
|
||||
for (I = 1; Args.Length > I; I++)
|
||||
{
|
||||
String Arg = Args[I];
|
||||
if ('-' != Arg[0])
|
||||
break;
|
||||
switch (Arg[1])
|
||||
{
|
||||
case '?':
|
||||
throw new CommandLineUsageException();
|
||||
case 'm':
|
||||
argtos(Args, ref I, ref MountPoint);
|
||||
break;
|
||||
case 'u':
|
||||
argtos(Args, ref I, ref VolumePrefix);
|
||||
break;
|
||||
default:
|
||||
throw new CommandLineUsageException();
|
||||
}
|
||||
}
|
||||
|
||||
if (Args.Length > I)
|
||||
throw new CommandLineUsageException();
|
||||
|
||||
if (null == MountPoint)
|
||||
throw new CommandLineUsageException();
|
||||
|
||||
FileSystemHost.SetDebugLogFile("-");
|
||||
|
||||
Host = new FileSystemHost(Notifyfs = new Notifyfs());
|
||||
Host.Prefix = VolumePrefix;
|
||||
if (0 > Host.Mount(MountPoint))
|
||||
throw new IOException("cannot mount file system");
|
||||
MountPoint = Host.MountPoint();
|
||||
_Host = Host;
|
||||
|
||||
Log(EVENTLOG_INFORMATION_TYPE, String.Format("{0}{1}{2} -m {3}",
|
||||
PROGNAME,
|
||||
null != VolumePrefix && 0 < VolumePrefix.Length ? " -u " : "",
|
||||
null != VolumePrefix && 0 < VolumePrefix.Length ? VolumePrefix : "",
|
||||
MountPoint));
|
||||
}
|
||||
catch (CommandLineUsageException ex)
|
||||
{
|
||||
Log(EVENTLOG_ERROR_TYPE, String.Format(
|
||||
"{0}" +
|
||||
"usage: {1} OPTIONS\n" +
|
||||
"\n" +
|
||||
"options:\n" +
|
||||
" -u \\Server\\Share [UNC prefix (single backslash)]\n" +
|
||||
" -m MountPoint [X:|*|directory]\n",
|
||||
ex.HasMessage ? ex.Message + "\n" : "",
|
||||
PROGNAME));
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log(EVENTLOG_ERROR_TYPE, String.Format("{0}", ex.Message));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
protected override void OnStop()
|
||||
{
|
||||
_Host.Unmount();
|
||||
_Host = null;
|
||||
}
|
||||
|
||||
private static void argtos(String[] Args, ref int I, ref String V)
|
||||
{
|
||||
if (Args.Length > ++I)
|
||||
V = Args[I];
|
||||
else
|
||||
throw new CommandLineUsageException();
|
||||
}
|
||||
|
||||
private FileSystemHost _Host;
|
||||
}
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Environment.ExitCode = new NotifyfsService().Run();
|
||||
}
|
||||
}
|
||||
}
|
77
tst/notifyfs-dotnet/notifyfs-dotnet.csproj
Normal file
77
tst/notifyfs-dotnet/notifyfs-dotnet.csproj
Normal file
@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>notifyfs</RootNamespace>
|
||||
<AssemblyName>notifyfs-dotnet</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>$(SolutionDir)build\$(Configuration)\</OutputPath>
|
||||
<BaseIntermediateOutputPath>$(SolutionDir)build\$(ProjectName).build\</BaseIntermediateOutputPath>
|
||||
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\</IntermediateOutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>$(SolutionDir)build\$(Configuration)\</OutputPath>
|
||||
<BaseIntermediateOutputPath>$(SolutionDir)build\$(ProjectName).build\</BaseIntermediateOutputPath>
|
||||
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\</IntermediateOutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="winfsp-msil">
|
||||
<HintPath>$(MSBuildProgramFiles32)\WinFsp\bin\winfsp-msil.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.5.2">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Microsoft .NET Framework 4.5.2 %28x86 and x64%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
25
tst/notifyfs-dotnet/notifyfs-dotnet.sln
Normal file
25
tst/notifyfs-dotnet/notifyfs-dotnet.sln
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30717.126
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "notifyfs-dotnet", "notifyfs-dotnet.csproj", "{DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {18D87005-09CA-40CF-9B51-139B486AB8D0}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
8
tst/notifyfs/.gitignore
vendored
Normal file
8
tst/notifyfs/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
build
|
||||
*.ncb
|
||||
*.suo
|
||||
*.vcproj.*
|
||||
*.vcxproj.user
|
||||
*.VC.db
|
||||
*.VC.opendb
|
||||
.vs
|
508
tst/notifyfs/notifyfs.c
Normal file
508
tst/notifyfs/notifyfs.c
Normal file
@ -0,0 +1,508 @@
|
||||
/**
|
||||
* @file notifyfs.c
|
||||
*
|
||||
* @copyright 2015-2020 Bill Zissimopoulos
|
||||
*/
|
||||
/*
|
||||
* This file is part of WinFsp.
|
||||
*
|
||||
* You can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License version 3 as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Licensees holding a valid commercial license may use this software
|
||||
* in accordance with the commercial license agreement provided in
|
||||
* conjunction with the software. The terms and conditions of any such
|
||||
* commercial license agreement shall govern, supersede, and render
|
||||
* ineffective any application of the GPLv3 license to this software,
|
||||
* notwithstanding of any reference thereto in the software or
|
||||
* associated repository.
|
||||
*/
|
||||
|
||||
#include <winfsp/winfsp.h>
|
||||
#include <sddl.h>
|
||||
|
||||
#define DEBUGFLAGS 0
|
||||
//#define DEBUGFLAGS -1
|
||||
|
||||
#define PROGNAME "notifyfs"
|
||||
#define ALLOCATION_UNIT 4096
|
||||
|
||||
#define info(format, ...) FspServiceLog(EVENTLOG_INFORMATION_TYPE, format, __VA_ARGS__)
|
||||
#define warn(format, ...) FspServiceLog(EVENTLOG_WARNING_TYPE, format, __VA_ARGS__)
|
||||
#define fail(format, ...) FspServiceLog(EVENTLOG_ERROR_TYPE, format, __VA_ARGS__)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FSP_FILE_SYSTEM *FileSystem;
|
||||
PTP_TIMER Timer;
|
||||
UINT32 Ticks;
|
||||
} NOTIFYFS;
|
||||
|
||||
static PSECURITY_DESCRIPTOR DefaultSecurity;
|
||||
static ULONG DefaultSecuritySize;
|
||||
|
||||
static UINT32 CountFromTicks(UINT32 Ticks)
|
||||
{
|
||||
/*
|
||||
* The formula below produces the periodic sequence:
|
||||
* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
||||
* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
||||
* ...
|
||||
*/
|
||||
UINT32 div10 = (Ticks % 20) / 10;
|
||||
UINT32 mod10 = Ticks % 10;
|
||||
UINT32 mdv10 = 1 - div10;
|
||||
UINT32 mmd10 = 10 - mod10;
|
||||
return mdv10 * mod10 + div10 * mmd10;
|
||||
}
|
||||
|
||||
static UINT32 FileCount(NOTIFYFS *Notifyfs)
|
||||
{
|
||||
UINT32 Ticks = InterlockedOr(&Notifyfs->Ticks, 0);
|
||||
return CountFromTicks(Ticks);
|
||||
}
|
||||
|
||||
static UINT32 FileLookup(NOTIFYFS *Notifyfs, PWSTR FileName)
|
||||
{
|
||||
FileName++;
|
||||
PWSTR Endp;
|
||||
UINT32 Count = FileCount(Notifyfs);
|
||||
UINT32 Index = wcstoul(FileName, &Endp, 10);
|
||||
if ('\0' != *Endp || (FileName != Endp && (0 == Index || Index > Count)))
|
||||
return -1; /* not found */
|
||||
if (FileName == Endp)
|
||||
return 0; /* root */
|
||||
return Index; /* regular file named 1, 2, ..., Count */
|
||||
}
|
||||
|
||||
static UINT32 FileContents(UINT32 Index, WCHAR P[32])
|
||||
{
|
||||
WCHAR Buffer[32];
|
||||
if (0 == P)
|
||||
P = Buffer;
|
||||
if (0 == Index)
|
||||
P[0] = '\0';
|
||||
else
|
||||
wsprintfW(P, L"%u\n", (unsigned)Index);
|
||||
return lstrlenW(P);
|
||||
}
|
||||
|
||||
static VOID FillFileInfo(UINT32 Index, FSP_FSCTL_FILE_INFO *FileInfo)
|
||||
{
|
||||
FILETIME SystemTime;
|
||||
|
||||
GetSystemTimeAsFileTime(&SystemTime);
|
||||
|
||||
memset(FileInfo, 0, sizeof FileInfo);
|
||||
FileInfo->FileAttributes = 0 == Index ? FILE_ATTRIBUTE_DIRECTORY : 0;
|
||||
FileInfo->FileSize = FileContents(Index, 0);
|
||||
FileInfo->AllocationSize = (FileInfo->FileSize + ALLOCATION_UNIT - 1)
|
||||
/ ALLOCATION_UNIT * ALLOCATION_UNIT;
|
||||
FileInfo->CreationTime =
|
||||
FileInfo->LastAccessTime =
|
||||
FileInfo->LastWriteTime =
|
||||
FileInfo->ChangeTime = *(PUINT64)&SystemTime;
|
||||
}
|
||||
|
||||
static BOOLEAN AddDirInfo(PWSTR FileName, UINT32 Index,
|
||||
PVOID Buffer, ULONG Length, PULONG PBytesTransferred)
|
||||
{
|
||||
union
|
||||
{
|
||||
UINT8 B[FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) + MAX_PATH * sizeof(WCHAR)];
|
||||
FSP_FSCTL_DIR_INFO D;
|
||||
} DirInfoBuf;
|
||||
FSP_FSCTL_DIR_INFO *DirInfo = &DirInfoBuf.D;
|
||||
|
||||
memset(DirInfo->Padding, 0, sizeof DirInfo->Padding);
|
||||
if (0 != FileName)
|
||||
lstrcpyW(DirInfo->FileNameBuf, FileName);
|
||||
else
|
||||
wsprintfW(DirInfo->FileNameBuf, L"%u", (unsigned)Index);
|
||||
DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + wcslen(DirInfo->FileNameBuf) * sizeof(WCHAR));
|
||||
FillFileInfo(Index, &DirInfo->FileInfo);
|
||||
|
||||
return FspFileSystemAddDirInfo(DirInfo, Buffer, Length, PBytesTransferred);
|
||||
}
|
||||
|
||||
static NTSTATUS GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem,
|
||||
FSP_FSCTL_VOLUME_INFO *VolumeInfo)
|
||||
{
|
||||
memset(VolumeInfo, 0, sizeof *VolumeInfo);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS GetSecurityByName(FSP_FILE_SYSTEM *FileSystem,
|
||||
PWSTR FileName, PUINT32 PFileAttributes,
|
||||
PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize)
|
||||
{
|
||||
NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext;
|
||||
UINT32 Index;
|
||||
|
||||
Index = FileLookup(Notifyfs, FileName);
|
||||
if (-1 == Index)
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
|
||||
if (0 != PFileAttributes)
|
||||
*PFileAttributes = 0 == Index ? FILE_ATTRIBUTE_DIRECTORY : 0;
|
||||
|
||||
if (0 != PSecurityDescriptorSize)
|
||||
{
|
||||
if (DefaultSecuritySize > *PSecurityDescriptorSize)
|
||||
{
|
||||
*PSecurityDescriptorSize = DefaultSecuritySize;
|
||||
return STATUS_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
*PSecurityDescriptorSize = DefaultSecuritySize;
|
||||
if (0 != SecurityDescriptor)
|
||||
memcpy(SecurityDescriptor, DefaultSecurity, DefaultSecuritySize);
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem,
|
||||
PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess,
|
||||
UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize,
|
||||
PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo)
|
||||
{
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
}
|
||||
|
||||
static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem,
|
||||
PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess,
|
||||
PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo)
|
||||
{
|
||||
NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext;
|
||||
FSP_FSCTL_TRANSACT_FULL_CONTEXT *FullContext = (PVOID)PFileContext;
|
||||
UINT32 Index;
|
||||
|
||||
Index = FileLookup(Notifyfs, FileName);
|
||||
if (-1 == Index)
|
||||
return STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
|
||||
FullContext->UserContext = Index;
|
||||
FullContext->UserContext2 = 0;
|
||||
|
||||
FillFileInfo(Index, FileInfo);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileContext, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize,
|
||||
FSP_FSCTL_FILE_INFO *FileInfo)
|
||||
{
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
}
|
||||
|
||||
static NTSTATUS Read(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileContext, PVOID Buffer, UINT64 Offset, ULONG Length,
|
||||
PULONG PBytesTransferred)
|
||||
{
|
||||
NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext;
|
||||
FSP_FSCTL_TRANSACT_FULL_CONTEXT *FullContext = FileContext;
|
||||
UINT32 Index = (UINT32)FullContext->UserContext;
|
||||
UINT64 EndOffset;
|
||||
WCHAR ContentBuf[32];
|
||||
UINT32 ContentLen;
|
||||
|
||||
ContentLen = FileContents(Index, ContentBuf);
|
||||
|
||||
if (Offset >= ContentLen)
|
||||
return STATUS_END_OF_FILE;
|
||||
|
||||
EndOffset = Offset + Length;
|
||||
if (EndOffset > ContentLen)
|
||||
EndOffset = ContentLen;
|
||||
|
||||
memcpy(Buffer, (PUINT8)ContentBuf + Offset, (size_t)(EndOffset - Offset));
|
||||
|
||||
*PBytesTransferred = (ULONG)(EndOffset - Offset);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS GetFileInfo(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileContext,
|
||||
FSP_FSCTL_FILE_INFO *FileInfo)
|
||||
{
|
||||
NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext;
|
||||
FSP_FSCTL_TRANSACT_FULL_CONTEXT *FullContext = FileContext;
|
||||
UINT32 Index = (UINT32)FullContext->UserContext;
|
||||
|
||||
FillFileInfo(Index, FileInfo);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS ReadDirectory(FSP_FILE_SYSTEM *FileSystem,
|
||||
PVOID FileContext, PWSTR Pattern, PWSTR Marker,
|
||||
PVOID Buffer, ULONG Length, PULONG PBytesTransferred)
|
||||
{
|
||||
NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext;
|
||||
UINT32 Count = FileCount(Notifyfs);
|
||||
UINT32 Index;
|
||||
|
||||
Index = 0 == Marker ? 1 : wcstoul(Marker, 0, 10) + 1;
|
||||
for (; Count >= Index; Index++)
|
||||
if (!AddDirInfo(0, Index, Buffer, Length, PBytesTransferred))
|
||||
break;
|
||||
FspFileSystemAddDirInfo(0, Buffer, Length, PBytesTransferred);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static FSP_FILE_SYSTEM_INTERFACE NotifyfsInterface =
|
||||
{
|
||||
.GetVolumeInfo = GetVolumeInfo,
|
||||
.GetSecurityByName = GetSecurityByName,
|
||||
.Create = Create,
|
||||
.Open = Open,
|
||||
.Overwrite = Overwrite,
|
||||
.Read = Read,
|
||||
.GetFileInfo = GetFileInfo,
|
||||
.ReadDirectory = ReadDirectory,
|
||||
};
|
||||
|
||||
static VOID CALLBACK NotifyfsTick(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_TIMER Timer)
|
||||
{
|
||||
NOTIFYFS *Notifyfs = Context;
|
||||
UINT32 Ticks = InterlockedIncrement(&Notifyfs->Ticks);
|
||||
UINT32 OldCount = CountFromTicks(Ticks - 1);
|
||||
UINT32 NewCount = CountFromTicks(Ticks);
|
||||
union
|
||||
{
|
||||
UINT8 B[FIELD_OFFSET(FSP_FSCTL_NOTIFY_INFO, FileNameBuf) + MAX_PATH * sizeof(WCHAR)];
|
||||
FSP_FSCTL_NOTIFY_INFO V;
|
||||
} NotifyInfoBuf;
|
||||
FSP_FSCTL_NOTIFY_INFO *NotifyInfo = &NotifyInfoBuf.V;
|
||||
|
||||
memset(NotifyInfo, 0, sizeof NotifyInfo);
|
||||
if (OldCount < NewCount)
|
||||
{
|
||||
wsprintfW(NotifyInfo->FileNameBuf, L"\\%u", (unsigned)NewCount);
|
||||
NotifyInfo->Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) +
|
||||
wcslen(NotifyInfo->FileNameBuf) * sizeof(WCHAR));
|
||||
NotifyInfo->Action = FILE_ACTION_ADDED;
|
||||
NotifyInfo->Filter = FILE_NOTIFY_CHANGE_FILE_NAME;
|
||||
FspDebugLog("CREATE \\%u\n", (unsigned)NewCount);
|
||||
}
|
||||
else if (OldCount > NewCount)
|
||||
{
|
||||
wsprintfW(NotifyInfo->FileNameBuf, L"\\%u", (unsigned)OldCount);
|
||||
NotifyInfo->Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) +
|
||||
wcslen(NotifyInfo->FileNameBuf) * sizeof(WCHAR));
|
||||
NotifyInfo->Action = FILE_ACTION_REMOVED;
|
||||
NotifyInfo->Filter = FILE_NOTIFY_CHANGE_FILE_NAME;
|
||||
FspDebugLog("REMOVE \\%u\n", (unsigned)OldCount);
|
||||
}
|
||||
|
||||
if (OldCount != NewCount)
|
||||
{
|
||||
if (STATUS_SUCCESS == FspFileSystemNotifyBegin(Notifyfs->FileSystem, 500))
|
||||
{
|
||||
FspFileSystemNotify(Notifyfs->FileSystem, NotifyInfo, NotifyInfo->Size);
|
||||
FspFileSystemNotifyEnd(Notifyfs->FileSystem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static VOID NotifyfsDelete(NOTIFYFS *Notifyfs);
|
||||
|
||||
static NTSTATUS NotifyfsCreate(PWSTR VolumePrefix, PWSTR MountPoint, NOTIFYFS **PNotifyfs)
|
||||
{
|
||||
FSP_FSCTL_VOLUME_PARAMS VolumeParams;
|
||||
NOTIFYFS *Notifyfs = 0;
|
||||
INT64 TimerDue;
|
||||
NTSTATUS Result;
|
||||
|
||||
*PNotifyfs = 0;
|
||||
|
||||
if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(
|
||||
L"O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)", SDDL_REVISION_1,
|
||||
&DefaultSecurity, &DefaultSecuritySize))
|
||||
{
|
||||
Result = FspNtStatusFromWin32(GetLastError());
|
||||
goto exit;
|
||||
}
|
||||
|
||||
Notifyfs = malloc(sizeof *Notifyfs);
|
||||
if (0 == Notifyfs)
|
||||
{
|
||||
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||
goto exit;
|
||||
}
|
||||
memset(Notifyfs, 0, sizeof *Notifyfs);
|
||||
|
||||
memset(&VolumeParams, 0, sizeof VolumeParams);
|
||||
VolumeParams.SectorSize = ALLOCATION_UNIT;
|
||||
VolumeParams.SectorsPerAllocationUnit = 1;
|
||||
VolumeParams.VolumeCreationTime = 0;
|
||||
VolumeParams.VolumeSerialNumber = 0;
|
||||
VolumeParams.FileInfoTimeout = 1000;
|
||||
VolumeParams.CaseSensitiveSearch = 0;
|
||||
VolumeParams.CasePreservedNames = 1;
|
||||
VolumeParams.UnicodeOnDisk = 1;
|
||||
VolumeParams.PersistentAcls = 0;
|
||||
VolumeParams.PostCleanupWhenModifiedOnly = 1;
|
||||
VolumeParams.UmFileContextIsFullContext = 1;
|
||||
if (0 != VolumePrefix)
|
||||
wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix);
|
||||
wcscpy_s(VolumeParams.FileSystemName, sizeof VolumeParams.FileSystemName / sizeof(WCHAR),
|
||||
L"" PROGNAME);
|
||||
|
||||
Result = FspFileSystemCreate(
|
||||
VolumeParams.Prefix[0] ? L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME,
|
||||
&VolumeParams,
|
||||
&NotifyfsInterface,
|
||||
&Notifyfs->FileSystem);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
Notifyfs->FileSystem->UserContext = Notifyfs;
|
||||
|
||||
FspFileSystemSetDebugLog(Notifyfs->FileSystem, DEBUGFLAGS);
|
||||
|
||||
Result = FspFileSystemSetMountPoint(Notifyfs->FileSystem, MountPoint);
|
||||
if (!NT_SUCCESS(Result))
|
||||
goto exit;
|
||||
|
||||
Notifyfs->Timer = CreateThreadpoolTimer(NotifyfsTick, Notifyfs, 0);
|
||||
if (0 == Notifyfs->Timer)
|
||||
{
|
||||
Result = FspNtStatusFromWin32(GetLastError());
|
||||
goto exit;
|
||||
}
|
||||
|
||||
TimerDue = -1000;
|
||||
SetThreadpoolTimer(Notifyfs->Timer, (PVOID)&TimerDue, 1000, 0);
|
||||
|
||||
Result = STATUS_SUCCESS;
|
||||
|
||||
exit:
|
||||
if (NT_SUCCESS(Result))
|
||||
*PNotifyfs = Notifyfs;
|
||||
else if (0 != Notifyfs)
|
||||
NotifyfsDelete(Notifyfs);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static VOID NotifyfsDelete(NOTIFYFS *Notifyfs)
|
||||
{
|
||||
if (0 != Notifyfs->Timer)
|
||||
{
|
||||
SetThreadpoolTimer(Notifyfs->Timer, 0, 0, 0);
|
||||
WaitForThreadpoolTimerCallbacks(Notifyfs->Timer, TRUE);
|
||||
CloseThreadpoolTimer(Notifyfs->Timer);
|
||||
}
|
||||
|
||||
if (0 != Notifyfs->FileSystem)
|
||||
FspFileSystemDelete(Notifyfs->FileSystem);
|
||||
|
||||
free(Notifyfs);
|
||||
}
|
||||
|
||||
static NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv)
|
||||
{
|
||||
#define argtos(v) if (arge > ++argp) v = *argp; else goto usage
|
||||
|
||||
wchar_t **argp, **arge;
|
||||
PWSTR VolumePrefix = 0;
|
||||
PWSTR MountPoint = 0;
|
||||
NOTIFYFS *Notifyfs = 0;
|
||||
NTSTATUS Result;
|
||||
|
||||
for (argp = argv + 1, arge = argv + argc; arge > argp; argp++)
|
||||
{
|
||||
if (L'-' != argp[0][0])
|
||||
break;
|
||||
switch (argp[0][1])
|
||||
{
|
||||
case L'?':
|
||||
goto usage;
|
||||
case L'm':
|
||||
argtos(MountPoint);
|
||||
break;
|
||||
case L'u':
|
||||
argtos(VolumePrefix);
|
||||
break;
|
||||
default:
|
||||
goto usage;
|
||||
}
|
||||
}
|
||||
|
||||
if (arge > argp)
|
||||
goto usage;
|
||||
|
||||
if (0 == MountPoint)
|
||||
goto usage;
|
||||
|
||||
FspDebugLogSetHandle(GetStdHandle(STD_ERROR_HANDLE));
|
||||
|
||||
Result = NotifyfsCreate(VolumePrefix, MountPoint, &Notifyfs);
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
fail(L"cannot create file system");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
Result = FspFileSystemStartDispatcher(Notifyfs->FileSystem, 0);
|
||||
if (!NT_SUCCESS(Result))
|
||||
{
|
||||
fail(L"cannot start file system");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
MountPoint = FspFileSystemMountPoint(Notifyfs->FileSystem);
|
||||
|
||||
info(L"%s%s%s -m %s",
|
||||
L"" PROGNAME,
|
||||
0 != VolumePrefix && L'\0' != VolumePrefix[0] ? L" -u " : L"",
|
||||
0 != VolumePrefix && L'\0' != VolumePrefix[0] ? VolumePrefix : L"",
|
||||
MountPoint);
|
||||
|
||||
Service->UserContext = Notifyfs;
|
||||
Result = STATUS_SUCCESS;
|
||||
|
||||
exit:
|
||||
if (!NT_SUCCESS(Result) && 0 != Notifyfs)
|
||||
NotifyfsDelete(Notifyfs);
|
||||
|
||||
return Result;
|
||||
|
||||
usage:;
|
||||
static wchar_t usage[] = L""
|
||||
"usage: %s OPTIONS\n"
|
||||
"\n"
|
||||
"options:\n"
|
||||
" -u \\Server\\Share [UNC prefix (single backslash)]\n"
|
||||
" -m MountPoint [X:|*|directory]\n";
|
||||
|
||||
fail(usage, L"" PROGNAME);
|
||||
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
|
||||
#undef argtos
|
||||
}
|
||||
|
||||
static NTSTATUS SvcStop(FSP_SERVICE *Service)
|
||||
{
|
||||
NOTIFYFS *Notifyfs = Service->UserContext;
|
||||
|
||||
FspFileSystemStopDispatcher(Notifyfs->FileSystem);
|
||||
NotifyfsDelete(Notifyfs);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int wmain(int argc, wchar_t **argv)
|
||||
{
|
||||
if (!NT_SUCCESS(FspLoad(0)))
|
||||
return ERROR_DELAY_LOAD_FAILED;
|
||||
|
||||
return FspServiceRun(L"" PROGNAME, SvcStart, SvcStop, 0);
|
||||
}
|
31
tst/notifyfs/notifyfs.sln
Normal file
31
tst/notifyfs/notifyfs.sln
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30717.126
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "notifyfs", "notifyfs.vcxproj", "{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Debug|x64.Build.0 = Debug|x64
|
||||
{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Debug|x86.Build.0 = Debug|Win32
|
||||
{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Release|x64.ActiveCfg = Release|x64
|
||||
{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Release|x64.Build.0 = Release|x64
|
||||
{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Release|x86.ActiveCfg = Release|Win32
|
||||
{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3CD0763B-2BFD-4D73-9539-13273788C41E}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
175
tst/notifyfs/notifyfs.vcxproj
Normal file
175
tst/notifyfs/notifyfs.vcxproj
Normal file
@ -0,0 +1,175 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{4ba1ded0-4268-408a-a4e2-8e1a6d55a99c}</ProjectGuid>
|
||||
<RootNamespace>notifyfs</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>$(LatestTargetPlatformVersion)</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)build\$(Configuration)\</OutDir>
|
||||
<IntDir>$(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\</IntDir>
|
||||
<TargetName>$(ProjectName)-$(PlatformTarget)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)build\$(Configuration)\</OutDir>
|
||||
<IntDir>$(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\</IntDir>
|
||||
<TargetName>$(ProjectName)-$(PlatformTarget)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)build\$(Configuration)\</OutDir>
|
||||
<IntDir>$(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\</IntDir>
|
||||
<TargetName>$(ProjectName)-$(PlatformTarget)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)build\$(Configuration)\</OutDir>
|
||||
<IntDir>$(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\</IntDir>
|
||||
<TargetName>$(ProjectName)-$(PlatformTarget)</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\WinFsp\inc</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>winfsp-$(PlatformTarget).dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\WinFsp\inc</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>winfsp-$(PlatformTarget).dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\WinFsp\inc</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>winfsp-$(PlatformTarget).dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\WinFsp\inc</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>winfsp-$(PlatformTarget).dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="notifyfs.c" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
14
tst/notifyfs/notifyfs.vcxproj.filters
Normal file
14
tst/notifyfs/notifyfs.vcxproj.filters
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="notifyfs.c">
|
||||
<Filter>Source</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -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);
|
||||
@ -1930,6 +2000,44 @@ void setvolinfo_test(void)
|
||||
}
|
||||
}
|
||||
|
||||
void query_winfsp_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout, BOOLEAN ExpectWinFsp)
|
||||
{
|
||||
void* memfs = memfs_start_ex(Flags, FileInfoTimeout);
|
||||
|
||||
WCHAR FilePath[MAX_PATH];
|
||||
HANDLE Handle;
|
||||
DWORD BytesTransferred;
|
||||
|
||||
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\",
|
||||
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||
|
||||
Handle = CreateFileW(FilePath,
|
||||
0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, 0);
|
||||
ASSERT(INVALID_HANDLE_VALUE != Handle);
|
||||
|
||||
if (ExpectWinFsp)
|
||||
ASSERT(DeviceIoControl(Handle, FSP_FSCTL_QUERY_WINFSP, 0, 0, 0, 0, &BytesTransferred, 0));
|
||||
else
|
||||
ASSERT(!DeviceIoControl(Handle, FSP_FSCTL_QUERY_WINFSP, 0, 0, 0, 0, &BytesTransferred, 0) &&
|
||||
ERROR_INVALID_FUNCTION == GetLastError());
|
||||
|
||||
CloseHandle(Handle);
|
||||
|
||||
memfs_stop(memfs);
|
||||
}
|
||||
|
||||
void query_winfsp_test(void)
|
||||
{
|
||||
if (NtfsTests)
|
||||
return;
|
||||
|
||||
if (WinFspDiskTests)
|
||||
query_winfsp_dotest(MemfsDisk, 0, 0, TRUE);
|
||||
if (WinFspNetTests)
|
||||
query_winfsp_dotest(MemfsNet, L"\\\\memfs\\share", 0, TRUE);
|
||||
}
|
||||
|
||||
void info_tests(void)
|
||||
{
|
||||
if (!OptShareName)
|
||||
@ -1944,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)
|
||||
@ -1955,4 +2064,6 @@ void info_tests(void)
|
||||
TEST(rename_pid_test);
|
||||
TEST(getvolinfo_test);
|
||||
TEST(setvolinfo_test);
|
||||
if (!NtfsTests)
|
||||
TEST(query_winfsp_test);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -25,7 +25,7 @@
|
||||
|
||||
#include "winfsp-tests.h"
|
||||
|
||||
void posix_map_sid_test(void)
|
||||
static void posix_map_sid_test(void)
|
||||
{
|
||||
struct
|
||||
{
|
||||
@ -171,7 +171,7 @@ void posix_map_sid_test(void)
|
||||
LocalFree(map[sizeof map / sizeof map[0] - 1].SidStr);
|
||||
}
|
||||
|
||||
void posix_map_sd_test(void)
|
||||
static void posix_map_sd_test(void)
|
||||
{
|
||||
struct
|
||||
{
|
||||
@ -237,7 +237,93 @@ void posix_map_sd_test(void)
|
||||
}
|
||||
}
|
||||
|
||||
void posix_map_path_test(void)
|
||||
static void posix_merge_sd_test(void)
|
||||
{
|
||||
struct
|
||||
{
|
||||
UINT32 Uid, Gid, Mode;
|
||||
PWSTR ExistingSddl;
|
||||
PWSTR Sddl;
|
||||
} map[] =
|
||||
{
|
||||
{
|
||||
18, 544, 00000,
|
||||
0,
|
||||
L"O:SYG:BAD:P(A;;0x1f0198;;;SY)(A;;0x120088;;;BA)(A;;0x120088;;;WD)"
|
||||
},
|
||||
{
|
||||
18, 544, 00000,
|
||||
L"",
|
||||
L"O:SYG:BA"
|
||||
},
|
||||
{
|
||||
18, 544, 00000,
|
||||
L"O:WD",
|
||||
L"O:WDG:BA"
|
||||
},
|
||||
{
|
||||
18, 544, 00000,
|
||||
L"G:WD",
|
||||
L"O:SYG:WD"
|
||||
},
|
||||
{
|
||||
18, 544, 00000,
|
||||
L"O:WDG:WD",
|
||||
L"O:WDG:WD"
|
||||
},
|
||||
{
|
||||
18, 544, 00000,
|
||||
L"D:P",
|
||||
L"O:SYG:BAD:P"
|
||||
},
|
||||
{
|
||||
18, 544, 00000,
|
||||
L"D:P(A;;FA;;;SY)",
|
||||
L"O:SYG:BAD:P(A;;FA;;;SY)"
|
||||
},
|
||||
{
|
||||
18, 544, 00000,
|
||||
L"O:WDG:WDD:P(A;;FA;;;SY)",
|
||||
L"O:WDG:WDD:P(A;;FA;;;SY)"
|
||||
},
|
||||
};
|
||||
NTSTATUS Result;
|
||||
BOOL Success;
|
||||
PSECURITY_DESCRIPTOR ExistingSecurityDescriptor, SecurityDescriptor;
|
||||
PWSTR Sddl;
|
||||
|
||||
for (size_t i = 0; sizeof map / sizeof map[0] > i; i++)
|
||||
{
|
||||
if (0 != map[i].ExistingSddl)
|
||||
{
|
||||
Success = ConvertStringSecurityDescriptorToSecurityDescriptorW(
|
||||
map[i].ExistingSddl, SDDL_REVISION_1,
|
||||
&ExistingSecurityDescriptor, 0);
|
||||
ASSERT(Success);
|
||||
}
|
||||
else
|
||||
ExistingSecurityDescriptor = 0;
|
||||
|
||||
Result = FspPosixMergePermissionsToSecurityDescriptor(
|
||||
map[i].Uid, map[i].Gid, map[i].Mode, ExistingSecurityDescriptor, &SecurityDescriptor);
|
||||
ASSERT(NT_SUCCESS(Result));
|
||||
|
||||
Success = ConvertSecurityDescriptorToStringSecurityDescriptorW(
|
||||
SecurityDescriptor, SDDL_REVISION_1,
|
||||
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
|
||||
&Sddl, 0);
|
||||
ASSERT(Success);
|
||||
ASSERT(0 == wcscmp(map[i].Sddl, Sddl));
|
||||
LocalFree(Sddl);
|
||||
|
||||
FspDeleteSecurityDescriptor(SecurityDescriptor,
|
||||
FspPosixMergePermissionsToSecurityDescriptor);
|
||||
|
||||
LocalFree(ExistingSecurityDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
static void posix_map_path_test(void)
|
||||
{
|
||||
struct
|
||||
{
|
||||
@ -274,5 +360,6 @@ void posix_tests(void)
|
||||
|
||||
TEST(posix_map_sid_test);
|
||||
TEST(posix_map_sd_test);
|
||||
TEST(posix_merge_sd_test);
|
||||
TEST(posix_map_path_test);
|
||||
}
|
||||
|
@ -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