mirror of
https://github.com/winfsp/winfsp.git
synced 2025-07-03 17:32:57 -05:00
Compare commits
48 Commits
Author | SHA1 | Date | |
---|---|---|---|
847eab3da4 | |||
5131ed5c01 | |||
b513128cfe | |||
3fe69f2208 | |||
29fd9bf779 | |||
3c391ca711 | |||
82a8545d8f | |||
79be3e445a | |||
b04266e0fe | |||
25adfaec00 | |||
ce20747534 | |||
d3d75bf977 | |||
6f1f1cda71 | |||
21dfeca124 | |||
6f585ce63e | |||
8f90305726 | |||
490d0577bb | |||
c9d3cb74c7 | |||
7d2ff3afeb | |||
c415c87195 | |||
d161ca59a7 | |||
bc03af3b2a | |||
ed1543665c | |||
a99fa512d4 | |||
a6800dd73d | |||
aa9354773b | |||
05b37c744b | |||
1d15c9546b | |||
51b33f02aa | |||
97ffa741b2 | |||
073645db3b | |||
fd9ac1c9e0 | |||
62b08c3d1e | |||
9436fd8402 | |||
de75454d50 | |||
430d7a5650 | |||
4655926d03 | |||
565caebe4c | |||
a47f853beb | |||
89ec3e6733 | |||
dbdfaeee1f | |||
2c64d59001 | |||
af8c74378e | |||
19c320350f | |||
d60b1de430 | |||
6a7b6c77c6 | |||
16b1b2b349 | |||
f181593f49 |
@ -1,6 +1,42 @@
|
|||||||
= Changelog
|
= Changelog
|
||||||
|
|
||||||
|
|
||||||
|
v1.5B3 (2019.3 B3)::
|
||||||
|
|
||||||
|
Changes since v1.4:
|
||||||
|
|
||||||
|
* [GEN] WinFsp file systems can now be used by WSLinux. Use the command `sudo mount -t drvfs x: /mnt/x` to mount.
|
||||||
|
* [GEN] Extended attribute support has been added for all WinFsp API's: native, .NET, FUSE2 and FUSE3.
|
||||||
|
* [GEN] Mount Manager support has been added and it works for current and new file systems:
|
||||||
|
** If the file system mountpoint is in the syntax `\\.\X:` then the Mount Manager is used.
|
||||||
|
** If the file system mountpoint is in the syntax `X:` then `DefineDosDeviceW` is used (i.e. same as today).
|
||||||
|
** If the file system mountpoint is in the syntax `X:\DIR` then a reparse point is used and the file system is mounted as a directory (i.e. same as today).
|
||||||
|
** Caveats:
|
||||||
|
*** It requires Administrator access. This is because opening the `\\.\MountPointManager` device requires Administrator access.
|
||||||
|
*** It currently works with drives (`\\.\X:`) but not directories (`\\.\X:\DIR`).
|
||||||
|
*** Mount Manager drives created by WinFsp are transient. WinFsp takes various steps to ensure that this is the case.
|
||||||
|
*** Mount Manager drives are global and are visible across Terminal Services sessions (they go into the `\GLOBAL??` portion of the NT namespace).
|
||||||
|
* [FSD] Support for kernel-mode file systems on top of WinFsp has been added. See `FspFsextProvider`. This is in preparation for WinFuse - FUSE for Windows and WSLinux.
|
||||||
|
* [FSD] FastIO support has been added. FastIO operations are enabled on cache-enabled file systems with the notable exception of `FastIoQueryOpen`, which allows opening files in kernel mode; this operation requires the file system to specify the `FSP_FSCTL_VOLUME_PARAMS::AllowOpenInKernelMode` flag.
|
||||||
|
* [FSD] Support for `FileFsSectorSizeInformation` and `IOCTL_STORAGE_QUERY_PROPERTY / StorageAccessAlignmentProperty` has been added.
|
||||||
|
* [DLL] The `FspFileSystemStartDispatcher` default number of threads (`ThreadCount==0`) has been changed. See commit 3902874ac93fe40685d9761f46a96358ba24f24c for more.
|
||||||
|
* [FUSE] FUSE has new `-o UserName=DOMAIN+USERNAME` and `-o GroupName=DOMAIN+GROUPNAME` options. These function like the `-o uid=UID` and `-o gid=GID` options, but accept Windows user and groups names.
|
||||||
|
* [FUSE] FUSE has new `-o dothidden` option that is used to add the Windows hidden file attribute to files that start with a dot.
|
||||||
|
* [FUSE] FUSE has new `-o create_file_umask=nnn` and `-o create_dir_umask=nnn` options that allow for more control than the `-o create_umask=nnn` option.
|
||||||
|
* [FUSE] FUSE has new `--ExactFileSystemName=FSNAME` option that removes the "FUSE-" prefix from the file system name. (Use with caution: see discussion in PR #251.) (Thanks @johntyner.)
|
||||||
|
* [.NET] The .NET API now supports asynchronous handling of `Read`, `Write` and `ReadDirectory`. (Thanks @dworkin.)
|
||||||
|
* [.NET] The .NET API now supports fine-grained timeouts (`VolumeInfoTimeout`, `DirInfoTimeout`, etc).
|
||||||
|
* [.NET] The .NET API has new method `FileSystemHost.MountEx` that adds a `ThreadCount` parameter.
|
||||||
|
* [LAUNCH] The Launcher can now rewrite path arguments passed to file systems during launching using "Path Transformation Language". See commit a73f1b95592617ac7484e16c2e642573a4d65644 for more.
|
||||||
|
* [MEMFS] A new memfs FUSE3 file system written in C++ has been added. See `tst/memfs-fuse3`.
|
||||||
|
* [AIRFS] John Oberschelp has done some fantastic work adding persistence to the airfs file system. (Thanks @JohnOberschelp.)
|
||||||
|
* [FIX] Fixes for very large (> 4GiB) files. (Thanks @dworkin.)
|
||||||
|
* [FIX] A fix for how FUSE handles the return value from `opendir`. (GitHub issue billziss-gh/sshfs-win#54)
|
||||||
|
* [FIX] A fix for an invalid UID to SID mapping on domains with a lot of users. (Thanks @sganis.)
|
||||||
|
* [FIX] A fix on the C++ layer. (Thanks @colatkinson.)
|
||||||
|
* Other fixes and improvements.
|
||||||
|
|
||||||
|
|
||||||
v1.5B2 (2019.3 B2)::
|
v1.5B2 (2019.3 B2)::
|
||||||
|
|
||||||
Changes since v1.4:
|
Changes since v1.4:
|
||||||
|
@ -56,11 +56,13 @@ CONTRIBUTOR LIST
|
|||||||
|===
|
|===
|
||||||
|Ben Rubson |ben.rubson at gmail.com
|
|Ben Rubson |ben.rubson at gmail.com
|
||||||
|Bill Zissimopoulos |billziss at navimatics.com
|
|Bill Zissimopoulos |billziss at navimatics.com
|
||||||
|
|Brett Dutro |brett.dutro at gmail.com
|
||||||
|Colin Atkinson (Atakama, https://atakama.com) |colin at atakama.com
|
|Colin Atkinson (Atakama, https://atakama.com) |colin at atakama.com
|
||||||
|Felix Croes |felix at dworkin.nl
|
|Felix Croes |felix at dworkin.nl
|
||||||
|Francois Karam (KS2, http://www.ks2.fr) |francois.karam at ks2.fr
|
|Francois Karam (KS2, http://www.ks2.fr) |francois.karam at ks2.fr
|
||||||
|Fritz Elfert |fritz-github at fritz-elfert.de
|
|Fritz Elfert |fritz-github at fritz-elfert.de
|
||||||
|John Oberschelp |john at oberschelp.net
|
|John Oberschelp |john at oberschelp.net
|
||||||
|
|John Tyner |jtyner at gmail.com
|
||||||
|Sam Kelly (DuroSoft Technologies LLC, https://durosoft.com) |sam at durosoft.com
|
|Sam Kelly (DuroSoft Technologies LLC, https://durosoft.com) |sam at durosoft.com
|
||||||
|Santiago Ganis |sganis at gmail.com
|
|Santiago Ganis |sganis at gmail.com
|
||||||
|Tobias Urlaub |saibotu at outlook.de
|
|Tobias Urlaub |saibotu at outlook.de
|
||||||
|
@ -15,6 +15,13 @@ init:
|
|||||||
#- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
#- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
- 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")
|
||||||
- git submodule update --init --recursive
|
- git submodule update --init --recursive
|
||||||
- appveyor AddMessage "Change boot configuration and reboot" -Category Information
|
- appveyor AddMessage "Change boot configuration and reboot" -Category Information
|
||||||
- bcdedit /set testsigning on
|
- bcdedit /set testsigning on
|
||||||
|
@ -369,6 +369,23 @@
|
|||||||
<File Name="uninstall.sh" KeyPath="yes" />
|
<File Name="uninstall.sh" KeyPath="yes" />
|
||||||
</Component>
|
</Component>
|
||||||
</Directory>
|
</Directory>
|
||||||
|
<Directory Id="OPTDIR.fsext" Name="fsext" FileSource="..\..\..\opt\fsext">
|
||||||
|
<Directory Id="OPTDIR.fsext.inc" Name="inc">
|
||||||
|
<Directory Id="OPTDIR.fsext.inc.winfsp" Name="winfsp">
|
||||||
|
<Component Id="C.fsext.h">
|
||||||
|
<File Name="fsext.h" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
</Directory>
|
||||||
|
</Directory>
|
||||||
|
<Directory Id="OPTDIR.fsext.lib" Name="lib">
|
||||||
|
<Component Id="C.fsext.winfsp_x64.lib">
|
||||||
|
<File Id="FILE.fsext.winfsp_x64.lib" Name="winfsp-x64.lib" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
<Component Id="C.fsext.winfsp_x86.lib">
|
||||||
|
<File Id="FILE.fsext.winfsp_x86.lib" Name="winfsp-x86.lib" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
</Directory>
|
||||||
|
</Directory>
|
||||||
</DirectoryRef>
|
</DirectoryRef>
|
||||||
<DirectoryRef Id="SMPDIR" FileSource="..\..\..\tst">
|
<DirectoryRef Id="SMPDIR" FileSource="..\..\..\tst">
|
||||||
<Directory Id="SMPDIR.memfs" Name="memfs">
|
<Directory Id="SMPDIR.memfs" Name="memfs">
|
||||||
@ -382,6 +399,29 @@
|
|||||||
<File Name="memfs-main.c" KeyPath="yes" />
|
<File Name="memfs-main.c" KeyPath="yes" />
|
||||||
</Component>
|
</Component>
|
||||||
</Directory>
|
</Directory>
|
||||||
|
<Directory Id="SMPDIR.memfs_fuse3" Name="memfs-fuse3">
|
||||||
|
<Component Id="C.memfs_fuse3.cpp">
|
||||||
|
<File Name="memfs-fuse3.cpp" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
<Component Id="C.memfs_fuse3.compat.h">
|
||||||
|
<File Id="F.memfs_fuse3.compat.h" Name="compat.h" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
<Component Id="C.memfs_fuse3.sln">
|
||||||
|
<File Name="memfs-fuse3.sln" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
<Component Id="C.memfs_fuse3.vcxproj">
|
||||||
|
<File Name="memfs-fuse3.vcxproj" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
<Component Id="C.memfs_fuse3.vcxproj.filters">
|
||||||
|
<File Name="memfs-fuse3.vcxproj.filters" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
<Component Id="C.memfs_fuse3.Makefile">
|
||||||
|
<File Id="F.memfs_fuse3.Makefile" Name="Makefile" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
<Component Id="C.memfs_fuse3.README.md">
|
||||||
|
<File Id="F.memfsx_fuse3.README.md" Name="README.md" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
</Directory>
|
||||||
<Directory Id="SMPDIR.memfs_dotnet" Name="memfs-dotnet">
|
<Directory Id="SMPDIR.memfs_dotnet" Name="memfs-dotnet">
|
||||||
<Component Id="C.memfs_dotnet.Program.cs">
|
<Component Id="C.memfs_dotnet.Program.cs">
|
||||||
<File Id="FILE.memfs_dotnet.Program.cs" Name="Program.cs" KeyPath="yes" />
|
<File Id="FILE.memfs_dotnet.Program.cs" Name="Program.cs" KeyPath="yes" />
|
||||||
@ -391,6 +431,12 @@
|
|||||||
<Component Id="C.airfs.cpp">
|
<Component Id="C.airfs.cpp">
|
||||||
<File Name="airfs.cpp" KeyPath="yes" />
|
<File Name="airfs.cpp" KeyPath="yes" />
|
||||||
</Component>
|
</Component>
|
||||||
|
<Component Id="C.persistence.cpp">
|
||||||
|
<File Name="persistence.cpp" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
|
<Component Id="C.common.h">
|
||||||
|
<File Name="common.h" KeyPath="yes" />
|
||||||
|
</Component>
|
||||||
<Component Id="C.airfs.sln">
|
<Component Id="C.airfs.sln">
|
||||||
<File Name="airfs.sln" KeyPath="yes" />
|
<File Name="airfs.sln" KeyPath="yes" />
|
||||||
</Component>
|
</Component>
|
||||||
@ -580,13 +626,27 @@
|
|||||||
<ComponentRef Id="C.fuse.install.sh" />
|
<ComponentRef Id="C.fuse.install.sh" />
|
||||||
<ComponentRef Id="C.fuse.uninstall.sh" />
|
<ComponentRef Id="C.fuse.uninstall.sh" />
|
||||||
</ComponentGroup>
|
</ComponentGroup>
|
||||||
|
<ComponentGroup Id="C.WinFsp.opt.fsext">
|
||||||
|
<ComponentRef Id="C.fsext.h" />
|
||||||
|
<ComponentRef Id="C.fsext.winfsp_x64.lib" />
|
||||||
|
<ComponentRef Id="C.fsext.winfsp_x86.lib" />
|
||||||
|
</ComponentGroup>
|
||||||
<ComponentGroup Id="C.WinFsp.smp">
|
<ComponentGroup Id="C.WinFsp.smp">
|
||||||
<ComponentRef Id="C.memfs_x64.exe" />
|
<ComponentRef Id="C.memfs_x64.exe" />
|
||||||
<ComponentRef Id="C.memfs_x86.exe" />
|
<ComponentRef Id="C.memfs_x86.exe" />
|
||||||
<ComponentRef Id="C.memfs.h" />
|
<ComponentRef Id="C.memfs.h" />
|
||||||
<ComponentRef Id="C.memfs.cpp" />
|
<ComponentRef Id="C.memfs.cpp" />
|
||||||
<ComponentRef Id="C.memfs_main.c" />
|
<ComponentRef Id="C.memfs_main.c" />
|
||||||
|
<ComponentRef Id="C.memfs_fuse3.cpp" />
|
||||||
|
<ComponentRef Id="C.memfs_fuse3.compat.h" />
|
||||||
|
<ComponentRef Id="C.memfs_fuse3.sln" />
|
||||||
|
<ComponentRef Id="C.memfs_fuse3.vcxproj" />
|
||||||
|
<ComponentRef Id="C.memfs_fuse3.vcxproj.filters" />
|
||||||
|
<ComponentRef Id="C.memfs_fuse3.Makefile" />
|
||||||
|
<ComponentRef Id="C.memfs_fuse3.README.md" />
|
||||||
<ComponentRef Id="C.airfs.cpp" />
|
<ComponentRef Id="C.airfs.cpp" />
|
||||||
|
<ComponentRef Id="C.persistence.cpp" />
|
||||||
|
<ComponentRef Id="C.common.h" />
|
||||||
<ComponentRef Id="C.airfs.sln" />
|
<ComponentRef Id="C.airfs.sln" />
|
||||||
<ComponentRef Id="C.airfs.vcxproj" />
|
<ComponentRef Id="C.airfs.vcxproj" />
|
||||||
<ComponentRef Id="C.airfs.vcxproj.filters" />
|
<ComponentRef Id="C.airfs.vcxproj.filters" />
|
||||||
@ -694,6 +754,16 @@
|
|||||||
<ComponentGroupRef Id="C.WinFsp.smp.net" />
|
<ComponentGroupRef Id="C.WinFsp.smp.net" />
|
||||||
<ComponentGroupRef Id="C.WinFsp.sym" />
|
<ComponentGroupRef Id="C.WinFsp.sym" />
|
||||||
</Feature>
|
</Feature>
|
||||||
|
<Feature
|
||||||
|
Id="F.KernelDeveloper"
|
||||||
|
Level="1000"
|
||||||
|
Title="Kernel Developer"
|
||||||
|
Description="Additional files needed for in-kernel development."
|
||||||
|
AllowAdvertise="no"
|
||||||
|
InstallDefault="local"
|
||||||
|
Absent="allow">
|
||||||
|
<ComponentGroupRef Id="C.WinFsp.opt.fsext" />
|
||||||
|
</Feature>
|
||||||
<Feature
|
<Feature
|
||||||
Id="F.Cygfuse"
|
Id="F.Cygfuse"
|
||||||
Level="1000"
|
Level="1000"
|
||||||
|
@ -204,7 +204,9 @@
|
|||||||
<ClCompile Include="..\..\..\tst\winfsp-tests\security-test.c" />
|
<ClCompile Include="..\..\..\tst\winfsp-tests\security-test.c" />
|
||||||
<ClCompile Include="..\..\..\tst\winfsp-tests\stream-tests.c" />
|
<ClCompile Include="..\..\..\tst\winfsp-tests\stream-tests.c" />
|
||||||
<ClCompile Include="..\..\..\tst\winfsp-tests\timeout-test.c" />
|
<ClCompile Include="..\..\..\tst\winfsp-tests\timeout-test.c" />
|
||||||
|
<ClCompile Include="..\..\..\tst\winfsp-tests\uuid5-test.c" />
|
||||||
<ClCompile Include="..\..\..\tst\winfsp-tests\version-test.c" />
|
<ClCompile Include="..\..\..\tst\winfsp-tests\version-test.c" />
|
||||||
|
<ClCompile Include="..\..\..\tst\winfsp-tests\volpath-test.c" />
|
||||||
<ClCompile Include="..\..\..\tst\winfsp-tests\winfsp-tests.c" />
|
<ClCompile Include="..\..\..\tst\winfsp-tests\winfsp-tests.c" />
|
||||||
<ClCompile Include="..\..\..\tst\winfsp-tests\wsl-test.c" />
|
<ClCompile Include="..\..\..\tst\winfsp-tests\wsl-test.c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -103,6 +103,12 @@
|
|||||||
<ClCompile Include="..\..\..\tst\winfsp-tests\launcher-ptrans-test.c">
|
<ClCompile Include="..\..\..\tst\winfsp-tests\launcher-ptrans-test.c">
|
||||||
<Filter>Source</Filter>
|
<Filter>Source</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\..\tst\winfsp-tests\volpath-test.c">
|
||||||
|
<Filter>Source</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\..\tst\winfsp-tests\uuid5-test.c">
|
||||||
|
<Filter>Source</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\..\..\ext\tlib\testsuite.h">
|
<ClInclude Include="..\..\..\ext\tlib\testsuite.h">
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
<MyCanonicalVersion>1.5</MyCanonicalVersion>
|
<MyCanonicalVersion>1.5</MyCanonicalVersion>
|
||||||
|
|
||||||
<MyProductVersion>2019.3 B2</MyProductVersion>
|
<MyProductVersion>2019.3 B3</MyProductVersion>
|
||||||
<MyProductStage>Beta</MyProductStage>
|
<MyProductStage>Beta</MyProductStage>
|
||||||
|
|
||||||
<MyVersion>$(MyCanonicalVersion).$(MyBuildNumber)</MyVersion>
|
<MyVersion>$(MyCanonicalVersion).$(MyBuildNumber)</MyVersion>
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
<ClCompile Include="..\..\src\dll\fuse\fuse_main.c" />
|
<ClCompile Include="..\..\src\dll\fuse\fuse_main.c" />
|
||||||
<ClCompile Include="..\..\src\dll\fuse\fuse_opt.c" />
|
<ClCompile Include="..\..\src\dll\fuse\fuse_opt.c" />
|
||||||
<ClCompile Include="..\..\src\dll\launch.c" />
|
<ClCompile Include="..\..\src\dll\launch.c" />
|
||||||
|
<ClCompile Include="..\..\src\dll\mount.c" />
|
||||||
<ClCompile Include="..\..\src\dll\np.c" />
|
<ClCompile Include="..\..\src\dll\np.c" />
|
||||||
<ClCompile Include="..\..\src\dll\security.c" />
|
<ClCompile Include="..\..\src\dll\security.c" />
|
||||||
<ClCompile Include="..\..\src\dll\debug.c" />
|
<ClCompile Include="..\..\src\dll\debug.c" />
|
||||||
|
@ -160,6 +160,9 @@
|
|||||||
<ClCompile Include="..\..\src\ku\posix.c">
|
<ClCompile Include="..\..\src\ku\posix.c">
|
||||||
<Filter>Source\ku</Filter>
|
<Filter>Source\ku</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\dll\mount.c">
|
||||||
|
<Filter>Source</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\..\src\dll\library.def">
|
<None Include="..\..\src\dll\library.def">
|
||||||
|
@ -105,7 +105,7 @@
|
|||||||
<PreprocessorDefinitions> _X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions> _X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<AdditionalDependencies>wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>cng.lib;wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<GenerateMapFile>true</GenerateMapFile>
|
<GenerateMapFile>true</GenerateMapFile>
|
||||||
<ProgramDatabaseFile>$(OutDir)$(TargetFileName).pdb</ProgramDatabaseFile>
|
<ProgramDatabaseFile>$(OutDir)$(TargetFileName).pdb</ProgramDatabaseFile>
|
||||||
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
|
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
|
||||||
@ -118,7 +118,7 @@
|
|||||||
<PreprocessorDefinitions> _X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions> _X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<AdditionalDependencies>wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>cng.lib;wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<GenerateMapFile>true</GenerateMapFile>
|
<GenerateMapFile>true</GenerateMapFile>
|
||||||
<ProgramDatabaseFile>$(OutDir)$(TargetFileName).pdb</ProgramDatabaseFile>
|
<ProgramDatabaseFile>$(OutDir)$(TargetFileName).pdb</ProgramDatabaseFile>
|
||||||
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
|
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
|
||||||
@ -131,7 +131,7 @@
|
|||||||
<PreprocessorDefinitions> _WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions> _WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<AdditionalDependencies>wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>cng.lib;wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<GenerateMapFile>true</GenerateMapFile>
|
<GenerateMapFile>true</GenerateMapFile>
|
||||||
<ProgramDatabaseFile>$(OutDir)$(TargetFileName).pdb</ProgramDatabaseFile>
|
<ProgramDatabaseFile>$(OutDir)$(TargetFileName).pdb</ProgramDatabaseFile>
|
||||||
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
|
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
|
||||||
@ -144,7 +144,7 @@
|
|||||||
<PreprocessorDefinitions> _WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions> _WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<AdditionalDependencies>wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>cng.lib;wdmsec.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
<GenerateMapFile>true</GenerateMapFile>
|
<GenerateMapFile>true</GenerateMapFile>
|
||||||
<ProgramDatabaseFile>$(OutDir)$(TargetFileName).pdb</ProgramDatabaseFile>
|
<ProgramDatabaseFile>$(OutDir)$(TargetFileName).pdb</ProgramDatabaseFile>
|
||||||
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
|
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
|
||||||
@ -156,6 +156,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\..\src\ku\posix.c" />
|
<ClCompile Include="..\..\src\ku\posix.c" />
|
||||||
|
<ClCompile Include="..\..\src\ku\uuid5.c" />
|
||||||
<ClCompile Include="..\..\src\sys\cleanup.c" />
|
<ClCompile Include="..\..\src\sys\cleanup.c" />
|
||||||
<ClCompile Include="..\..\src\sys\close.c" />
|
<ClCompile Include="..\..\src\sys\close.c" />
|
||||||
<ClCompile Include="..\..\src\sys\create.c" />
|
<ClCompile Include="..\..\src\sys\create.c" />
|
||||||
@ -175,6 +176,7 @@
|
|||||||
<ClCompile Include="..\..\src\sys\ioq.c" />
|
<ClCompile Include="..\..\src\sys\ioq.c" />
|
||||||
<ClCompile Include="..\..\src\sys\lockctl.c" />
|
<ClCompile Include="..\..\src\sys\lockctl.c" />
|
||||||
<ClCompile Include="..\..\src\sys\meta.c" />
|
<ClCompile Include="..\..\src\sys\meta.c" />
|
||||||
|
<ClCompile Include="..\..\src\sys\mountdev.c" />
|
||||||
<ClCompile Include="..\..\src\sys\mup.c" />
|
<ClCompile Include="..\..\src\sys\mup.c" />
|
||||||
<ClCompile Include="..\..\src\sys\name.c" />
|
<ClCompile Include="..\..\src\sys\name.c" />
|
||||||
<ClCompile Include="..\..\src\sys\psbuffer.c" />
|
<ClCompile Include="..\..\src\sys\psbuffer.c" />
|
||||||
|
@ -113,6 +113,12 @@
|
|||||||
<ClCompile Include="..\..\src\ku\posix.c">
|
<ClCompile Include="..\..\src\ku\posix.c">
|
||||||
<Filter>Source\ku</Filter>
|
<Filter>Source\ku</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\ku\uuid5.c">
|
||||||
|
<Filter>Source\ku</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\sys\mountdev.c">
|
||||||
|
<Filter>Source</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\..\src\sys\driver.h">
|
<ClInclude Include="..\..\src\sys\driver.h">
|
||||||
|
@ -18,10 +18,6 @@ Disconnecting (unmapping) a network drive does not work.::
|
|||||||
Case-sensitive file systems do not work properly when mounted as a directory.::
|
Case-sensitive file systems do not work properly when mounted as a directory.::
|
||||||
|
|
||||||
This is fixed as of WinFsp 2018.2 B3.
|
This is fixed as of WinFsp 2018.2 B3.
|
||||||
+
|
|
||||||
Windows and WinFsp support case-sensitive file systems. These file systems work properly when mounted as a drive. Unfortunately when a file system is mounted as a directory over NTFS, Windows expects it to be case-insensitive and will UPPERCASE many of the file names sent to the file system.
|
|
||||||
+
|
|
||||||
This is an unfortunate but well understood Windows limitation. Case-sensitive file systems should only be mounted as drives.
|
|
||||||
|
|
||||||
|
|
||||||
Why is the DLL not installed in the Windows system directories?::
|
Why is the DLL not installed in the Windows system directories?::
|
||||||
@ -68,5 +64,10 @@ With this in mind here are the reasons for the current WinFsp-FUSE behavior:
|
|||||||
WinFsp-FUSE does not have the ability to support multiple file systems from within the same process. Why?::
|
WinFsp-FUSE does not have the ability to support multiple file systems from within the same process. Why?::
|
||||||
|
|
||||||
This is supported as of WinFsp 2018.2 B2.
|
This is supported as of WinFsp 2018.2 B2.
|
||||||
|
|
||||||
|
|
||||||
|
I have problems getting permissions to work properly in a WinFsp-FUSE file system. Can you help?::
|
||||||
|
|
||||||
|
The WinFsp-FUSE layer includes a built-in command line option that can help: `-o uid=-1`. This instructs the WinFsp-FUSE layer to present all file system files as if they are owned by the user that launched the file system.
|
||||||
+
|
+
|
||||||
The core WinFsp layer always supported multiple file systems in the same process either simultaneously or one after another. However this was not the case with WinFsp-FUSE (i.e. the FUSE layer of WinFsp) prior to version 2018.2 B2. This limitation has been rectified as of WinFsp 2018.2 B2.
|
Alternatives include `-o uid=-1,gid=-1`, which presents files as owned by the user *and* group that launched the file system and `-o uid=-1,gid=11`, which presents files as owned by the user that launched the file system and the group "Authenticated Users". (The `fsptool` utility in the `bin` subdirectory of the WinFsp installation directory can be used to convert Windows accounts/SID's to UID's and vice versa.)
|
||||||
|
@ -5,6 +5,7 @@ This document contains a list of known file systems and file system libraries th
|
|||||||
== File Systems
|
== File Systems
|
||||||
|
|
||||||
- https://github.com/vgough/encfs[EncFS] - an Encrypted Filesystem for FUSE
|
- https://github.com/vgough/encfs[EncFS] - an Encrypted Filesystem for FUSE
|
||||||
|
- https://github.com/lowleveldesign/fsmemfs[fsmemfs] - Memory File System written in F#
|
||||||
- https://github.com/ihaveamac/fuse-3ds[fuse-3ds] - FUSE Filesystem Python scripts for Nintendo 3DS files
|
- https://github.com/ihaveamac/fuse-3ds[fuse-3ds] - FUSE Filesystem Python scripts for Nintendo 3DS files
|
||||||
- https://github.com/FrKaram/KS2.Drive[KS2.Drive] - Mount a webDAV/AOS server as a local drive
|
- https://github.com/FrKaram/KS2.Drive[KS2.Drive] - Mount a webDAV/AOS server as a local drive
|
||||||
- https://github.com/billziss-gh/nfs-win[nfs-win] - NFS for Windows
|
- https://github.com/billziss-gh/nfs-win[nfs-win] - NFS for Windows
|
||||||
@ -13,6 +14,7 @@ This document contains a list of known file systems and file system libraries th
|
|||||||
- https://github.com/billziss-gh/redditfs[redditfs] - ls -l /r/programming
|
- https://github.com/billziss-gh/redditfs[redditfs] - ls -l /r/programming
|
||||||
- https://github.com/netheril96/securefs[securefs] - Filesystem in userspace (FUSE) with transparent authenticated encryption
|
- https://github.com/netheril96/securefs[securefs] - Filesystem in userspace (FUSE) with transparent authenticated encryption
|
||||||
- https://github.com/billziss-gh/sshfs-win[sshfs-win] - SSHFS for Windows
|
- https://github.com/billziss-gh/sshfs-win[sshfs-win] - SSHFS for Windows
|
||||||
|
- https://github.com/UtrechtUniversity/YodaDrive[YodaDrive] - Mount a Yoda drive as a local drive
|
||||||
|
|
||||||
== File System Libraries
|
== File System Libraries
|
||||||
|
|
||||||
@ -20,3 +22,4 @@ This document contains a list of known file systems and file system libraries th
|
|||||||
- https://github.com/DuroSoft/fuse-bindings[Nodejs: fuse-bindings] - Fully maintained FUSE bindings for Node that aims to cover the entire FUSE api
|
- https://github.com/DuroSoft/fuse-bindings[Nodejs: fuse-bindings] - Fully maintained FUSE bindings for Node that aims to cover the entire FUSE api
|
||||||
- https://github.com/SerCeMan/jnr-fuse[Java: jnr-fuse] - FUSE implementation in Java using Java Native Runtime (JNR)
|
- https://github.com/SerCeMan/jnr-fuse[Java: jnr-fuse] - FUSE implementation in Java using Java Native Runtime (JNR)
|
||||||
- https://github.com/billziss-gh/fusepy[Python: fusepy] - Simple ctypes bindings for FUSE
|
- https://github.com/billziss-gh/fusepy[Python: fusepy] - Simple ctypes bindings for FUSE
|
||||||
|
- https://github.com/Scille/winfspy[Python: winfspy] - WinFSP binding for Python
|
||||||
|
@ -54,6 +54,8 @@ extern const __declspec(selectany) GUID FspFsvrtDeviceClassGuid =
|
|||||||
#define FSP_FSCTL_DECLSPEC_ALIGN __declspec(align(FSP_FSCTL_DEFAULT_ALIGNMENT))
|
#define FSP_FSCTL_DECLSPEC_ALIGN __declspec(align(FSP_FSCTL_DEFAULT_ALIGNMENT))
|
||||||
|
|
||||||
/* fsctl device codes */
|
/* fsctl device codes */
|
||||||
|
#define FSP_FSCTL_MOUNTDEV \
|
||||||
|
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'M', METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
#define FSP_FSCTL_VOLUME_NAME \
|
#define FSP_FSCTL_VOLUME_NAME \
|
||||||
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'N', METHOD_BUFFERED, FILE_ANY_ACCESS)
|
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'N', METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||||
#define FSP_FSCTL_VOLUME_LIST \
|
#define FSP_FSCTL_VOLUME_LIST \
|
||||||
@ -598,6 +600,8 @@ FSP_API NTSTATUS FspFsctlCreateVolume(PWSTR DevicePath,
|
|||||||
const FSP_FSCTL_VOLUME_PARAMS *VolumeParams,
|
const FSP_FSCTL_VOLUME_PARAMS *VolumeParams,
|
||||||
PWCHAR VolumeNameBuf, SIZE_T VolumeNameSize,
|
PWCHAR VolumeNameBuf, SIZE_T VolumeNameSize,
|
||||||
PHANDLE PVolumeHandle);
|
PHANDLE PVolumeHandle);
|
||||||
|
FSP_API NTSTATUS FspFsctlMakeMountdev(HANDLE VolumeHandle,
|
||||||
|
BOOLEAN Persistent, GUID *UniqueId);
|
||||||
FSP_API NTSTATUS FspFsctlTransact(HANDLE VolumeHandle,
|
FSP_API NTSTATUS FspFsctlTransact(HANDLE VolumeHandle,
|
||||||
PVOID ResponseBuf, SIZE_T ResponseBufSize,
|
PVOID ResponseBuf, SIZE_T ResponseBufSize,
|
||||||
PVOID RequestBuf, SIZE_T *PRequestBufSize,
|
PVOID RequestBuf, SIZE_T *PRequestBufSize,
|
||||||
@ -606,6 +610,20 @@ FSP_API NTSTATUS FspFsctlStop(HANDLE VolumeHandle);
|
|||||||
FSP_API NTSTATUS FspFsctlGetVolumeList(PWSTR DevicePath,
|
FSP_API NTSTATUS FspFsctlGetVolumeList(PWSTR DevicePath,
|
||||||
PWCHAR VolumeListBuf, PSIZE_T PVolumeListSize);
|
PWCHAR VolumeListBuf, PSIZE_T PVolumeListSize);
|
||||||
FSP_API NTSTATUS FspFsctlPreflight(PWSTR DevicePath);
|
FSP_API NTSTATUS FspFsctlPreflight(PWSTR DevicePath);
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/* in */
|
||||||
|
HANDLE VolumeHandle; /* volume handle returned by FspFsctlCreateVolume */
|
||||||
|
PWSTR VolumeName; /* volume name returned by FspFsctlCreateVolume */
|
||||||
|
PSECURITY_DESCRIPTOR Security; /* optional: security descriptor for directories */
|
||||||
|
UINT64 Reserved; /* reserved for future use */
|
||||||
|
/* in/out */
|
||||||
|
PWSTR MountPoint; /* FspMountSet sets drive in buffer when passed "*:" */
|
||||||
|
HANDLE MountHandle; /* FspMountSet sets, FspMountRemove uses */
|
||||||
|
} FSP_MOUNT_DESC;
|
||||||
|
FSP_API NTSTATUS FspMountSet(FSP_MOUNT_DESC *Desc);
|
||||||
|
FSP_API NTSTATUS FspMountRemove(FSP_MOUNT_DESC *Desc);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
329
src/dll/fs.c
329
src/dll/fs.c
@ -32,35 +32,11 @@ static FSP_FILE_SYSTEM_INTERFACE FspFileSystemNullInterface;
|
|||||||
|
|
||||||
static INIT_ONCE FspFileSystemInitOnce = INIT_ONCE_STATIC_INIT;
|
static INIT_ONCE FspFileSystemInitOnce = INIT_ONCE_STATIC_INIT;
|
||||||
static DWORD FspFileSystemTlsKey = TLS_OUT_OF_INDEXES;
|
static DWORD FspFileSystemTlsKey = TLS_OUT_OF_INDEXES;
|
||||||
static NTSTATUS (NTAPI *FspNtOpenSymbolicLinkObject)(
|
|
||||||
PHANDLE LinkHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes);
|
|
||||||
static NTSTATUS (NTAPI *FspNtMakeTemporaryObject)(
|
|
||||||
HANDLE Handle);
|
|
||||||
static NTSTATUS (NTAPI *FspNtClose)(
|
|
||||||
HANDLE Handle);
|
|
||||||
|
|
||||||
static BOOL WINAPI FspFileSystemInitialize(
|
static BOOL WINAPI FspFileSystemInitialize(
|
||||||
PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
|
PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
|
||||||
{
|
{
|
||||||
HANDLE Handle;
|
|
||||||
|
|
||||||
FspFileSystemTlsKey = TlsAlloc();
|
FspFileSystemTlsKey = TlsAlloc();
|
||||||
|
|
||||||
Handle = GetModuleHandleW(L"ntdll.dll");
|
|
||||||
if (0 != Handle)
|
|
||||||
{
|
|
||||||
FspNtOpenSymbolicLinkObject = (PVOID)GetProcAddress(Handle, "NtOpenSymbolicLinkObject");
|
|
||||||
FspNtMakeTemporaryObject = (PVOID)GetProcAddress(Handle, "NtMakeTemporaryObject");
|
|
||||||
FspNtClose = (PVOID)GetProcAddress(Handle, "NtClose");
|
|
||||||
|
|
||||||
if (0 == FspNtOpenSymbolicLinkObject || 0 == FspNtMakeTemporaryObject || 0 == FspNtClose)
|
|
||||||
{
|
|
||||||
FspNtOpenSymbolicLinkObject = 0;
|
|
||||||
FspNtMakeTemporaryObject = 0;
|
|
||||||
FspNtClose = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +69,9 @@ FSP_API NTSTATUS FspFileSystemPreflight(PWSTR DevicePath,
|
|||||||
Result = STATUS_SUCCESS;
|
Result = STATUS_SUCCESS;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (FspPathIsDrive(MountPoint))
|
if (FspPathIsMountmgrMountPoint(MountPoint))
|
||||||
|
Result = STATUS_SUCCESS; /* cannot check with the mount manager, assume success */
|
||||||
|
else if (FspPathIsDrive(MountPoint))
|
||||||
Result = QueryDosDeviceW(MountPoint, TargetPath, MAX_PATH) ?
|
Result = QueryDosDeviceW(MountPoint, TargetPath, MAX_PATH) ?
|
||||||
STATUS_OBJECT_NAME_COLLISION : STATUS_SUCCESS;
|
STATUS_OBJECT_NAME_COLLISION : STATUS_SUCCESS;
|
||||||
else
|
else
|
||||||
@ -196,205 +174,6 @@ FSP_API VOID FspFileSystemDelete(FSP_FILE_SYSTEM *FileSystem)
|
|||||||
MemFree(FileSystem);
|
MemFree(FileSystem);
|
||||||
}
|
}
|
||||||
|
|
||||||
static NTSTATUS FspFileSystemLauncherDefineDosDevice(
|
|
||||||
WCHAR Sign, PWSTR MountPoint, PWSTR VolumeName)
|
|
||||||
{
|
|
||||||
if (2 != lstrlenW(MountPoint) ||
|
|
||||||
FSP_FSCTL_VOLUME_NAME_SIZEMAX / sizeof(WCHAR) <= lstrlenW(VolumeName))
|
|
||||||
return STATUS_INVALID_PARAMETER;
|
|
||||||
|
|
||||||
WCHAR Argv0[4];
|
|
||||||
PWSTR Argv[2];
|
|
||||||
NTSTATUS Result;
|
|
||||||
ULONG ErrorCode;
|
|
||||||
|
|
||||||
Argv0[0] = Sign;
|
|
||||||
Argv0[1] = MountPoint[0];
|
|
||||||
Argv0[2] = MountPoint[1];
|
|
||||||
Argv0[3] = L'\0';
|
|
||||||
|
|
||||||
Argv[0] = Argv0;
|
|
||||||
Argv[1] = VolumeName;
|
|
||||||
|
|
||||||
Result = FspLaunchCallLauncherPipe(FspLaunchCmdDefineDosDevice, 2, Argv, 0, 0, 0, &ErrorCode);
|
|
||||||
return !NT_SUCCESS(Result) ? Result : FspNtStatusFromWin32(ErrorCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
static NTSTATUS FspFileSystemSetMountPoint_Drive(PWSTR MountPoint, PWSTR VolumeName,
|
|
||||||
PHANDLE PMountHandle)
|
|
||||||
{
|
|
||||||
NTSTATUS Result;
|
|
||||||
BOOLEAN IsLocalSystem, IsServiceContext;
|
|
||||||
|
|
||||||
*PMountHandle = 0;
|
|
||||||
|
|
||||||
Result = FspServiceContextCheck(0, &IsLocalSystem);
|
|
||||||
IsServiceContext = NT_SUCCESS(Result) && !IsLocalSystem;
|
|
||||||
if (IsServiceContext)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* If the current process is in the service context but not LocalSystem,
|
|
||||||
* ask the launcher to DefineDosDevice for us. This is because the launcher
|
|
||||||
* runs in the LocalSystem context and can create global drives.
|
|
||||||
*
|
|
||||||
* In this case the launcher will also add DELETE access to the drive symlink
|
|
||||||
* for us, so that we can make it temporary below.
|
|
||||||
*/
|
|
||||||
Result = FspFileSystemLauncherDefineDosDevice(L'+', MountPoint, VolumeName);
|
|
||||||
if (!NT_SUCCESS(Result))
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!DefineDosDeviceW(DDD_RAW_TARGET_PATH, MountPoint, VolumeName))
|
|
||||||
return FspNtStatusFromWin32(GetLastError());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0 != FspNtOpenSymbolicLinkObject)
|
|
||||||
{
|
|
||||||
WCHAR SymlinkBuf[6];
|
|
||||||
UNICODE_STRING Symlink;
|
|
||||||
OBJECT_ATTRIBUTES Obja;
|
|
||||||
|
|
||||||
memcpy(SymlinkBuf, L"\\??\\X:", sizeof SymlinkBuf);
|
|
||||||
SymlinkBuf[4] = MountPoint[0];
|
|
||||||
Symlink.Length = Symlink.MaximumLength = sizeof SymlinkBuf;
|
|
||||||
Symlink.Buffer = SymlinkBuf;
|
|
||||||
|
|
||||||
memset(&Obja, 0, sizeof Obja);
|
|
||||||
Obja.Length = sizeof Obja;
|
|
||||||
Obja.ObjectName = &Symlink;
|
|
||||||
Obja.Attributes = OBJ_CASE_INSENSITIVE;
|
|
||||||
|
|
||||||
Result = FspNtOpenSymbolicLinkObject(PMountHandle, DELETE, &Obja);
|
|
||||||
if (NT_SUCCESS(Result))
|
|
||||||
{
|
|
||||||
Result = FspNtMakeTemporaryObject(*PMountHandle);
|
|
||||||
if (!NT_SUCCESS(Result))
|
|
||||||
{
|
|
||||||
FspNtClose(*PMountHandle);
|
|
||||||
*PMountHandle = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* HACK:
|
|
||||||
*
|
|
||||||
* Handles do not use the low 2 bits (unless they are console handles).
|
|
||||||
* Abuse this fact to remember that we are running in the service context.
|
|
||||||
*/
|
|
||||||
*PMountHandle = (HANDLE)(UINT_PTR)((DWORD)(UINT_PTR)*PMountHandle | IsServiceContext);
|
|
||||||
|
|
||||||
return STATUS_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static NTSTATUS FspFileSystemSetMountPoint_Directory(PWSTR MountPoint, PWSTR VolumeName,
|
|
||||||
PSECURITY_DESCRIPTOR SecurityDescriptor, PHANDLE PMountHandle)
|
|
||||||
{
|
|
||||||
NTSTATUS Result;
|
|
||||||
SECURITY_ATTRIBUTES SecurityAttributes;
|
|
||||||
HANDLE MountHandle = INVALID_HANDLE_VALUE;
|
|
||||||
DWORD Backslashes, Bytes;
|
|
||||||
USHORT VolumeNameLength, BackslashLength, ReparseDataLength;
|
|
||||||
PREPARSE_DATA_BUFFER ReparseData = 0;
|
|
||||||
PWSTR P, PathBuffer;
|
|
||||||
|
|
||||||
*PMountHandle = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Windows does not allow mount points (junctions) to point to network file systems.
|
|
||||||
*
|
|
||||||
* Count how many backslashes our VolumeName has. If it is 3 or more this is a network
|
|
||||||
* file system. Preemptively return STATUS_NETWORK_ACCESS_DENIED.
|
|
||||||
*/
|
|
||||||
for (P = VolumeName, Backslashes = 0; *P; P++)
|
|
||||||
if (L'\\' == *P)
|
|
||||||
if (3 == ++Backslashes)
|
|
||||||
{
|
|
||||||
Result = STATUS_NETWORK_ACCESS_DENIED;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&SecurityAttributes, 0, sizeof SecurityAttributes);
|
|
||||||
SecurityAttributes.nLength = sizeof SecurityAttributes;
|
|
||||||
SecurityAttributes.lpSecurityDescriptor = SecurityDescriptor;
|
|
||||||
|
|
||||||
MountHandle = CreateFileW(MountPoint,
|
|
||||||
FILE_WRITE_ATTRIBUTES,
|
|
||||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
||||||
&SecurityAttributes,
|
|
||||||
CREATE_NEW,
|
|
||||||
FILE_ATTRIBUTE_DIRECTORY |
|
|
||||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE,
|
|
||||||
0);
|
|
||||||
if (INVALID_HANDLE_VALUE == MountHandle)
|
|
||||||
{
|
|
||||||
Result = FspNtStatusFromWin32(GetLastError());
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
VolumeNameLength = (USHORT)lstrlenW(VolumeName);
|
|
||||||
BackslashLength = 0 == VolumeNameLength || L'\\' != VolumeName[VolumeNameLength - 1];
|
|
||||||
VolumeNameLength *= sizeof(WCHAR);
|
|
||||||
BackslashLength *= sizeof(WCHAR);
|
|
||||||
|
|
||||||
ReparseDataLength = (USHORT)(
|
|
||||||
FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) -
|
|
||||||
FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer)) +
|
|
||||||
2 * (VolumeNameLength + BackslashLength + sizeof(WCHAR));
|
|
||||||
ReparseData = MemAlloc(REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseDataLength);
|
|
||||||
if (0 == ReparseData)
|
|
||||||
{
|
|
||||||
Result = STATUS_INSUFFICIENT_RESOURCES;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReparseData->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
|
|
||||||
ReparseData->ReparseDataLength = ReparseDataLength;
|
|
||||||
ReparseData->Reserved = 0;
|
|
||||||
ReparseData->MountPointReparseBuffer.SubstituteNameOffset = 0;
|
|
||||||
ReparseData->MountPointReparseBuffer.SubstituteNameLength =
|
|
||||||
VolumeNameLength + BackslashLength;
|
|
||||||
ReparseData->MountPointReparseBuffer.PrintNameOffset =
|
|
||||||
ReparseData->MountPointReparseBuffer.SubstituteNameLength + sizeof(WCHAR);
|
|
||||||
ReparseData->MountPointReparseBuffer.PrintNameLength =
|
|
||||||
VolumeNameLength + BackslashLength;
|
|
||||||
|
|
||||||
PathBuffer = ReparseData->MountPointReparseBuffer.PathBuffer;
|
|
||||||
memcpy(PathBuffer, VolumeName, VolumeNameLength);
|
|
||||||
if (BackslashLength)
|
|
||||||
PathBuffer[VolumeNameLength / sizeof(WCHAR)] = L'\\';
|
|
||||||
PathBuffer[(VolumeNameLength + BackslashLength) / sizeof(WCHAR)] = L'\0';
|
|
||||||
|
|
||||||
PathBuffer = ReparseData->MountPointReparseBuffer.PathBuffer +
|
|
||||||
(ReparseData->MountPointReparseBuffer.PrintNameOffset) / sizeof(WCHAR);
|
|
||||||
memcpy(PathBuffer, VolumeName, VolumeNameLength);
|
|
||||||
if (BackslashLength)
|
|
||||||
PathBuffer[VolumeNameLength / sizeof(WCHAR)] = L'\\';
|
|
||||||
PathBuffer[(VolumeNameLength + BackslashLength) / sizeof(WCHAR)] = L'\0';
|
|
||||||
|
|
||||||
if (!DeviceIoControl(MountHandle, FSCTL_SET_REPARSE_POINT,
|
|
||||||
ReparseData, REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseData->ReparseDataLength,
|
|
||||||
0, 0,
|
|
||||||
&Bytes, 0))
|
|
||||||
{
|
|
||||||
Result = FspNtStatusFromWin32(GetLastError());
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
*PMountHandle = MountHandle;
|
|
||||||
|
|
||||||
Result = STATUS_SUCCESS;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
if (!NT_SUCCESS(Result) && INVALID_HANDLE_VALUE != MountHandle)
|
|
||||||
CloseHandle(MountHandle);
|
|
||||||
|
|
||||||
MemFree(ReparseData);
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
FSP_API NTSTATUS FspFileSystemSetMountPoint(FSP_FILE_SYSTEM *FileSystem, PWSTR MountPoint)
|
FSP_API NTSTATUS FspFileSystemSetMountPoint(FSP_FILE_SYSTEM *FileSystem, PWSTR MountPoint)
|
||||||
{
|
{
|
||||||
return FspFileSystemSetMountPointEx(FileSystem, MountPoint, 0);
|
return FspFileSystemSetMountPointEx(FileSystem, MountPoint, 0);
|
||||||
@ -406,105 +185,55 @@ FSP_API NTSTATUS FspFileSystemSetMountPointEx(FSP_FILE_SYSTEM *FileSystem, PWSTR
|
|||||||
if (0 != FileSystem->MountPoint)
|
if (0 != FileSystem->MountPoint)
|
||||||
return STATUS_INVALID_PARAMETER;
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
FSP_MOUNT_DESC Desc;
|
||||||
|
int Size;
|
||||||
NTSTATUS Result;
|
NTSTATUS Result;
|
||||||
HANDLE MountHandle = 0;
|
|
||||||
|
memset(&Desc, 0, sizeof Desc);
|
||||||
|
Desc.VolumeHandle = FileSystem->VolumeHandle;
|
||||||
|
Desc.VolumeName = FileSystem->VolumeName;
|
||||||
|
Desc.Security = SecurityDescriptor;
|
||||||
|
|
||||||
if (0 == MountPoint)
|
if (0 == MountPoint)
|
||||||
{
|
MountPoint = L"*:";
|
||||||
DWORD Drives;
|
|
||||||
WCHAR Drive;
|
|
||||||
|
|
||||||
MountPoint = MemAlloc(3 * sizeof(WCHAR));
|
Size = (lstrlenW(MountPoint) + 1) * sizeof(WCHAR);
|
||||||
if (0 == MountPoint)
|
Desc.MountPoint = MemAlloc(Size);
|
||||||
return STATUS_INSUFFICIENT_RESOURCES;
|
if (0 == Desc.MountPoint)
|
||||||
MountPoint[1] = L':';
|
|
||||||
MountPoint[2] = L'\0';
|
|
||||||
|
|
||||||
Drives = GetLogicalDrives();
|
|
||||||
if (0 != Drives)
|
|
||||||
{
|
{
|
||||||
for (Drive = 'Z'; 'D' <= Drive; Drive--)
|
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||||
if (0 == (Drives & (1 << (Drive - 'A'))))
|
|
||||||
{
|
|
||||||
MountPoint[0] = Drive;
|
|
||||||
Result = FspFileSystemSetMountPoint_Drive(MountPoint, FileSystem->VolumeName,
|
|
||||||
&MountHandle);
|
|
||||||
if (NT_SUCCESS(Result))
|
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
Result = STATUS_NO_SUCH_DEVICE;
|
memcpy(Desc.MountPoint, MountPoint, Size);
|
||||||
}
|
|
||||||
else
|
|
||||||
Result = FspNtStatusFromWin32(GetLastError());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PWSTR P;
|
|
||||||
ULONG L;
|
|
||||||
|
|
||||||
L = (ULONG)((lstrlenW(MountPoint) + 1) * sizeof(WCHAR));
|
Result = FspMountSet(&Desc);
|
||||||
|
|
||||||
P = MemAlloc(L);
|
|
||||||
if (0 == P)
|
|
||||||
return STATUS_INSUFFICIENT_RESOURCES;
|
|
||||||
memcpy(P, MountPoint, L);
|
|
||||||
MountPoint = P;
|
|
||||||
|
|
||||||
if (FspPathIsDrive(MountPoint))
|
|
||||||
Result = FspFileSystemSetMountPoint_Drive(MountPoint, FileSystem->VolumeName,
|
|
||||||
&MountHandle);
|
|
||||||
else
|
|
||||||
Result = FspFileSystemSetMountPoint_Directory(MountPoint, FileSystem->VolumeName,
|
|
||||||
SecurityDescriptor, &MountHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
if (NT_SUCCESS(Result))
|
if (NT_SUCCESS(Result))
|
||||||
{
|
{
|
||||||
FileSystem->MountPoint = MountPoint;
|
FileSystem->MountPoint = Desc.MountPoint;
|
||||||
FileSystem->MountHandle = MountHandle;
|
FileSystem->MountHandle = Desc.MountHandle;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
MemFree(MountPoint);
|
MemFree(Desc.MountPoint);
|
||||||
|
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static VOID FspFileSystemRemoveMountPoint_Drive(PWSTR MountPoint, PWSTR VolumeName, HANDLE MountHandle)
|
|
||||||
{
|
|
||||||
BOOLEAN IsServiceContext = 0 != ((DWORD)(UINT_PTR)MountHandle & 1);
|
|
||||||
MountHandle = (HANDLE)(UINT_PTR)((DWORD)(UINT_PTR)MountHandle & ~1);
|
|
||||||
if (IsServiceContext)
|
|
||||||
/*
|
|
||||||
* If the current process is in the service context but not LocalSystem,
|
|
||||||
* ask the launcher to DefineDosDevice for us. This is because the launcher
|
|
||||||
* runs in the LocalSystem context and can remove global drives.
|
|
||||||
*/
|
|
||||||
FspFileSystemLauncherDefineDosDevice(L'-', MountPoint, VolumeName);
|
|
||||||
else
|
|
||||||
DefineDosDeviceW(DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE,
|
|
||||||
MountPoint, VolumeName);
|
|
||||||
|
|
||||||
if (0 != MountHandle)
|
|
||||||
FspNtClose(MountHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
static VOID FspFileSystemRemoveMountPoint_Directory(HANDLE MountHandle)
|
|
||||||
{
|
|
||||||
/* directory is marked DELETE_ON_CLOSE */
|
|
||||||
CloseHandle(MountHandle);
|
|
||||||
}
|
|
||||||
|
|
||||||
FSP_API VOID FspFileSystemRemoveMountPoint(FSP_FILE_SYSTEM *FileSystem)
|
FSP_API VOID FspFileSystemRemoveMountPoint(FSP_FILE_SYSTEM *FileSystem)
|
||||||
{
|
{
|
||||||
if (0 == FileSystem->MountPoint)
|
if (0 == FileSystem->MountPoint)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (FspPathIsDrive(FileSystem->MountPoint))
|
FSP_MOUNT_DESC Desc;
|
||||||
FspFileSystemRemoveMountPoint_Drive(FileSystem->MountPoint, FileSystem->VolumeName,
|
|
||||||
FileSystem->MountHandle);
|
memset(&Desc, 0, sizeof Desc);
|
||||||
else
|
Desc.VolumeHandle = FileSystem->VolumeHandle;
|
||||||
FspFileSystemRemoveMountPoint_Directory(FileSystem->MountHandle);
|
Desc.VolumeName = FileSystem->VolumeName;
|
||||||
|
Desc.MountPoint = FileSystem->MountPoint;
|
||||||
|
Desc.MountHandle = FileSystem->MountHandle;
|
||||||
|
|
||||||
|
FspMountRemove(&Desc);
|
||||||
|
|
||||||
MemFree(FileSystem->MountPoint);
|
MemFree(FileSystem->MountPoint);
|
||||||
FileSystem->MountPoint = 0;
|
FileSystem->MountPoint = 0;
|
||||||
|
@ -107,6 +107,20 @@ exit:
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FSP_API NTSTATUS FspFsctlMakeMountdev(HANDLE VolumeHandle,
|
||||||
|
BOOLEAN Persistent, GUID *UniqueId)
|
||||||
|
{
|
||||||
|
DWORD Bytes;
|
||||||
|
|
||||||
|
if (!DeviceIoControl(VolumeHandle,
|
||||||
|
FSP_FSCTL_MOUNTDEV,
|
||||||
|
&Persistent, sizeof Persistent, UniqueId, sizeof *UniqueId,
|
||||||
|
&Bytes, 0))
|
||||||
|
return FspNtStatusFromWin32(GetLastError());
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
FSP_API NTSTATUS FspFsctlTransact(HANDLE VolumeHandle,
|
FSP_API NTSTATUS FspFsctlTransact(HANDLE VolumeHandle,
|
||||||
PVOID ResponseBuf, SIZE_T ResponseBufSize,
|
PVOID ResponseBuf, SIZE_T ResponseBufSize,
|
||||||
PVOID RequestBuf, SIZE_T *PRequestBufSize,
|
PVOID RequestBuf, SIZE_T *PRequestBufSize,
|
||||||
|
@ -102,6 +102,8 @@ static struct fuse_opt fsp_fuse_core_opts[] =
|
|||||||
FUSE_OPT_KEY("--VolumePrefix=", 'U'),
|
FUSE_OPT_KEY("--VolumePrefix=", 'U'),
|
||||||
FUSE_OPT_KEY("FileSystemName=", 'F'),
|
FUSE_OPT_KEY("FileSystemName=", 'F'),
|
||||||
FUSE_OPT_KEY("--FileSystemName=", 'F'),
|
FUSE_OPT_KEY("--FileSystemName=", 'F'),
|
||||||
|
FUSE_OPT_KEY("ExactFileSystemName=", 'E'),
|
||||||
|
FUSE_OPT_KEY("--ExactFileSystemName=", 'E'),
|
||||||
|
|
||||||
FSP_FUSE_CORE_OPT("UserName=", set_uid, 1),
|
FSP_FUSE_CORE_OPT("UserName=", set_uid, 1),
|
||||||
FUSE_OPT_KEY("UserName=", 'u'),
|
FUSE_OPT_KEY("UserName=", 'u'),
|
||||||
@ -367,6 +369,18 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
|
|||||||
[sizeof opt_data->VolumeParams.FileSystemName / sizeof(WCHAR) - 1] = L'\0';
|
[sizeof opt_data->VolumeParams.FileSystemName / sizeof(WCHAR) - 1] = L'\0';
|
||||||
memcpy(opt_data->VolumeParams.FileSystemName, L"FUSE-", 5 * sizeof(WCHAR));
|
memcpy(opt_data->VolumeParams.FileSystemName, L"FUSE-", 5 * sizeof(WCHAR));
|
||||||
return 0;
|
return 0;
|
||||||
|
case 'E':
|
||||||
|
if ('E' == arg[0])
|
||||||
|
arg += sizeof "ExactFileSystemName=" - 1;
|
||||||
|
else if ('E' == arg[2])
|
||||||
|
arg += sizeof "--ExactFileSystemName=" - 1;
|
||||||
|
if (0 == MultiByteToWideChar(CP_UTF8, 0, 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 'u':
|
case 'u':
|
||||||
if ('U' == arg[0])
|
if ('U' == arg[0])
|
||||||
arg += sizeof "UserName=" - 1;
|
arg += sizeof "UserName=" - 1;
|
||||||
|
@ -1026,6 +1026,20 @@ static NTSTATUS fsp_fuse_intf_Open(FSP_FILE_SYSTEM *FileSystem,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* Some Windows applications (notably Go programs) specify FILE_APPEND_DATA without
|
||||||
|
* FILE_WRITE_DATA when opening files for appending. This caused the WinFsp-FUSE layer
|
||||||
|
* to erroneously pass O_RDONLY to the FUSE file system in such cases. We add a test
|
||||||
|
* for FILE_APPEND_DATA to ensure that either O_WRONLY or O_RDWR is specified and that
|
||||||
|
* the O_APPEND flag is set.
|
||||||
|
*/
|
||||||
|
if (GrantedAccess & FILE_APPEND_DATA)
|
||||||
|
{
|
||||||
|
if (fi.flags == 0)
|
||||||
|
fi.flags = 1; /* need O_WRONLY as a bare minimum in order to append */
|
||||||
|
fi.flags |= 8/*O_APPEND*/;
|
||||||
|
}
|
||||||
|
|
||||||
if (0 != f->ops.open)
|
if (0 != f->ops.open)
|
||||||
{
|
{
|
||||||
err = f->ops.open(contexthdr->PosixPath, &fi);
|
err = f->ops.open(contexthdr->PosixPath, &fi);
|
||||||
|
@ -91,6 +91,21 @@ static inline BOOLEAN FspPathIsDrive(PWSTR FileName)
|
|||||||
) &&
|
) &&
|
||||||
L':' == FileName[1] && L'\0' == FileName[2];
|
L':' == FileName[1] && L'\0' == FileName[2];
|
||||||
}
|
}
|
||||||
|
static inline BOOLEAN FspPathIsMountmgrMountPoint(PWSTR FileName)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
(
|
||||||
|
L'\\' == FileName[0] &&
|
||||||
|
L'\\' == FileName[1] &&
|
||||||
|
(L'?' == FileName[2] || L'.' == FileName[2]) &&
|
||||||
|
L'\\' == FileName[3]
|
||||||
|
) &&
|
||||||
|
(
|
||||||
|
(L'A' <= FileName[4] && FileName[4] <= L'Z') ||
|
||||||
|
(L'a' <= FileName[4] && FileName[4] <= L'z')
|
||||||
|
) &&
|
||||||
|
L':' == FileName[5];
|
||||||
|
}
|
||||||
|
|
||||||
#define FSP_NEXT_EA(Ea, EaEnd) \
|
#define FSP_NEXT_EA(Ea, EaEnd) \
|
||||||
(0 != (Ea)->NextEntryOffset ? (PVOID)((PUINT8)(Ea) + (Ea)->NextEntryOffset) : (EaEnd))
|
(0 != (Ea)->NextEntryOffset ? (PVOID)((PUINT8)(Ea) + (Ea)->NextEntryOffset) : (EaEnd))
|
||||||
|
612
src/dll/mount.c
Normal file
612
src/dll/mount.c
Normal file
@ -0,0 +1,612 @@
|
|||||||
|
/**
|
||||||
|
* @file dll/mount.c
|
||||||
|
*
|
||||||
|
* @copyright 2015-2019 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>
|
||||||
|
|
||||||
|
static INIT_ONCE FspMountInitOnce = INIT_ONCE_STATIC_INIT;
|
||||||
|
static NTSTATUS (NTAPI *FspNtOpenSymbolicLinkObject)(
|
||||||
|
PHANDLE LinkHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes);
|
||||||
|
static NTSTATUS (NTAPI *FspNtMakeTemporaryObject)(
|
||||||
|
HANDLE Handle);
|
||||||
|
static NTSTATUS (NTAPI *FspNtClose)(
|
||||||
|
HANDLE Handle);
|
||||||
|
|
||||||
|
static BOOL WINAPI FspMountInitialize(
|
||||||
|
PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
|
||||||
|
{
|
||||||
|
HANDLE Handle;
|
||||||
|
|
||||||
|
Handle = GetModuleHandleW(L"ntdll.dll");
|
||||||
|
if (0 != Handle)
|
||||||
|
{
|
||||||
|
FspNtOpenSymbolicLinkObject = (PVOID)GetProcAddress(Handle, "NtOpenSymbolicLinkObject");
|
||||||
|
FspNtMakeTemporaryObject = (PVOID)GetProcAddress(Handle, "NtMakeTemporaryObject");
|
||||||
|
FspNtClose = (PVOID)GetProcAddress(Handle, "NtClose");
|
||||||
|
|
||||||
|
if (0 == FspNtOpenSymbolicLinkObject || 0 == FspNtMakeTemporaryObject || 0 == FspNtClose)
|
||||||
|
{
|
||||||
|
FspNtOpenSymbolicLinkObject = 0;
|
||||||
|
FspNtMakeTemporaryObject = 0;
|
||||||
|
FspNtClose = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NTSTATUS FspMountmgrControl(ULONG IoControlCode,
|
||||||
|
PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, PULONG POutputBufferLength)
|
||||||
|
{
|
||||||
|
HANDLE MgrHandle = INVALID_HANDLE_VALUE;
|
||||||
|
DWORD Bytes = 0;
|
||||||
|
NTSTATUS Result;
|
||||||
|
|
||||||
|
if (0 == POutputBufferLength)
|
||||||
|
POutputBufferLength = &Bytes;
|
||||||
|
|
||||||
|
MgrHandle = CreateFileW(L"\\\\.\\MountPointManager",
|
||||||
|
GENERIC_READ | GENERIC_WRITE,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||||
|
0,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
0,
|
||||||
|
0);
|
||||||
|
if (INVALID_HANDLE_VALUE == MgrHandle)
|
||||||
|
{
|
||||||
|
Result = FspNtStatusFromWin32(GetLastError());
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DeviceIoControl(MgrHandle,
|
||||||
|
IoControlCode,
|
||||||
|
InputBuffer, InputBufferLength, OutputBuffer, *POutputBufferLength,
|
||||||
|
&Bytes, 0))
|
||||||
|
{
|
||||||
|
Result = FspNtStatusFromWin32(GetLastError());
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
*POutputBufferLength = Bytes;
|
||||||
|
Result = STATUS_SUCCESS;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (INVALID_HANDLE_VALUE != MgrHandle)
|
||||||
|
CloseHandle(MgrHandle);
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NTSTATUS FspMountSet_Mountmgr(HANDLE VolumeHandle, PWSTR VolumeName, PWSTR MountPoint)
|
||||||
|
{
|
||||||
|
/* only support drives for now! (format: \\.\X:) */
|
||||||
|
if (L'\0' != MountPoint[6])
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
/* mountmgr.h */
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
Disabled = 0,
|
||||||
|
Enabled,
|
||||||
|
} MOUNTMGR_AUTO_MOUNT_STATE;
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
MOUNTMGR_AUTO_MOUNT_STATE CurrentState;
|
||||||
|
} MOUNTMGR_QUERY_AUTO_MOUNT;
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
MOUNTMGR_AUTO_MOUNT_STATE NewState;
|
||||||
|
} MOUNTMGR_SET_AUTO_MOUNT;
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
USHORT DeviceNameLength;
|
||||||
|
WCHAR DeviceName[1];
|
||||||
|
} MOUNTMGR_TARGET_NAME;
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
USHORT SymbolicLinkNameOffset;
|
||||||
|
USHORT SymbolicLinkNameLength;
|
||||||
|
USHORT DeviceNameOffset;
|
||||||
|
USHORT DeviceNameLength;
|
||||||
|
} MOUNTMGR_CREATE_POINT_INPUT;
|
||||||
|
|
||||||
|
GUID UniqueId;
|
||||||
|
MOUNTMGR_QUERY_AUTO_MOUNT QueryAutoMount;
|
||||||
|
MOUNTMGR_SET_AUTO_MOUNT SetAutoMount;
|
||||||
|
MOUNTMGR_TARGET_NAME *TargetName = 0;
|
||||||
|
MOUNTMGR_CREATE_POINT_INPUT *CreatePointInput = 0;
|
||||||
|
ULONG VolumeNameSize, QueryAutoMountSize, TargetNameSize, CreatePointInputSize;
|
||||||
|
HKEY RegKey;
|
||||||
|
LONG RegResult;
|
||||||
|
WCHAR RegValueName[MAX_PATH];
|
||||||
|
UINT8 RegValueData[sizeof UniqueId];
|
||||||
|
DWORD RegValueNameSize, RegValueDataSize;
|
||||||
|
DWORD RegType;
|
||||||
|
NTSTATUS Result;
|
||||||
|
|
||||||
|
/* transform our volume into one that can be used by the MountManager */
|
||||||
|
Result = FspFsctlMakeMountdev(VolumeHandle, FALSE, &UniqueId);
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
VolumeNameSize = lstrlenW(VolumeName) * sizeof(WCHAR);
|
||||||
|
QueryAutoMountSize = sizeof QueryAutoMount;
|
||||||
|
TargetNameSize = FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) + VolumeNameSize;
|
||||||
|
CreatePointInputSize = sizeof *CreatePointInput +
|
||||||
|
sizeof L"\\DosDevices\\X:" - sizeof(WCHAR) + VolumeNameSize;
|
||||||
|
|
||||||
|
TargetName = MemAlloc(TargetNameSize);
|
||||||
|
if (0 == TargetName)
|
||||||
|
{
|
||||||
|
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
CreatePointInput = MemAlloc(CreatePointInputSize);
|
||||||
|
if (0 == CreatePointInput)
|
||||||
|
{
|
||||||
|
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* query the current AutoMount value and save it */
|
||||||
|
Result = FspMountmgrControl(
|
||||||
|
CTL_CODE('m', 15, METHOD_BUFFERED, FILE_ANY_ACCESS),
|
||||||
|
/* IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT */
|
||||||
|
0, 0, &QueryAutoMount, &QueryAutoMountSize);
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
/* disable AutoMount */
|
||||||
|
SetAutoMount.NewState = 0;
|
||||||
|
Result = FspMountmgrControl(
|
||||||
|
CTL_CODE('m', 16, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
|
/* IOCTL_MOUNTMGR_SET_AUTO_MOUNT */
|
||||||
|
&SetAutoMount, sizeof SetAutoMount, 0, 0);
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
/* announce volume arrival */
|
||||||
|
memset(TargetName, 0, sizeof *TargetName);
|
||||||
|
TargetName->DeviceNameLength = (USHORT)VolumeNameSize;
|
||||||
|
memcpy(TargetName->DeviceName,
|
||||||
|
VolumeName, TargetName->DeviceNameLength);
|
||||||
|
Result = FspMountmgrControl(
|
||||||
|
CTL_CODE('m', 11, METHOD_BUFFERED, FILE_READ_ACCESS),
|
||||||
|
/* IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION */
|
||||||
|
TargetName, TargetNameSize, 0, 0);
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
/* reset the AutoMount value to the saved one */
|
||||||
|
SetAutoMount.NewState = QueryAutoMount.CurrentState;
|
||||||
|
FspMountmgrControl(
|
||||||
|
CTL_CODE('m', 16, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
|
/* IOCTL_MOUNTMGR_SET_AUTO_MOUNT */
|
||||||
|
&SetAutoMount, sizeof SetAutoMount, 0, 0);
|
||||||
|
#if 0
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
goto exit;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* create mount point */
|
||||||
|
memset(CreatePointInput, 0, sizeof *CreatePointInput);
|
||||||
|
CreatePointInput->SymbolicLinkNameOffset = sizeof *CreatePointInput;
|
||||||
|
CreatePointInput->SymbolicLinkNameLength = sizeof L"\\DosDevices\\X:" - sizeof(WCHAR);
|
||||||
|
CreatePointInput->DeviceNameOffset =
|
||||||
|
CreatePointInput->SymbolicLinkNameOffset + CreatePointInput->SymbolicLinkNameLength;
|
||||||
|
CreatePointInput->DeviceNameLength = (USHORT)VolumeNameSize;
|
||||||
|
memcpy((PUINT8)CreatePointInput + CreatePointInput->SymbolicLinkNameOffset,
|
||||||
|
L"\\DosDevices\\X:", CreatePointInput->SymbolicLinkNameLength);
|
||||||
|
((PWCHAR)((PUINT8)CreatePointInput + CreatePointInput->SymbolicLinkNameOffset))[12] =
|
||||||
|
MountPoint[4] & ~0x20;
|
||||||
|
/* convert to uppercase */
|
||||||
|
memcpy((PUINT8)CreatePointInput + CreatePointInput->DeviceNameOffset,
|
||||||
|
VolumeName, CreatePointInput->DeviceNameLength);
|
||||||
|
Result = FspMountmgrControl(
|
||||||
|
CTL_CODE('m', 0, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
|
/* IOCTL_MOUNTMGR_CREATE_POINT */
|
||||||
|
CreatePointInput, CreatePointInputSize, 0, 0);
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
/* HACK: delete the MountManager registry entries */
|
||||||
|
RegResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"System\\MountedDevices",
|
||||||
|
0, KEY_READ | KEY_WRITE, &RegKey);
|
||||||
|
if (ERROR_SUCCESS == RegResult)
|
||||||
|
{
|
||||||
|
for (DWORD I = 0;; I++)
|
||||||
|
{
|
||||||
|
RegValueNameSize = MAX_PATH;
|
||||||
|
RegValueDataSize = sizeof RegValueData;
|
||||||
|
RegResult = RegEnumValueW(RegKey,
|
||||||
|
I, RegValueName, &RegValueNameSize, 0, &RegType, RegValueData, &RegValueDataSize);
|
||||||
|
if (ERROR_NO_MORE_ITEMS == RegResult)
|
||||||
|
break;
|
||||||
|
else if (ERROR_SUCCESS != RegResult)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (REG_BINARY == RegType &&
|
||||||
|
sizeof RegValueData == RegValueDataSize &&
|
||||||
|
InlineIsEqualGUID((GUID *)&RegValueData, &UniqueId))
|
||||||
|
{
|
||||||
|
RegResult = RegDeleteValueW(RegKey, RegValueName);
|
||||||
|
if (ERROR_SUCCESS == RegResult)
|
||||||
|
/* reset index after modifying key; only safe way to use RegEnumValueW with modifications */
|
||||||
|
I = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RegCloseKey(RegKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result = STATUS_SUCCESS;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
MemFree(CreatePointInput);
|
||||||
|
MemFree(TargetName);
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NTSTATUS FspMountRemove_Mountmgr(PWSTR MountPoint)
|
||||||
|
{
|
||||||
|
/* mountmgr.h */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
ULONG SymbolicLinkNameOffset;
|
||||||
|
USHORT SymbolicLinkNameLength;
|
||||||
|
USHORT Reserved1;
|
||||||
|
ULONG UniqueIdOffset;
|
||||||
|
USHORT UniqueIdLength;
|
||||||
|
USHORT Reserved2;
|
||||||
|
ULONG DeviceNameOffset;
|
||||||
|
USHORT DeviceNameLength;
|
||||||
|
USHORT Reserved3;
|
||||||
|
} MOUNTMGR_MOUNT_POINT;
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
ULONG Size;
|
||||||
|
ULONG NumberOfMountPoints;
|
||||||
|
MOUNTMGR_MOUNT_POINT MountPoints[1];
|
||||||
|
} MOUNTMGR_MOUNT_POINTS;
|
||||||
|
|
||||||
|
MOUNTMGR_MOUNT_POINT *Input = 0;
|
||||||
|
MOUNTMGR_MOUNT_POINTS *Output = 0;
|
||||||
|
ULONG InputSize, OutputSize;
|
||||||
|
NTSTATUS Result;
|
||||||
|
|
||||||
|
InputSize = sizeof *Input + sizeof L"\\DosDevices\\X:" - sizeof(WCHAR);
|
||||||
|
OutputSize = 4096;
|
||||||
|
|
||||||
|
Input = MemAlloc(InputSize);
|
||||||
|
if (0 == Input)
|
||||||
|
{
|
||||||
|
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
Output = MemAlloc(OutputSize);
|
||||||
|
if (0 == Output)
|
||||||
|
{
|
||||||
|
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(Input, 0, sizeof *Input);
|
||||||
|
Input->SymbolicLinkNameOffset = sizeof *Input;
|
||||||
|
Input->SymbolicLinkNameLength = sizeof L"\\DosDevices\\X:" - sizeof(WCHAR);
|
||||||
|
memcpy((PUINT8)Input + Input->SymbolicLinkNameOffset,
|
||||||
|
L"\\DosDevices\\X:", Input->SymbolicLinkNameLength);
|
||||||
|
((PWCHAR)((PUINT8)Input + Input->SymbolicLinkNameOffset))[12] = MountPoint[4] & ~0x20;
|
||||||
|
/* convert to uppercase */
|
||||||
|
Result = FspMountmgrControl(
|
||||||
|
CTL_CODE('m', 1, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
|
||||||
|
/* IOCTL_MOUNTMGR_DELETE_POINTS */
|
||||||
|
Input, InputSize, Output, &OutputSize);
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
Result = STATUS_SUCCESS;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
MemFree(Output);
|
||||||
|
MemFree(Input);
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NTSTATUS FspLauncherDefineDosDevice(
|
||||||
|
WCHAR Sign, PWSTR MountPoint, PWSTR VolumeName)
|
||||||
|
{
|
||||||
|
if (2 != lstrlenW(MountPoint) ||
|
||||||
|
FSP_FSCTL_VOLUME_NAME_SIZEMAX / sizeof(WCHAR) <= lstrlenW(VolumeName))
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
WCHAR Argv0[4];
|
||||||
|
PWSTR Argv[2];
|
||||||
|
NTSTATUS Result;
|
||||||
|
ULONG ErrorCode;
|
||||||
|
|
||||||
|
Argv0[0] = Sign;
|
||||||
|
Argv0[1] = MountPoint[0];
|
||||||
|
Argv0[2] = MountPoint[1];
|
||||||
|
Argv0[3] = L'\0';
|
||||||
|
|
||||||
|
Argv[0] = Argv0;
|
||||||
|
Argv[1] = VolumeName;
|
||||||
|
|
||||||
|
Result = FspLaunchCallLauncherPipe(FspLaunchCmdDefineDosDevice, 2, Argv, 0, 0, 0, &ErrorCode);
|
||||||
|
return !NT_SUCCESS(Result) ? Result : FspNtStatusFromWin32(ErrorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static NTSTATUS FspMountSet_Drive(PWSTR VolumeName, PWSTR MountPoint, PHANDLE PMountHandle)
|
||||||
|
{
|
||||||
|
NTSTATUS Result;
|
||||||
|
BOOLEAN IsLocalSystem, IsServiceContext;
|
||||||
|
|
||||||
|
*PMountHandle = 0;
|
||||||
|
|
||||||
|
Result = FspServiceContextCheck(0, &IsLocalSystem);
|
||||||
|
IsServiceContext = NT_SUCCESS(Result) && !IsLocalSystem;
|
||||||
|
if (IsServiceContext)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If the current process is in the service context but not LocalSystem,
|
||||||
|
* ask the launcher to DefineDosDevice for us. This is because the launcher
|
||||||
|
* runs in the LocalSystem context and can create global drives.
|
||||||
|
*
|
||||||
|
* In this case the launcher will also add DELETE access to the drive symlink
|
||||||
|
* for us, so that we can make it temporary below.
|
||||||
|
*/
|
||||||
|
Result = FspLauncherDefineDosDevice(L'+', MountPoint, VolumeName);
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!DefineDosDeviceW(DDD_RAW_TARGET_PATH, MountPoint, VolumeName))
|
||||||
|
return FspNtStatusFromWin32(GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 != FspNtOpenSymbolicLinkObject)
|
||||||
|
{
|
||||||
|
WCHAR SymlinkBuf[6];
|
||||||
|
UNICODE_STRING Symlink;
|
||||||
|
OBJECT_ATTRIBUTES Obja;
|
||||||
|
|
||||||
|
memcpy(SymlinkBuf, L"\\??\\X:", sizeof SymlinkBuf);
|
||||||
|
SymlinkBuf[4] = MountPoint[0];
|
||||||
|
Symlink.Length = Symlink.MaximumLength = sizeof SymlinkBuf;
|
||||||
|
Symlink.Buffer = SymlinkBuf;
|
||||||
|
|
||||||
|
memset(&Obja, 0, sizeof Obja);
|
||||||
|
Obja.Length = sizeof Obja;
|
||||||
|
Obja.ObjectName = &Symlink;
|
||||||
|
Obja.Attributes = OBJ_CASE_INSENSITIVE;
|
||||||
|
|
||||||
|
Result = FspNtOpenSymbolicLinkObject(PMountHandle, DELETE, &Obja);
|
||||||
|
if (NT_SUCCESS(Result))
|
||||||
|
{
|
||||||
|
Result = FspNtMakeTemporaryObject(*PMountHandle);
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
{
|
||||||
|
FspNtClose(*PMountHandle);
|
||||||
|
*PMountHandle = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HACK:
|
||||||
|
*
|
||||||
|
* Handles do not use the low 2 bits (unless they are console handles).
|
||||||
|
* Abuse this fact to remember that we are running in the service context.
|
||||||
|
*/
|
||||||
|
*PMountHandle = (HANDLE)(UINT_PTR)((DWORD)(UINT_PTR)*PMountHandle | IsServiceContext);
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NTSTATUS FspMountRemove_Drive(PWSTR VolumeName, PWSTR MountPoint, HANDLE MountHandle)
|
||||||
|
{
|
||||||
|
NTSTATUS Result;
|
||||||
|
BOOLEAN IsServiceContext;
|
||||||
|
|
||||||
|
IsServiceContext = 0 != ((DWORD)(UINT_PTR)MountHandle & 1);
|
||||||
|
MountHandle = (HANDLE)(UINT_PTR)((DWORD)(UINT_PTR)MountHandle & ~1);
|
||||||
|
if (IsServiceContext)
|
||||||
|
/*
|
||||||
|
* If the current process is in the service context but not LocalSystem,
|
||||||
|
* ask the launcher to DefineDosDevice for us. This is because the launcher
|
||||||
|
* runs in the LocalSystem context and can remove global drives.
|
||||||
|
*/
|
||||||
|
Result = FspLauncherDefineDosDevice(L'-', MountPoint, VolumeName);
|
||||||
|
else
|
||||||
|
Result = DefineDosDeviceW(DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE,
|
||||||
|
MountPoint, VolumeName) ? STATUS_SUCCESS : FspNtStatusFromWin32(GetLastError());
|
||||||
|
|
||||||
|
if (0 != MountHandle)
|
||||||
|
FspNtClose(MountHandle);
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NTSTATUS FspMountSet_Directory(PWSTR VolumeName, PWSTR MountPoint,
|
||||||
|
PSECURITY_DESCRIPTOR SecurityDescriptor, PHANDLE PMountHandle)
|
||||||
|
{
|
||||||
|
NTSTATUS Result;
|
||||||
|
SECURITY_ATTRIBUTES SecurityAttributes;
|
||||||
|
HANDLE MountHandle = INVALID_HANDLE_VALUE;
|
||||||
|
DWORD Backslashes, Bytes;
|
||||||
|
USHORT VolumeNameLength, BackslashLength, ReparseDataLength;
|
||||||
|
PREPARSE_DATA_BUFFER ReparseData = 0;
|
||||||
|
PWSTR P, PathBuffer;
|
||||||
|
|
||||||
|
*PMountHandle = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Windows does not allow mount points (junctions) to point to network file systems.
|
||||||
|
*
|
||||||
|
* Count how many backslashes our VolumeName has. If it is 3 or more this is a network
|
||||||
|
* file system. Preemptively return STATUS_NETWORK_ACCESS_DENIED.
|
||||||
|
*/
|
||||||
|
for (P = VolumeName, Backslashes = 0; *P; P++)
|
||||||
|
if (L'\\' == *P)
|
||||||
|
if (3 == ++Backslashes)
|
||||||
|
{
|
||||||
|
Result = STATUS_NETWORK_ACCESS_DENIED;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&SecurityAttributes, 0, sizeof SecurityAttributes);
|
||||||
|
SecurityAttributes.nLength = sizeof SecurityAttributes;
|
||||||
|
SecurityAttributes.lpSecurityDescriptor = SecurityDescriptor;
|
||||||
|
|
||||||
|
MountHandle = CreateFileW(MountPoint,
|
||||||
|
FILE_WRITE_ATTRIBUTES,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||||
|
&SecurityAttributes,
|
||||||
|
CREATE_NEW,
|
||||||
|
FILE_ATTRIBUTE_DIRECTORY |
|
||||||
|
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE,
|
||||||
|
0);
|
||||||
|
if (INVALID_HANDLE_VALUE == MountHandle)
|
||||||
|
{
|
||||||
|
Result = FspNtStatusFromWin32(GetLastError());
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
VolumeNameLength = (USHORT)lstrlenW(VolumeName);
|
||||||
|
BackslashLength = 0 == VolumeNameLength || L'\\' != VolumeName[VolumeNameLength - 1];
|
||||||
|
VolumeNameLength *= sizeof(WCHAR);
|
||||||
|
BackslashLength *= sizeof(WCHAR);
|
||||||
|
|
||||||
|
ReparseDataLength = (USHORT)(
|
||||||
|
FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) -
|
||||||
|
FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer)) +
|
||||||
|
2 * (VolumeNameLength + BackslashLength + sizeof(WCHAR));
|
||||||
|
ReparseData = MemAlloc(REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseDataLength);
|
||||||
|
if (0 == ReparseData)
|
||||||
|
{
|
||||||
|
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReparseData->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
|
||||||
|
ReparseData->ReparseDataLength = ReparseDataLength;
|
||||||
|
ReparseData->Reserved = 0;
|
||||||
|
ReparseData->MountPointReparseBuffer.SubstituteNameOffset = 0;
|
||||||
|
ReparseData->MountPointReparseBuffer.SubstituteNameLength =
|
||||||
|
VolumeNameLength + BackslashLength;
|
||||||
|
ReparseData->MountPointReparseBuffer.PrintNameOffset =
|
||||||
|
ReparseData->MountPointReparseBuffer.SubstituteNameLength + sizeof(WCHAR);
|
||||||
|
ReparseData->MountPointReparseBuffer.PrintNameLength =
|
||||||
|
VolumeNameLength + BackslashLength;
|
||||||
|
|
||||||
|
PathBuffer = ReparseData->MountPointReparseBuffer.PathBuffer;
|
||||||
|
memcpy(PathBuffer, VolumeName, VolumeNameLength);
|
||||||
|
if (BackslashLength)
|
||||||
|
PathBuffer[VolumeNameLength / sizeof(WCHAR)] = L'\\';
|
||||||
|
PathBuffer[(VolumeNameLength + BackslashLength) / sizeof(WCHAR)] = L'\0';
|
||||||
|
|
||||||
|
PathBuffer = ReparseData->MountPointReparseBuffer.PathBuffer +
|
||||||
|
(ReparseData->MountPointReparseBuffer.PrintNameOffset) / sizeof(WCHAR);
|
||||||
|
memcpy(PathBuffer, VolumeName, VolumeNameLength);
|
||||||
|
if (BackslashLength)
|
||||||
|
PathBuffer[VolumeNameLength / sizeof(WCHAR)] = L'\\';
|
||||||
|
PathBuffer[(VolumeNameLength + BackslashLength) / sizeof(WCHAR)] = L'\0';
|
||||||
|
|
||||||
|
if (!DeviceIoControl(MountHandle, FSCTL_SET_REPARSE_POINT,
|
||||||
|
ReparseData, REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseData->ReparseDataLength,
|
||||||
|
0, 0,
|
||||||
|
&Bytes, 0))
|
||||||
|
{
|
||||||
|
Result = FspNtStatusFromWin32(GetLastError());
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
*PMountHandle = MountHandle;
|
||||||
|
|
||||||
|
Result = STATUS_SUCCESS;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (!NT_SUCCESS(Result) && INVALID_HANDLE_VALUE != MountHandle)
|
||||||
|
CloseHandle(MountHandle);
|
||||||
|
|
||||||
|
MemFree(ReparseData);
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NTSTATUS FspMountRemove_Directory(HANDLE MountHandle)
|
||||||
|
{
|
||||||
|
/* directory is marked DELETE_ON_CLOSE */
|
||||||
|
return CloseHandle(MountHandle) ? STATUS_SUCCESS : FspNtStatusFromWin32(GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
FSP_API NTSTATUS FspMountSet(FSP_MOUNT_DESC *Desc)
|
||||||
|
{
|
||||||
|
InitOnceExecuteOnce(&FspMountInitOnce, FspMountInitialize, 0, 0);
|
||||||
|
|
||||||
|
Desc->MountHandle = 0;
|
||||||
|
|
||||||
|
if (L'*' == Desc->MountPoint[0] && ':' == Desc->MountPoint[1] && L'\0' == Desc->MountPoint[2])
|
||||||
|
{
|
||||||
|
NTSTATUS Result;
|
||||||
|
DWORD Drives;
|
||||||
|
WCHAR Drive;
|
||||||
|
|
||||||
|
Drives = GetLogicalDrives();
|
||||||
|
if (0 == Drives)
|
||||||
|
return FspNtStatusFromWin32(GetLastError());
|
||||||
|
|
||||||
|
for (Drive = 'Z'; 'D' <= Drive; Drive--)
|
||||||
|
if (0 == (Drives & (1 << (Drive - 'A'))))
|
||||||
|
{
|
||||||
|
Desc->MountPoint[0] = Drive;
|
||||||
|
Result = FspMountSet_Drive(Desc->VolumeName, Desc->MountPoint,
|
||||||
|
&Desc->MountHandle);
|
||||||
|
if (NT_SUCCESS(Result))
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
Desc->MountPoint[0] = L'*';
|
||||||
|
return STATUS_NO_SUCH_DEVICE;
|
||||||
|
}
|
||||||
|
else if (FspPathIsMountmgrMountPoint(Desc->MountPoint))
|
||||||
|
return FspMountSet_Mountmgr(Desc->VolumeHandle, Desc->VolumeName, Desc->MountPoint);
|
||||||
|
else if (FspPathIsDrive(Desc->MountPoint))
|
||||||
|
return FspMountSet_Drive(Desc->VolumeName, Desc->MountPoint,
|
||||||
|
&Desc->MountHandle);
|
||||||
|
else
|
||||||
|
return FspMountSet_Directory(Desc->VolumeName, Desc->MountPoint, Desc->Security,
|
||||||
|
&Desc->MountHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
FSP_API NTSTATUS FspMountRemove(FSP_MOUNT_DESC *Desc)
|
||||||
|
{
|
||||||
|
InitOnceExecuteOnce(&FspMountInitOnce, FspMountInitialize, 0, 0);
|
||||||
|
|
||||||
|
if (FspPathIsMountmgrMountPoint(Desc->MountPoint))
|
||||||
|
return FspMountRemove_Mountmgr(Desc->MountPoint);
|
||||||
|
else if (FspPathIsDrive(Desc->MountPoint))
|
||||||
|
return FspMountRemove_Drive(Desc->VolumeName, Desc->MountPoint, Desc->MountHandle);
|
||||||
|
else
|
||||||
|
return FspMountRemove_Directory(Desc->MountHandle);
|
||||||
|
}
|
134
src/ku/uuid5.c
Normal file
134
src/ku/uuid5.c
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/**
|
||||||
|
* @file dll/uuid5.c
|
||||||
|
*
|
||||||
|
* @copyright 2015-2019 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 <ku/library.h>
|
||||||
|
#include <bcrypt.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This module is used to create UUID v5 identifiers. UUID v5 identifiers
|
||||||
|
* are effectively SHA1 hashes that are modified to fit within the UUID
|
||||||
|
* format. The resulting identifiers use version 5 and variant 2. The hash
|
||||||
|
* is taken over the concatenation of a namespace ID and a name; the namespace
|
||||||
|
* ID is another UUID and the name can be any string of bytes ("octets").
|
||||||
|
*
|
||||||
|
* For details see RFC 4122: https://tools.ietf.org/html/rfc4122
|
||||||
|
*/
|
||||||
|
|
||||||
|
NTSTATUS FspUuid5Make(const UUID *Namespace, const VOID *Buffer, ULONG Size, UUID *Uuid)
|
||||||
|
{
|
||||||
|
BCRYPT_ALG_HANDLE ProvHandle = 0;
|
||||||
|
BCRYPT_HASH_HANDLE HashHandle = 0;
|
||||||
|
UINT8 Temp[20];
|
||||||
|
NTSTATUS Result;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Windows UUID's are encoded in little-endian format. RFC 4122 specifies that for
|
||||||
|
* UUID v5 computation, UUID's must be converted to/from big-endian.
|
||||||
|
*
|
||||||
|
* Note that Windows is always little-endian:
|
||||||
|
* https://community.osr.com/discussion/comment/146810/#Comment_146810
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* copy Namespace to local buffer in network byte order (big-endian) */
|
||||||
|
Temp[ 0] = ((PUINT8)Namespace)[ 3];
|
||||||
|
Temp[ 1] = ((PUINT8)Namespace)[ 2];
|
||||||
|
Temp[ 2] = ((PUINT8)Namespace)[ 1];
|
||||||
|
Temp[ 3] = ((PUINT8)Namespace)[ 0];
|
||||||
|
Temp[ 4] = ((PUINT8)Namespace)[ 5];
|
||||||
|
Temp[ 5] = ((PUINT8)Namespace)[ 4];
|
||||||
|
Temp[ 6] = ((PUINT8)Namespace)[ 7];
|
||||||
|
Temp[ 7] = ((PUINT8)Namespace)[ 6];
|
||||||
|
Temp[ 8] = ((PUINT8)Namespace)[ 8];
|
||||||
|
Temp[ 9] = ((PUINT8)Namespace)[ 9];
|
||||||
|
Temp[10] = ((PUINT8)Namespace)[10];
|
||||||
|
Temp[11] = ((PUINT8)Namespace)[11];
|
||||||
|
Temp[12] = ((PUINT8)Namespace)[12];
|
||||||
|
Temp[13] = ((PUINT8)Namespace)[13];
|
||||||
|
Temp[14] = ((PUINT8)Namespace)[14];
|
||||||
|
Temp[15] = ((PUINT8)Namespace)[15];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unfortunately we cannot reuse the hashing object, because BCRYPT_HASH_REUSABLE_FLAG
|
||||||
|
* is available in Windows 8 and later. (WinFsp currently supports Windows 7 or later).
|
||||||
|
*/
|
||||||
|
|
||||||
|
Result = BCryptOpenAlgorithmProvider(&ProvHandle, BCRYPT_SHA1_ALGORITHM, 0, 0);
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
Result = BCryptCreateHash(ProvHandle, &HashHandle, 0, 0, 0, 0, 0);
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
Result = BCryptHashData(HashHandle, (PVOID)Temp, 16, 0);
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
Result = BCryptHashData(HashHandle, (PVOID)Buffer, Size, 0);
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
Result = BCryptFinishHash(HashHandle, Temp, 20, 0);
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
/* copy local buffer to Uuid in host byte order (little-endian) */
|
||||||
|
((PUINT8)Uuid)[ 0] = Temp[ 3];
|
||||||
|
((PUINT8)Uuid)[ 1] = Temp[ 2];
|
||||||
|
((PUINT8)Uuid)[ 2] = Temp[ 1];
|
||||||
|
((PUINT8)Uuid)[ 3] = Temp[ 0];
|
||||||
|
((PUINT8)Uuid)[ 4] = Temp[ 5];
|
||||||
|
((PUINT8)Uuid)[ 5] = Temp[ 4];
|
||||||
|
((PUINT8)Uuid)[ 6] = Temp[ 7];
|
||||||
|
((PUINT8)Uuid)[ 7] = Temp[ 6];
|
||||||
|
((PUINT8)Uuid)[ 8] = Temp[ 8];
|
||||||
|
((PUINT8)Uuid)[ 9] = Temp[ 9];
|
||||||
|
((PUINT8)Uuid)[10] = Temp[10];
|
||||||
|
((PUINT8)Uuid)[11] = Temp[11];
|
||||||
|
((PUINT8)Uuid)[12] = Temp[12];
|
||||||
|
((PUINT8)Uuid)[13] = Temp[13];
|
||||||
|
((PUINT8)Uuid)[14] = Temp[14];
|
||||||
|
((PUINT8)Uuid)[15] = Temp[15];
|
||||||
|
|
||||||
|
/* [RFC 4122 Section 4.3]
|
||||||
|
* Set the four most significant bits (bits 12 through 15) of the
|
||||||
|
* time_hi_and_version field to the appropriate 4-bit version number
|
||||||
|
* from Section 4.1.3.
|
||||||
|
*/
|
||||||
|
Uuid->Data3 = (5 << 12) | (Uuid->Data3 & 0x0fff);
|
||||||
|
|
||||||
|
/* [RFC 4122 Section 4.3]
|
||||||
|
* Set the two most significant bits (bits 6 and 7) of the
|
||||||
|
* clock_seq_hi_and_reserved to zero and one, respectively.
|
||||||
|
*/
|
||||||
|
Uuid->Data4[0] = (2 << 6) | (Uuid->Data4[0] & 0x3f);
|
||||||
|
|
||||||
|
Result = STATUS_SUCCESS;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (0 != HashHandle)
|
||||||
|
BCryptDestroyHash(HashHandle);
|
||||||
|
|
||||||
|
if (0 != ProvHandle)
|
||||||
|
BCryptCloseAlgorithmProvider(ProvHandle, 0);
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
@ -23,6 +23,9 @@
|
|||||||
|
|
||||||
static NTSTATUS FspFsvrtDeviceControl(
|
static NTSTATUS FspFsvrtDeviceControl(
|
||||||
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||||
|
static BOOLEAN FspFsvrtDeviceControlStorageQuery(
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
|
||||||
|
PNTSTATUS PResult);
|
||||||
static NTSTATUS FspFsvolDeviceControl(
|
static NTSTATUS FspFsvolDeviceControl(
|
||||||
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||||
FSP_IOCMPL_DISPATCH FspFsvolDeviceControlComplete;
|
FSP_IOCMPL_DISPATCH FspFsvolDeviceControlComplete;
|
||||||
@ -31,6 +34,7 @@ FSP_DRIVER_DISPATCH FspDeviceControl;
|
|||||||
|
|
||||||
#ifdef ALLOC_PRAGMA
|
#ifdef ALLOC_PRAGMA
|
||||||
#pragma alloc_text(PAGE, FspFsvrtDeviceControl)
|
#pragma alloc_text(PAGE, FspFsvrtDeviceControl)
|
||||||
|
#pragma alloc_text(PAGE, FspFsvrtDeviceControlStorageQuery)
|
||||||
#pragma alloc_text(PAGE, FspFsvolDeviceControl)
|
#pragma alloc_text(PAGE, FspFsvolDeviceControl)
|
||||||
#pragma alloc_text(PAGE, FspFsvolDeviceControlComplete)
|
#pragma alloc_text(PAGE, FspFsvolDeviceControlComplete)
|
||||||
#pragma alloc_text(PAGE, FspFsvolDeviceControlRequestFini)
|
#pragma alloc_text(PAGE, FspFsvolDeviceControlRequestFini)
|
||||||
@ -43,10 +47,18 @@ enum
|
|||||||
};
|
};
|
||||||
|
|
||||||
static NTSTATUS FspFsvrtDeviceControl(
|
static NTSTATUS FspFsvrtDeviceControl(
|
||||||
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
||||||
{
|
{
|
||||||
PAGED_CODE();
|
PAGED_CODE();
|
||||||
|
|
||||||
|
NTSTATUS Result;
|
||||||
|
|
||||||
|
if (FspFsvrtDeviceControlStorageQuery(FsvrtDeviceObject, Irp, IrpSp, &Result))
|
||||||
|
return Result;
|
||||||
|
|
||||||
|
if (FspMountdevDeviceControl(FsvrtDeviceObject, Irp, IrpSp, &Result))
|
||||||
|
return Result;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fix GitHub issue #177. All credit for the investigation of this issue
|
* Fix GitHub issue #177. All credit for the investigation of this issue
|
||||||
* and the suggested steps to reproduce and work around the problem goes
|
* and the suggested steps to reproduce and work around the problem goes
|
||||||
@ -64,6 +76,62 @@ static NTSTATUS FspFsvrtDeviceControl(
|
|||||||
return STATUS_UNRECOGNIZED_VOLUME;
|
return STATUS_UNRECOGNIZED_VOLUME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BOOLEAN FspFsvrtDeviceControlStorageQuery(
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
|
||||||
|
PNTSTATUS PResult)
|
||||||
|
{
|
||||||
|
PAGED_CODE();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SQL Server insists on sending us storage level IOCTL's even though
|
||||||
|
* WinFsp file systems are not on top of a real disk. So bite the bullet
|
||||||
|
* and implement some of those storage IOCTL's to make SQL Server happy.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (IOCTL_STORAGE_QUERY_PROPERTY != IrpSp->Parameters.DeviceIoControl.IoControlCode ||
|
||||||
|
sizeof(STORAGE_PROPERTY_QUERY) > IrpSp->Parameters.DeviceIoControl.InputBufferLength)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
PSTORAGE_PROPERTY_QUERY Query = Irp->AssociatedIrp.SystemBuffer;
|
||||||
|
switch (Query->PropertyId)
|
||||||
|
{
|
||||||
|
case StorageAccessAlignmentProperty:
|
||||||
|
if (PropertyExistsQuery == Query->QueryType)
|
||||||
|
*PResult = STATUS_SUCCESS;
|
||||||
|
else if (PropertyStandardQuery == Query->QueryType)
|
||||||
|
{
|
||||||
|
FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject);
|
||||||
|
PSTORAGE_ACCESS_ALIGNMENT_DESCRIPTOR Descriptor = Irp->AssociatedIrp.SystemBuffer;
|
||||||
|
ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
||||||
|
|
||||||
|
if (sizeof(STORAGE_DESCRIPTOR_HEADER) > OutputBufferLength)
|
||||||
|
*PResult = STATUS_BUFFER_TOO_SMALL;
|
||||||
|
else if (sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR) > OutputBufferLength)
|
||||||
|
{
|
||||||
|
Descriptor->Version = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
|
||||||
|
Descriptor->Size = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
|
||||||
|
Irp->IoStatus.Information = sizeof(STORAGE_DESCRIPTOR_HEADER);
|
||||||
|
*PResult = STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RtlZeroMemory(Descriptor, sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR));
|
||||||
|
Descriptor->Version = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
|
||||||
|
Descriptor->Size = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
|
||||||
|
Descriptor->BytesPerLogicalSector = FsvrtDeviceExtension->SectorSize;
|
||||||
|
Descriptor->BytesPerPhysicalSector = FsvrtDeviceExtension->SectorSize;
|
||||||
|
Irp->IoStatus.Information = sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR);
|
||||||
|
*PResult = STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
*PResult = STATUS_NOT_SUPPORTED;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static NTSTATUS FspFsvolDeviceControl(
|
static NTSTATUS FspFsvolDeviceControl(
|
||||||
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
||||||
{
|
{
|
||||||
@ -72,6 +140,15 @@ static NTSTATUS FspFsvolDeviceControl(
|
|||||||
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||||
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
||||||
ULONG IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
|
ULONG IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
|
||||||
|
NTSTATUS Result;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Possibly forward the IOCTL request to the user mode file system. The rules are:
|
||||||
|
*
|
||||||
|
* - File system must support DeviceControl.
|
||||||
|
* - Only IOCTL with custom devices (see DEVICE_TYPE_FROM_CTL_CODE) and
|
||||||
|
* METHOD_BUFFERED will be forwarded.
|
||||||
|
*/
|
||||||
|
|
||||||
/* do we support DeviceControl? */
|
/* do we support DeviceControl? */
|
||||||
if (!FsvolDeviceExtension->VolumeParams.DeviceControl)
|
if (!FsvolDeviceExtension->VolumeParams.DeviceControl)
|
||||||
@ -90,7 +167,6 @@ static NTSTATUS FspFsvolDeviceControl(
|
|||||||
if (!FspFileNodeIsValid(FileObject->FsContext))
|
if (!FspFileNodeIsValid(FileObject->FsContext))
|
||||||
return STATUS_INVALID_PARAMETER;
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
NTSTATUS Result;
|
|
||||||
FSP_FILE_NODE *FileNode = FileObject->FsContext;
|
FSP_FILE_NODE *FileNode = FileObject->FsContext;
|
||||||
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
|
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
|
||||||
PVOID InputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
PVOID InputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
||||||
|
@ -126,10 +126,12 @@ NTSTATUS FspDeviceCreateSecure(UINT32 Kind, ULONG ExtraSize,
|
|||||||
case FspFsvolDeviceExtensionKind:
|
case FspFsvolDeviceExtensionKind:
|
||||||
DeviceExtensionSize = sizeof(FSP_FSVOL_DEVICE_EXTENSION);
|
DeviceExtensionSize = sizeof(FSP_FSVOL_DEVICE_EXTENSION);
|
||||||
break;
|
break;
|
||||||
|
case FspFsvrtDeviceExtensionKind:
|
||||||
|
DeviceExtensionSize = sizeof(FSP_FSVRT_DEVICE_EXTENSION);
|
||||||
|
break;
|
||||||
case FspFsmupDeviceExtensionKind:
|
case FspFsmupDeviceExtensionKind:
|
||||||
DeviceExtensionSize = sizeof(FSP_FSMUP_DEVICE_EXTENSION);
|
DeviceExtensionSize = sizeof(FSP_FSMUP_DEVICE_EXTENSION);
|
||||||
break;
|
break;
|
||||||
case FspFsvrtDeviceExtensionKind:
|
|
||||||
case FspFsctlDeviceExtensionKind:
|
case FspFsctlDeviceExtensionKind:
|
||||||
DeviceExtensionSize = sizeof(FSP_DEVICE_EXTENSION);
|
DeviceExtensionSize = sizeof(FSP_DEVICE_EXTENSION);
|
||||||
break;
|
break;
|
||||||
@ -184,10 +186,12 @@ NTSTATUS FspDeviceInitialize(PDEVICE_OBJECT DeviceObject)
|
|||||||
case FspFsvolDeviceExtensionKind:
|
case FspFsvolDeviceExtensionKind:
|
||||||
Result = FspFsvolDeviceInit(DeviceObject);
|
Result = FspFsvolDeviceInit(DeviceObject);
|
||||||
break;
|
break;
|
||||||
|
case FspFsvrtDeviceExtensionKind:
|
||||||
|
Result = STATUS_SUCCESS;
|
||||||
|
break;
|
||||||
case FspFsmupDeviceExtensionKind:
|
case FspFsmupDeviceExtensionKind:
|
||||||
Result = FspFsmupDeviceInit(DeviceObject);
|
Result = FspFsmupDeviceInit(DeviceObject);
|
||||||
break;
|
break;
|
||||||
case FspFsvrtDeviceExtensionKind:
|
|
||||||
case FspFsctlDeviceExtensionKind:
|
case FspFsctlDeviceExtensionKind:
|
||||||
Result = STATUS_SUCCESS;
|
Result = STATUS_SUCCESS;
|
||||||
break;
|
break;
|
||||||
@ -213,10 +217,11 @@ VOID FspDeviceDelete(PDEVICE_OBJECT DeviceObject)
|
|||||||
case FspFsvolDeviceExtensionKind:
|
case FspFsvolDeviceExtensionKind:
|
||||||
FspFsvolDeviceFini(DeviceObject);
|
FspFsvolDeviceFini(DeviceObject);
|
||||||
break;
|
break;
|
||||||
|
case FspFsvrtDeviceExtensionKind:
|
||||||
|
break;
|
||||||
case FspFsmupDeviceExtensionKind:
|
case FspFsmupDeviceExtensionKind:
|
||||||
FspFsmupDeviceFini(DeviceObject);
|
FspFsmupDeviceFini(DeviceObject);
|
||||||
break;
|
break;
|
||||||
case FspFsvrtDeviceExtensionKind:
|
|
||||||
case FspFsctlDeviceExtensionKind:
|
case FspFsctlDeviceExtensionKind:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -26,6 +26,8 @@
|
|||||||
|
|
||||||
#define POOL_NX_OPTIN 1
|
#define POOL_NX_OPTIN 1
|
||||||
#include <ntifs.h>
|
#include <ntifs.h>
|
||||||
|
#include <mountdev.h>
|
||||||
|
#include <ntddstor.h>
|
||||||
#include <ntstrsafe.h>
|
#include <ntstrsafe.h>
|
||||||
#include <wdmsec.h>
|
#include <wdmsec.h>
|
||||||
#include <winfsp/fsctl.h>
|
#include <winfsp/fsctl.h>
|
||||||
@ -493,6 +495,9 @@ NTSTATUS FspFileNameInExpression(
|
|||||||
PWCH UpcaseTable,
|
PWCH UpcaseTable,
|
||||||
PBOOLEAN PResult);
|
PBOOLEAN PResult);
|
||||||
|
|
||||||
|
/* UUID5 creation (ku) */
|
||||||
|
NTSTATUS FspUuid5Make(const UUID *Namespace, const VOID *Buffer, ULONG Size, UUID *Uuid);
|
||||||
|
|
||||||
/* utility */
|
/* utility */
|
||||||
PVOID FspAllocatePoolMustSucceed(POOL_TYPE PoolType, SIZE_T Size, ULONG Tag);
|
PVOID FspAllocatePoolMustSucceed(POOL_TYPE PoolType, SIZE_T Size, ULONG Tag);
|
||||||
PVOID FspAllocateIrpMustSucceed(CCHAR StackSize);
|
PVOID FspAllocateIrpMustSucceed(CCHAR StackSize);
|
||||||
@ -510,6 +515,8 @@ NTSTATUS FspSendQuerySecurityIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileO
|
|||||||
NTSTATUS FspSendQueryEaIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
|
NTSTATUS FspSendQueryEaIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
|
||||||
PFILE_GET_EA_INFORMATION GetEa, ULONG GetEaLength,
|
PFILE_GET_EA_INFORMATION GetEa, ULONG GetEaLength,
|
||||||
PFILE_FULL_EA_INFORMATION Ea, PULONG PEaLength);
|
PFILE_FULL_EA_INFORMATION Ea, PULONG PEaLength);
|
||||||
|
NTSTATUS FspSendMountmgrDeviceControlIrp(ULONG IoControlCode,
|
||||||
|
PVOID SystemBuffer, ULONG InputBufferLength, PULONG POutputBufferLength);
|
||||||
NTSTATUS FspBufferUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation);
|
NTSTATUS FspBufferUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation);
|
||||||
NTSTATUS FspLockUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation);
|
NTSTATUS FspLockUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation);
|
||||||
NTSTATUS FspMapLockedPagesInUserMode(PMDL Mdl, PVOID *PAddress, ULONG ExtraPriorityFlags);
|
NTSTATUS FspMapLockedPagesInUserMode(PMDL Mdl, PVOID *PAddress, ULONG ExtraPriorityFlags);
|
||||||
@ -1098,6 +1105,16 @@ typedef struct
|
|||||||
FSP_FSCTL_DECLSPEC_ALIGN UINT8 FsextData[];
|
FSP_FSCTL_DECLSPEC_ALIGN UINT8 FsextData[];
|
||||||
} FSP_FSVOL_DEVICE_EXTENSION;
|
} FSP_FSVOL_DEVICE_EXTENSION;
|
||||||
typedef struct
|
typedef struct
|
||||||
|
{
|
||||||
|
FSP_DEVICE_EXTENSION Base;
|
||||||
|
UINT16 SectorSize;
|
||||||
|
LONG IsMountdev;
|
||||||
|
BOOLEAN Persistent;
|
||||||
|
GUID UniqueId;
|
||||||
|
UNICODE_STRING VolumeName;
|
||||||
|
WCHAR VolumeNameBuf[FSP_FSCTL_VOLUME_NAME_SIZE / sizeof(WCHAR)];
|
||||||
|
} FSP_FSVRT_DEVICE_EXTENSION;
|
||||||
|
typedef struct
|
||||||
{
|
{
|
||||||
FSP_DEVICE_EXTENSION Base;
|
FSP_DEVICE_EXTENSION Base;
|
||||||
UINT32 InitDonePfxTab:1;
|
UINT32 InitDonePfxTab:1;
|
||||||
@ -1117,6 +1134,12 @@ FSP_FSVOL_DEVICE_EXTENSION *FspFsvolDeviceExtension(PDEVICE_OBJECT DeviceObject)
|
|||||||
return DeviceObject->DeviceExtension;
|
return DeviceObject->DeviceExtension;
|
||||||
}
|
}
|
||||||
static inline
|
static inline
|
||||||
|
FSP_FSVRT_DEVICE_EXTENSION *FspFsvrtDeviceExtension(PDEVICE_OBJECT DeviceObject)
|
||||||
|
{
|
||||||
|
ASSERT(FspFsvrtDeviceExtensionKind == ((FSP_DEVICE_EXTENSION *)DeviceObject->DeviceExtension)->Kind);
|
||||||
|
return DeviceObject->DeviceExtension;
|
||||||
|
}
|
||||||
|
static inline
|
||||||
FSP_FSMUP_DEVICE_EXTENSION *FspFsmupDeviceExtension(PDEVICE_OBJECT DeviceObject)
|
FSP_FSMUP_DEVICE_EXTENSION *FspFsmupDeviceExtension(PDEVICE_OBJECT DeviceObject)
|
||||||
{
|
{
|
||||||
ASSERT(FspFsmupDeviceExtensionKind == ((FSP_DEVICE_EXTENSION *)DeviceObject->DeviceExtension)->Kind);
|
ASSERT(FspFsmupDeviceExtensionKind == ((FSP_DEVICE_EXTENSION *)DeviceObject->DeviceExtension)->Kind);
|
||||||
@ -1221,6 +1244,20 @@ BOOLEAN FspQueryDirectoryIrpShouldUseProcessBuffer(PIRP Irp, SIZE_T BufferSize)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* mountdev */
|
||||||
|
NTSTATUS FspMountdevQueryDeviceName(
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||||
|
NTSTATUS FspMountdevQueryUniqueId(
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||||
|
BOOLEAN FspMountdevDeviceControl(
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
|
||||||
|
PNTSTATUS PResult);
|
||||||
|
NTSTATUS FspMountdevMake(
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject, PDEVICE_OBJECT FsvolDeviceObject,
|
||||||
|
BOOLEAN Persistent);
|
||||||
|
VOID FspMountdevFini(
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject);
|
||||||
|
|
||||||
/* fsmup */
|
/* fsmup */
|
||||||
NTSTATUS FspMupRegister(
|
NTSTATUS FspMupRegister(
|
||||||
PDEVICE_OBJECT FsmupDeviceObject, PDEVICE_OBJECT FsvolDeviceObject);
|
PDEVICE_OBJECT FsmupDeviceObject, PDEVICE_OBJECT FsvolDeviceObject);
|
||||||
@ -1236,6 +1273,8 @@ VOID FspVolumeDelete(
|
|||||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||||
NTSTATUS FspVolumeMount(
|
NTSTATUS FspVolumeMount(
|
||||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||||
|
NTSTATUS FspVolumeMakeMountdev(
|
||||||
|
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||||
NTSTATUS FspVolumeGetName(
|
NTSTATUS FspVolumeGetName(
|
||||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||||
NTSTATUS FspVolumeGetNameList(
|
NTSTATUS FspVolumeGetNameList(
|
||||||
|
@ -77,6 +77,10 @@ static NTSTATUS FspFsctlFileSystemControl(
|
|||||||
case IRP_MN_USER_FS_REQUEST:
|
case IRP_MN_USER_FS_REQUEST:
|
||||||
switch (IrpSp->Parameters.FileSystemControl.FsControlCode)
|
switch (IrpSp->Parameters.FileSystemControl.FsControlCode)
|
||||||
{
|
{
|
||||||
|
case FSP_FSCTL_MOUNTDEV:
|
||||||
|
if (0 != IrpSp->FileObject->FsContext2)
|
||||||
|
Result = FspVolumeMakeMountdev(FsctlDeviceObject, Irp, IrpSp);
|
||||||
|
break;
|
||||||
case FSP_FSCTL_VOLUME_NAME:
|
case FSP_FSCTL_VOLUME_NAME:
|
||||||
if (0 != IrpSp->FileObject->FsContext2)
|
if (0 != IrpSp->FileObject->FsContext2)
|
||||||
Result = FspVolumeGetName(FsctlDeviceObject, Irp, IrpSp);
|
Result = FspVolumeGetName(FsctlDeviceObject, Irp, IrpSp);
|
||||||
|
@ -22,8 +22,41 @@
|
|||||||
#include <sys/driver.h>
|
#include <sys/driver.h>
|
||||||
#include <winfsp/fsext.h>
|
#include <winfsp/fsext.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Maximum number of allowed fsext providers. This must be kept small,
|
||||||
|
* because we do a linear search to find the provider. If this changes
|
||||||
|
* the data structure used to store providers (currently 2 parallel
|
||||||
|
* arrays) must be revisited.
|
||||||
|
*/
|
||||||
|
#define FSP_FSEXT_PROVIDER_COUNTMAX 4
|
||||||
|
|
||||||
static KSPIN_LOCK FsextSpinLock = 0;
|
static KSPIN_LOCK FsextSpinLock = 0;
|
||||||
FSP_FSEXT_PROVIDER *FsextProvider;
|
static UINT32 FsextControlCodes[FSP_FSEXT_PROVIDER_COUNTMAX];
|
||||||
|
static FSP_FSEXT_PROVIDER *FsextProviders[FSP_FSEXT_PROVIDER_COUNTMAX];
|
||||||
|
|
||||||
|
static inline
|
||||||
|
FSP_FSEXT_PROVIDER *FspFsextProviderGet(UINT32 FsextControlCode)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
for (ULONG I = 0; FSP_FSEXT_PROVIDER_COUNTMAX > I; I++)
|
||||||
|
if (FsextControlCode == FsextControlCodes[I])
|
||||||
|
return FsextProviders[I];
|
||||||
|
#else
|
||||||
|
/* unroll by hand */
|
||||||
|
FSP_FSCTL_STATIC_ASSERT(4 == FSP_FSEXT_PROVIDER_COUNTMAX,
|
||||||
|
"unrolled loop expects FsextProviders to have 4 elements");
|
||||||
|
if (FsextControlCode == FsextControlCodes[0])
|
||||||
|
return FsextProviders[0];
|
||||||
|
if (FsextControlCode == FsextControlCodes[1])
|
||||||
|
return FsextProviders[1];
|
||||||
|
if (FsextControlCode == FsextControlCodes[2])
|
||||||
|
return FsextProviders[2];
|
||||||
|
if (FsextControlCode == FsextControlCodes[3])
|
||||||
|
return FsextProviders[3];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
FSP_FSEXT_PROVIDER *FspFsextProvider(UINT32 FsextControlCode, PNTSTATUS PLoadResult)
|
FSP_FSEXT_PROVIDER *FspFsextProvider(UINT32 FsextControlCode, PNTSTATUS PLoadResult)
|
||||||
{
|
{
|
||||||
@ -31,10 +64,9 @@ FSP_FSEXT_PROVIDER *FspFsextProvider(UINT32 FsextControlCode, PNTSTATUS PLoadRes
|
|||||||
KIRQL Irql;
|
KIRQL Irql;
|
||||||
|
|
||||||
KeAcquireSpinLock(&FsextSpinLock, &Irql);
|
KeAcquireSpinLock(&FsextSpinLock, &Irql);
|
||||||
Provider = FsextProvider;
|
Provider = FspFsextProviderGet(FsextControlCode);
|
||||||
KeReleaseSpinLock(&FsextSpinLock, Irql);
|
KeReleaseSpinLock(&FsextSpinLock, Irql);
|
||||||
if (0 != Provider && FsextControlCode != Provider->DeviceTransactCode)
|
ASSERT(0 == Provider || FsextControlCode == Provider->DeviceTransactCode);
|
||||||
Provider = 0;
|
|
||||||
|
|
||||||
if (0 != PLoadResult)
|
if (0 != PLoadResult)
|
||||||
{
|
{
|
||||||
@ -81,10 +113,9 @@ FSP_FSEXT_PROVIDER *FspFsextProvider(UINT32 FsextControlCode, PNTSTATUS PLoadRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
KeAcquireSpinLock(&FsextSpinLock, &Irql);
|
KeAcquireSpinLock(&FsextSpinLock, &Irql);
|
||||||
Provider = FsextProvider;
|
Provider = FspFsextProviderGet(FsextControlCode);
|
||||||
KeReleaseSpinLock(&FsextSpinLock, Irql);
|
KeReleaseSpinLock(&FsextSpinLock, Irql);
|
||||||
if (0 != Provider && FsextControlCode != Provider->DeviceTransactCode)
|
ASSERT(0 == Provider || FsextControlCode == Provider->DeviceTransactCode);
|
||||||
Provider = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*PLoadResult = 0 != Provider ? STATUS_SUCCESS : STATUS_OBJECT_NAME_NOT_FOUND;
|
*PLoadResult = 0 != Provider ? STATUS_SUCCESS : STATUS_OBJECT_NAME_NOT_FOUND;
|
||||||
@ -100,18 +131,17 @@ NTSTATUS FspFsextProviderRegister(FSP_FSEXT_PROVIDER *Provider)
|
|||||||
|
|
||||||
KeAcquireSpinLock(&FsextSpinLock, &Irql);
|
KeAcquireSpinLock(&FsextSpinLock, &Irql);
|
||||||
|
|
||||||
if (0 != FsextProvider)
|
|
||||||
{
|
|
||||||
Result = STATUS_TOO_LATE;
|
Result = STATUS_TOO_LATE;
|
||||||
goto exit;
|
for (ULONG I = 0; FSP_FSEXT_PROVIDER_COUNTMAX > I; I++)
|
||||||
|
if (0 == FsextControlCodes[I])
|
||||||
|
{
|
||||||
|
Provider->DeviceExtensionOffset = FIELD_OFFSET(FSP_FSVOL_DEVICE_EXTENSION, FsextData);
|
||||||
|
FsextControlCodes[I] = Provider->DeviceTransactCode;
|
||||||
|
FsextProviders[I] = Provider;
|
||||||
|
Result = STATUS_SUCCESS;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Provider->DeviceExtensionOffset = FIELD_OFFSET(FSP_FSVOL_DEVICE_EXTENSION, FsextData);
|
|
||||||
FsextProvider = Provider;
|
|
||||||
|
|
||||||
Result = STATUS_SUCCESS;
|
|
||||||
|
|
||||||
exit:
|
|
||||||
KeReleaseSpinLock(&FsextSpinLock, Irql);
|
KeReleaseSpinLock(&FsextSpinLock, Irql);
|
||||||
|
|
||||||
return Result;
|
return Result;
|
||||||
|
232
src/sys/mountdev.c
Normal file
232
src/sys/mountdev.c
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
/**
|
||||||
|
* @file sys/mountdev.c
|
||||||
|
*
|
||||||
|
* @copyright 2015-2019 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 FspMountdevQueryDeviceName(
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||||
|
NTSTATUS FspMountdevQueryUniqueId(
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||||
|
BOOLEAN FspMountdevDeviceControl(
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
|
||||||
|
PNTSTATUS PResult);
|
||||||
|
NTSTATUS FspMountdevMake(
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject, PDEVICE_OBJECT FsvolDeviceObject,
|
||||||
|
BOOLEAN Persistent);
|
||||||
|
VOID FspMountdevFini(
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject);
|
||||||
|
|
||||||
|
#ifdef ALLOC_PRAGMA
|
||||||
|
#pragma alloc_text(PAGE, FspMountdevQueryDeviceName)
|
||||||
|
#pragma alloc_text(PAGE, FspMountdevQueryUniqueId)
|
||||||
|
#pragma alloc_text(PAGE, FspMountdevDeviceControl)
|
||||||
|
#pragma alloc_text(PAGE, FspMountdevMake)
|
||||||
|
#pragma alloc_text(PAGE, FspMountdevFini)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
NTSTATUS FspMountdevQueryDeviceName(
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
||||||
|
{
|
||||||
|
PAGED_CODE();
|
||||||
|
|
||||||
|
FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject);
|
||||||
|
ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
||||||
|
PMOUNTDEV_NAME OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
||||||
|
|
||||||
|
if (sizeof(MOUNTDEV_NAME) > OutputBufferLength)
|
||||||
|
/* NOTE: Windows storage samples also set: Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME) */
|
||||||
|
return STATUS_BUFFER_TOO_SMALL;
|
||||||
|
|
||||||
|
RtlZeroMemory(OutputBuffer, sizeof(MOUNTDEV_NAME));
|
||||||
|
OutputBuffer->NameLength = FsvrtDeviceExtension->VolumeName.Length;
|
||||||
|
|
||||||
|
Irp->IoStatus.Information =
|
||||||
|
FIELD_OFFSET(MOUNTDEV_NAME, Name) + OutputBuffer->NameLength;
|
||||||
|
if (Irp->IoStatus.Information > OutputBufferLength)
|
||||||
|
{
|
||||||
|
Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
|
||||||
|
return STATUS_BUFFER_OVERFLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
RtlCopyMemory(OutputBuffer->Name,
|
||||||
|
FsvrtDeviceExtension->VolumeName.Buffer, OutputBuffer->NameLength);
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS FspMountdevQueryUniqueId(
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
||||||
|
{
|
||||||
|
PAGED_CODE();
|
||||||
|
|
||||||
|
FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject);
|
||||||
|
ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
||||||
|
PMOUNTDEV_UNIQUE_ID OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
||||||
|
|
||||||
|
if (sizeof(MOUNTDEV_UNIQUE_ID) > OutputBufferLength)
|
||||||
|
/* NOTE: Windows storage samples also set: Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID) */
|
||||||
|
return STATUS_BUFFER_TOO_SMALL;
|
||||||
|
|
||||||
|
RtlZeroMemory(OutputBuffer, sizeof(MOUNTDEV_UNIQUE_ID));
|
||||||
|
OutputBuffer->UniqueIdLength = sizeof FsvrtDeviceExtension->UniqueId;
|
||||||
|
|
||||||
|
Irp->IoStatus.Information =
|
||||||
|
FIELD_OFFSET(MOUNTDEV_UNIQUE_ID, UniqueId) + OutputBuffer->UniqueIdLength;
|
||||||
|
if (Irp->IoStatus.Information > OutputBufferLength)
|
||||||
|
{
|
||||||
|
Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
|
||||||
|
return STATUS_BUFFER_OVERFLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
RtlCopyMemory(OutputBuffer->UniqueId,
|
||||||
|
&FsvrtDeviceExtension->UniqueId, OutputBuffer->UniqueIdLength);
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOLEAN FspMountdevDeviceControl(
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
|
||||||
|
PNTSTATUS PResult)
|
||||||
|
{
|
||||||
|
PAGED_CODE();
|
||||||
|
|
||||||
|
if (0 != FsvrtDeviceObject)
|
||||||
|
{
|
||||||
|
FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject);
|
||||||
|
if (0 != InterlockedCompareExchange(&FsvrtDeviceExtension->IsMountdev, 0, 0))
|
||||||
|
{
|
||||||
|
switch (IrpSp->Parameters.DeviceIoControl.IoControlCode)
|
||||||
|
{
|
||||||
|
case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
|
||||||
|
*PResult = FspMountdevQueryDeviceName(FsvrtDeviceObject, Irp, IrpSp);
|
||||||
|
return TRUE;
|
||||||
|
case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
|
||||||
|
*PResult = FspMountdevQueryUniqueId(FsvrtDeviceObject, Irp, IrpSp);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS FspMountdevMake(
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject, PDEVICE_OBJECT FsvolDeviceObject,
|
||||||
|
BOOLEAN Persistent)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This function converts the fsvrt device into a mountdev device that
|
||||||
|
* responds to MountManager IOCTL's. This allows the fsvrt device to
|
||||||
|
* be mounted using the MountManager.
|
||||||
|
*
|
||||||
|
* This function requires protection against concurrency. In general this
|
||||||
|
* is achieved by acquiring the GlobalDeviceLock.
|
||||||
|
*/
|
||||||
|
|
||||||
|
PAGED_CODE();
|
||||||
|
|
||||||
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||||
|
FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject);
|
||||||
|
UNICODE_STRING String;
|
||||||
|
WCHAR StringBuf[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR) + 18];
|
||||||
|
GUID Guid;
|
||||||
|
NTSTATUS Result;
|
||||||
|
|
||||||
|
ASSERT(FsvolDeviceExtension->FsvrtDeviceObject == FsvrtDeviceObject);
|
||||||
|
|
||||||
|
if (0 != InterlockedCompareExchange(&FsvrtDeviceExtension->IsMountdev, 0, 0))
|
||||||
|
return Persistent == FsvrtDeviceExtension->Persistent ?
|
||||||
|
STATUS_TOO_LATE : STATUS_ACCESS_DENIED;
|
||||||
|
|
||||||
|
FsvrtDeviceExtension->Persistent = Persistent;
|
||||||
|
|
||||||
|
if (Persistent)
|
||||||
|
{
|
||||||
|
/* make UUID v5 from the fsvrt device GUID and a unique string derived from VolumeParams */
|
||||||
|
RtlInitEmptyUnicodeString(&String, StringBuf, sizeof StringBuf);
|
||||||
|
Result = RtlUnicodeStringPrintf(&String,
|
||||||
|
L"%s:%08lx:%08lx",
|
||||||
|
FsvolDeviceExtension->VolumeParams.FileSystemName,
|
||||||
|
FsvolDeviceExtension->VolumeParams.VolumeSerialNumber,
|
||||||
|
FsvolDeviceExtension->VolumeParams.VolumeCreationTime);
|
||||||
|
ASSERT(NT_SUCCESS(Result));
|
||||||
|
Result = FspUuid5Make(&FspFsvrtDeviceClassGuid, String.Buffer, String.Length, &Guid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
/* create volume guid */
|
||||||
|
Result = FspCreateGuid(&Guid);
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
/* initialize the fsvrt device extension */
|
||||||
|
RtlCopyMemory(&FsvrtDeviceExtension->UniqueId, &Guid, sizeof Guid);
|
||||||
|
RtlInitEmptyUnicodeString(&FsvrtDeviceExtension->VolumeName,
|
||||||
|
FsvrtDeviceExtension->VolumeNameBuf, sizeof FsvrtDeviceExtension->VolumeNameBuf);
|
||||||
|
RtlCopyUnicodeString(&FsvrtDeviceExtension->VolumeName, &FsvolDeviceExtension->VolumeName);
|
||||||
|
|
||||||
|
/* mark the fsvrt device as initialized */
|
||||||
|
InterlockedIncrement(&FspFsvrtDeviceExtension(FsvrtDeviceObject)->IsMountdev);
|
||||||
|
|
||||||
|
Result = STATUS_SUCCESS;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID FspMountdevFini(
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject)
|
||||||
|
{
|
||||||
|
PAGED_CODE();
|
||||||
|
|
||||||
|
FSP_FSVRT_DEVICE_EXTENSION *FsvrtDeviceExtension = FspFsvrtDeviceExtension(FsvrtDeviceObject);
|
||||||
|
PVOID Buffer = 0;
|
||||||
|
ULONG Length = 4096;
|
||||||
|
MOUNTMGR_MOUNT_POINT *MountPoint;
|
||||||
|
NTSTATUS Result;
|
||||||
|
|
||||||
|
if (0 == InterlockedCompareExchange(&FsvrtDeviceExtension->IsMountdev, 0, 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (FsvrtDeviceExtension->Persistent)
|
||||||
|
/* if the mountdev is marked as persistent do not purge the MountManager */
|
||||||
|
return;
|
||||||
|
|
||||||
|
Buffer = FspAllocNonPaged(Length);
|
||||||
|
if (0 == Buffer)
|
||||||
|
{
|
||||||
|
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
MountPoint = Buffer;
|
||||||
|
RtlZeroMemory(MountPoint, sizeof *MountPoint);
|
||||||
|
MountPoint->UniqueIdOffset = sizeof(MOUNTMGR_MOUNT_POINT);
|
||||||
|
MountPoint->UniqueIdLength = sizeof FsvrtDeviceExtension->UniqueId;
|
||||||
|
RtlCopyMemory((PUINT8)MountPoint + MountPoint->UniqueIdOffset,
|
||||||
|
&FsvrtDeviceExtension->UniqueId, MountPoint->UniqueIdLength);
|
||||||
|
|
||||||
|
Result = FspSendMountmgrDeviceControlIrp(IOCTL_MOUNTMGR_DELETE_POINTS,
|
||||||
|
Buffer, MountPoint->UniqueIdOffset + MountPoint->UniqueIdLength, &Length);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (0 != Buffer)
|
||||||
|
FspFree(Buffer);
|
||||||
|
}
|
@ -35,6 +35,8 @@ NTSTATUS FspSendQuerySecurityIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileO
|
|||||||
NTSTATUS FspSendQueryEaIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
|
NTSTATUS FspSendQueryEaIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
|
||||||
PFILE_GET_EA_INFORMATION GetEa, ULONG GetEaLength,
|
PFILE_GET_EA_INFORMATION GetEa, ULONG GetEaLength,
|
||||||
PFILE_FULL_EA_INFORMATION Ea, PULONG PEaLength);
|
PFILE_FULL_EA_INFORMATION Ea, PULONG PEaLength);
|
||||||
|
NTSTATUS FspSendMountmgrDeviceControlIrp(ULONG IoControlCode,
|
||||||
|
PVOID SystemBuffer, ULONG InputBufferLength, PULONG POutputBufferLength);
|
||||||
static NTSTATUS FspSendIrpCompletion(
|
static NTSTATUS FspSendIrpCompletion(
|
||||||
PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context0);
|
PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context0);
|
||||||
NTSTATUS FspBufferUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation);
|
NTSTATUS FspBufferUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation);
|
||||||
@ -136,6 +138,7 @@ NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context);
|
|||||||
#pragma alloc_text(PAGE, FspSendSetInformationIrp)
|
#pragma alloc_text(PAGE, FspSendSetInformationIrp)
|
||||||
#pragma alloc_text(PAGE, FspSendQuerySecurityIrp)
|
#pragma alloc_text(PAGE, FspSendQuerySecurityIrp)
|
||||||
#pragma alloc_text(PAGE, FspSendQueryEaIrp)
|
#pragma alloc_text(PAGE, FspSendQueryEaIrp)
|
||||||
|
#pragma alloc_text(PAGE, FspSendMountmgrDeviceControlIrp)
|
||||||
#pragma alloc_text(PAGE, FspBufferUserBuffer)
|
#pragma alloc_text(PAGE, FspBufferUserBuffer)
|
||||||
#pragma alloc_text(PAGE, FspLockUserBuffer)
|
#pragma alloc_text(PAGE, FspLockUserBuffer)
|
||||||
#pragma alloc_text(PAGE, FspMapLockedPagesInUserMode)
|
#pragma alloc_text(PAGE, FspMapLockedPagesInUserMode)
|
||||||
@ -444,6 +447,63 @@ NTSTATUS FspSendQueryEaIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
|
|||||||
return Context.IoStatus.Status;
|
return Context.IoStatus.Status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NTSTATUS FspSendMountmgrDeviceControlIrp(ULONG IoControlCode,
|
||||||
|
PVOID SystemBuffer, ULONG InputBufferLength, PULONG POutputBufferLength)
|
||||||
|
{
|
||||||
|
PAGED_CODE();
|
||||||
|
|
||||||
|
ASSERT(METHOD_BUFFERED == (IoControlCode & 3));
|
||||||
|
|
||||||
|
NTSTATUS Result;
|
||||||
|
UNICODE_STRING DeviceName;
|
||||||
|
PFILE_OBJECT FileObject;
|
||||||
|
PDEVICE_OBJECT DeviceObject;
|
||||||
|
PIRP Irp;
|
||||||
|
PIO_STACK_LOCATION IrpSp;
|
||||||
|
FSP_SEND_IRP_CONTEXT Context;
|
||||||
|
ULONG OutputBufferLength = 0;
|
||||||
|
|
||||||
|
if (0 == POutputBufferLength)
|
||||||
|
POutputBufferLength = &OutputBufferLength;
|
||||||
|
|
||||||
|
RtlInitUnicodeString(&DeviceName, MOUNTMGR_DEVICE_NAME);
|
||||||
|
Result = IoGetDeviceObjectPointer(&DeviceName, FILE_READ_ATTRIBUTES,
|
||||||
|
&FileObject, &DeviceObject);
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
||||||
|
if (0 == Irp)
|
||||||
|
{
|
||||||
|
Result = STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
IrpSp = IoGetNextIrpStackLocation(Irp);
|
||||||
|
Irp->RequestorMode = KernelMode;
|
||||||
|
Irp->AssociatedIrp.SystemBuffer = SystemBuffer;
|
||||||
|
IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
||||||
|
IrpSp->Parameters.DeviceIoControl.OutputBufferLength = *POutputBufferLength;
|
||||||
|
IrpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
|
||||||
|
IrpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
|
||||||
|
|
||||||
|
KeInitializeEvent(&Context.Event, NotificationEvent, FALSE);
|
||||||
|
IoSetCompletionRoutine(Irp, FspSendIrpCompletion, &Context, TRUE, TRUE, TRUE);
|
||||||
|
|
||||||
|
Result = IoCallDriver(DeviceObject, Irp);
|
||||||
|
if (STATUS_PENDING == Result)
|
||||||
|
KeWaitForSingleObject(&Context.Event, Executive, KernelMode, FALSE, 0);
|
||||||
|
|
||||||
|
*POutputBufferLength = (ULONG)Context.IoStatus.Information;
|
||||||
|
Result = Context.IoStatus.Status;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (0 != FileObject)
|
||||||
|
ObDereferenceObject(FileObject);
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
static NTSTATUS FspSendIrpCompletion(
|
static NTSTATUS FspSendIrpCompletion(
|
||||||
PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context0)
|
PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context0)
|
||||||
{
|
{
|
||||||
|
@ -28,6 +28,9 @@ static NTSTATUS FspFsvolQueryFsDeviceInformation(
|
|||||||
static NTSTATUS FspFsvolQueryFsFullSizeInformation(
|
static NTSTATUS FspFsvolQueryFsFullSizeInformation(
|
||||||
PDEVICE_OBJECT FsvolDeviceObject, PUINT8 *PBuffer, PUINT8 BufferEnd,
|
PDEVICE_OBJECT FsvolDeviceObject, PUINT8 *PBuffer, PUINT8 BufferEnd,
|
||||||
const FSP_FSCTL_VOLUME_INFO *VolumeInfo);
|
const FSP_FSCTL_VOLUME_INFO *VolumeInfo);
|
||||||
|
static NTSTATUS FspFsvolQueryFsSectorSizeInformation(
|
||||||
|
PDEVICE_OBJECT FsvolDeviceObject, PUINT8 *PBuffer, PUINT8 BufferEnd,
|
||||||
|
const FSP_FSCTL_VOLUME_INFO *VolumeInfo);
|
||||||
static NTSTATUS FspFsvolQueryFsSizeInformation(
|
static NTSTATUS FspFsvolQueryFsSizeInformation(
|
||||||
PDEVICE_OBJECT FsvolDeviceObject, PUINT8 *PBuffer, PUINT8 BufferEnd,
|
PDEVICE_OBJECT FsvolDeviceObject, PUINT8 *PBuffer, PUINT8 BufferEnd,
|
||||||
const FSP_FSCTL_VOLUME_INFO *VolumeInfo);
|
const FSP_FSCTL_VOLUME_INFO *VolumeInfo);
|
||||||
@ -50,6 +53,7 @@ FSP_DRIVER_DISPATCH FspSetVolumeInformation;
|
|||||||
#pragma alloc_text(PAGE, FspFsvolQueryFsAttributeInformation)
|
#pragma alloc_text(PAGE, FspFsvolQueryFsAttributeInformation)
|
||||||
#pragma alloc_text(PAGE, FspFsvolQueryFsDeviceInformation)
|
#pragma alloc_text(PAGE, FspFsvolQueryFsDeviceInformation)
|
||||||
#pragma alloc_text(PAGE, FspFsvolQueryFsFullSizeInformation)
|
#pragma alloc_text(PAGE, FspFsvolQueryFsFullSizeInformation)
|
||||||
|
#pragma alloc_text(PAGE, FspFsvolQueryFsSectorSizeInformation)
|
||||||
#pragma alloc_text(PAGE, FspFsvolQueryFsSizeInformation)
|
#pragma alloc_text(PAGE, FspFsvolQueryFsSizeInformation)
|
||||||
#pragma alloc_text(PAGE, FspFsvolQueryFsVolumeInformation)
|
#pragma alloc_text(PAGE, FspFsvolQueryFsVolumeInformation)
|
||||||
#pragma alloc_text(PAGE, FspFsvolQueryVolumeInformation)
|
#pragma alloc_text(PAGE, FspFsvolQueryVolumeInformation)
|
||||||
@ -186,6 +190,35 @@ static NTSTATUS FspFsvolQueryFsFullSizeInformation(
|
|||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static NTSTATUS FspFsvolQueryFsSectorSizeInformation(
|
||||||
|
PDEVICE_OBJECT FsvolDeviceObject, PUINT8 *PBuffer, PUINT8 BufferEnd,
|
||||||
|
const FSP_FSCTL_VOLUME_INFO *VolumeInfo)
|
||||||
|
{
|
||||||
|
PAGED_CODE();
|
||||||
|
|
||||||
|
if (*PBuffer + sizeof(FILE_FS_SECTOR_SIZE_INFORMATION) > BufferEnd)
|
||||||
|
return STATUS_BUFFER_TOO_SMALL;
|
||||||
|
|
||||||
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||||
|
PFILE_FS_SECTOR_SIZE_INFORMATION Info = (PFILE_FS_SECTOR_SIZE_INFORMATION)*PBuffer;
|
||||||
|
|
||||||
|
Info->LogicalBytesPerSector =
|
||||||
|
Info->PhysicalBytesPerSectorForAtomicity =
|
||||||
|
Info->PhysicalBytesPerSectorForPerformance =
|
||||||
|
Info->FileSystemEffectivePhysicalBytesPerSectorForAtomicity =
|
||||||
|
FsvolDeviceExtension->VolumeParams.SectorSize;
|
||||||
|
Info->Flags =
|
||||||
|
SSINFO_FLAGS_ALIGNED_DEVICE |
|
||||||
|
SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE |
|
||||||
|
SSINFO_FLAGS_NO_SEEK_PENALTY;
|
||||||
|
Info->ByteOffsetForSectorAlignment = 0;
|
||||||
|
Info->ByteOffsetForPartitionAlignment = 0;
|
||||||
|
|
||||||
|
*PBuffer += sizeof(FILE_FS_SECTOR_SIZE_INFORMATION);
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
static NTSTATUS FspFsvolQueryFsSizeInformation(
|
static NTSTATUS FspFsvolQueryFsSizeInformation(
|
||||||
PDEVICE_OBJECT FsvolDeviceObject, PUINT8 *PBuffer, PUINT8 BufferEnd,
|
PDEVICE_OBJECT FsvolDeviceObject, PUINT8 *PBuffer, PUINT8 BufferEnd,
|
||||||
const FSP_FSCTL_VOLUME_INFO *VolumeInfo)
|
const FSP_FSCTL_VOLUME_INFO *VolumeInfo)
|
||||||
@ -255,7 +288,7 @@ static NTSTATUS FspFsvolQueryVolumeInformation(
|
|||||||
|
|
||||||
NTSTATUS Result;
|
NTSTATUS Result;
|
||||||
PUINT8 Buffer = Irp->AssociatedIrp.SystemBuffer;
|
PUINT8 Buffer = Irp->AssociatedIrp.SystemBuffer;
|
||||||
PUINT8 BufferEnd = Buffer + IrpSp->Parameters.QueryFile.Length;
|
PUINT8 BufferEnd = Buffer + IrpSp->Parameters.QueryVolume.Length;
|
||||||
|
|
||||||
switch (IrpSp->Parameters.QueryVolume.FsInformationClass)
|
switch (IrpSp->Parameters.QueryVolume.FsInformationClass)
|
||||||
{
|
{
|
||||||
@ -268,6 +301,9 @@ static NTSTATUS FspFsvolQueryVolumeInformation(
|
|||||||
case FileFsFullSizeInformation:
|
case FileFsFullSizeInformation:
|
||||||
Result = FspFsvolQueryFsFullSizeInformation(FsvolDeviceObject, &Buffer, BufferEnd, 0);
|
Result = FspFsvolQueryFsFullSizeInformation(FsvolDeviceObject, &Buffer, BufferEnd, 0);
|
||||||
break;
|
break;
|
||||||
|
case FileFsSectorSizeInformation:
|
||||||
|
Result = FspFsvolQueryFsSectorSizeInformation(FsvolDeviceObject, &Buffer, BufferEnd, 0);
|
||||||
|
break;
|
||||||
case FileFsSizeInformation:
|
case FileFsSizeInformation:
|
||||||
Result = FspFsvolQueryFsSizeInformation(FsvolDeviceObject, &Buffer, BufferEnd, 0);
|
Result = FspFsvolQueryFsSizeInformation(FsvolDeviceObject, &Buffer, BufferEnd, 0);
|
||||||
break;
|
break;
|
||||||
@ -310,7 +346,7 @@ NTSTATUS FspFsvolQueryVolumeInformationComplete(
|
|||||||
|
|
||||||
PDEVICE_OBJECT FsvolDeviceObject = IrpSp->DeviceObject;
|
PDEVICE_OBJECT FsvolDeviceObject = IrpSp->DeviceObject;
|
||||||
PUINT8 Buffer = Irp->AssociatedIrp.SystemBuffer;
|
PUINT8 Buffer = Irp->AssociatedIrp.SystemBuffer;
|
||||||
PUINT8 BufferEnd = Buffer + IrpSp->Parameters.QueryFile.Length;
|
PUINT8 BufferEnd = Buffer + IrpSp->Parameters.QueryVolume.Length;
|
||||||
|
|
||||||
FspFsvolDeviceSetVolumeInfo(FsvolDeviceObject, &Response->Rsp.QueryVolumeInformation.VolumeInfo);
|
FspFsvolDeviceSetVolumeInfo(FsvolDeviceObject, &Response->Rsp.QueryVolumeInformation.VolumeInfo);
|
||||||
|
|
||||||
|
@ -34,6 +34,8 @@ NTSTATUS FspVolumeMount(
|
|||||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||||
static NTSTATUS FspVolumeMountNoLock(
|
static NTSTATUS FspVolumeMountNoLock(
|
||||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||||
|
NTSTATUS FspVolumeMakeMountdev(
|
||||||
|
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||||
NTSTATUS FspVolumeGetName(
|
NTSTATUS FspVolumeGetName(
|
||||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
|
||||||
NTSTATUS FspVolumeGetNameList(
|
NTSTATUS FspVolumeGetNameList(
|
||||||
@ -57,6 +59,7 @@ NTSTATUS FspVolumeWork(
|
|||||||
// ! #pragma alloc_text(PAGE, FspVolumeDeleteDelayed)
|
// ! #pragma alloc_text(PAGE, FspVolumeDeleteDelayed)
|
||||||
// ! #pragma alloc_text(PAGE, FspVolumeMount)
|
// ! #pragma alloc_text(PAGE, FspVolumeMount)
|
||||||
// ! #pragma alloc_text(PAGE, FspVolumeMountNoLock)
|
// ! #pragma alloc_text(PAGE, FspVolumeMountNoLock)
|
||||||
|
#pragma alloc_text(PAGE, FspVolumeMakeMountdev)
|
||||||
#pragma alloc_text(PAGE, FspVolumeGetName)
|
#pragma alloc_text(PAGE, FspVolumeGetName)
|
||||||
#pragma alloc_text(PAGE, FspVolumeGetNameList)
|
#pragma alloc_text(PAGE, FspVolumeGetNameList)
|
||||||
#pragma alloc_text(PAGE, FspVolumeGetNameListNoLock)
|
#pragma alloc_text(PAGE, FspVolumeGetNameListNoLock)
|
||||||
@ -278,8 +281,12 @@ static NTSTATUS FspVolumeCreateNoLock(
|
|||||||
if (NT_SUCCESS(Result))
|
if (NT_SUCCESS(Result))
|
||||||
{
|
{
|
||||||
if (0 != FsvrtDeviceObject)
|
if (0 != FsvrtDeviceObject)
|
||||||
|
{
|
||||||
|
FspFsvrtDeviceExtension(FsvrtDeviceObject)->SectorSize =
|
||||||
|
FsvolDeviceExtension->VolumeParams.SectorSize;
|
||||||
Result = FspDeviceInitialize(FsvrtDeviceObject);
|
Result = FspDeviceInitialize(FsvrtDeviceObject);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!NT_SUCCESS(Result))
|
if (!NT_SUCCESS(Result))
|
||||||
{
|
{
|
||||||
if (0 != FsvrtDeviceObject)
|
if (0 != FsvrtDeviceObject)
|
||||||
@ -321,10 +328,22 @@ VOID FspVolumeDelete(
|
|||||||
// !PAGED_CODE();
|
// !PAGED_CODE();
|
||||||
|
|
||||||
PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2;
|
PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2;
|
||||||
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject = FsvolDeviceExtension->FsvrtDeviceObject;
|
||||||
FSP_FILE_NODE **FileNodes;
|
FSP_FILE_NODE **FileNodes;
|
||||||
ULONG FileNodeCount, Index;
|
ULONG FileNodeCount, Index;
|
||||||
NTSTATUS Result;
|
NTSTATUS Result;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have an fsvrt that is a mountdev, finalize it now! Finalizing a mountdev
|
||||||
|
* involves interaction with the MountManager, which tries to open our devices.
|
||||||
|
* So if we delay this interaction and we do it during final fsvrt teardown (i.e.
|
||||||
|
* FspDeviceDelete time) we will fail such opens with STATUS_CANCELLED, which will
|
||||||
|
* confuse the MountManager.
|
||||||
|
*/
|
||||||
|
if (0 != FsvrtDeviceObject)
|
||||||
|
FspMountdevFini(FsvrtDeviceObject);
|
||||||
|
|
||||||
FspDeviceReference(FsvolDeviceObject);
|
FspDeviceReference(FsvolDeviceObject);
|
||||||
|
|
||||||
FspDeviceGlobalLock();
|
FspDeviceGlobalLock();
|
||||||
@ -543,6 +562,50 @@ static NTSTATUS FspVolumeMountNoLock(
|
|||||||
return STATUS_SUCCESS;
|
return STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NTSTATUS FspVolumeMakeMountdev(
|
||||||
|
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
||||||
|
{
|
||||||
|
PAGED_CODE();
|
||||||
|
|
||||||
|
ASSERT(IRP_MJ_FILE_SYSTEM_CONTROL == IrpSp->MajorFunction);
|
||||||
|
ASSERT(IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction);
|
||||||
|
ASSERT(FSP_FSCTL_MOUNTDEV == IrpSp->Parameters.FileSystemControl.FsControlCode);
|
||||||
|
ASSERT(0 != IrpSp->FileObject->FsContext2);
|
||||||
|
|
||||||
|
PDEVICE_OBJECT FsvolDeviceObject = IrpSp->FileObject->FsContext2;
|
||||||
|
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
|
||||||
|
PDEVICE_OBJECT FsvrtDeviceObject = FsvolDeviceExtension->FsvrtDeviceObject;
|
||||||
|
ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
|
||||||
|
ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
|
||||||
|
BOOLEAN Persistent = 0 < InputBufferLength ? !!*(PBOOLEAN)Irp->AssociatedIrp.SystemBuffer : FALSE;
|
||||||
|
NTSTATUS Result;
|
||||||
|
|
||||||
|
if (0 == FsvrtDeviceObject)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
if (sizeof(GUID) > OutputBufferLength)
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
|
||||||
|
FspDeviceGlobalLock();
|
||||||
|
|
||||||
|
Result = FspMountdevMake(FsvrtDeviceObject, FsvolDeviceObject, Persistent);
|
||||||
|
if (!NT_SUCCESS(Result))
|
||||||
|
{
|
||||||
|
if (STATUS_TOO_LATE != Result)
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,
|
||||||
|
&FspFsvrtDeviceExtension(FsvrtDeviceObject)->UniqueId, sizeof(GUID));
|
||||||
|
|
||||||
|
Irp->IoStatus.Information = sizeof(GUID);
|
||||||
|
Result = STATUS_SUCCESS;
|
||||||
|
|
||||||
|
exit:
|
||||||
|
FspDeviceGlobalUnlock();
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
NTSTATUS FspVolumeGetName(
|
NTSTATUS FspVolumeGetName(
|
||||||
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
|
||||||
{
|
{
|
||||||
|
@ -78,6 +78,10 @@ set opt_tests=^
|
|||||||
ifstest-memfs-x64-disk ^
|
ifstest-memfs-x64-disk ^
|
||||||
ifstest-memfs-x86-disk ^
|
ifstest-memfs-x86-disk ^
|
||||||
ifstest-memfs-dotnet-disk ^
|
ifstest-memfs-dotnet-disk ^
|
||||||
|
sample-memfs-fuse3-x64 ^
|
||||||
|
sample-fsx-memfs-fuse3-x64 ^
|
||||||
|
sample-memfs-fuse3-x86 ^
|
||||||
|
sample-fsx-memfs-fuse3-x86 ^
|
||||||
sample-airfs-x64 ^
|
sample-airfs-x64 ^
|
||||||
sample-airfs-x86 ^
|
sample-airfs-x86 ^
|
||||||
sample-passthrough-x64 ^
|
sample-passthrough-x64 ^
|
||||||
@ -675,6 +679,28 @@ for /F "delims=" %%l in ('call "%ProjRoot%\tools\ifstest.bat" %* /v ^| findstr /
|
|||||||
if not X!IfsTestFound!==XYES set IfsTestExit=1
|
if not X!IfsTestFound!==XYES set IfsTestExit=1
|
||||||
exit /b !IfsTestExit!
|
exit /b !IfsTestExit!
|
||||||
|
|
||||||
|
:sample-memfs-fuse3-x64
|
||||||
|
call :__run_sample_fuse_test memfs-fuse3 x64 memfs-fuse3-x64 winfsp-tests-x64 ^
|
||||||
|
"-create_fileattr_test -create_readonlydir_test -setfileinfo_test"
|
||||||
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:sample-memfs-fuse3-x86
|
||||||
|
call :__run_sample_fuse_test memfs-fuse3 x86 memfs-fuse3-x86 winfsp-tests-x86 ^
|
||||||
|
"-create_fileattr_test -create_readonlydir_test -setfileinfo_test"
|
||||||
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:sample-fsx-memfs-fuse3-x64
|
||||||
|
call :__run_sample_fsx_fuse_test memfs-fuse3 x64 memfs-fuse3-x64 fsx
|
||||||
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
|
:sample-fsx-memfs-fuse3-x86
|
||||||
|
call :__run_sample_fsx_fuse_test memfs-fuse3 x86 memfs-fuse3-x86 fsx
|
||||||
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
|
exit /b 0
|
||||||
|
|
||||||
:sample-airfs-x64
|
:sample-airfs-x64
|
||||||
call :__run_sample_disk_test airfs x64 airfs-x64 winfsp-tests-x64 NOEXCL
|
call :__run_sample_disk_test airfs x64 airfs-x64 winfsp-tests-x64 NOEXCL
|
||||||
if !ERRORLEVEL! neq 0 goto fail
|
if !ERRORLEVEL! neq 0 goto fail
|
||||||
|
1283
tst/airfs/airfs.cpp
1283
tst/airfs/airfs.cpp
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<ItemGroup Label="ProjectConfigurations">
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
<ProjectConfiguration Include="Debug|Win32">
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
@ -171,6 +171,10 @@
|
|||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="airfs.cpp" />
|
<ClCompile Include="airfs.cpp" />
|
||||||
|
<ClCompile Include="persistence.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="common.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Filter Include="Source">
|
<Filter Include="Source">
|
||||||
@ -10,5 +10,13 @@
|
|||||||
<ClCompile Include="airfs.cpp">
|
<ClCompile Include="airfs.cpp">
|
||||||
<Filter>Source</Filter>
|
<Filter>Source</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="persistence.cpp">
|
||||||
|
<Filter>Source</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="common.h">
|
||||||
|
<Filter>Source</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
210
tst/airfs/common.h
Normal file
210
tst/airfs/common.h
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
/**
|
||||||
|
* @file common.h
|
||||||
|
*
|
||||||
|
* @copyright 2015-2019 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.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Airfs is based on Memfs with changes contributed by John Oberschelp.
|
||||||
|
* The contributed changes are under joint copyright by Bill Zissimopoulos
|
||||||
|
* and John Oberschelp per the Contributor Agreement found at the
|
||||||
|
* root of this project.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <winfsp/winfsp.h>
|
||||||
|
#include <io.h>
|
||||||
|
#include <sddl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define PROGNAME "airfs"
|
||||||
|
#define ROUND_UP( bytes, units ) (((bytes) + (units) - 1) / (units) * (units))
|
||||||
|
#define ROUND_DOWN( bytes, units ) (((bytes) ) / (units) * (units))
|
||||||
|
#define MINIMUM_ALLOCSIZE 196
|
||||||
|
#define MAXIMUM_ALLOCSIZE ROUND_DOWN(10*1024*1024, MINIMUM_ALLOCSIZE)
|
||||||
|
#define SECTOR_SIZE 512
|
||||||
|
#define SECTORS_PER_ALLOCATION_UNIT 1
|
||||||
|
#define ALLOCATION_UNIT ( SECTOR_SIZE * SECTORS_PER_ALLOCATION_UNIT )
|
||||||
|
#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__)
|
||||||
|
#define AIRFS_MAX_PATH 512
|
||||||
|
#define FILEBLOCK_OVERHEAD 40 // size of ( P + E + L + R + FileOffset ) = 8 * 5 = 40
|
||||||
|
#define ARG_TO_S(v) if (arge > ++argp) v = *argp; else goto usage
|
||||||
|
#define ARG_TO_4(v) if (arge > ++argp) v = (int32_t) wcstoll_default(*argp, v); else goto usage
|
||||||
|
#define ARG_TO_8(v) if (arge > ++argp) v = wcstoll_default(*argp, v); else goto usage
|
||||||
|
|
||||||
|
enum StorageFileAccessType {ZERO=0,READ,WRITE};
|
||||||
|
enum Neighbor {LT=-2,LE=-1,EQ=0,GE=1,GT=2};
|
||||||
|
|
||||||
|
struct NODE;
|
||||||
|
typedef NODE* NODE_;
|
||||||
|
|
||||||
|
typedef int CompareFunction (void* key, NODE_);
|
||||||
|
|
||||||
|
inline NTSTATUS GetLastErrorAsStatus()
|
||||||
|
{
|
||||||
|
return FspNtStatusFromWin32(GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline UINT64 SystemTime()
|
||||||
|
{
|
||||||
|
FILETIME FileTime;
|
||||||
|
GetSystemTimeAsFileTime(&FileTime);
|
||||||
|
return ((PLARGE_INTEGER)&FileTime)->QuadPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t wcstoll_default(wchar_t *w, int64_t deflt)
|
||||||
|
{
|
||||||
|
wchar_t *endp;
|
||||||
|
int64_t i = wcstoll(w, &endp, 0);
|
||||||
|
return L'\0' != w[0] && L'\0' == *endp ? i : deflt;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Where<T> Class: This class manages an offset within our memory-mapped
|
||||||
|
// volume to another location within our memory-mapped volume. Because it is
|
||||||
|
// a self-relative offset, this delta is constant regardless of where in
|
||||||
|
// memory the file system is mapped, so we can always reobtain its address.
|
||||||
|
// A delta of 0 is the special case for "null".
|
||||||
|
//
|
||||||
|
|
||||||
|
template <class T> class Where
|
||||||
|
{
|
||||||
|
int64_t delta;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Where() = default;
|
||||||
|
~Where() = default;
|
||||||
|
Where(T t) : delta( t ?( (char*)t -(char*)this ):0) {}
|
||||||
|
Where(Where& w) : delta( w.delta?( ((char*)&w+w.delta)-(char*)this ):0) {}
|
||||||
|
|
||||||
|
operator bool () { return delta != 0; }
|
||||||
|
operator T () { return (T) ( delta?( (char*)this+delta ):0); }
|
||||||
|
T operator -> () { return (T) ( delta?( (char*)this+delta ):0); }
|
||||||
|
operator void* () { return (void*)( delta?( (char*)this+delta ):0); }
|
||||||
|
|
||||||
|
bool operator == (Where& rhs) { return (char*)this+delta == (char*)&rhs+rhs.delta; }
|
||||||
|
bool operator != (Where& rhs) { return (char*)this+delta != (char*)&rhs+rhs.delta; }
|
||||||
|
bool operator == (T rhs) { return (char*)this+delta == (char*)rhs; }
|
||||||
|
bool operator != (T rhs) { return (char*)this+delta != (char*)rhs; }
|
||||||
|
|
||||||
|
Where& operator = (Where& rhs) { delta = rhs.delta?( ((char*)&rhs+rhs.delta) - ((char*)this) ):0; return *this; }
|
||||||
|
Where& operator = (void* rhs) { delta = rhs ?( (char*)rhs - ((char*)this) ):0; return *this; }
|
||||||
|
|
||||||
|
char* Address () { return (char*)this+delta; }
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// The header for an Airfs volume
|
||||||
|
//
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char Signature[8]; // Airfs\0\0\0
|
||||||
|
char MapFormatVersion[4]; // Major.Minor.Patch.Build
|
||||||
|
char filler[4];
|
||||||
|
Where<NODE_> Root;
|
||||||
|
Where<NODE_> Available;
|
||||||
|
UINT64 VolumeSize;
|
||||||
|
UINT64 FreeSize;
|
||||||
|
WCHAR VolumeLabel[32];
|
||||||
|
UINT16 VolumeLabelLength;
|
||||||
|
UINT16 filler1,filler2,filler3;
|
||||||
|
UINT32 CaseInsensitive;
|
||||||
|
UINT32 filler4;
|
||||||
|
int64_t VolumeLength;
|
||||||
|
FSP_FSCTL_VOLUME_PARAMS VolumeParams;
|
||||||
|
FSP_FILE_SYSTEM *FileSystem;
|
||||||
|
HANDLE MapFileHandle;
|
||||||
|
HANDLE MapHandle;
|
||||||
|
} AIRFS, *AIRFS_;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Information per file or directory
|
||||||
|
//
|
||||||
|
struct NODE
|
||||||
|
{
|
||||||
|
Where<NODE_> P,L,R,E; // Sorted sibling tree: Parent, Left, Right, and Equal
|
||||||
|
union
|
||||||
|
{
|
||||||
|
Where<WCHAR*> Name;
|
||||||
|
int64_t FileOffset;
|
||||||
|
};
|
||||||
|
Where<NODE_> Parent;
|
||||||
|
Where<NODE_> Children;
|
||||||
|
FSP_FSCTL_FILE_INFO FileInfo;
|
||||||
|
uint64_t SecurityDescriptorSize;
|
||||||
|
Where<char*> SecurityDescriptor;
|
||||||
|
Where<NODE_> FileBlocks;
|
||||||
|
uint64_t ReparseDataSize;
|
||||||
|
Where<char*> ReparseData;
|
||||||
|
volatile LONG RefCount;
|
||||||
|
Where<NODE_> Streams;
|
||||||
|
BOOLEAN IsAStream;
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class SpinLock
|
||||||
|
{
|
||||||
|
LONG C; // Counter
|
||||||
|
HANDLE S; // Semaphore
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
SpinLock() { C = 0; S = CreateSemaphore(NULL, 0, 1, NULL); }
|
||||||
|
~SpinLock() { CloseHandle(S); }
|
||||||
|
|
||||||
|
void Acquire() { if (_InterlockedIncrement(&C) > 1) WaitForSingleObject(S, INFINITE); }
|
||||||
|
void Release() { if (_InterlockedDecrement(&C) > 0) ReleaseSemaphore(S, 1, NULL); }
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void Airprint (const char * format, ...);
|
||||||
|
|
||||||
|
int SizeCmp (void* key, NODE_);
|
||||||
|
int ExactNameCmp (void* key, NODE_);
|
||||||
|
int CaselessNameCmp (void* key, NODE_);
|
||||||
|
|
||||||
|
NODE_ Find (Where<NODE_> &root, void* key, CompareFunction);
|
||||||
|
NODE_ Near (Where<NODE_> &root, void* key, CompareFunction, Neighbor);
|
||||||
|
void Attach (Where<NODE_> &root, NODE_ attach, CompareFunction, void* key);
|
||||||
|
void Detach (Where<NODE_> &root, NODE_ detach);
|
||||||
|
NODE_ First (NODE_ start);
|
||||||
|
NODE_ Last (NODE_ start);
|
||||||
|
NODE_ Next (NODE_);
|
||||||
|
NODE_ Prev (NODE_);
|
||||||
|
|
||||||
|
NTSTATUS StorageStartup (AIRFS_ &, WCHAR* MapName, WCHAR* StorageFileName, int64_t Length);
|
||||||
|
NTSTATUS StorageShutdown (AIRFS_);
|
||||||
|
void* StorageAllocate (AIRFS_, int64_t RequestedSize);
|
||||||
|
void* StorageReallocate (AIRFS_, void* Reallocate, int64_t RequestedSize);
|
||||||
|
void StorageFree (AIRFS_, void* Release);
|
||||||
|
NTSTATUS StorageSetFileCapacity (AIRFS_, NODE_, int64_t MinimumRequiredCapacity);
|
||||||
|
void StorageAccessFile (StorageFileAccessType, NODE_, int64_t Offset, int64_t NumBytes, char* Address);
|
||||||
|
|
||||||
|
static_assert(AIRFS_MAX_PATH > MAX_PATH, "AIRFS_MAX_PATH must be greater than MAX_PATH.");
|
||||||
|
static_assert(sizeof NODE + sizeof int32_t == MINIMUM_ALLOCSIZE, "MINIMUM_ALLOCSIZE should be 196.");
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
594
tst/airfs/persistence.cpp
Normal file
594
tst/airfs/persistence.cpp
Normal file
@ -0,0 +1,594 @@
|
|||||||
|
/**
|
||||||
|
* @file persistence.cpp
|
||||||
|
*
|
||||||
|
* @copyright 2015-2019 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.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Airfs is based on Memfs with changes contributed by John Oberschelp.
|
||||||
|
* The contributed changes are under joint copyright by Bill Zissimopoulos
|
||||||
|
* and John Oberschelp per the Contributor Agreement found at the
|
||||||
|
* root of this project.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Airfs uses a memory-mapped file per volume to achieve persistence.
|
||||||
|
* The primary advantage of this is that volume loads and saves are automatic.
|
||||||
|
* The two primary disadvantages, and our workarounds are:
|
||||||
|
* 1. We can't use standard containers or memory management,
|
||||||
|
* so the below Rubbertree and Storage functions are used instead.
|
||||||
|
* 2. Each process will map the volume to an arbitrary address,
|
||||||
|
* so Where<T> offsets are used in place of pointers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
SpinLock StorageLock, AirprintLock, SetLock;
|
||||||
|
|
||||||
|
int SizeCmp ( void* key, NODE_ x)
|
||||||
|
{
|
||||||
|
return *(int32_t*)key - ((int32_t*)x)[-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
int BlockCmp ( void* key, NODE_ x)
|
||||||
|
{
|
||||||
|
int64_t left = *(int64_t*)key;
|
||||||
|
int64_t right = x->FileOffset;
|
||||||
|
return left == right ? 0 : (left < right ? -1 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CaselessNameCmp ( void* key, NODE_ x)
|
||||||
|
{
|
||||||
|
WCHAR* c1 = (WCHAR*) key;
|
||||||
|
WCHAR* c2 = x->Name;
|
||||||
|
return _wcsicmp(c1,c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ExactNameCmp ( void* key, NODE_ x)
|
||||||
|
{
|
||||||
|
WCHAR* c1 = (WCHAR*) key;
|
||||||
|
WCHAR* c2 = x->Name;
|
||||||
|
return wcscmp(c1,c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void Airprint (const char * format, ...)
|
||||||
|
{
|
||||||
|
AirprintLock.Acquire();
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
char szBuffer[512];
|
||||||
|
sprintf_s(szBuffer, 511, "Airfs %5.5f ----", SystemTime() / 10'000'000.0);
|
||||||
|
vsnprintf(szBuffer+25, 511-25, format, args);
|
||||||
|
OutputDebugStringA(szBuffer);
|
||||||
|
va_end(args);
|
||||||
|
AirprintLock.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Rubbertree (because it is flexible!)
|
||||||
|
// Implements a sorted set of elements, using a binary tree.
|
||||||
|
// Has a function, Near, that finds nodes at or adjacent to a key.
|
||||||
|
// Attach, Find, and Near use splay to improve random access times.
|
||||||
|
// First, Last, Next, and Prev do not, to improve sequential access times.
|
||||||
|
// Replacing each Where<NODE_> with NODE_ would make this a pointer-based tree.
|
||||||
|
// In addition to Left, Right, and Parent references, each node has an Equal
|
||||||
|
// reference that may be used to keep "equivalent" nodes. This is used by
|
||||||
|
// our memory heap manager's tree of available memory blocks, sorted by size.
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
inline void rotateL(Where<NODE_> &root, NODE_ x)
|
||||||
|
{
|
||||||
|
NODE_ y = x->R, p = x->P;
|
||||||
|
if (x->R = y->L) y->L->P = x;
|
||||||
|
if (!(y->P = p))
|
||||||
|
root = y;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (x == p->L) p->L = y;
|
||||||
|
else p->R = y;
|
||||||
|
}
|
||||||
|
(y->L = x)->P = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
inline void rotateR(Where<NODE_> &root, NODE_ y)
|
||||||
|
{
|
||||||
|
NODE_ x = y->L, p = y->P;
|
||||||
|
if (y->L = x->R) x->R->P = y;
|
||||||
|
if (!(x->P = p))
|
||||||
|
root = x;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (y == p->L) p->L = x;
|
||||||
|
else p->R = x;
|
||||||
|
}
|
||||||
|
(x->R = y)->P = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void splay(Where<NODE_> &root, NODE_ x)
|
||||||
|
{
|
||||||
|
while (NODE_ p = x->P)
|
||||||
|
{
|
||||||
|
if (!p->P)
|
||||||
|
{
|
||||||
|
if (p->L == x) rotateR(root, p);
|
||||||
|
else rotateL(root, p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (p == p->P->R)
|
||||||
|
{
|
||||||
|
if (p->R == x) rotateL(root, p->P);
|
||||||
|
else rotateR(root, p);
|
||||||
|
rotateL(root, x->P);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (p->L == x) rotateR(root, p->P);
|
||||||
|
else rotateL(root, p);
|
||||||
|
rotateR(root, x->P);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
inline int seek(Where<NODE_> &root, NODE_ &x, void* key, CompareFunction CMP)
|
||||||
|
{
|
||||||
|
x = root;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
int diff = CMP(key, x);
|
||||||
|
if (diff < 0)
|
||||||
|
{
|
||||||
|
if (!x->L) return -1;
|
||||||
|
x = x->L;
|
||||||
|
}
|
||||||
|
else if (diff > 0)
|
||||||
|
{
|
||||||
|
if (!x->R) return 1;
|
||||||
|
x = x->R;
|
||||||
|
}
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
inline NODE_ next(NODE_ x)
|
||||||
|
{
|
||||||
|
if (x->R) { x = x->R; while (x->L) x = x->L; return x; }
|
||||||
|
NODE_ p = x->P;
|
||||||
|
while (p && x == p->R) { x = p; p = p->P; }
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
inline NODE_ prev(NODE_ x)
|
||||||
|
{
|
||||||
|
if (x->L) { x = x->L; while (x->R) x = x->R; return x; }
|
||||||
|
NODE_ p = x->P;
|
||||||
|
while (p && x == p->L) { x = p; p = p->P; }
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
NODE_ First(NODE_ x)
|
||||||
|
{
|
||||||
|
SetLock.Acquire();
|
||||||
|
if (x) while (x->L) x = x->L;
|
||||||
|
SetLock.Release();
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
NODE_ Last(NODE_ x)
|
||||||
|
{
|
||||||
|
SetLock.Acquire();
|
||||||
|
if (x) while (x->R) x = x->R;
|
||||||
|
SetLock.Release();
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
NODE_ Next(NODE_ x)
|
||||||
|
{
|
||||||
|
SetLock.Acquire();
|
||||||
|
x = next(x);
|
||||||
|
SetLock.Release();
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
NODE_ Prev(NODE_ x)
|
||||||
|
{
|
||||||
|
SetLock.Acquire();
|
||||||
|
x = prev(x);
|
||||||
|
SetLock.Release();
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
NODE_ Near(Where<NODE_> &root, void* key, CompareFunction CMP, Neighbor want)
|
||||||
|
{
|
||||||
|
// Return a node relative to (just <, <=, ==, >=, or >) a key.
|
||||||
|
if (!root) return 0;
|
||||||
|
SetLock.Acquire();
|
||||||
|
NODE_ x;
|
||||||
|
int dir = seek(root, x, key, CMP);
|
||||||
|
if ((dir == 0 && want == GT) || (dir > 0 && want >= GE)) x = next(x);
|
||||||
|
else
|
||||||
|
if ((dir == 0 && want == LT) || (dir < 0 && want <= LE)) x = prev(x);
|
||||||
|
else
|
||||||
|
if (dir != 0 && want == EQ) x = 0;
|
||||||
|
if (x) splay(root, x);
|
||||||
|
SetLock.Release();
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
NODE_ Find(Where<NODE_> &root, void* key, CompareFunction CMP)
|
||||||
|
{
|
||||||
|
if (!root) return 0;
|
||||||
|
SetLock.Acquire();
|
||||||
|
NODE_ x;
|
||||||
|
int direction = seek(root, x, key, CMP);
|
||||||
|
splay(root, x);
|
||||||
|
SetLock.Release();
|
||||||
|
return direction?0:x;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
void Attach(Where<NODE_> &root, NODE_ x, CompareFunction CMP, void* key)
|
||||||
|
{
|
||||||
|
SetLock.Acquire();
|
||||||
|
if (!root)
|
||||||
|
{
|
||||||
|
root = x;
|
||||||
|
x->P = x->L = x->R = x->E = 0;
|
||||||
|
SetLock.Release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NODE_ f;
|
||||||
|
int diff = seek(root, f, key, CMP);
|
||||||
|
if (!diff)
|
||||||
|
{
|
||||||
|
if (x->L = f->L) x->L->P = x;
|
||||||
|
if (x->R = f->R) x->R->P = x;
|
||||||
|
NODE_ p = f->P;
|
||||||
|
if (x->P = p) { if (p->L == f) p->L = x; else p->R = x; }
|
||||||
|
else root = x;
|
||||||
|
(x->E = f)->P = x;
|
||||||
|
f->L = f->R = 0;
|
||||||
|
splay(root, x);
|
||||||
|
SetLock.Release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (diff < 0) f->L = x; else f->R = x;
|
||||||
|
x->P = f;
|
||||||
|
x->L = x->R = x->E = 0;
|
||||||
|
splay(root, x);
|
||||||
|
SetLock.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
void Detach(Where<NODE_> &root, NODE_ x)
|
||||||
|
{
|
||||||
|
SetLock.Acquire();
|
||||||
|
NODE_ e = x->E, p = x->P;
|
||||||
|
if (p && p->E == x) { if (p->E = e) e->P = p; }
|
||||||
|
else if (e)
|
||||||
|
{
|
||||||
|
if (e->L = x->L) e->L->P = e;
|
||||||
|
if (e->R = x->R) e->R->P = e;
|
||||||
|
if (e->P = p) { if (p->L == x) p->L = e; else p->R = e; }
|
||||||
|
else root = e;
|
||||||
|
}
|
||||||
|
else if (!x->L)
|
||||||
|
{
|
||||||
|
if (p) { if ( p->L == x) p->L = x->R; else p->R = x->R; }
|
||||||
|
else root = x->R;
|
||||||
|
if (x->R) x->R->P = p;
|
||||||
|
}
|
||||||
|
else if (!x->R)
|
||||||
|
{
|
||||||
|
if (p) { if ( p->L == x) p->L = x->L; else p->R = x->L; }
|
||||||
|
else root = x->L;
|
||||||
|
if (x->L) x->L->P = p;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
e = x->L;
|
||||||
|
if (e->R)
|
||||||
|
{
|
||||||
|
do { e = e->R; } while (e->R);
|
||||||
|
if (e->P->R = e->L) e->L->P = e->P;
|
||||||
|
(e->L = x->L)->P = e;
|
||||||
|
}
|
||||||
|
(e->R = x->R)->P = e;
|
||||||
|
if (e->P = x->P) { if (e->P->L == x) e->P->L = e; else e->P->R = e; }
|
||||||
|
else root = e;
|
||||||
|
}
|
||||||
|
SetLock.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Storage Functions for our memory-mapped file-based persistent volumes
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
void* StorageAllocate(AIRFS_ Airfs, int64_t RequestedSize)
|
||||||
|
{
|
||||||
|
if (!RequestedSize) return 0;
|
||||||
|
if (RequestedSize + sizeof int32_t > MAXIMUM_ALLOCSIZE) return 0;
|
||||||
|
|
||||||
|
StorageLock.Acquire();
|
||||||
|
int32_t RoundedSize = (int32_t) ROUND_UP(RequestedSize, MINIMUM_ALLOCSIZE - sizeof int32_t);
|
||||||
|
int32_t SplitableSize = RoundedSize + MINIMUM_ALLOCSIZE;
|
||||||
|
|
||||||
|
// See if we have a freed node of the size we requested.
|
||||||
|
NODE_ NewItem = Near(Airfs->Available, &RoundedSize, SizeCmp, GE);
|
||||||
|
if (NewItem)
|
||||||
|
{
|
||||||
|
int32_t FoundSize = ((int32_t*)NewItem)[-1];
|
||||||
|
if (FoundSize < SplitableSize)
|
||||||
|
{
|
||||||
|
Detach(Airfs->Available, NewItem);
|
||||||
|
Airfs->FreeSize -= FoundSize;
|
||||||
|
StorageLock.Release();
|
||||||
|
return NewItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not, see if we can downsize a larger freed element.
|
||||||
|
NewItem = Near(Airfs->Available, &SplitableSize, SizeCmp, GE);
|
||||||
|
if (NewItem)
|
||||||
|
{
|
||||||
|
int32_t FoundSize = ((int32_t*)NewItem)[-1];
|
||||||
|
Detach(Airfs->Available, NewItem);
|
||||||
|
Airfs->FreeSize -= FoundSize;
|
||||||
|
char* Addr = (char*)NewItem + RoundedSize + sizeof int32_t;
|
||||||
|
NODE_ Remainder = (NODE_) Addr;
|
||||||
|
int32_t RemainderSize = FoundSize - (RoundedSize + sizeof int32_t);
|
||||||
|
((int32_t*)Remainder)[-1] = RemainderSize;
|
||||||
|
Attach(Airfs->Available, Remainder, SizeCmp, &RemainderSize);
|
||||||
|
Airfs->FreeSize += RemainderSize;
|
||||||
|
((int32_t*)NewItem)[-1] = RoundedSize;
|
||||||
|
StorageLock.Release();
|
||||||
|
return NewItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not, give up.
|
||||||
|
StorageLock.Release();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
void* StorageReallocate(AIRFS_ Airfs, void* OldAlloc, int64_t RequestedSize)
|
||||||
|
{
|
||||||
|
if (!OldAlloc)
|
||||||
|
{
|
||||||
|
return StorageAllocate(Airfs, RequestedSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!RequestedSize)
|
||||||
|
{
|
||||||
|
StorageFree(Airfs, OldAlloc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t OldSize = ((int32_t*)OldAlloc)[-1];
|
||||||
|
void* NewAlloc = StorageAllocate(Airfs, RequestedSize);
|
||||||
|
if (!NewAlloc) return 0;
|
||||||
|
memcpy(NewAlloc, OldAlloc, min(RequestedSize, OldSize));
|
||||||
|
StorageFree(Airfs, OldAlloc);
|
||||||
|
return NewAlloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
void StorageFree(AIRFS_ Airfs, void* r)
|
||||||
|
{
|
||||||
|
if (!r) return;
|
||||||
|
StorageLock.Acquire();
|
||||||
|
NODE_ release = (NODE_) r;
|
||||||
|
int32_t Size = ((int32_t*)r)[-1];
|
||||||
|
Attach(Airfs->Available, release, SizeCmp, &Size);
|
||||||
|
Airfs->FreeSize += Size;
|
||||||
|
StorageLock.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
void StorageAccessFile(StorageFileAccessType Type, NODE_ Node, int64_t AccessOffset, int64_t NumBytes, char* MemoryAddress)
|
||||||
|
{
|
||||||
|
StorageLock.Acquire();
|
||||||
|
|
||||||
|
NODE_ Block = Near(Node->FileBlocks, &AccessOffset, BlockCmp, LE);
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
int32_t BlockSize = ((int32_t*)Block)[-1];
|
||||||
|
int64_t BlockOffset = Block->FileOffset;
|
||||||
|
int64_t BlockIndex = AccessOffset - BlockOffset + FILEBLOCK_OVERHEAD;
|
||||||
|
int64_t BlockNum = min(BlockSize-BlockIndex, NumBytes);
|
||||||
|
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case ZERO : { memset((char*)Block + BlockIndex, 0, BlockNum); break; }
|
||||||
|
case READ : { memcpy(MemoryAddress, (char*)Block + BlockIndex, BlockNum); break; }
|
||||||
|
case WRITE : { memcpy((char*)Block + BlockIndex, MemoryAddress, BlockNum); break; }
|
||||||
|
}
|
||||||
|
NumBytes -= BlockNum;
|
||||||
|
if (!NumBytes) break;
|
||||||
|
MemoryAddress += BlockNum;
|
||||||
|
AccessOffset += BlockNum;
|
||||||
|
Block = Next(Block);
|
||||||
|
}
|
||||||
|
|
||||||
|
StorageLock.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
NTSTATUS StorageSetFileCapacity(AIRFS_ Airfs, NODE_ Node, int64_t minimumRequiredCapacity)
|
||||||
|
{
|
||||||
|
StorageLock.Acquire();
|
||||||
|
|
||||||
|
int64_t TargetCapacity = ROUND_UP(minimumRequiredCapacity, ALLOCATION_UNIT);
|
||||||
|
NODE_ Block = Last(Node->FileBlocks);
|
||||||
|
int32_t BlockSize = Block ? ((int32_t*)Block)[-1] : 0;
|
||||||
|
int64_t CurrentCapacity = Block ? Block->FileOffset + BlockSize - FILEBLOCK_OVERHEAD: 0;
|
||||||
|
int64_t Add = TargetCapacity - CurrentCapacity;
|
||||||
|
|
||||||
|
while (Add > 0)
|
||||||
|
{
|
||||||
|
// Add a block if we can, preferably as large or larger than we need.
|
||||||
|
Add += FILEBLOCK_OVERHEAD;
|
||||||
|
Block = Near(Airfs->Available, &Add, SizeCmp, GE);
|
||||||
|
if (!Block) Block = Near(Airfs->Available, &Add, SizeCmp, LT);
|
||||||
|
Add -= FILEBLOCK_OVERHEAD;
|
||||||
|
if (Block)
|
||||||
|
{
|
||||||
|
Detach(Airfs->Available, Block);
|
||||||
|
BlockSize = ((int32_t*)Block)[-1];
|
||||||
|
Airfs->FreeSize -= BlockSize;
|
||||||
|
Block->FileOffset = CurrentCapacity;
|
||||||
|
Attach(Node->FileBlocks, Block, BlockCmp, &CurrentCapacity);
|
||||||
|
CurrentCapacity += BlockSize - FILEBLOCK_OVERHEAD;
|
||||||
|
Add -= BlockSize - FILEBLOCK_OVERHEAD;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
StorageLock.Release();
|
||||||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Throw away any trailing blocks that are no longer needed.
|
||||||
|
while (Add < 0)
|
||||||
|
{
|
||||||
|
Block = Last(Node->FileBlocks);
|
||||||
|
BlockSize = ((int32_t*)Block)[-1];
|
||||||
|
if (BlockSize - FILEBLOCK_OVERHEAD > -Add) break;
|
||||||
|
Add += BlockSize - FILEBLOCK_OVERHEAD;
|
||||||
|
Detach(Node->FileBlocks, Block);
|
||||||
|
Attach(Airfs->Available, Block, SizeCmp, &BlockSize);
|
||||||
|
Airfs->FreeSize += BlockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Possibly downsize the last block.
|
||||||
|
if (Add < 0)
|
||||||
|
{
|
||||||
|
Block = Last(Node->FileBlocks);
|
||||||
|
int32_t OldBlockSize = ((int32_t*)Block)[-1];
|
||||||
|
int32_t NewBlockSize = OldBlockSize - (int32_t) ROUND_DOWN(-Add, MINIMUM_ALLOCSIZE);
|
||||||
|
if (NewBlockSize < MINIMUM_ALLOCSIZE) NewBlockSize = MINIMUM_ALLOCSIZE;
|
||||||
|
int32_t RemainderBlockSize = OldBlockSize - NewBlockSize - sizeof int32_t;
|
||||||
|
if (RemainderBlockSize >= MINIMUM_ALLOCSIZE) // i.e. if not too near the end
|
||||||
|
{
|
||||||
|
char* Addr = (char*)Block + NewBlockSize + sizeof int32_t;
|
||||||
|
NODE_ Remainder = (NODE_) Addr;
|
||||||
|
((int32_t*)Remainder)[-1] = RemainderBlockSize;
|
||||||
|
Attach(Airfs->Available, Remainder, SizeCmp, &RemainderBlockSize);
|
||||||
|
Airfs->FreeSize += RemainderBlockSize;
|
||||||
|
((int32_t*)Block)[-1] = NewBlockSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StorageLock.Release();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
NTSTATUS StorageStartup(AIRFS_ &Airfs, WCHAR* MapName, WCHAR* StorageFileName, int64_t VolumeLength)
|
||||||
|
{
|
||||||
|
HANDLE MapFileHandle = INVALID_HANDLE_VALUE;
|
||||||
|
Airfs = 0;
|
||||||
|
|
||||||
|
// Open.
|
||||||
|
if (*StorageFileName)
|
||||||
|
{
|
||||||
|
MapFileHandle = CreateFileW(StorageFileName, GENERIC_READ|GENERIC_WRITE|GENERIC_EXECUTE, 0, NULL, OPEN_ALWAYS, NULL, NULL);
|
||||||
|
if (MapFileHandle == INVALID_HANDLE_VALUE) return GetLastErrorAsStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map.
|
||||||
|
HANDLE MapHandle = CreateFileMappingW(MapFileHandle, NULL, PAGE_EXECUTE_READWRITE, VolumeLength>>32, VolumeLength & 0xFFFFFFFF, MapName);
|
||||||
|
if (!MapHandle) return GetLastErrorAsStatus();
|
||||||
|
|
||||||
|
// Point.
|
||||||
|
char* MappedAddress = (char*) MapViewOfFile(MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, VolumeLength);
|
||||||
|
if (!MappedAddress) return GetLastErrorAsStatus();
|
||||||
|
|
||||||
|
// Keep.
|
||||||
|
Airfs = (AIRFS_) MappedAddress;
|
||||||
|
Airfs->MapFileHandle = MapFileHandle;
|
||||||
|
Airfs->MapHandle = MapHandle;
|
||||||
|
Airfs->VolumeLength = VolumeLength;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------
|
||||||
|
|
||||||
|
NTSTATUS StorageShutdown(AIRFS_ Airfs)
|
||||||
|
{
|
||||||
|
BOOL Ok;
|
||||||
|
NTSTATUS Result = 0;
|
||||||
|
HANDLE M = Airfs->MapHandle;
|
||||||
|
HANDLE F = Airfs->MapFileHandle;
|
||||||
|
|
||||||
|
Ok = FlushViewOfFile(Airfs, 0); if (!Ok && !Result) Result = GetLastErrorAsStatus();
|
||||||
|
if (F != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
Ok = FlushFileBuffers(F); if (!Ok && !Result) Result = GetLastErrorAsStatus();
|
||||||
|
// TODO: Set and write a flag something like Airfs->UpdatesCompleted here?
|
||||||
|
}
|
||||||
|
Ok = UnmapViewOfFile(Airfs); if (!Ok && !Result) Result = GetLastErrorAsStatus();
|
||||||
|
|
||||||
|
if (M)
|
||||||
|
{
|
||||||
|
Ok = CloseHandle(M); if (!Ok && !Result) Result = GetLastErrorAsStatus();
|
||||||
|
}
|
||||||
|
if (F != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
Ok = CloseHandle(F); if (!Ok && !Result) Result = GetLastErrorAsStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
10
tst/memfs-fuse3/.gitignore
vendored
Normal file
10
tst/memfs-fuse3/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
build
|
||||||
|
*.ncb
|
||||||
|
*.suo
|
||||||
|
*.vcproj.*
|
||||||
|
*.vcxproj.user
|
||||||
|
*.VC.db
|
||||||
|
*.VC.opendb
|
||||||
|
.vs
|
||||||
|
*.exe
|
||||||
|
*.install
|
18
tst/memfs-fuse3/Makefile
Normal file
18
tst/memfs-fuse3/Makefile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
usage:
|
||||||
|
@echo "make cygfuse3|winfsp-fuse3" 1>&2
|
||||||
|
@echo "" 1>&2
|
||||||
|
@echo " cygfuse3 Link with CYGFUSE3" 1>&2
|
||||||
|
@echo " winfsp-fuse3 Link with WinFsp-FUSE3" 1>&2
|
||||||
|
@exit 2
|
||||||
|
|
||||||
|
cygfuse3: memfs-cygfuse3
|
||||||
|
|
||||||
|
winfsp-fuse3: memfs-winfsp-fuse3
|
||||||
|
|
||||||
|
memfs-cygfuse3: memfs-fuse3.cpp
|
||||||
|
g++ $^ -o $@ -g -Wall `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`
|
7
tst/memfs-fuse3/README.md
Normal file
7
tst/memfs-fuse3/README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
`Memfs-fuse3` is an in-memory FUSE3 file system.
|
||||||
|
|
||||||
|
It can be built with the following tools:
|
||||||
|
|
||||||
|
- Using Visual Studio (`memfs-fuse3.sln`).
|
||||||
|
- Using Cygwin GCC and linking directly with the WinFsp DLL (`make winfsp-fuse3`).
|
||||||
|
- Using Cygwin GCC and linking to CYGFUSE3 (`make cygfuse3`).
|
104
tst/memfs-fuse3/compat.h
Normal file
104
tst/memfs-fuse3/compat.h
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/**
|
||||||
|
* @file compat.h
|
||||||
|
*
|
||||||
|
* @copyright 2015-2019 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COMPAT_H_INCLUDED
|
||||||
|
#define COMPAT_H_INCLUDED
|
||||||
|
|
||||||
|
#if defined(_WIN32) && defined(FSP_FUSE_SYM)
|
||||||
|
#include <winfsp/winfsp.h>
|
||||||
|
#undef fuse_main
|
||||||
|
#define fuse_main(argc, argv, ops, data)\
|
||||||
|
(FspLoad(0), fuse_main_real(argc, argv, ops, sizeof *(ops), data))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_WIN32) && !defined(fuse_stat)
|
||||||
|
|
||||||
|
#define fuse_uid_t uid_t
|
||||||
|
#define fuse_gid_t gid_t
|
||||||
|
#define fuse_pid_t pid_t
|
||||||
|
|
||||||
|
#define fuse_dev_t dev_t
|
||||||
|
#define fuse_mode_t mode_t
|
||||||
|
#define fuse_nlink_t nlink_t
|
||||||
|
#define fuse_off_t off_t
|
||||||
|
|
||||||
|
#define fuse_fsblkcnt_t fsblkcnt_t
|
||||||
|
#define fuse_fsfilcnt_t fsfilcnt_t
|
||||||
|
#define fuse_blksize_t blksize_t
|
||||||
|
#define fuse_blkcnt_t blkcnt_t
|
||||||
|
|
||||||
|
#define fuse_timespec timespec
|
||||||
|
|
||||||
|
#define fuse_stat stat
|
||||||
|
|
||||||
|
#define fuse_statvfs statvfs
|
||||||
|
|
||||||
|
#define fuse_flock flock
|
||||||
|
|
||||||
|
#define fuse_iovec iovec
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(S_IFMT)
|
||||||
|
#define S_IFMT 0170000
|
||||||
|
#endif
|
||||||
|
#if !defined(S_IFDIR)
|
||||||
|
#define S_IFDIR 0040000
|
||||||
|
#endif
|
||||||
|
#if !defined(S_IFCHR)
|
||||||
|
#define S_IFCHR 0020000
|
||||||
|
#endif
|
||||||
|
#if !defined(S_IFBLK)
|
||||||
|
#define S_IFBLK 0060000
|
||||||
|
#endif
|
||||||
|
#if !defined(S_IFREG)
|
||||||
|
#define S_IFREG 0100000
|
||||||
|
#endif
|
||||||
|
#if !defined(S_IFLNK)
|
||||||
|
#define S_IFLNK 0120000
|
||||||
|
#endif
|
||||||
|
#if !defined(S_IFSOCK)
|
||||||
|
#define S_IFSOCK 0140000
|
||||||
|
#endif
|
||||||
|
#if !defined(S_IFIFO)
|
||||||
|
#define S_IFIFO 0010000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#define st_atim st_atimespec
|
||||||
|
#define st_ctim st_ctimespec
|
||||||
|
#define st_mtim st_mtimespec
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__APPLE__) || defined(__linux__) || defined(__CYGWIN__)
|
||||||
|
#include <sys/xattr.h>
|
||||||
|
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(_WIN32)
|
||||||
|
#define XATTR_CREATE 1
|
||||||
|
#define XATTR_REPLACE 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(ENOATTR)
|
||||||
|
#define ENOATTR ENODATA
|
||||||
|
#elif !defined(ENODATA)
|
||||||
|
#define ENODATA ENOATTR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
619
tst/memfs-fuse3/memfs-fuse3.cpp
Normal file
619
tst/memfs-fuse3/memfs-fuse3.cpp
Normal file
@ -0,0 +1,619 @@
|
|||||||
|
/**
|
||||||
|
* @file memfs-fuse3.c
|
||||||
|
*
|
||||||
|
* @copyright 2015-2019 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 <cerrno>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstring>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <fuse.h>
|
||||||
|
#include "compat.h"
|
||||||
|
|
||||||
|
class memfs
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
memfs() : _ino(1), _root(std::make_shared<node_t>(_ino, S_IFDIR | 00777, 0, 0))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
static fuse_operations ops =
|
||||||
|
{
|
||||||
|
getattr,
|
||||||
|
readlink,
|
||||||
|
mknod,
|
||||||
|
mkdir,
|
||||||
|
unlink,
|
||||||
|
rmdir,
|
||||||
|
symlink,
|
||||||
|
rename,
|
||||||
|
link,
|
||||||
|
chmod,
|
||||||
|
chown,
|
||||||
|
truncate,
|
||||||
|
open,
|
||||||
|
read,
|
||||||
|
write,
|
||||||
|
statfs,
|
||||||
|
flush,
|
||||||
|
release,
|
||||||
|
0, // fsync
|
||||||
|
setxattr,
|
||||||
|
getxattr,
|
||||||
|
listxattr,
|
||||||
|
removexattr,
|
||||||
|
opendir,
|
||||||
|
readdir,
|
||||||
|
releasedir,
|
||||||
|
0, // fsyncdir
|
||||||
|
init,
|
||||||
|
0, // destroy
|
||||||
|
0, // access
|
||||||
|
0, // create
|
||||||
|
0, // lock
|
||||||
|
utimens,
|
||||||
|
0, // bmap
|
||||||
|
#if 0
|
||||||
|
ioctl,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
return fuse_main(argc, argv, &ops, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct node_t
|
||||||
|
{
|
||||||
|
node_t(fuse_ino_t ino, fuse_mode_t mode, fuse_uid_t uid, fuse_gid_t gid, fuse_dev_t dev = 0)
|
||||||
|
: stat()
|
||||||
|
{
|
||||||
|
stat.st_ino = ino;
|
||||||
|
stat.st_mode = mode;
|
||||||
|
stat.st_nlink = 1;
|
||||||
|
stat.st_uid = uid;
|
||||||
|
stat.st_gid = gid;
|
||||||
|
stat.st_rdev = dev;
|
||||||
|
stat.st_atim = stat.st_mtim = stat.st_ctim = now();
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize(size_t size, bool capacity)
|
||||||
|
{
|
||||||
|
if (capacity)
|
||||||
|
{
|
||||||
|
const size_t unit = 64 * 1024;
|
||||||
|
size_t newcap = (size + unit - 1) / unit * unit;
|
||||||
|
size_t oldcap = data.capacity();
|
||||||
|
if (newcap > oldcap)
|
||||||
|
data.reserve(newcap);
|
||||||
|
else if (newcap < oldcap)
|
||||||
|
{
|
||||||
|
data.resize(newcap);
|
||||||
|
data.shrink_to_fit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.resize(size);
|
||||||
|
stat.st_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fuse_stat stat;
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
std::unordered_map<std::string, std::shared_ptr<node_t>> childmap;
|
||||||
|
std::unordered_map<std::string, std::vector<uint8_t>> xattrmap;
|
||||||
|
};
|
||||||
|
|
||||||
|
static fuse_timespec now()
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
auto now = system_clock::now();
|
||||||
|
auto sec = floor<seconds>(now);
|
||||||
|
auto nsec = floor<nanoseconds>(now) - floor<nanoseconds>(sec);
|
||||||
|
return fuse_timespec
|
||||||
|
{
|
||||||
|
static_cast<decltype(fuse_timespec::tv_sec)>(sec.time_since_epoch().count()),
|
||||||
|
/* std::chrono epoch is UNIX epoch in C++20 */
|
||||||
|
static_cast<decltype(fuse_timespec::tv_nsec)>(nsec.count()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static memfs *getself()
|
||||||
|
{
|
||||||
|
return static_cast<memfs *>(fuse_get_context()->private_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getattr(const char *path, struct fuse_stat *stbuf, struct fuse_file_info *fi)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
auto node = self->get_node(path, fi);
|
||||||
|
if (!node)
|
||||||
|
return -ENOENT;
|
||||||
|
*stbuf = node->stat;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int readlink(const char *path, char *buf, size_t size)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
auto node = self->get_node(path);
|
||||||
|
if (!node)
|
||||||
|
return -ENOENT;
|
||||||
|
if (S_IFLNK != (node->stat.st_mode & S_IFMT))
|
||||||
|
return EINVAL;
|
||||||
|
size = (std::min)(size - 1, node->data.size());
|
||||||
|
std::memcpy(buf, node->data.data(), size);
|
||||||
|
buf[size] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mknod(const char *path, fuse_mode_t mode, fuse_dev_t dev)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
return self->make_node(path, mode, dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mkdir(const char *path, fuse_mode_t mode)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
return self->make_node(path, S_IFDIR | (mode & 07777), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int unlink(const char *path)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
return self->remove_node(path, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rmdir(const char *path)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
return self->remove_node(path, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int symlink(const char *dstpath, const char *srcpath)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
return self->make_node(srcpath, S_IFLNK | 00777, 0, dstpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rename(const char *oldpath, const char *newpath, unsigned int flags)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
auto oldlookup = self->lookup_node(oldpath);
|
||||||
|
auto oldprnt = std::get<0>(oldlookup);
|
||||||
|
auto oldname = std::get<1>(oldlookup);
|
||||||
|
auto oldnode = std::get<2>(oldlookup);
|
||||||
|
if (!oldnode)
|
||||||
|
return -ENOENT;
|
||||||
|
auto newlookup = self->lookup_node(newpath);
|
||||||
|
auto newprnt = std::get<0>(newlookup);
|
||||||
|
auto newname = std::get<1>(newlookup);
|
||||||
|
auto newnode = std::get<2>(newlookup);
|
||||||
|
if (!newprnt)
|
||||||
|
return -ENOENT;
|
||||||
|
if (newname.empty())
|
||||||
|
// guard against directory loop creation
|
||||||
|
return -EINVAL;
|
||||||
|
if (oldprnt == newprnt && oldname == newname)
|
||||||
|
return 0;
|
||||||
|
if (newnode)
|
||||||
|
{
|
||||||
|
if (int errc = self->remove_node(newpath, S_IFDIR == (oldnode->stat.st_mode & S_IFMT)))
|
||||||
|
return errc;
|
||||||
|
}
|
||||||
|
oldprnt->childmap.erase(oldname);
|
||||||
|
newprnt->childmap[newname] = oldnode;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int link(const char *oldpath, const char *newpath)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
auto oldlookup = self->lookup_node(oldpath);
|
||||||
|
auto oldnode = std::get<2>(oldlookup);
|
||||||
|
if (!oldnode)
|
||||||
|
return -ENOENT;
|
||||||
|
auto newlookup = self->lookup_node(newpath);
|
||||||
|
auto newprnt = std::get<0>(newlookup);
|
||||||
|
auto newname = std::get<1>(newlookup);
|
||||||
|
auto newnode = std::get<2>(newlookup);
|
||||||
|
if (!newprnt)
|
||||||
|
return -ENOENT;
|
||||||
|
if (newnode)
|
||||||
|
return -EEXIST;
|
||||||
|
oldnode->stat.st_nlink++;
|
||||||
|
newprnt->childmap[newname] = oldnode;
|
||||||
|
oldnode->stat.st_ctim = newprnt->stat.st_ctim = newprnt->stat.st_mtim = now();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int chmod(const char *path, fuse_mode_t mode,
|
||||||
|
struct fuse_file_info *fi)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
auto node = self->get_node(path, fi);
|
||||||
|
if (!node)
|
||||||
|
return -ENOENT;
|
||||||
|
node->stat.st_mode = (node->stat.st_mode & S_IFMT) | (mode & 07777);
|
||||||
|
node->stat.st_ctim = now();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int chown(const char *path, fuse_uid_t uid, fuse_gid_t gid,
|
||||||
|
struct fuse_file_info *fi)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
auto node = self->get_node(path, fi);
|
||||||
|
if (!node)
|
||||||
|
return -ENOENT;
|
||||||
|
if (-1 != uid)
|
||||||
|
node->stat.st_uid = uid;
|
||||||
|
if (-1 != gid)
|
||||||
|
node->stat.st_gid = gid;
|
||||||
|
node->stat.st_ctim = now();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int truncate(const char *path, fuse_off_t size,
|
||||||
|
struct fuse_file_info *fi)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
auto node = self->get_node(path, fi);
|
||||||
|
if (!node)
|
||||||
|
return -ENOENT;
|
||||||
|
if (SIZE_MAX < size)
|
||||||
|
return -EFBIG;
|
||||||
|
node->resize(static_cast<size_t>(size), true);
|
||||||
|
node->stat.st_ctim = node->stat.st_mtim = now();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int open(const char *path, struct fuse_file_info *fi)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
return self->open_node(path, false, fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read(const char *path, char *buf, size_t size, fuse_off_t off,
|
||||||
|
struct fuse_file_info *fi)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
auto node = self->get_node(path, fi);
|
||||||
|
if (!node)
|
||||||
|
return -ENOENT;
|
||||||
|
fuse_off_t endoff = (std::min)(
|
||||||
|
off + static_cast<fuse_off_t>(size), static_cast<fuse_off_t>(node->data.size()));
|
||||||
|
if (off > endoff)
|
||||||
|
return 0;
|
||||||
|
std::memcpy(buf, node->data.data() + off, static_cast<int>(endoff - off));
|
||||||
|
node->stat.st_atim = now();
|
||||||
|
return static_cast<int>(endoff - off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write(const char *path, const char *buf, size_t size, fuse_off_t off,
|
||||||
|
struct fuse_file_info *fi)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
auto node = self->get_node(path, fi);
|
||||||
|
if (!node)
|
||||||
|
return -ENOENT;
|
||||||
|
fuse_off_t endoff = off + static_cast<fuse_off_t>(size);
|
||||||
|
if (SIZE_MAX < endoff)
|
||||||
|
return -EFBIG;
|
||||||
|
if (node->data.size() < endoff)
|
||||||
|
node->resize(static_cast<size_t>(endoff), true);
|
||||||
|
std::memcpy(node->data.data() + off, buf, static_cast<int>(endoff - off));
|
||||||
|
node->stat.st_ctim = node->stat.st_mtim = now();
|
||||||
|
return static_cast<int>(endoff - off);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int statfs(const char *path, struct fuse_statvfs *stbuf)
|
||||||
|
{
|
||||||
|
std::memset(stbuf, 0, sizeof *stbuf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int flush(const char *path, struct fuse_file_info *fi)
|
||||||
|
{
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int release(const char *path, struct fuse_file_info *fi)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
return self->close_node(fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int setxattr(const char *path, const char *name0, const char *value, size_t size,
|
||||||
|
int flags)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
auto node = self->get_node(path);
|
||||||
|
if (!node)
|
||||||
|
return -ENOENT;
|
||||||
|
if (0 == std::strcmp("com.apple.ResourceFork", name0))
|
||||||
|
return -ENOTSUP;
|
||||||
|
std::string name = name0;
|
||||||
|
if (XATTR_CREATE == flags)
|
||||||
|
{
|
||||||
|
if (node->xattrmap.end() != node->xattrmap.find(name))
|
||||||
|
return -EEXIST;
|
||||||
|
}
|
||||||
|
else if (XATTR_REPLACE == flags)
|
||||||
|
{
|
||||||
|
if (node->xattrmap.end() == node->xattrmap.find(name))
|
||||||
|
return -ENOATTR;
|
||||||
|
}
|
||||||
|
node->xattrmap[name].assign(value, value + size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getxattr(const char *path, const char *name0, char *value, size_t size)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
auto node = self->get_node(path);
|
||||||
|
if (!node)
|
||||||
|
return -ENOENT;
|
||||||
|
if (0 == std::strcmp("com.apple.ResourceFork", name0))
|
||||||
|
return -ENOTSUP;
|
||||||
|
std::string name = name0;
|
||||||
|
auto iter = node->xattrmap.find(name);
|
||||||
|
if (node->xattrmap.end() == iter)
|
||||||
|
return -ENOATTR;
|
||||||
|
if (0 != size)
|
||||||
|
{
|
||||||
|
if (iter->second.size() > size)
|
||||||
|
return -ERANGE;
|
||||||
|
std::memcpy(value, iter->second.data(), iter->second.size());
|
||||||
|
}
|
||||||
|
return static_cast<int>(iter->second.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
static int listxattr(const char *path, char *namebuf, size_t size)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
auto node = self->get_node(path);
|
||||||
|
if (!node)
|
||||||
|
return -ENOENT;
|
||||||
|
size_t copysize = 0;
|
||||||
|
for (auto elem : node->xattrmap)
|
||||||
|
{
|
||||||
|
size_t namesize = elem.first.size() + 1;
|
||||||
|
if (0 != size)
|
||||||
|
{
|
||||||
|
if (copysize + namesize > size)
|
||||||
|
return -ERANGE;
|
||||||
|
std::memcpy(namebuf + copysize, elem.first.c_str(), namesize);
|
||||||
|
copysize += namesize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return static_cast<int>(copysize);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int removexattr(const char *path, const char *name0)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
auto node = self->get_node(path);
|
||||||
|
if (!node)
|
||||||
|
return -ENOENT;
|
||||||
|
if (0 == std::strcmp("com.apple.ResourceFork", name0))
|
||||||
|
return -ENOTSUP;
|
||||||
|
std::string name = name0;
|
||||||
|
return node->xattrmap.erase(name) ? 0 : -ENOATTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int opendir(const char *path, struct fuse_file_info *fi)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
return self->open_node(path, true, fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int readdir(const char *path, void *buf, fuse_fill_dir_t filler, fuse_off_t off,
|
||||||
|
struct fuse_file_info *fi, enum fuse_readdir_flags)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
auto node = self->get_node(path, fi);
|
||||||
|
if (!node)
|
||||||
|
return -ENOENT;
|
||||||
|
filler(buf, ".", &node->stat, 0, FUSE_FILL_DIR_PLUS);
|
||||||
|
filler(buf, "..", nullptr, 0, FUSE_FILL_DIR_PLUS);
|
||||||
|
for (auto elem : node->childmap)
|
||||||
|
if (0 != filler(buf, elem.first.c_str(), &elem.second->stat, 0, FUSE_FILL_DIR_PLUS))
|
||||||
|
break;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int releasedir(const char *path, struct fuse_file_info *fi)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
return self->close_node(fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *init(struct fuse_conn_info *conn,
|
||||||
|
struct fuse_config *conf)
|
||||||
|
{
|
||||||
|
conn->want |= (conn->capable & FUSE_CAP_READDIRPLUS);
|
||||||
|
return getself();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int utimens(const char *path, const struct fuse_timespec tmsp[2],
|
||||||
|
struct fuse_file_info *fi)
|
||||||
|
{
|
||||||
|
auto self = getself();
|
||||||
|
std::lock_guard<std::mutex> lock(self->_mutex);
|
||||||
|
auto node = self->get_node(path, fi);
|
||||||
|
if (!node)
|
||||||
|
return -ENOENT;
|
||||||
|
if (tmsp)
|
||||||
|
{
|
||||||
|
node->stat.st_ctim = now();
|
||||||
|
node->stat.st_atim = tmsp[0];
|
||||||
|
node->stat.st_mtim = tmsp[1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
node->stat.st_ctim = node->stat.st_atim = node->stat.st_mtim = now();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
static int ioctl(const char *path, int cmd, void *arg, struct fuse_file_info *fi,
|
||||||
|
unsigned int flags, void *data)
|
||||||
|
{
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::tuple<std::shared_ptr<node_t>, std::string, std::shared_ptr<node_t>>
|
||||||
|
lookup_node(const char *path, node_t *ancestor = nullptr)
|
||||||
|
{
|
||||||
|
auto prnt = _root;
|
||||||
|
std::string name;
|
||||||
|
auto node = prnt;
|
||||||
|
for (const char *part = path, *p; *part; part = p + !!(*p))
|
||||||
|
{
|
||||||
|
for (p = part; *p && '/' != *p; p++)
|
||||||
|
;
|
||||||
|
if (part == p)
|
||||||
|
continue;
|
||||||
|
prnt = node;
|
||||||
|
if (!node)
|
||||||
|
break;
|
||||||
|
name.assign(part, p);
|
||||||
|
auto iter = node->childmap.find(name);
|
||||||
|
node = node->childmap.end() != iter ? iter->second : nullptr;
|
||||||
|
if (ancestor && node.get() == ancestor)
|
||||||
|
{
|
||||||
|
name.assign(""); // special case loop condition
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::make_tuple(prnt, name, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
int make_node(const char *path, fuse_mode_t mode, fuse_dev_t dev, const char *data = nullptr)
|
||||||
|
{
|
||||||
|
auto lookup = lookup_node(path);
|
||||||
|
auto prnt = std::get<0>(lookup);
|
||||||
|
auto name = std::get<1>(lookup);
|
||||||
|
auto node = std::get<2>(lookup);
|
||||||
|
if (!prnt)
|
||||||
|
return -ENOENT;
|
||||||
|
if (node)
|
||||||
|
return -EEXIST;
|
||||||
|
fuse_context *context = fuse_get_context();
|
||||||
|
node = std::make_shared<node_t>(++_ino, mode, context->uid, context->gid, dev);
|
||||||
|
if (data)
|
||||||
|
{
|
||||||
|
node->resize(std::strlen(data), false);
|
||||||
|
std::memcpy(node->data.data(), data, node->data.size());
|
||||||
|
}
|
||||||
|
prnt->childmap[name] = node;
|
||||||
|
prnt->stat.st_ctim = prnt->stat.st_mtim = node->stat.st_ctim;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int remove_node(const char *path, bool dir)
|
||||||
|
{
|
||||||
|
auto lookup = lookup_node(path);
|
||||||
|
auto prnt = std::get<0>(lookup);
|
||||||
|
auto name = std::get<1>(lookup);
|
||||||
|
auto node = std::get<2>(lookup);
|
||||||
|
if (!node)
|
||||||
|
return -ENOENT;
|
||||||
|
if (!dir && S_IFDIR == (node->stat.st_mode & S_IFMT))
|
||||||
|
return -EISDIR;
|
||||||
|
if (dir && S_IFDIR != (node->stat.st_mode & S_IFMT))
|
||||||
|
return -ENOTDIR;
|
||||||
|
if (0 < node->childmap.size())
|
||||||
|
return -ENOTEMPTY;
|
||||||
|
node->stat.st_nlink--;
|
||||||
|
prnt->childmap.erase(name);
|
||||||
|
node->stat.st_ctim = prnt->stat.st_ctim = prnt->stat.st_mtim = now();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int open_node(const char *path, bool dir, struct fuse_file_info *fi)
|
||||||
|
{
|
||||||
|
auto node = std::get<2>(lookup_node(path));
|
||||||
|
if (!node)
|
||||||
|
return -ENOENT;
|
||||||
|
if (!dir && S_IFDIR == (node->stat.st_mode & S_IFMT))
|
||||||
|
return -EISDIR;
|
||||||
|
if (dir && S_IFDIR != (node->stat.st_mode & S_IFMT))
|
||||||
|
return -ENOTDIR;
|
||||||
|
// A file descriptor is a raw pointer to a shared_ptr.
|
||||||
|
// This has the effect of incrementing the shared_ptr
|
||||||
|
// refcount, thus keeping an open node around even
|
||||||
|
// if the node is unlinked.
|
||||||
|
fi->fh = (uint64_t)(uintptr_t)new std::shared_ptr<node_t>(node);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int close_node(struct fuse_file_info *fi)
|
||||||
|
{
|
||||||
|
delete (std::shared_ptr<node_t> *)(uintptr_t)fi->fh;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<node_t> get_node(const char *path, struct fuse_file_info *fi = nullptr)
|
||||||
|
{
|
||||||
|
if (!fi)
|
||||||
|
return std::get<2>(lookup_node(path));
|
||||||
|
else
|
||||||
|
return *(std::shared_ptr<node_t> *)(uintptr_t)fi->fh;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex _mutex;
|
||||||
|
fuse_ino_t _ino;
|
||||||
|
std::shared_ptr<node_t> _root;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
return memfs().main(argc, argv);
|
||||||
|
}
|
28
tst/memfs-fuse3/memfs-fuse3.sln
Normal file
28
tst/memfs-fuse3/memfs-fuse3.sln
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 14
|
||||||
|
VisualStudioVersion = 14.0.25420.1
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "memfs-fuse3", "memfs-fuse3.vcxproj", "{CF538F42-C714-4653-B351-E72FD7B0B217}"
|
||||||
|
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
|
||||||
|
{CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x64.Build.0 = Release|x64
|
||||||
|
{CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x86.Build.0 = Release|Win32
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
189
tst/memfs-fuse3/memfs-fuse3.vcxproj
Normal file
189
tst/memfs-fuse3/memfs-fuse3.vcxproj
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="14.0" 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">
|
||||||
|
<ProjectGuid>{CF538F42-C714-4653-B351-E72FD7B0B217}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>memfsfuse3</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)'=='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|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)'=='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>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\WinFsp\inc\fuse3;$(MSBuildProgramFiles32)\WinFsp\inc</AdditionalIncludeDirectories>
|
||||||
|
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||||
|
<DisableSpecificWarnings>4018</DisableSpecificWarnings>
|
||||||
|
</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)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\WinFsp\inc\fuse3;$(MSBuildProgramFiles32)\WinFsp\inc</AdditionalIncludeDirectories>
|
||||||
|
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||||
|
<DisableSpecificWarnings>4018</DisableSpecificWarnings>
|
||||||
|
</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>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\WinFsp\inc\fuse3;$(MSBuildProgramFiles32)\WinFsp\inc</AdditionalIncludeDirectories>
|
||||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
|
<DisableSpecificWarnings>4018</DisableSpecificWarnings>
|
||||||
|
</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)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<AdditionalIncludeDirectories>$(MSBuildProgramFiles32)\WinFsp\inc\fuse3;$(MSBuildProgramFiles32)\WinFsp\inc</AdditionalIncludeDirectories>
|
||||||
|
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||||
|
<DisableSpecificWarnings>4018</DisableSpecificWarnings>
|
||||||
|
</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>
|
||||||
|
<ClInclude Include="compat.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="memfs-fuse3.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
19
tst/memfs-fuse3/memfs-fuse3.vcxproj.filters
Normal file
19
tst/memfs-fuse3/memfs-fuse3.vcxproj.filters
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?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;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="compat.h">
|
||||||
|
<Filter>Source</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="memfs-fuse3.cpp">
|
||||||
|
<Filter>Source</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
@ -319,6 +319,15 @@ void mount_preflight_dotest(PWSTR DeviceName)
|
|||||||
MountPoint[2] = L'\0';
|
MountPoint[2] = L'\0';
|
||||||
|
|
||||||
GetTestDirectory(DirBuf);
|
GetTestDirectory(DirBuf);
|
||||||
|
/*
|
||||||
|
* Mount points starting with \\?\X: or \\.\X: are now considered MountManager mountpoints.
|
||||||
|
* So skip the \\?\ prefix to avoid this problem.
|
||||||
|
*/
|
||||||
|
if (L'\\' == DirBuf[0] &&
|
||||||
|
L'\\' == DirBuf[1] &&
|
||||||
|
(L'?' == DirBuf[2] || L'.' == DirBuf[2]) &&
|
||||||
|
L'\\' == DirBuf[3])
|
||||||
|
memmove(DirBuf, DirBuf + 4, (wcslen(DirBuf + 4) + 1) * sizeof(WCHAR));
|
||||||
|
|
||||||
Drives = GetLogicalDrives();
|
Drives = GetLogicalDrives();
|
||||||
ASSERT(0 != Drives);
|
ASSERT(0 != Drives);
|
||||||
|
60
tst/winfsp-tests/uuid5-test.c
Normal file
60
tst/winfsp-tests/uuid5-test.c
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* @file uuid5-test.c
|
||||||
|
*
|
||||||
|
* @copyright 2015-2019 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 "winfsp-tests.h"
|
||||||
|
|
||||||
|
#pragma comment(lib, "bcrypt.lib")
|
||||||
|
#include <ku/uuid5.c>
|
||||||
|
|
||||||
|
static void uuid5_test(void)
|
||||||
|
{
|
||||||
|
// 6ba7b810-9dad-11d1-80b4-00c04fd430c8
|
||||||
|
static const GUID GuidNs =
|
||||||
|
{ 0x6ba7b810, 0x9dad, 0x11d1, { 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 } };
|
||||||
|
// 74738ff5-5367-5958-9aee-98fffdcd1876
|
||||||
|
static const GUID Guid0 =
|
||||||
|
{ 0x74738ff5, 0x5367, 0x5958, { 0x9a, 0xee, 0x98, 0xff, 0xfd, 0xcd, 0x18, 0x76 } };
|
||||||
|
// 63b5d721-0b97-5e7a-a550-2f0e589b5478
|
||||||
|
static const GUID Guid1 =
|
||||||
|
{ 0x63b5d721, 0x0b97, 0x5e7a, { 0xa5, 0x50, 0x2f, 0x0e, 0x58, 0x9b, 0x54, 0x78 } };
|
||||||
|
|
||||||
|
NTSTATUS Result;
|
||||||
|
GUID Guid;
|
||||||
|
|
||||||
|
Result = FspUuid5Make(&GuidNs, "www.example.org", 15, &Guid);
|
||||||
|
ASSERT(NT_SUCCESS(Result));
|
||||||
|
ASSERT(IsEqualGUID(&Guid0, &Guid));
|
||||||
|
|
||||||
|
Result = FspUuid5Make(&FspFsvrtDeviceClassGuid, "hello", 5, &Guid);
|
||||||
|
ASSERT(NT_SUCCESS(Result));
|
||||||
|
ASSERT(IsEqualGUID(&Guid1, &Guid));
|
||||||
|
}
|
||||||
|
|
||||||
|
void uuid5_tests(void)
|
||||||
|
{
|
||||||
|
if (OptExternal)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TEST(uuid5_test);
|
||||||
|
}
|
296
tst/winfsp-tests/volpath-test.c
Normal file
296
tst/winfsp-tests/volpath-test.c
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
/**
|
||||||
|
* @file volpath-test.c
|
||||||
|
*
|
||||||
|
* @copyright 2015-2019 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 <strsafe.h>
|
||||||
|
#include "memfs.h"
|
||||||
|
|
||||||
|
#include "winfsp-tests.h"
|
||||||
|
|
||||||
|
static void volpath_dotest(ULONG Flags, PWSTR Prefix)
|
||||||
|
{
|
||||||
|
void *memfs = memfs_start(Flags);
|
||||||
|
|
||||||
|
HANDLE Handle;
|
||||||
|
BOOLEAN Success, VolumePathNameSuccess[8];
|
||||||
|
WCHAR FilePath[MAX_PATH];
|
||||||
|
WCHAR VolumePathName[MAX_PATH];
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
Success = CreateDirectoryW(FilePath, 0);
|
||||||
|
ASSERT(Success);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\file2",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
Handle = CreateFileW(FilePath,
|
||||||
|
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
|
||||||
|
ASSERT(INVALID_HANDLE_VALUE != Handle);
|
||||||
|
CloseHandle(Handle);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
VolumePathNameSuccess[0] = GetVolumePathNameW(FilePath, VolumePathName, MAX_PATH);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
VolumePathNameSuccess[1] = GetVolumePathNameW(FilePath, VolumePathName, MAX_PATH);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\file2",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
VolumePathNameSuccess[2] = GetVolumePathNameW(FilePath, VolumePathName, MAX_PATH);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\file2",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
Success = DeleteFileW(FilePath);
|
||||||
|
ASSERT(Success);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
Success = RemoveDirectoryW(FilePath);
|
||||||
|
ASSERT(Success);
|
||||||
|
|
||||||
|
memfs_stop(memfs);
|
||||||
|
|
||||||
|
ASSERT(VolumePathNameSuccess[0] == VolumePathNameSuccess[1]);
|
||||||
|
ASSERT(VolumePathNameSuccess[1] == VolumePathNameSuccess[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void volpath_test(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* GetVolumePathName is not reliable on WinFsp file systems
|
||||||
|
* when *not* using the MountManager and therefore disable
|
||||||
|
* this test when using a non-MountManager mount point.
|
||||||
|
*/
|
||||||
|
if (!NtfsTests && !OptMountPoint)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (WinFspDiskTests)
|
||||||
|
volpath_dotest(MemfsDisk, 0);
|
||||||
|
if (WinFspNetTests)
|
||||||
|
volpath_dotest(MemfsNet, L"\\\\memfs\\share");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void volpath_mount_dotest(ULONG Flags, PWSTR Prefix, PWSTR MountPoint)
|
||||||
|
{
|
||||||
|
void *memfs = memfs_start(Flags);
|
||||||
|
|
||||||
|
NTSTATUS Result;
|
||||||
|
HANDLE Handle;
|
||||||
|
BOOLEAN Success, VolumePathNameSuccess[8];
|
||||||
|
WCHAR FilePath[MAX_PATH];
|
||||||
|
WCHAR VolumePathName[MAX_PATH], VolumeName[MAX_PATH];
|
||||||
|
|
||||||
|
Result = FspFileSystemSetMountPoint(MemfsFileSystem(memfs), MountPoint);
|
||||||
|
ASSERT(NT_SUCCESS(Result));
|
||||||
|
|
||||||
|
Prefix = FspFileSystemMountPoint(MemfsFileSystem(memfs));
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
Success = CreateDirectoryW(FilePath, 0);
|
||||||
|
ASSERT(Success);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\file2",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
Handle = CreateFileW(FilePath,
|
||||||
|
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
|
||||||
|
ASSERT(INVALID_HANDLE_VALUE != Handle);
|
||||||
|
CloseHandle(Handle);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
VolumePathNameSuccess[0] = GetVolumePathNameW(FilePath, VolumePathName, MAX_PATH);
|
||||||
|
VolumePathNameSuccess[4] = GetVolumeNameForVolumeMountPointW(VolumePathName, VolumeName, MAX_PATH);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
VolumePathNameSuccess[1] = GetVolumePathNameW(FilePath, VolumePathName, MAX_PATH);
|
||||||
|
VolumePathNameSuccess[5] = GetVolumeNameForVolumeMountPointW(VolumePathName, VolumeName, MAX_PATH);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\file2",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
VolumePathNameSuccess[2] = GetVolumePathNameW(FilePath, VolumePathName, MAX_PATH);
|
||||||
|
VolumePathNameSuccess[6] = GetVolumeNameForVolumeMountPointW(VolumePathName, VolumeName, MAX_PATH);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\file2",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
Success = DeleteFileW(FilePath);
|
||||||
|
ASSERT(Success);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
Success = RemoveDirectoryW(FilePath);
|
||||||
|
ASSERT(Success);
|
||||||
|
|
||||||
|
FspFileSystemRemoveMountPoint(MemfsFileSystem(memfs));
|
||||||
|
|
||||||
|
ASSERT(VolumePathNameSuccess[0]);
|
||||||
|
ASSERT(VolumePathNameSuccess[1]);
|
||||||
|
ASSERT(VolumePathNameSuccess[2]);
|
||||||
|
|
||||||
|
if (MemfsNet != Flags)
|
||||||
|
{
|
||||||
|
ASSERT(VolumePathNameSuccess[4]);
|
||||||
|
ASSERT(VolumePathNameSuccess[5]);
|
||||||
|
ASSERT(VolumePathNameSuccess[6]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result = FspFileSystemSetMountPoint(MemfsFileSystem(memfs), MountPoint);
|
||||||
|
ASSERT(NT_SUCCESS(Result));
|
||||||
|
|
||||||
|
Prefix = FspFileSystemMountPoint(MemfsFileSystem(memfs));
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
Success = CreateDirectoryW(FilePath, 0);
|
||||||
|
ASSERT(Success);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\file2",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
Handle = CreateFileW(FilePath,
|
||||||
|
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
|
||||||
|
ASSERT(INVALID_HANDLE_VALUE != Handle);
|
||||||
|
CloseHandle(Handle);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
VolumePathNameSuccess[0] = GetVolumePathNameW(FilePath, VolumePathName, MAX_PATH);
|
||||||
|
VolumePathNameSuccess[4] = GetVolumeNameForVolumeMountPointW(VolumePathName, VolumeName, MAX_PATH);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
VolumePathNameSuccess[1] = GetVolumePathNameW(FilePath, VolumePathName, MAX_PATH);
|
||||||
|
VolumePathNameSuccess[5] = GetVolumeNameForVolumeMountPointW(VolumePathName, VolumeName, MAX_PATH);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\file2",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
VolumePathNameSuccess[2] = GetVolumePathNameW(FilePath, VolumePathName, MAX_PATH);
|
||||||
|
VolumePathNameSuccess[6] = GetVolumeNameForVolumeMountPointW(VolumePathName, VolumeName, MAX_PATH);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\file2",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
Success = DeleteFileW(FilePath);
|
||||||
|
ASSERT(Success);
|
||||||
|
|
||||||
|
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1",
|
||||||
|
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
|
||||||
|
|
||||||
|
Success = RemoveDirectoryW(FilePath);
|
||||||
|
ASSERT(Success);
|
||||||
|
|
||||||
|
FspFileSystemRemoveMountPoint(MemfsFileSystem(memfs));
|
||||||
|
|
||||||
|
ASSERT(VolumePathNameSuccess[0]);
|
||||||
|
ASSERT(VolumePathNameSuccess[1]);
|
||||||
|
ASSERT(VolumePathNameSuccess[2]);
|
||||||
|
|
||||||
|
if (MemfsNet != Flags)
|
||||||
|
{
|
||||||
|
ASSERT(VolumePathNameSuccess[4]);
|
||||||
|
ASSERT(VolumePathNameSuccess[5]);
|
||||||
|
ASSERT(VolumePathNameSuccess[6]);
|
||||||
|
}
|
||||||
|
|
||||||
|
memfs_stop(memfs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void volpath_mount_test(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This test does FspFileSystemSetMountPoint and therefore
|
||||||
|
* cannot be used with --external or --mountpoint.
|
||||||
|
*/
|
||||||
|
if (NtfsTests || OptMountPoint)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (WinFspDiskTests)
|
||||||
|
{
|
||||||
|
WCHAR MountPoint[7];
|
||||||
|
DWORD Drives;
|
||||||
|
WCHAR Drive;
|
||||||
|
|
||||||
|
MountPoint[0] = L'\\';
|
||||||
|
MountPoint[1] = L'\\';
|
||||||
|
MountPoint[2] = L'.';
|
||||||
|
MountPoint[3] = L'\\';
|
||||||
|
MountPoint[4] = L'C';
|
||||||
|
MountPoint[5] = L':';
|
||||||
|
MountPoint[6] = L'\0';
|
||||||
|
|
||||||
|
Drives = GetLogicalDrives();
|
||||||
|
ASSERT(0 != Drives);
|
||||||
|
|
||||||
|
for (Drive = 'Z'; 'A' <= Drive; Drive--)
|
||||||
|
if (0 == (Drives & (1 << (Drive - 'A'))))
|
||||||
|
break;
|
||||||
|
ASSERT('A' <= Drive);
|
||||||
|
|
||||||
|
MountPoint[4] = Drive;
|
||||||
|
|
||||||
|
//volpath_mount_dotest(MemfsDisk, 0, 0);
|
||||||
|
volpath_mount_dotest(MemfsDisk, 0, MountPoint);
|
||||||
|
}
|
||||||
|
if (WinFspNetTests)
|
||||||
|
{
|
||||||
|
volpath_mount_dotest(MemfsNet, L"\\\\memfs\\share", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void volpath_tests(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* GetVolumePathName is not reliable on WinFsp file systems
|
||||||
|
* when *not* using the MountManager and therefore disable
|
||||||
|
* this test when using a non-MountManager mount point.
|
||||||
|
*/
|
||||||
|
if (!NtfsTests && !OptMountPoint)
|
||||||
|
TEST(volpath_test);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This test does FspFileSystemSetMountPoint and therefore
|
||||||
|
* cannot be used with --external or --mountpoint.
|
||||||
|
*/
|
||||||
|
if (!NtfsTests && !OptMountPoint)
|
||||||
|
TEST(volpath_mount_test);
|
||||||
|
}
|
@ -188,6 +188,7 @@ int main(int argc, char *argv[])
|
|||||||
TESTSUITE(fuse_opt_tests);
|
TESTSUITE(fuse_opt_tests);
|
||||||
TESTSUITE(fuse_tests);
|
TESTSUITE(fuse_tests);
|
||||||
TESTSUITE(posix_tests);
|
TESTSUITE(posix_tests);
|
||||||
|
TESTSUITE(uuid5_tests);
|
||||||
TESTSUITE(eventlog_tests);
|
TESTSUITE(eventlog_tests);
|
||||||
TESTSUITE(path_tests);
|
TESTSUITE(path_tests);
|
||||||
TESTSUITE(dirbuf_tests);
|
TESTSUITE(dirbuf_tests);
|
||||||
@ -211,6 +212,7 @@ int main(int argc, char *argv[])
|
|||||||
TESTSUITE(stream_tests);
|
TESTSUITE(stream_tests);
|
||||||
TESTSUITE(oplock_tests);
|
TESTSUITE(oplock_tests);
|
||||||
TESTSUITE(wsl_tests);
|
TESTSUITE(wsl_tests);
|
||||||
|
TESTSUITE(volpath_tests);
|
||||||
|
|
||||||
atexit(exiting);
|
atexit(exiting);
|
||||||
signal(SIGABRT, abort_handler);
|
signal(SIGABRT, abort_handler);
|
||||||
|
Reference in New Issue
Block a user