Compare commits

...

129 Commits
v0.13 ... v0.16

Author SHA1 Message Date
20e547c6d0 Merge branch 'master' of https://github.com/billziss-gh/winfsp 2016-09-19 17:25:50 -07:00
d0d0f60033 doc: SSHFS Port Case Study: Step 5: POSIX special files 2016-09-19 17:29:56 -07:00
fbc3d3efce Update Changelog 2016-09-19 15:11:01 -07:00
2c3f177314 tst: winfsp-tests: disable HookCreateFileW for now 2016-09-16 17:34:32 -07:00
91e8bb1229 tst: winfsp-tests: HookCreateFileW 2016-09-16 10:08:16 -07:00
4b48502232 tools: run-tests: correctly test ERRORLEVEL 2016-09-15 21:18:52 -07:00
74af44e2e7 sys: create: FspFsvolCreateNoLock: fix open of network drive root 2016-09-15 20:23:21 -07:00
915279d41d Merge branch 'master' of https://bitbucket.org/billziss/winfsp 2016-09-15 15:30:50 -07:00
316a2940dc sys: write: FspFsvolWriteCached: fix simple but major bug when using FILE_APPEND_DATA 2016-09-15 15:28:55 -07:00
453c1753ab doc: update Changelog 2016-09-15 13:41:18 -07:00
b11b622fc2 doc: Changelog formatting fix 2016-09-15 13:25:59 -07:00
f1ba60e172 doc: update Changelog 2016-09-15 13:09:24 -07:00
7c1b592b84 installer: add public (stripped) symbols 2016-09-15 11:49:42 -07:00
1e88fedb19 src: sys: FspFsvolCreateComplete: minor comment fix 2016-09-15 00:16:25 -07:00
5ecc92f2ba update submodules 2016-09-14 23:46:05 -07:00
5771eedc45 sys,dll: FspFileSystemResolveReparsePoints: use IO_REPARSE_TAG_SYMLINK instead of IO_REPARSE for symlink resolution
- FspFsvolCreateComplete STATUS_REPARSE handling changed to support device-relative symlink reparse points
2016-09-14 22:16:40 -07:00
46a29f663a inc: fsctl: simplify FSP_FSCTL_TRANSACT_RSP Rsp.Create.Reparse 2016-09-14 17:18:59 -07:00
49cd11b34b tst: memfs: GetSecurityByName: reparse point fix 2016-09-14 16:20:21 -07:00
aeff3dc21e dll: debug: add diagnostic information for FileSystemControl requests/responses 2016-09-14 16:17:20 -07:00
b7368336d2 dll: debug: add diagnostic information for FileSystemControl requests/responses 2016-09-14 15:02:40 -07:00
5afe7a5315 tools: run-tests: include reparse point tests 2016-09-14 11:28:34 -07:00
7815b9e2eb dll: fuse: directory symlinks can now be deleted 2016-09-14 10:41:32 -07:00
cbcea380ef dll: fuse: correctly handle NFS_SPECFILE_LNK reparse points, which are in POSIX UTF-16 format 2016-09-14 10:36:52 -07:00
10635db99b dll: fuse: reparse point fixes
- symlinks to directories have the FILE_ATTRIBUTE_DIRECTORY added
- new symlinks/special files are now created with correct uid/gid in fuse_context
2016-09-13 22:17:31 -07:00
cb00f913cc appveyor.yml: the previous version of Visual Studio 2015 is hosed too, so remove :( 2016-09-13 15:49:24 -07:00
c406c89158 appveyor.yml: choose previous version of VS2015 so that AppVeyor builds can complete 2016-09-13 15:45:42 -07:00
f600d51ace dll: fuse: SetReparsePoint testing 2016-09-13 15:17:59 -07:00
7401481d42 dll: fuse: allow opening of reparse points 2016-09-13 14:36:40 -07:00
2d3d92fb2d dll: fuse: reparse point testing 2016-09-13 13:41:09 -07:00
eca93bbdb3 installer: add diag.bat file to installation 2016-09-13 10:24:14 -07:00
5fa0c36c8f dll: relative symbolic link resolution testing 2016-09-13 00:14:35 -07:00
ea2cc54677 dll: relative symbolic link resolution testing 2016-09-12 22:45:47 -07:00
ccec269dd6 dll: relative symbolic link resolution testing 2016-09-12 21:50:25 -07:00
5888e9ab05 dll: relative symbolic link resolution testing 2016-09-12 21:42:02 -07:00
c12b88286d dll: fuse: warning fix 2016-09-12 20:26:14 -07:00
e412d735ed tst: winfsp-tests: reparse_symlink_relative_test 2016-09-12 19:55:35 -07:00
582997b5a4 tst: winfsp-tests: reparse_symlink_large_test 2016-09-12 18:13:25 -07:00
1647702a65 tools: add diag.bat diagnostics batch file 2016-09-12 15:45:48 -07:00
ccf58122a2 sys: FspFsvolFileSystemControlReparsePoint: comments 2016-09-12 15:27:31 -07:00
be8c29114a sys: FspFsvolFileSystemControlReparsePoint: improve computation of TargetOnFileSystem field when MUP is being used 2016-09-12 15:11:45 -07:00
30f2807e2b sys: FspGetDeviceObjectByName: PAGED_CODE() macro 2016-09-11 21:45:32 -07:00
25a2873556 sys: symbolic link testing 2016-09-11 17:18:56 -07:00
bd0acf2289 sys,dll: symbolic link testing 2016-09-11 17:04:01 -07:00
997476f015 sys,launcher: MUP volume prefixes (share names) are now case insensitive 2016-09-11 14:35:56 -07:00
f89c91cd10 sys,dll: SYMLINK_FLAG_RELATIVE 2016-09-11 13:18:54 -07:00
af2cc10c15 sys,dll: reparse point testing 2016-09-10 23:00:55 -07:00
0d25e73364 Submodule secfs.test now pulled from github
- run `git submodule sync` after pull
2016-09-09 22:12:55 -07:00
86d74371aa doc: add Native API vs FUSE document 2016-09-09 21:54:51 -07:00
f32e335855 bump version to 0.16 and update Changelog 2016-09-09 21:45:19 -07:00
165f3ec45d sys,dll: symlink testing 2016-09-09 21:21:24 -07:00
811696d939 sys,dll: reparse point testing 2016-09-09 19:40:37 -07:00
0c07be9628 sys,dll: reparse point testing 2016-09-09 17:21:56 -07:00
5dcbbaa4e7 tst: winfsp-tests: NFS reparse point testing 2016-09-09 15:24:33 -07:00
12b70f661f dll: fuse: nfs reparse point fixes 2016-09-09 15:14:40 -07:00
9b4b5abe48 tst: winfsp-tests: reparse point testing 2016-09-09 14:55:15 -07:00
5cd0dfb1b9 sys: reparse point testing 2016-09-09 14:42:22 -07:00
46ce4b1a6c dll: fuse: properly acquire op guard for reparse point operations 2016-09-09 11:43:49 -07:00
28a20d5199 dll: FspFileSystemOpLeave: bug fix 2016-09-09 11:29:09 -07:00
10ea9a833f dll: fuse: fixes 2016-09-09 11:23:29 -07:00
d9713668aa dll: FspFileSystemCanReplaceReparsePoint: fix 2016-09-09 11:09:41 -07:00
00c0574f1f tst: memfs: minor fix 2016-09-08 11:42:05 -07:00
44c86ff9a4 dll: fuse: use NFS reparse points for POSIX special files 2016-09-08 10:43:01 -07:00
28931f4687 tst: reparse point testing 2016-09-04 12:38:12 -07:00
80e07cead6 sys,dll: reparse point implementation: DeleteReparsePoint 2016-08-26 07:43:25 -07:00
b88b2ec51d dll: acquire exclusive lock during flush volume 2016-08-22 01:04:07 -07:00
82e4dcb4a1 inc: winfsp.h: minor srcdoc fix 2016-08-22 00:54:15 -07:00
34e653a275 dll: fsop: implement FSCTL_DELETE_REPARSE_POINT parameter checking 2016-08-22 00:40:01 -07:00
981e60643f dll: reparse points: allow file system to provide directory symlink behavior 2016-08-22 00:04:49 -07:00
fee75590a8 sys,dll: rename VolumeParams::ReparsePoints* fields 2016-08-21 23:36:09 -07:00
1298dd842d update test submodule 2016-08-21 09:37:16 -07:00
8334daa048 update test submodule 2016-08-21 07:46:56 -07:00
2f73bfa069 dll: reparse point implementation: fixes 2016-08-20 02:47:14 -07:00
d2d2dd5099 inc: winfsp.h: srcdoc corrections 2016-08-20 01:19:07 -07:00
146570dd74 dll: FspFileSystemResolveReparsePoints: return STATUS_OBJECT_PATH_NOT_FOUND when appropriate 2016-08-20 01:11:46 -07:00
f509281be4 dll: fuse: reimplement reparse point support using FspFileSystemFindReparsePoint and FspFileSystemResolveReparsePoints 2016-08-20 01:06:52 -07:00
613a564ca2 tst: memfs: implement reparse points 2016-08-19 23:33:40 -07:00
7ffc60f512 dll: reparse point implementation: WIP 2016-08-19 10:24:03 -07:00
499a0cb866 dll: fuse: GetReparsePoint: return STATUS_NOT_A_REPARSE_POINT when appropriate 2016-08-15 08:05:09 -07:00
edff3054db dll: fuse: disallow deletion of reparse point (which we cannot support) 2016-08-15 07:21:05 -07:00
f7e0362350 sys,dll: reparse point implementation: WIP 2016-08-15 04:53:11 -07:00
7337f3c6cd sys,dll: symbolic link (reparse point) support: WIP 2016-08-04 11:25:35 -07:00
ab278d7b60 Merge branch 'master' into symlink 2016-07-31 00:16:38 -07:00
858df29ba2 Merge branch 'master' of https://github.com/billziss-gh/winfsp 2016-07-28 23:51:34 -07:00
4366866653 launcher: SvcInstanceStart: STATUS_TIMEOUT is not error; handle it correctly 2016-07-28 23:50:49 -07:00
e4b808806c Update README.md 2016-07-28 17:03:47 -07:00
13cee6e019 Update Changelog 2016-07-28 16:54:07 -07:00
35b9cb15a3 tst: do not test for read-only buffer during Write when not on Win8+ 2016-07-28 16:37:49 -07:00
0d8f0f9ac8 Merge branch 'master' of https://bitbucket.org/billziss/winfsp 2016-07-28 14:56:40 -07:00
a4e2ad9dd6 dll: FspFsctlStop: fix problem in call to DeviceIoControl
- The DeviceIoControl was invoked in an incorrect fashion that nevertheless worked in Win64, but not Win32.
2016-07-28 14:56:06 -07:00
9b4318c655 sys: fix request header alignment on 32-bit builds 2016-07-28 09:57:31 -07:00
5827b1fa9c sys: fixes for Win7 x86 2016-07-28 00:03:53 -07:00
913f7ac9cd dll: suppress compiler warning on x86 builds 2016-07-27 16:25:41 -07:00
0e2f46dc90 Define NTDDI_VERSION,_WIN32_WINNT; remove GetOverlappedResultEx
- Ensures that only Vista+ DDI/API's are used
- Project should now run on Win 7
2016-07-27 16:15:28 -07:00
a8d76d3e46 sys, dll: reparse point (symbolic link) support: WIP 2016-07-25 21:27:48 -07:00
380ec074ca sys: ioq: clarify comment 2016-07-23 18:43:02 -07:00
e7cba96c74 Update README 2016-07-21 14:08:00 -07:00
03db443406 Update README 2016-07-20 23:41:55 -07:00
0b65bc7e01 Update README 2016-07-19 21:08:02 -07:00
12e1caaa98 Update README 2016-07-19 21:02:14 -07:00
7a690a789b Update README 2016-07-19 12:21:21 -07:00
8ca419830c art: installer banners 2016-07-17 16:55:12 -07:00
c310d8ea1b art: installer banners 2016-07-17 16:46:54 -07:00
c3ddf73661 art: add installer banners 2016-07-17 16:26:47 -07:00
6b00b8e28a art: add winfsp icon and installer banners 2016-07-17 16:24:21 -07:00
35c722e91b appveyor: enable verifier for FSD 2016-07-15 23:10:44 -07:00
ae8802514b appveyor: enable verifier for FSD 2016-07-15 23:04:45 -07:00
e90aa46a27 build: bump version to 0.15 2016-07-15 22:43:40 -07:00
760cd5e46f opt: cygfuse: bump release number to 3 2016-07-15 17:58:17 -07:00
b36b6c60e2 inc: fuse: allow for future expansion of fsp_fuse_env 2016-07-15 17:52:24 -07:00
e4984bf675 doc: update Changelog 2016-07-15 17:49:08 -07:00
0c8bcd5d7d doc: update Changelog 2016-07-15 17:39:57 -07:00
b5d8cd3ea6 installer: wix: avoid using autogenerated GUID for INSTALLDIR component 2016-07-14 11:13:26 -07:00
af5f409233 dll: np: improve username/password prompts 2016-07-13 23:19:11 -07:00
cffb066d46 ext/test/winfstest: fix build with VS2015 Update 3 2016-07-11 21:21:05 -07:00
804434d836 dll: fuse: respect the uid,gid,umask options which were being ignored 2016-06-30 23:47:01 -07:00
f7595e40b6 dll: np: FSP_NP_CREDENTIAL_MANAGER 2016-06-29 23:31:07 -07:00
669dd07ce2 dll: np: credentials testing 2016-06-29 22:35:00 -07:00
b6fa54d301 update Changelog 2016-06-29 18:09:06 -07:00
342e7e39e2 tst: secret: small program to aid with testing launcher secrets 2016-06-29 16:27:01 -07:00
9dfdd19616 launcher: startWithSecret testing 2016-06-29 16:10:27 -07:00
2c651b1bd8 launcher: check Credentials registry value during svc instance creation 2016-06-29 15:02:15 -07:00
41764f7b41 launcher, launchctl: fixes 2016-06-29 13:05:15 -07:00
08e697c52c launcher: send the password to service instance as UTF-8 2016-06-29 12:34:06 -07:00
66cc043149 dll: np: credentials support 2016-06-29 12:18:53 -07:00
518cd0e8c0 launcher, launchctl: StartWithSecret 2016-06-28 23:09:10 -07:00
c0344f53b0 Merge branch 'master' into launchpass 2016-06-28 12:10:27 -07:00
76445a5403 bump version to 0.14 2016-06-28 12:10:08 -07:00
0577b8febb dll: posix: use the S-1-0-65534 <-> 65534 for the unmapped SID/UID 2016-06-28 11:45:35 -07:00
610a7ac2a6 dll: np: support launcher passwords: WIP 2016-06-28 11:26:25 -07:00
74 changed files with 4480 additions and 488 deletions

2
.gitmodules vendored
View File

@ -1,3 +1,3 @@
[submodule "ext/test"]
path = ext/test
url = https://bitbucket.org/billziss/secfs.test.git
url = https://github.com/billziss-gh/secfs.test.git

View File

@ -1,5 +1,7 @@
# WinFsp - Windows File System Proxy
![WinFsp Demo](http://www.secfs.net/winfsp/files/cap.gif)
WinFsp is a set of software components for Windows computers that allows the creation of user mode file systems. In this sense it is similar to FUSE (Filesystem in Userspace), which provides the same functionality on UNIX-like computers.
Some of the benefits and features of using WinFsp are listed below:
@ -34,10 +36,41 @@ The project source code is organized as follows:
* tst/memfs: Source code to an example file system written in C++ (memfs).
* tst/winfsp-tests: WinFsp test suite.
## Building
## Building and Running
In order to build WinFsp you will need Windows 10 and Visual Studio 2015. You will also need the Windows Driver Kit (WDK) 10.
In order to build WinFsp you will need the following:
* Windows 10
* Visual Studio 2015
* Windows Driver Kit (WDK) 10
* [Wix toolset](http://wixtoolset.org)
If you build the driver yourself it will not be signed and Windows will refuse to load it unless you enable "testsigning". You can enable "testsigning" using the command `bcdedit.exe -set testsigning`. For more information see this [document](http://www.secfs.net/winfsp/develop/debug/).
WinFsp is designed to run on Vista and above. It has been tested on the following platforms so far:
* Windows 7 Enterprise
* Windows 8 Pro
* Windows 10 Pro
* Windows Server 2012
## How to Help
I am looking for help in the following areas:
* If you have a file system that runs on FUSE please consider porting it to WinFsp. WinFsp has a native API, but it also has a FUSE (high-level) API.
* If you are working with a language other than C/C++ (e.g. Delphi, C#, etc.) and you are interested in porting/wrapping WinFsp I would love to hear from you.
* There are a number of outstanding issues listed in the [GitHub repository](https://github.com/billziss-gh/winfsp/issues) ~~[BitBucket repository](https://bitbucket.org/billziss/winfsp/issues?status=new&status=open)~~. Many of these require knowledge of Windows kernel-mode and an understanding of the internals of WinFsp so they are not for the faint of heart. If you decide to tackle any of those please coordinate with me as I am actively working on that issue list.
In all cases I can provide ideas and/or support.
## Where to Discuss
If you wish to discuss WinFsp there are now two options:
- [WinFsp Google Group](https://groups.google.com/forum/#!forum/winfsp)
- [Author's Twitter](https://twitter.com/BZissimopoulos)
## License
WinFsp is available under the [AGPLv3](http://www.gnu.org/licenses/agpl-3.0.html) license. If you find the constraints of the AGPLv3 too onerous, a commercial license is also available. Please contact Bill Zissimopoulos <billziss at navimatics.com> for more details.
WinFsp is available under the [AGPLv3](http://www.gnu.org/licenses/agpl-3.0.html) license. If you find the constraints of the AGPLv3 too onerous, a commercial license is also available. Please contact Bill Zissimopoulos <billziss at navimatics.com> for more details.

View File

@ -6,6 +6,7 @@ environment:
install:
- git submodule update --init --recursive
- appveyor AddMessage "Change boot configuration and reboot" -Category Information
- verifier /standard /driver winfsp-x64.sys
- bcdedit /set testsigning on
- ps: Restart-Computer -Force
- ps: Start-Sleep -s 10
@ -19,3 +20,4 @@ test_script:
- for %%f in ("build\VStudio\build\%CONFIGURATION%\winfsp-*.msi") do start /wait msiexec /i %%f /qn INSTALLLEVEL=1000
- tools\nmake-ext-test.bat %CONFIGURATION%
- tools\run-tests.bat %CONFIGURATION%
- verifier /query

BIN
art/winfsp-outln.afdesign Normal file

Binary file not shown.

BIN
art/winfsp-solid.afdesign Normal file

Binary file not shown.

BIN
art/winfsp-solid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
art/wixbanner.pxm Normal file

Binary file not shown.

BIN
art/wixdialog.pxm Normal file

Binary file not shown.

View File

@ -40,18 +40,20 @@
<Directory Id="INCDIR" Name="inc" />
<Directory Id="LIBDIR" Name="lib" />
<Directory Id="SMPDIR" Name="samples" />
<Directory Id="SYMDIR" Name="sym" />
</Directory>
</Directory>
</Directory>
<DirectoryRef Id="INSTALLDIR">
<Component Id="C.INSTALLDIR">
<Component Id="C.INSTALLDIR" Guid="{F876F26E-5016-4AC6-93B3-653C0312A6CE}">
<RegistryValue
Root="HKLM"
Key="[P.RegistryKey]"
Name="InstallDir"
Type="string"
Value="[INSTALLDIR]" />
Value="[INSTALLDIR]"
KeyPath="yes" />
</Component>
</DirectoryRef>
<DirectoryRef Id="BINDIR" FileSource="..\build\$(var.Configuration)">
@ -135,6 +137,10 @@
<File Name="launchctl-x86.exe" KeyPath="yes" />
</Component>
<Component Id="C.diag.bat">
<File Name="diag.bat" Source="..\..\..\tools\diag.bat" KeyPath="yes" />
</Component>
<Component Id="C.memfs_x64.exe">
<File Name="memfs-x64.exe" KeyPath="yes" />
<RegistryKey
@ -253,6 +259,38 @@
</Component>
</Directory>
</DirectoryRef>
<DirectoryRef Id="SYMDIR">
<Component Id="C.winfsp_x64.sys.pdb">
<File Name="winfsp-x64.sys.pdb" Source="..\build\$(var.Configuration)\winfsp-x64.sys.public.pdb" KeyPath="yes" />
</Component>
<Component Id="C.winfsp_x86.sys.pdb">
<File Name="winfsp-x86.sys.pdb" Source="..\build\$(var.Configuration)\winfsp-x86.sys.public.pdb" KeyPath="yes" />
</Component>
<Component Id="C.winfsp_x64.dll.pdb">
<File Name="winfsp-x64.dll.pdb" Source="..\build\$(var.Configuration)\winfsp-x64.dll.public.pdb" KeyPath="yes" />
</Component>
<Component Id="C.winfsp_x86.dll.pdb">
<File Name="winfsp-x86.dll.pdb" Source="..\build\$(var.Configuration)\winfsp-x86.dll.public.pdb" KeyPath="yes" />
</Component>
<Component Id="C.launcher_x64.pdb">
<File Name="launcher-x64.pdb" Source="..\build\$(var.Configuration)\launcher-x64.public.pdb" KeyPath="yes" />
</Component>
<Component Id="C.launcher_x86.pdb">
<File Name="launcher-x86.pdb" Source="..\build\$(var.Configuration)\launcher-x86.public.pdb" KeyPath="yes" />
</Component>
<Component Id="C.launchctl_x64.pdb">
<File Name="launchctl-x64.pdb" Source="..\build\$(var.Configuration)\launchctl-x64.public.pdb" KeyPath="yes" />
</Component>
<Component Id="C.launchctl_x86.pdb">
<File Name="launchctl-x86.pdb" Source="..\build\$(var.Configuration)\launchctl-x86.public.pdb" KeyPath="yes" />
</Component>
<Component Id="C.memfs_x64.pdb">
<File Name="memfs-x64.pdb" Source="..\build\$(var.Configuration)\memfs-x64.public.pdb" KeyPath="yes" />
</Component>
<Component Id="C.memfs_x86.pdb">
<File Name="memfs-x86.pdb" Source="..\build\$(var.Configuration)\memfs-x86.public.pdb" KeyPath="yes" />
</Component>
</DirectoryRef>
<ComponentGroup Id="C.WinFsp.bin">
<ComponentRef Id="C.winfsp_x64.sys" />
@ -267,6 +305,7 @@
<ComponentRef Id="C.launcher_x86.exe.svcinst" />
<ComponentRef Id="C.launchctl_x64.exe" />
<ComponentRef Id="C.launchctl_x86.exe" />
<ComponentRef Id="C.diag.bat" />
</ComponentGroup>
<ComponentGroup Id="C.WinFsp.inc">
<ComponentRef Id="C.fsctl.h" />
@ -289,6 +328,18 @@
<ComponentRef Id="C.memfs.cpp" />
<ComponentRef Id="C.memfs_main.c" />
</ComponentGroup>
<ComponentGroup Id="C.WinFsp.sym">
<ComponentRef Id="C.winfsp_x64.sys.pdb" />
<ComponentRef Id="C.winfsp_x86.sys.pdb" />
<ComponentRef Id="C.winfsp_x86.dll.pdb" />
<ComponentRef Id="C.winfsp_x64.dll.pdb" />
<ComponentRef Id="C.launcher_x86.pdb" />
<ComponentRef Id="C.launcher_x64.pdb" />
<ComponentRef Id="C.launchctl_x64.pdb" />
<ComponentRef Id="C.launchctl_x86.pdb" />
<ComponentRef Id="C.memfs_x64.pdb" />
<ComponentRef Id="C.memfs_x86.pdb" />
</ComponentGroup>
<Feature
Id="F.Main"
@ -322,9 +373,12 @@
<ComponentGroupRef Id="C.WinFsp.inc" />
<ComponentGroupRef Id="C.WinFsp.lib" />
<ComponentGroupRef Id="C.WinFsp.smp" />
<ComponentGroupRef Id="C.WinFsp.sym" />
</Feature>
</Feature>
<WixVariable Id="WixUIBannerBmp" Value="wixbanner.bmp" />
<WixVariable Id="WixUIDialogBmp" Value="wixdialog.bmp" />
<UI Id="FeatureTree">
<UIRef Id="WixUI_FeatureTree" />
<!-- skip the license agreement dialog; higher Order takes priority (weird) -->

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 KiB

View File

@ -105,11 +105,13 @@
<BufferSecurityCheck>false</BufferSecurityCheck>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions>/Gs16384 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<StripPrivateSymbols>$(OutDir)$(TargetName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -123,11 +125,13 @@
<BufferSecurityCheck>false</BufferSecurityCheck>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions>/Gs16384 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<StripPrivateSymbols>$(OutDir)$(TargetName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@ -142,6 +146,7 @@
<AdditionalIncludeDirectories>..\..\..\src;..\..\..\inc</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions>/Gs16384 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -149,6 +154,7 @@
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<StripPrivateSymbols>$(OutDir)$(TargetName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -163,6 +169,7 @@
<AdditionalIncludeDirectories>..\..\..\src;..\..\..\inc</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions>/Gs16384 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -170,6 +177,7 @@
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<StripPrivateSymbols>$(OutDir)$(TargetName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemGroup>

View File

@ -105,11 +105,14 @@
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<BufferSecurityCheck>false</BufferSecurityCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions>/Gs16384 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<AdditionalDependencies>%(AdditionalDependencies);rpcrt4.lib</AdditionalDependencies>
<StripPrivateSymbols>$(OutDir)$(TargetName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -123,11 +126,14 @@
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<BufferSecurityCheck>false</BufferSecurityCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions>/Gs16384 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<AdditionalDependencies>%(AdditionalDependencies);rpcrt4.lib</AdditionalDependencies>
<StripPrivateSymbols>$(OutDir)$(TargetName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@ -142,6 +148,7 @@
<AdditionalIncludeDirectories>..\..\..\src;..\..\..\inc</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions>/Gs16384 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -149,6 +156,8 @@
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<AdditionalDependencies>%(AdditionalDependencies);rpcrt4.lib</AdditionalDependencies>
<StripPrivateSymbols>$(OutDir)$(TargetName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -163,6 +172,7 @@
<AdditionalIncludeDirectories>..\..\..\src;..\..\..\inc</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalOptions>/Gs16384 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -170,6 +180,8 @@
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<AdditionalDependencies>%(AdditionalDependencies);rpcrt4.lib</AdditionalDependencies>
<StripPrivateSymbols>$(OutDir)$(TargetName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemGroup>

View File

@ -108,6 +108,7 @@
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<StripPrivateSymbols>$(OutDir)$(TargetName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -124,6 +125,7 @@
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<StripPrivateSymbols>$(OutDir)$(TargetName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@ -144,6 +146,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<StripPrivateSymbols>$(OutDir)$(TargetName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -164,6 +167,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<StripPrivateSymbols>$(OutDir)$(TargetName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemGroup>

View File

@ -186,6 +186,7 @@
<ClCompile Include="..\..\..\tst\winfsp-tests\eventlog-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\flush-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\fuse-opt-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\hook.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\info-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\lock-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\memfs-test.c" />
@ -193,6 +194,7 @@
<ClCompile Include="..\..\..\tst\winfsp-tests\path-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\posix-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\rdwr-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\reparse-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\security-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\timeout-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\winfsp-tests.c" />
@ -200,6 +202,7 @@
<ItemGroup>
<ClInclude Include="..\..\..\ext\tlib\testsuite.h" />
<ClInclude Include="..\..\..\tst\memfs\memfs.h" />
<ClInclude Include="..\..\..\tst\winfsp-tests\winfsp-tests.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\winfsp_dll.vcxproj">

View File

@ -61,6 +61,12 @@
<ClCompile Include="..\..\..\tst\winfsp-tests\posix-test.c">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="..\..\..\tst\winfsp-tests\reparse-test.c">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="..\..\..\tst\winfsp-tests\hook.c">
<Filter>Source</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\ext\tlib\testsuite.h">
@ -69,5 +75,8 @@
<ClInclude Include="..\..\..\tst\memfs\memfs.h">
<Filter>Source</Filter>
</ClInclude>
<ClInclude Include="..\..\..\tst\winfsp-tests\winfsp-tests.h">
<Filter>Source</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -7,7 +7,12 @@
<MyCopyright>2015-2016 Bill Zissimopoulos</MyCopyright>
<!-- build number: concat 2-digit year with 3-digit day of the year (16-bits until 2066) -->
<MyBuildNumber>$([System.DateTime]::Now.ToString(`yy`))$([System.DateTime]::Now.DayOfYear.ToString(`000`))</MyBuildNumber>
<MyVersion>0.13.$(MyBuildNumber)</MyVersion>
<MyVersion>0.16.$(MyBuildNumber)</MyVersion>
<MyVersionWithCommas>$(MyVersion.Replace('.',',')),0</MyVersionWithCommas>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>NTDDI_VERSION=0x06000000;_WIN32_WINNT=0x0600</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
</Project>

View File

@ -173,6 +173,7 @@ copy /b $(OutDir)fuse-$(PlatformTarget).pc + %(FullPath) $(OutDir)fuse-$(Platfor
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<BufferSecurityCheck>false</BufferSecurityCheck>
<AdditionalOptions>/Gs16384 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -182,7 +183,8 @@ copy /b $(OutDir)fuse-$(PlatformTarget).pc + %(FullPath) $(OutDir)fuse-$(Platfor
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<ModuleDefinitionFile>..\..\src\dll\library.def</ModuleDefinitionFile>
<AdditionalDependencies>%(AdditionalDependencies);version.lib</AdditionalDependencies>
<AdditionalDependencies>%(AdditionalDependencies);rpcrt4.lib;credui.lib;version.lib</AdditionalDependencies>
<StripPrivateSymbols>$(OutDir)$(TargetFileName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -198,6 +200,7 @@ copy /b $(OutDir)fuse-$(PlatformTarget).pc + %(FullPath) $(OutDir)fuse-$(Platfor
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<BufferSecurityCheck>false</BufferSecurityCheck>
<AdditionalOptions>/Gs16384 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -207,7 +210,8 @@ copy /b $(OutDir)fuse-$(PlatformTarget).pc + %(FullPath) $(OutDir)fuse-$(Platfor
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<ModuleDefinitionFile>..\..\src\dll\library.def</ModuleDefinitionFile>
<AdditionalDependencies>%(AdditionalDependencies);version.lib</AdditionalDependencies>
<AdditionalDependencies>%(AdditionalDependencies);rpcrt4.lib;credui.lib;version.lib</AdditionalDependencies>
<StripPrivateSymbols>$(OutDir)$(TargetFileName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@ -224,6 +228,7 @@ copy /b $(OutDir)fuse-$(PlatformTarget).pc + %(FullPath) $(OutDir)fuse-$(Platfor
<AdditionalIncludeDirectories>..\..\src;..\..\inc</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
<AdditionalOptions>/Gs16384 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -235,7 +240,8 @@ copy /b $(OutDir)fuse-$(PlatformTarget).pc + %(FullPath) $(OutDir)fuse-$(Platfor
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<ModuleDefinitionFile>..\..\src\dll\library.def</ModuleDefinitionFile>
<AdditionalDependencies>%(AdditionalDependencies);version.lib</AdditionalDependencies>
<AdditionalDependencies>%(AdditionalDependencies);rpcrt4.lib;credui.lib;version.lib</AdditionalDependencies>
<StripPrivateSymbols>$(OutDir)$(TargetFileName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -252,6 +258,7 @@ copy /b $(OutDir)fuse-$(PlatformTarget).pc + %(FullPath) $(OutDir)fuse-$(Platfor
<AdditionalIncludeDirectories>..\..\src;..\..\inc</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<BufferSecurityCheck>false</BufferSecurityCheck>
<AdditionalOptions>/Gs16384 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -263,7 +270,8 @@ copy /b $(OutDir)fuse-$(PlatformTarget).pc + %(FullPath) $(OutDir)fuse-$(Platfor
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
<IgnoreAllDefaultLibraries>true</IgnoreAllDefaultLibraries>
<ModuleDefinitionFile>..\..\src\dll\library.def</ModuleDefinitionFile>
<AdditionalDependencies>%(AdditionalDependencies);version.lib</AdditionalDependencies>
<AdditionalDependencies>%(AdditionalDependencies);rpcrt4.lib;credui.lib;version.lib</AdditionalDependencies>
<StripPrivateSymbols>$(OutDir)$(TargetFileName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@ -118,6 +118,8 @@
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="..\..\src\dll\fuse\fuse.pc" />
<CustomBuild Include="..\..\src\dll\fuse\fuse.pc">
<Filter>Source\fuse</Filter>
</CustomBuild>
</ItemGroup>
</Project>

View File

@ -34,6 +34,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<UseDebugLibraries>true</UseDebugLibraries>
<KernelBufferOverflowLib>$(DDK_LIB_PATH)\BufferOverflowK.lib</KernelBufferOverflowLib>
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
<DriverType>WDM</DriverType>
@ -41,6 +42,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<UseDebugLibraries>false</UseDebugLibraries>
<KernelBufferOverflowLib>$(DDK_LIB_PATH)\BufferOverflowK.lib</KernelBufferOverflowLib>
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
<DriverType>WDM</DriverType>
@ -48,6 +50,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<UseDebugLibraries>true</UseDebugLibraries>
<KernelBufferOverflowLib>$(DDK_LIB_PATH)\BufferOverflowK.lib</KernelBufferOverflowLib>
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
<DriverType>WDM</DriverType>
@ -55,6 +58,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<UseDebugLibraries>false</UseDebugLibraries>
<KernelBufferOverflowLib>$(DDK_LIB_PATH)\BufferOverflowK.lib</KernelBufferOverflowLib>
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
<DriverType>WDM</DriverType>
@ -105,6 +109,7 @@
<GenerateMapFile>true</GenerateMapFile>
<ProgramDatabaseFile>$(OutDir)$(TargetFileName).pdb</ProgramDatabaseFile>
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
<StripPrivateSymbols>$(OutDir)$(TargetFileName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@ -117,6 +122,7 @@
<GenerateMapFile>true</GenerateMapFile>
<ProgramDatabaseFile>$(OutDir)$(TargetFileName).pdb</ProgramDatabaseFile>
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
<StripPrivateSymbols>$(OutDir)$(TargetFileName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -129,6 +135,7 @@
<GenerateMapFile>true</GenerateMapFile>
<ProgramDatabaseFile>$(OutDir)$(TargetFileName).pdb</ProgramDatabaseFile>
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
<StripPrivateSymbols>$(OutDir)$(TargetFileName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -141,6 +148,7 @@
<GenerateMapFile>true</GenerateMapFile>
<ProgramDatabaseFile>$(OutDir)$(TargetFileName).pdb</ProgramDatabaseFile>
<MapFileName>$(OutDir)$(TargetFileName).map</MapFileName>
<StripPrivateSymbols>$(OutDir)$(TargetFileName).public.pdb</StripPrivateSymbols>
</Link>
</ItemDefinitionGroup>
<ItemGroup>

View File

@ -1,6 +1,32 @@
= Changelog
v0.16::
This release brings support for reparse points and symbolic links as well as other minor changes.
- Reparse points are a general mechanism for attaching special behavior to files. Symbolic links in Windows are implemented as reparse points. WinFsp supports any kind of reparse point including symbolic links.
- The WinFsp FUSE implementation supports symbolic links. It also supports POSIX special files (FIFO, SOCK, CHR, BLK) as NFS reparse points (see https://msdn.microsoft.com/en-us/library/dn617178.aspx).
- User mode file systems that wish to support reparse points will have to set the `FSP_FSCTL_VOLUME_PARAMS::ReparsePoints` flag and implement the `FSP_FILE_SYSTEM_INTERFACE` methods `ResolveReparsePoints`, `GetReparsePoint`, `SetReparsePoint`, `DeleteReparsePoint`. More information in this blog article: http://www.secfs.net/winfsp/blog/files/reparse-points-symlinks-api-changes.html
- The installation now includes public symbol files for all WinFsp components shipped.
v0.15::
This is a minor release that brings support for Windows 7 and 32-bit OS'es.
- Fixes a number of issues for Windows 7. Windows 7 is now officially supported.
- Fixes a number of issues with the 32-bit FSD and user mode components. 32-bit versions of Windows are now officially supported.
v0.14::
This release includes support for file systems protected by credentials.
- WinFsp now supports file systems that require username/password to be unlocked (e.g. sshfs/secfs). Such file systems must add a DWORD registry value with name "Credentials" and value 1 under their WinFsp.Launcher service entry. The WinFsp network provider will then prompt for credentials using the `CredUIPromptForWindowsCredentials` API. Credentials can optionally be saved with the Windows Credential Manager.
- WinFsp-FUSE now uses the S-1-0-65534 <--> 65534 mapping for unmapped SID/UID's. The Anonymous SID mapping from the previous release had security issues.
v0.13::
This release includes a Cygwin package, an API change and some other minor changes:

View File

@ -0,0 +1,50 @@
= Native API vs FUSE
This document compares the "native" WinFsp API to the FUSE API and provides a rationale for the existence of both within WinFsp.
== Overview
WinFsp provides two different but conceptually similar API's for the same purpose of implementing a user mode file system:
- The WinFsp API, which is documented in the include file `inc/winfsp/winfsp.h` (and online at http://www.secfs.net/winfsp/apiref/). This API consists of the `FSP_FILE_SYSTEM_INTERFACE` "class" and the `FspFileSystem*` functions.
- The FUSE (high-level) API, which is the well understood API from the FUSE project originally by Miklos Szeredi.
Given the similarities between the two API's some questions naturally arise:
- What are the differences between the two API's?
- Why are both needed?
- What is the target audience for each API?
== Comparison
The primary difference between the two API's is that the WinFsp API is being designed to use all features available to a Windows file system, whereas the FUSE API is being designed (by the FUSE project) to better fit a POSIX file system. For example, a Windows file system can do the following, that cannot be (easily) made available to FUSE:
- Create and manage alternate data streams.
- Manage arbitrary security descriptors (SID's and ACL's vs POSIX permissions).
- Create and manage special files beyond what is supported through FUSE `mknod` (using reparse points).
- Support volume labels.
- Allow the file system to fulfill Read/Write requests using asynchronous I/O.
Furthermore there are other smaller, but still important differences:
- The file deletion model on Windows is different from the FUSE/POSIX model.
- The reparse mechanism (which supports symbolic links) on Windows is quite more powerful (and more complicated) than FUSE/POSIX symbolic links.
- Windows uses UTF-16 for file names, whereas FUSE uses UTF-8, requiring constant conversions between the two.
These and other differences make the creation of the WinFsp FUSE compatibility layer non-trivial and suggest that a native API that more closely resembles the Windows file system model is desirable. At the same time there are hundreds of FUSE (high-level) file systems and having a FUSE compatible API is also very desirable.
== Target Audiences
As mentioned WinFsp provides two different API's; to further complicate matters the FUSE API can be used from both a native Windows application and a Cygwin (POSIX) application. There are then 3 different audiences that the API's cater for:
- The WinFsp API audience. This consists of Windows-only file systems or cross-platform file systems that wish to provide maximum features and/or performance on Windows.
- The FUSE API for native Windows audience. This consists of FUSE file-systems that have had their core file system code ported to Windows, but have not yet been integrated into the operating system. It also includes cross-platform file systems that do not wish to include advanced (non-POSIX) Windows file system features.
- The FUSE API for Cygwin audience. This consists of FUSE file-systems that are ported to Windows/Cygwin with minimal work. For example, the author of this document has ported SSHFS to Cygwin using this API and a minimal SSHFS patch.
For the developer of a new or Windows exclusive file system the recommendation is to use the WinFsp API as it provides support for all features of the Windows file system.
For the developer of a FUSE file system that wishes to port their file system to Windows a natural process may be the following:
- Use the FUSE API for Cygwin to port the file system to Cygwin. In many cases little or no changes to the file system code are required.
- Use the FUSE API for native Windows to port the file system to native Windows. This would require porting the core file system code (i.e. those parts of the file system code that actually manage and organize files). Little to no changes should be required for the file system FUSE layer glue.
- Use the WinFsp API only if the file system requires maximum features and/or performance under Windows.

View File

@ -1,6 +1,6 @@
= SSHFS Port Case Study
This document is a case study in porting SSHFS to Windows and WinFsp. At the time of this writing WinFsp has a native API, but no FUSE compatible API. The main purpose of this case study is to develop a FUSE compatible API for WinFsp.
This document is a case study in porting SSHFS to Windows and WinFsp. At the time that the document was started WinFsp had a native API, but no FUSE compatible API. The main purpose of the case study was to develop a FUSE compatible API for WinFsp.
== Step 1: Gather Information about SSHFS
@ -215,3 +215,52 @@ fuse_destroy
----
With this change `fuse_daemonize` works and allows me to declare the Cygwin portion of the SSHFS port complete!
== Step 5: POSIX special files
Although WinFsp now has a working FUSE implementation there remains an important problem: how to handle POSIX special files such as named pipes (FIFO), devices (CHR, BLK), sockets (SOCK) or symbolic links (LNK).
While Windows has support for symbolic links (LNK) there is no direct support for other POSIX special files. The question then is how to represent such files when they are accessed by Windows. This is especially important to systems like Cygwin that understand POSIX special files and can even create them.
Cygwin normally emulates symbolic links and special files using special shortcut (.lnk) files. However many FUSE file systems support POSIX special files; it is desirable then that applications, like Cygwin, that understand them should be able to create and access them without resorting to hacks like using .lnk files.
The problem was originally mentioned by Herbert Stocker on the Cygwin mailing list:
[quote]
____
The mkfifo system call will have Cygwin create a .lnk file and
WinFsp will forward it as such to the file system process. The
system calls readdir or open will then have the file system
process tell WinFsp that there is a .lnk file and Cygwin will
translate this back to a fifo, so in this sense it does work.
But the file system will see a file (with name *.lnk) where it
should see a pipe (mknod call with \'mode' set to S_IFIFO).
IMHO one could say this is a break of the FUSE API.
Practically it will break:
- File systems that special-treat pipe files (or .lnk files).
- If one uses sshfs to connect to a Linux based server and
issues the command mkfifo foo from Cygwin, the server will
end up with a .lnk file instead of a pipe special file.
- Imagine something like mysqlfs, which stores the stuff in a
database. When you run SQL statements to analyze the data
in the file system, you won't see the pipes as such. Or if
you open the file system from Linux you'll see the .lnk
files.
____
Herbert is of course right. A .lnk file is not a FIFO to any application other than Cygwin. We need a better mechanism for representing special files. One such mechanism is reparse points.
Reparse points can be viewed as a form of special metadata that can be attached to a file or directory. The interesting thing about reparse points is that they can have special meaning to a file system driver (NTFS/WinFsp), a filter driver (e.g. a hierarchical storage system) or even an application (Cygwin).
Symbolic links are already implemented as reparse points on Windows. We could perhaps define a new reparse point type for representing POSIX special files. Turns out that this is unnecessary, because Microsoft has already defined a reparse point type for special files on NFS: https://msdn.microsoft.com/en-us/library/dn617178.aspx
It is a relatively straightforward task then to map reparse point operations into their FUSE equivalents:
GetReparsePoint:: Mapped to `getattr`/`fgetattr` and possibly `readlink` (in the case of a symbolic link). The returned `stat.st_mode` information is transformed to the appropriate reparse point information.
SetReparsePoint:: Mapped to `symlink` or `mknod` depending on whether a symbolic link or other special file is created.

View File

@ -244,6 +244,7 @@ struct fsp_fuse_env
void (*memfree)(void *);
int (*daemonize)(int);
int (*set_signal_handlers)(void *);
void (*reserved[4])();
};
FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_signal_handler)(int sig);

View File

@ -59,8 +59,12 @@ extern const __declspec(selectany) GUID FspFsvrtDeviceClassGuid =
#define FSP_FSCTL_VOLUME_PREFIX_SIZE (64 * sizeof(WCHAR))
#define FSP_FSCTL_VOLUME_NAME_SIZEMAX (FSP_FSCTL_VOLUME_NAME_SIZE + FSP_FSCTL_VOLUME_PREFIX_SIZE)
#define FSP_FSCTL_TRANSACT_PATH_SIZEMAX 2048
#define FSP_FSCTL_TRANSACT_REQ_SIZEMAX (4096 - 64) /* 64: size for internal request header */
#define FSP_FSCTL_TRANSACT_RSP_SIZEMAX (4096 - 64) /* symmetry! */
#define FSP_FSCTL_TRANSACT_REQ_BUFFER_SIZEMAX (FSP_FSCTL_TRANSACT_REQ_SIZEMAX - sizeof(FSP_FSCTL_TRANSACT_REQ))
#define FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX (FSP_FSCTL_TRANSACT_RSP_SIZEMAX - sizeof(FSP_FSCTL_TRANSACT_RSP))
#define FSP_FSCTL_TRANSACT_BATCH_BUFFER_SIZEMIN 16384
#define FSP_FSCTL_TRANSACT_BUFFER_SIZEMIN FSP_FSCTL_TRANSACT_REQ_SIZEMAX
@ -124,7 +128,8 @@ typedef struct
UINT32 CasePreservedNames:1; /* file system preserves the case of file names */
UINT32 UnicodeOnDisk:1; /* file system supports Unicode in file names */
UINT32 PersistentAcls:1; /* file system preserves and enforces access control lists */
UINT32 ReparsePoints:1; /* file system supports reparse points (!!!: unimplemented) */
UINT32 ReparsePoints:1; /* file system supports reparse points */
UINT32 ReparsePointsAccessCheck:1; /* file system performs reparse point access checks */
UINT32 NamedStreams:1; /* file system supports named streams (!!!: unimplemented) */
UINT32 HardLinks:1; /* unimplemented; set to 0 */
UINT32 ExtendedAttributes:1; /* unimplemented; set to 0 */
@ -288,6 +293,14 @@ typedef struct
FSP_FSCTL_TRANSACT_BUF Pattern;
} QueryDirectory;
struct
{
UINT64 UserContext;
UINT64 UserContext2;
UINT32 FsControlCode;
FSP_FSCTL_TRANSACT_BUF Buffer;
UINT16 TargetOnFileSystem; /* the target of the symbolic link is on this file system */
} FileSystemControl;
struct
{
UINT64 UserContext;
UINT64 UserContext2;
@ -330,7 +343,7 @@ typedef struct
/* IoStatus.Status == STATUS_REPARSE */
struct
{
FSP_FSCTL_TRANSACT_BUF FileName; /* file name to use for STATUS_REPARSE */
FSP_FSCTL_TRANSACT_BUF Buffer;
} Reparse;
} Create;
struct
@ -358,6 +371,10 @@ typedef struct
FSP_FSCTL_VOLUME_INFO VolumeInfo;
} SetVolumeInformation;
struct
{
FSP_FSCTL_TRANSACT_BUF Buffer;
} FileSystemControl;
struct
{
FSP_FSCTL_TRANSACT_BUF SecurityDescriptor;
} QuerySecurity;

View File

@ -40,13 +40,51 @@
extern "C" {
#endif
/*
* The REPARSE_DATA_BUFFER definitions appear to be missing from the user mode headers.
*/
#if !defined(SYMLINK_FLAG_RELATIVE)
#define SYMLINK_FLAG_RELATIVE 1
#define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer)
typedef struct _REPARSE_DATA_BUFFER
{
ULONG ReparseTag;
USHORT ReparseDataLength;
USHORT Reserved;
union
{
struct
{
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
struct
{
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
} MountPointReparseBuffer;
struct
{
UCHAR DataBuffer[1];
} GenericReparseBuffer;
} DUMMYUNIONNAME;
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
#endif
/**
* @group File System
*
* A user mode file system is a program that uses the WinFsp API to expose a file system to
* Windows. The user mode file system must implement the operations in FSP_FILE_SYSTEM_INTERFACE,
* create a file system object using FspFileSystemCreate and start its dispatcher using
* FspFileSystemStartDispatcher. At that point it will start receing file system requests on the
* FspFileSystemStartDispatcher. At that point it will start receiving file system requests on the
* FSP_FILE_SYSTEM_INTERFACE operations.
*/
typedef struct _FSP_FILE_SYSTEM FSP_FILE_SYSTEM;
@ -54,6 +92,36 @@ typedef NTSTATUS FSP_FILE_SYSTEM_OPERATION_GUARD(FSP_FILE_SYSTEM *,
FSP_FSCTL_TRANSACT_REQ *, FSP_FSCTL_TRANSACT_RSP *);
typedef NTSTATUS FSP_FILE_SYSTEM_OPERATION(FSP_FILE_SYSTEM *,
FSP_FSCTL_TRANSACT_REQ *, FSP_FSCTL_TRANSACT_RSP *);
/**
* User mode file system locking strategy.
*
* Two concurrency models are provided:
*
* 1. A fine-grained concurrency model where file system NAMESPACE accesses
* are guarded using an exclusive-shared (read-write) lock. File I/O is not
* guarded and concurrent reads/writes/etc. are possible. [Note that the FSD
* will still apply an exclusive-shared lock PER INDIVIDUAL FILE, but it will
* not limit I/O operations for different files.]
*
* The fine-grained concurrency model applies the exclusive-shared lock as
* follows:
* <ul>
* <li>EXCL: SetVolumeLabel, Flush(Volume),
* Create, Cleanup(Delete), SetInformation(Rename)</li>
* <li>SHRD: GetVolumeInfo, Open, SetInformation(Disposition), ReadDirectory</li>
* <li>NONE: all other operations</li>
* </ul>
*
* 2. A coarse-grained concurrency model where all file system accesses are
* guarded by a mutually exclusive lock.
*
* @see FspFileSystemSetOperationGuardStrategy
*/
typedef enum
{
FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FINE = 0,
FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE,
} FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY;
/**
* @class FSP_FILE_SYSTEM
* File system interface.
@ -74,7 +142,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* Pointer to a structure that will receive the volume information on successful return
* from this call.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
NTSTATUS (*GetVolumeInfo)(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
@ -92,7 +160,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* Pointer to a structure that will receive the volume information on successful return
* from this call.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
NTSTATUS (*SetVolumeLabel)(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
@ -108,6 +176,10 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* @param PFileAttributes
* Pointer to a memory location that will receive the file attributes on successful return
* from this call. May be NULL.
*
* If this call returns STATUS_REPARSE, the file system MAY place here the index of the
* first reparse point within FileName. The file system MAY also leave this at its default
* value of 0.
* @param SecurityDescriptor
* Pointer to a buffer that will receive the file security descriptor on successful return
* from this call. May be NULL.
@ -116,10 +188,14 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* security descriptor buffer. On output it will contain the actual size of the security
* descriptor copied into the security descriptor buffer. May be NULL.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS, STATUS_REPARSE or error code.
*
* STATUS_REPARSE should be returned by file systems that support reparse points when
* they encounter a FileName that contains reparse points anywhere but the final path
* component.
*/
NTSTATUS (*GetSecurityByName)(FSP_FILE_SYSTEM *FileSystem,
PWSTR FileName, PUINT32 PFileAttributes,
PWSTR FileName, PUINT32 PFileAttributes/* or ReparsePointIndex */,
PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize);
/**
* Create new file or directory.
@ -158,7 +234,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* Pointer to a structure that will receive the file information on successful return
* from this call. This information includes file attributes, file times, etc.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
NTSTATUS (*Create)(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
@ -193,7 +269,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* Pointer to a structure that will receive the file information on successful return
* from this call. This information includes file attributes, file times, etc.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
NTSTATUS (*Open)(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
@ -217,7 +293,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* Pointer to a structure that will receive the file information on successful return
* from this call. This information includes file attributes, file times, etc.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
NTSTATUS (*Overwrite)(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
@ -292,7 +368,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* @param PBytesTransferred [out]
* Pointer to a memory location that will receive the actual number of bytes read.
* @return
* STATUS_SUCCESS on error code. STATUS_PENDING is supported allowing for asynchronous
* STATUS_SUCCESS or error code. STATUS_PENDING is supported allowing for asynchronous
* operation.
*/
NTSTATUS (*Read)(FSP_FILE_SYSTEM *FileSystem,
@ -325,7 +401,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* Pointer to a structure that will receive the file information on successful return
* from this call. This information includes file attributes, file times, etc.
* @return
* STATUS_SUCCESS on error code. STATUS_PENDING is supported allowing for asynchronous
* STATUS_SUCCESS or error code. STATUS_PENDING is supported allowing for asynchronous
* operation.
*/
NTSTATUS (*Write)(FSP_FILE_SYSTEM *FileSystem,
@ -345,7 +421,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* @param FileNode
* The file node of the file to be flushed. When NULL the whole volume is being flushed.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
NTSTATUS (*Flush)(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
@ -363,7 +439,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* Pointer to a structure that will receive the file information on successful return
* from this call. This information includes file attributes, file times, etc.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
NTSTATUS (*GetFileInfo)(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
@ -394,7 +470,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* Pointer to a structure that will receive the file information on successful return
* from this call. This information includes file attributes, file times, etc.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
NTSTATUS (*SetBasicInfo)(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
@ -435,7 +511,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* Pointer to a structure that will receive the file information on successful return
* from this call. This information includes file attributes, file times, etc.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
NTSTATUS (*SetFileSize)(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
@ -463,7 +539,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* @param FileName
* The name of the file or directory to test for deletion.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
* @see
* Cleanup
*/
@ -495,7 +571,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* @param ReplaceIfExists
* Whether to replace a file that already exists at NewFileName.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
NTSTATUS (*Rename)(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
@ -516,7 +592,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* security descriptor buffer. On output it will contain the actual size of the security
* descriptor copied into the security descriptor buffer. Cannot be NULL.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
NTSTATUS (*GetSecurity)(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
@ -535,7 +611,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* Security descriptor to apply to the file or directory. This security descriptor will
* always be in self-relative format.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
NTSTATUS (*SetSecurity)(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
@ -566,7 +642,7 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
* @param PBytesTransferred [out]
* Pointer to a memory location that will receive the actual number of bytes read.
* @return
* STATUS_SUCCESS on error code. STATUS_PENDING is supported allowing for asynchronous
* STATUS_SUCCESS or error code. STATUS_PENDING is supported allowing for asynchronous
* operation.
* @see
* FspFileSystemAddDirInfo
@ -576,12 +652,129 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
PVOID FileNode, PVOID Buffer, UINT64 Offset, ULONG Length,
PWSTR Pattern,
PULONG PBytesTransferred);
/**
* Resolve reparse points.
*
* Reparse points are a general mechanism for attaching special behavior to files.
* A file or directory can contain a reparse point. A reparse point is data that has
* special meaning to the file system, Windows or user applications. For example, NTFS
* and Windows use reparse points to implement symbolic links. As another example,
* a particular file system may use reparse points to emulate UNIX FIFO's.
*
* This function is expected to resolve as many reparse points as possible. If a reparse
* point is encountered that is not understood by the file system further reparse point
* resolution should stop; the reparse point data should be returned to the FSD with status
* STATUS_REPARSE/reparse-tag. If a reparse point (symbolic link) is encountered that is
* understood by the file system but points outside it, the reparse point should be
* resolved, but further reparse point resolution should stop; the resolved file name
* should be returned to the FSD with status STATUS_REPARSE/IO_REPARSE.
*
* @param FileSystem
* The file system on which this request is posted.
* @param FileName
* The name of the file or directory to have its reparse points resolved.
* @param ReparsePointIndex
* The index of the first reparse point within FileName.
* @param ResolveLastPathComponent
* If FALSE, the last path component of FileName should not be resolved, even
* if it is a reparse point that can be resolved. If TRUE, all path components
* should be resolved if possible.
* @param PIoStatus
* Pointer to storage that will receive the status to return to the FSD. When
* this function succeeds it must set PIoStatus->Status to STATUS_REPARSE and
* PIoStatus->Information to either IO_REPARSE or the reparse tag.
* @param Buffer
* Pointer to a buffer that will receive the resolved file name (IO_REPARSE) or
* reparse data (reparse tag). If the function returns a file name, it should
* not be NULL terminated.
* @param PSize [in,out]
* Pointer to the buffer size. On input it contains the size of the buffer.
* On output it will contain the actual size of data copied.
* @return
* STATUS_REPARSE or error code.
*/
NTSTATUS (*ResolveReparsePoints)(FSP_FILE_SYSTEM *FileSystem,
PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent,
PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize);
/**
* Get reparse point.
*
* @param FileSystem
* The file system on which this request is posted.
* @param Request
* The request posted by the kernel mode FSD.
* @param FileNode
* The file node of the reparse point.
* @param FileName
* The file name of the reparse point.
* @param Buffer
* Pointer to a buffer that will receive the results of this operation. If
* the function returns a symbolic link path, it should not be NULL terminated.
* @param PSize [in,out]
* Pointer to the buffer size. On input it contains the size of the buffer.
* On output it will contain the actual size of data copied.
* @return
* STATUS_SUCCESS or error code.
* @see
* SetReparsePoint
*/
NTSTATUS (*GetReparsePoint)(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
PVOID FileNode,
PWSTR FileName, PVOID Buffer, PSIZE_T PSize);
/**
* Set reparse point.
*
* @param FileSystem
* The file system on which this request is posted.
* @param Request
* The request posted by the kernel mode FSD.
* @param FileNode
* The file node of the reparse point.
* @param FileName
* The file name of the reparse point.
* @param Buffer
* Pointer to a buffer that contains the data for this operation. If this buffer
* contains a symbolic link path, it should not be assumed to be NULL terminated.
* @param Size
* Size of data to write.
* @return
* STATUS_SUCCESS or error code.
* @see
* GetReparsePoint
*/
NTSTATUS (*SetReparsePoint)(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
PVOID FileNode,
PWSTR FileName, PVOID Buffer, SIZE_T Size);
/**
* Delete reparse point.
*
* @param FileSystem
* The file system on which this request is posted.
* @param Request
* The request posted by the kernel mode FSD.
* @param FileNode
* The file node of the reparse point.
* @param FileName
* The file name of the reparse point.
* @param Buffer
* Pointer to a buffer that contains the data for this operation.
* @param Size
* Size of data to write.
* @return
* STATUS_SUCCESS or error code.
*/
NTSTATUS (*DeleteReparsePoint)(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
PVOID FileNode,
PWSTR FileName, PVOID Buffer, SIZE_T Size);
/*
* This ensures that this interface will always contain 64 function pointers.
* Please update when changing the interface as it is important for future compatibility.
*/
NTSTATUS (*Reserved[45])();
NTSTATUS (*Reserved[41])();
} FSP_FILE_SYSTEM_INTERFACE;
#if defined(WINFSP_DLL_INTERNAL)
/*
@ -591,11 +784,6 @@ typedef struct _FSP_FILE_SYSTEM_INTERFACE
static_assert(sizeof(FSP_FILE_SYSTEM_INTERFACE) == 64 * sizeof(NTSTATUS (*)()),
"FSP_FILE_SYSTEM_INTERFACE must have 64 entries.");
#endif
typedef enum
{
FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FINE = 0,
FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE,
} FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY;
typedef struct _FSP_FILE_SYSTEM
{
UINT16 Version;
@ -628,7 +816,7 @@ typedef struct _FSP_FILE_SYSTEM
* Pointer that will receive the file system object created on successful return from this
* call.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
FSP_API NTSTATUS FspFileSystemCreate(PWSTR DevicePath,
const FSP_FSCTL_VOLUME_PARAMS *VolumeParams,
@ -654,7 +842,7 @@ FSP_API VOID FspFileSystemDelete(FSP_FILE_SYSTEM *FileSystem);
* The mount point for the new file system. A value of NULL means that the file system should
* use the next available drive letter counting downwards from Z: as its mount point.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
FSP_API NTSTATUS FspFileSystemSetMountPoint(FSP_FILE_SYSTEM *FileSystem, PWSTR MountPoint);
/**
@ -677,7 +865,7 @@ FSP_API VOID FspFileSystemRemoveMountPoint(FSP_FILE_SYSTEM *FileSystem);
* The number of threads for the file system dispatcher. A value of 0 will create a default
* number of threads and should be chosen in most cases.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
FSP_API NTSTATUS FspFileSystemStartDispatcher(FSP_FILE_SYSTEM *FileSystem, ULONG ThreadCount);
/**
@ -740,6 +928,16 @@ VOID FspFileSystemSetOperationGuard(FSP_FILE_SYSTEM *FileSystem,
FileSystem->EnterOperation = EnterOperation;
FileSystem->LeaveOperation = LeaveOperation;
}
/**
* Set file system locking strategy.
*
* @param FileSystem
* The file system object.
* @param GuardStrategy
* The locking (guard) strategy.
* @see
* FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY
*/
static inline
VOID FspFileSystemSetOperationGuardStrategy(FSP_FILE_SYSTEM *FileSystem,
FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY GuardStrategy)
@ -807,6 +1005,8 @@ FSP_API NTSTATUS FspFileSystemOpSetVolumeInformation(FSP_FILE_SYSTEM *FileSystem
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response);
FSP_API NTSTATUS FspFileSystemOpQueryDirectory(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response);
FSP_API NTSTATUS FspFileSystemOpFileSystemControl(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response);
FSP_API NTSTATUS FspFileSystemOpQuerySecurity(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response);
FSP_API NTSTATUS FspFileSystemOpSetSecurity(FSP_FILE_SYSTEM *FileSystem,
@ -841,6 +1041,116 @@ FSP_API NTSTATUS FspFileSystemOpSetSecurity(FSP_FILE_SYSTEM *FileSystem,
*/
FSP_API BOOLEAN FspFileSystemAddDirInfo(FSP_FSCTL_DIR_INFO *DirInfo,
PVOID Buffer, ULONG Length, PULONG PBytesTransferred);
/**
* Find reparse point in file name.
*
* Given a file name this function returns an index to the first path component that is a reparse
* point. The function will call the supplied GetReparsePointByName function for every path
* component until it finds a reparse point or the whole path is processed.
*
* This is a helper for implementing the GetSecurityByName operation in file systems
* that support reparse points.
*
* @param FileSystem
* The file system object.
* @param GetReparsePointByName
* Pointer to function that can retrieve reparse point information by name. The
* FspFileSystemFindReparsePoint will call this function with the Buffer and PSize
* arguments set to NULL. The function should return STATUS_SUCCESS if the passed
* FileName is a reparse point or STATUS_NOT_A_REPARSE_POINT (or other error code)
* otherwise.
* @param Context
* User context to supply to GetReparsePointByName.
* @param FileName
* The name of the file or directory.
* @param PReparsePointIndex
* Pointer to a memory location that will receive the index of the first reparse point
* within FileName. A value is only placed in this memory location if the function returns
* TRUE. May be NULL.
* @return
* TRUE if a reparse point was found, FALSE otherwise.
* @see
* GetSecurityByName
*/
FSP_API BOOLEAN FspFileSystemFindReparsePoint(FSP_FILE_SYSTEM *FileSystem,
NTSTATUS (*GetReparsePointByName)(
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize),
PVOID Context,
PWSTR FileName, PUINT32 PReparsePointIndex);
/**
* Resolve reparse points.
*
* Given a file name (and an index where to start resolving) this function will attempt to
* resolve as many reparse points as possible. The function will call the supplied
* GetReparsePointByName function for every path component until it resolves the reparse points
* or the whole path is processed.
*
* This is a helper for implementing the ResolveReparsePoints operation in file systems
* that support reparse points.
*
* @param FileSystem
* The file system object.
* @param GetReparsePointByName
* Pointer to function that can retrieve reparse point information by name. The function
* should return STATUS_SUCCESS if the passed FileName is a reparse point or
* STATUS_NOT_A_REPARSE_POINT (or other error code) otherwise.
* @param Context
* User context to supply to GetReparsePointByName.
* @param FileName
* The name of the file or directory to have its reparse points resolved.
* @param ReparsePointIndex
* The index of the first reparse point within FileName.
* @param ResolveLastPathComponent
* If FALSE, the last path component of FileName should not be resolved, even
* if it is a reparse point that can be resolved. If TRUE, all path components
* should be resolved if possible.
* @param PIoStatus
* Pointer to storage that will receive the status to return to the FSD. When
* this function succeeds it must set PIoStatus->Status to STATUS_REPARSE and
* PIoStatus->Information to either IO_REPARSE or the reparse tag.
* @param Buffer
* Pointer to a buffer that will receive the resolved file name (IO_REPARSE) or
* reparse data (reparse tag). If the function returns a file name, it should
* not be NULL terminated.
* @param PSize [in,out]
* Pointer to the buffer size. On input it contains the size of the buffer.
* On output it will contain the actual size of data copied.
* @return
* STATUS_REPARSE or error code.
* @see
* ResolveReparsePoints
*/
FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem,
NTSTATUS (*GetReparsePointByName)(
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize),
PVOID Context,
PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent,
PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize);
/**
* Test whether reparse data can be replaced.
*
* This is a helper for implementing the SetReparsePoint/DeleteReparsePoint operation
* in file systems that support reparse points.
*
* @param CurrentReparseData
* Pointer to the current reparse data.
* @param CurrentReparseDataSize
* Pointer to the current reparse data size.
* @param ReplaceReparseData
* Pointer to the replacement reparse data.
* @param ReplaceReparseDataSize
* Pointer to the replacement reparse data size.
* @return
* STATUS_SUCCESS or error code.
* @see
* SetReparsePoint
* DeleteReparsePoint
*/
FSP_API NTSTATUS FspFileSystemCanReplaceReparsePoint(
PVOID CurrentReparseData, SIZE_T CurrentReparseDataSize,
PVOID ReplaceReparseData, SIZE_T ReplaceReparseDataSize);
/*
* Security
@ -849,7 +1159,7 @@ FSP_API PGENERIC_MAPPING FspGetFileGenericMapping(VOID);
FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
BOOLEAN CheckParentDirectory, BOOLEAN AllowTraverseCheck,
UINT32 DesiredAccess, PUINT32 PGrantedAccess,
UINT32 DesiredAccess, PUINT32 PGrantedAccess/* or ReparsePointIndex */,
PSECURITY_DESCRIPTOR *PSecurityDescriptor);
FSP_API NTSTATUS FspCreateSecurityDescriptor(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
@ -885,8 +1195,20 @@ FSP_API NTSTATUS FspPosixMapPermissionsToSecurityDescriptor(
FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions(
PSECURITY_DESCRIPTOR SecurityDescriptor,
PUINT32 PUid, PUINT32 PGid, PUINT32 PMode);
FSP_API NTSTATUS FspPosixMapWindowsToPosixPath(PWSTR WindowsPath, char **PPosixPath);
FSP_API NTSTATUS FspPosixMapPosixToWindowsPath(const char *PosixPath, PWSTR *PWindowsPath);
FSP_API NTSTATUS FspPosixMapWindowsToPosixPathEx(PWSTR WindowsPath, char **PPosixPath,
BOOLEAN Translate);
FSP_API NTSTATUS FspPosixMapPosixToWindowsPathEx(const char *PosixPath, PWSTR *PWindowsPath,
BOOLEAN Translate);
static inline
NTSTATUS FspPosixMapWindowsToPosixPath(PWSTR WindowsPath, char **PPosixPath)
{
return FspPosixMapWindowsToPosixPathEx(WindowsPath, PPosixPath, TRUE);
}
static inline
NTSTATUS FspPosixMapPosixToWindowsPath(const char *PosixPath, PWSTR *PWindowsPath)
{
return FspPosixMapPosixToWindowsPathEx(PosixPath, PWindowsPath, TRUE);
}
FSP_API VOID FspPosixDeletePath(void *Path);
/*
@ -973,7 +1295,7 @@ ULONG FspServiceRun(PWSTR ServiceName,
* Pointer that will receive the service object created on successful return from this
* call.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
FSP_API NTSTATUS FspServiceCreate(PWSTR ServiceName,
FSP_SERVICE_START *OnStart,
@ -1053,7 +1375,7 @@ FSP_API ULONG FspServiceGetExitCode(FSP_SERVICE *Service);
* @param Service
* The service object.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
FSP_API NTSTATUS FspServiceLoop(FSP_SERVICE *Service);
/**
@ -1066,7 +1388,7 @@ FSP_API NTSTATUS FspServiceLoop(FSP_SERVICE *Service);
* @param Service
* The service object.
* @return
* STATUS_SUCCESS on error code.
* STATUS_SUCCESS or error code.
*/
FSP_API VOID FspServiceStop(FSP_SERVICE *Service);
/**

View File

@ -1,6 +1,6 @@
NAME="fuse"
VERSION=2.8
RELEASE=2
RELEASE=3
CATEGORY="Utils"
SUMMARY="WinFsp-FUSE compatibility layer"
DESCRIPTION="WinFsp-FUSE enables FUSE file systems to be run on Cygwin."

View File

@ -161,6 +161,98 @@ static const char *FspDebugLogVolumeInfoString(FSP_FSCTL_VOLUME_INFO *VolumeInfo
return Buf;
}
static const char *FspDebugLogWideCharBufferString(PVOID WideCharBuf, ULONG Length, char *Buf)
{
WCHAR TempWideCharBuf[64 + 1];
if (Length > sizeof TempWideCharBuf - sizeof(WCHAR))
Length = sizeof TempWideCharBuf - sizeof(WCHAR);
memcpy(TempWideCharBuf, WideCharBuf, Length);
TempWideCharBuf[Length / sizeof(WCHAR)] = L'\0';
wsprintfA(Buf, "%.64S", TempWideCharBuf);
return Buf;
}
static const char *FspDebugLogReparseDataString(PVOID ReparseData0, char *Buf)
{
union
{
PREPARSE_DATA_BUFFER D;
PREPARSE_GUID_DATA_BUFFER G;
} ReparseData;
char SubstituteName[64 + 1], PrintName[64 + 1];
ReparseData.D = ReparseData0;
if (0 == ReparseData.D->ReparseDataLength)
wsprintfA(Buf,
"{"
"ReparseTag=%#lx, "
"ReparseDataLength=%hu"
"}",
ReparseData.D->ReparseTag, ReparseData.D->ReparseDataLength);
else if (IO_REPARSE_TAG_MOUNT_POINT == ReparseData.D->ReparseTag)
wsprintfA(Buf,
"{"
"ReparseTag=IO_REPARSE_TAG_MOUNT_POINT, "
"SubstituteName=\"%s\", "
"PrintName=\"%s\""
"}",
FspDebugLogWideCharBufferString(
ReparseData.D->MountPointReparseBuffer.PathBuffer +
ReparseData.D->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR),
ReparseData.D->MountPointReparseBuffer.SubstituteNameLength,
SubstituteName),
FspDebugLogWideCharBufferString(
ReparseData.D->MountPointReparseBuffer.PathBuffer +
ReparseData.D->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR),
ReparseData.D->MountPointReparseBuffer.PrintNameLength,
PrintName));
else if (IO_REPARSE_TAG_SYMLINK == ReparseData.D->ReparseTag)
wsprintfA(Buf,
"{"
"ReparseTag=IO_REPARSE_TAG_SYMLINK, "
"SubstituteName=\"%s\", "
"PrintName=\"%s\", "
"Flags=%u"
"}",
FspDebugLogWideCharBufferString(
ReparseData.D->SymbolicLinkReparseBuffer.PathBuffer +
ReparseData.D->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR),
ReparseData.D->SymbolicLinkReparseBuffer.SubstituteNameLength,
SubstituteName),
FspDebugLogWideCharBufferString(
ReparseData.D->SymbolicLinkReparseBuffer.PathBuffer +
ReparseData.D->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR),
ReparseData.D->SymbolicLinkReparseBuffer.PrintNameLength,
PrintName),
ReparseData.D->SymbolicLinkReparseBuffer.Flags);
else if (IsReparseTagMicrosoft(ReparseData.D->ReparseTag))
wsprintfA(Buf,
"{"
"ReparseTag=%#lx, "
"ReparseDataLength=%hu"
"}",
ReparseData.D->ReparseTag, ReparseData.D->ReparseDataLength);
else
#define Guid ReparseData.G->ReparseGuid
wsprintfA(Buf,
"{"
"ReparseTag=%#lx, "
"ReparseDataLength=%hu, "
"ReparseGuid={%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"
"}",
ReparseData.G->ReparseTag, ReparseData.G->ReparseDataLength,
Guid.Data1, Guid.Data2, Guid.Data3,
Guid.Data4[0], Guid.Data4[1], Guid.Data4[2], Guid.Data4[3],
Guid.Data4[4], Guid.Data4[5], Guid.Data4[6], Guid.Data4[7]);
#undef Guid
return Buf;
}
static VOID FspDebugLogRequestVoid(FSP_FSCTL_TRANSACT_REQ *Request, const char *Name)
{
FspDebugLog("%S[TID=%04lx]: %p: >>%s\n",
@ -178,6 +270,7 @@ FSP_API VOID FspDebugLogRequest(FSP_FSCTL_TRANSACT_REQ *Request)
{
char UserContextBuf[40];
char CreationTimeBuf[32], LastAccessTimeBuf[32], LastWriteTimeBuf[32];
char InfoBuf[256];
char *Sddl = 0;
switch (Request->Kind)
@ -368,6 +461,7 @@ FSP_API VOID FspDebugLogRequest(FSP_FSCTL_TRANSACT_REQ *Request)
FspDebugLogUserContextString(
Request->Req.SetInformation.UserContext, Request->Req.SetInformation.UserContext2,
UserContextBuf));
break;
}
break;
case FspFsctlTransactQueryEaKind:
@ -423,7 +517,45 @@ FSP_API VOID FspDebugLogRequest(FSP_FSCTL_TRANSACT_REQ *Request)
Request->Req.QueryDirectory.Pattern.Size ? "\"" : "");
break;
case FspFsctlTransactFileSystemControlKind:
FspDebugLogRequestVoid(Request, "FILESYSTEMCONTROL");
switch (Request->Req.FileSystemControl.FsControlCode)
{
case FSCTL_GET_REPARSE_POINT:
FspDebugLog("%S[TID=%04lx]: %p: >>FileSystemControl [FSCTL_GET_REPARSE_POINT] %s%S%s%s\n",
FspDiagIdent(), GetCurrentThreadId(), Request->Hint,
Request->FileName.Size ? "\"" : "",
Request->FileName.Size ? (PWSTR)Request->Buffer : L"",
Request->FileName.Size ? "\", " : "",
FspDebugLogUserContextString(
Request->Req.FileSystemControl.UserContext, Request->Req.FileSystemControl.UserContext2,
UserContextBuf));
break;
case FSCTL_SET_REPARSE_POINT:
case FSCTL_DELETE_REPARSE_POINT:
FspDebugLog("%S[TID=%04lx]: %p: >>FileSystemControl [%s] %s%S%s%s "
"ReparseData=%s\n",
FspDiagIdent(), GetCurrentThreadId(), Request->Hint,
FSCTL_SET_REPARSE_POINT == Request->Req.FileSystemControl.FsControlCode ?
"FSCTL_SET_REPARSE_POINT" : "FSCTL_DELETE_REPARSE_POINT",
Request->FileName.Size ? "\"" : "",
Request->FileName.Size ? (PWSTR)Request->Buffer : L"",
Request->FileName.Size ? "\", " : "",
FspDebugLogUserContextString(
Request->Req.FileSystemControl.UserContext, Request->Req.FileSystemControl.UserContext2,
UserContextBuf),
FspDebugLogReparseDataString(Request->Buffer + Request->Req.FileSystemControl.Buffer.Offset,
InfoBuf));
break;
default:
FspDebugLog("%S[TID=%04lx]: %p: >>FileSystemControl [INVALID] %s%S%s%s\n",
FspDiagIdent(), GetCurrentThreadId(), Request->Hint,
Request->FileName.Size ? "\"" : "",
Request->FileName.Size ? (PWSTR)Request->Buffer : L"",
Request->FileName.Size ? "\", " : "",
FspDebugLogUserContextString(
Request->Req.FileSystemControl.UserContext, Request->Req.FileSystemControl.UserContext2,
UserContextBuf));
break;
}
break;
case FspFsctlTransactDeviceControlKind:
FspDebugLogRequestVoid(Request, "DEVICECONTROL");
@ -492,11 +624,27 @@ FSP_API VOID FspDebugLogResponse(FSP_FSCTL_TRANSACT_RSP *Response)
if (!NT_SUCCESS(Response->IoStatus.Status))
FspDebugLogResponseStatus(Response, "Create");
else if (STATUS_REPARSE == Response->IoStatus.Status)
FspDebugLog("%S[TID=%04lx]: %p: <<Create IoStatus=%lx[%ld] "
"Reparse.FileName=\"%S\"\n",
FspDiagIdent(), GetCurrentThreadId(), Response->Hint,
Response->IoStatus.Status, Response->IoStatus.Information,
(PWSTR)(Response->Buffer + Response->Rsp.Create.Reparse.FileName.Offset));
{
if (0/*IO_REPARSE*/ == Response->IoStatus.Information)
FspDebugLog("%S[TID=%04lx]: %p: <<Create IoStatus=%lx[%ld] "
"Reparse.FileName=\"%s\"\n",
FspDiagIdent(), GetCurrentThreadId(), Response->Hint,
Response->IoStatus.Status, Response->IoStatus.Information,
FspDebugLogWideCharBufferString(
Response->Buffer + Response->Rsp.Create.Reparse.Buffer.Offset,
Response->Rsp.Create.Reparse.Buffer.Size,
InfoBuf));
else if (1/*IO_REMOUNT*/ == Response->IoStatus.Information)
FspDebugLogResponseStatus(Response, "Create");
else
FspDebugLog("%S[TID=%04lx]: %p: <<Create IoStatus=%lx[%ld] "
"Reparse.Data=\"%s\"\n",
FspDiagIdent(), GetCurrentThreadId(), Response->Hint,
Response->IoStatus.Status, Response->IoStatus.Information,
FspDebugLogReparseDataString(
Response->Buffer + Response->Rsp.Create.Reparse.Buffer.Offset,
InfoBuf));
}
else
FspDebugLog("%S[TID=%04lx]: %p: <<Create IoStatus=%lx[%ld] "
"UserContext=%s, GrantedAccess=%lx, FileInfo=%s\n",
@ -590,7 +738,16 @@ FSP_API VOID FspDebugLogResponse(FSP_FSCTL_TRANSACT_RSP *Response)
FspDebugLogResponseStatus(Response, "QueryDirectory");
break;
case FspFsctlTransactFileSystemControlKind:
FspDebugLogResponseStatus(Response, "FILESYSTEMCONTROL");
if (!NT_SUCCESS(Response->IoStatus.Status) ||
0 == Response->Rsp.FileSystemControl.Buffer.Size)
FspDebugLogResponseStatus(Response, "FileSystemControl");
else
FspDebugLog("%S[TID=%04lx]: %p: <<FileSystemControl IoStatus=%lx[%ld] "
"ReparseData=%s\n",
FspDiagIdent(), GetCurrentThreadId(), Response->Hint,
Response->IoStatus.Status, Response->IoStatus.Information,
FspDebugLogReparseDataString(Response->Buffer + Response->Rsp.FileSystemControl.Buffer.Offset,
InfoBuf));
break;
case FspFsctlTransactDeviceControlKind:
FspDebugLogResponseStatus(Response, "DEVICECONTROL");

View File

@ -96,6 +96,7 @@ FSP_API NTSTATUS FspFileSystemCreate(PWSTR DevicePath,
FileSystem->Operations[FspFsctlTransactQueryVolumeInformationKind] = FspFileSystemOpQueryVolumeInformation;
FileSystem->Operations[FspFsctlTransactSetVolumeInformationKind] = FspFileSystemOpSetVolumeInformation;
FileSystem->Operations[FspFsctlTransactQueryDirectoryKind] = FspFileSystemOpQueryDirectory;
FileSystem->Operations[FspFsctlTransactFileSystemControlKind] = FspFileSystemOpFileSystemControl;
FileSystem->Operations[FspFsctlTransactQuerySecurityKind] = FspFileSystemOpQuerySecurity;
FileSystem->Operations[FspFsctlTransactSetSecurityKind] = FspFileSystemOpSetSecurity;
FileSystem->Interface = Interface;

View File

@ -132,7 +132,9 @@ exit:
FSP_API NTSTATUS FspFsctlStop(HANDLE VolumeHandle)
{
if (!DeviceIoControl(VolumeHandle, FSP_FSCTL_STOP, 0, 0, 0, 0, 0, 0))
DWORD Bytes;
if (!DeviceIoControl(VolumeHandle, FSP_FSCTL_STOP, 0, 0, 0, 0, &Bytes, 0))
return FspNtStatusFromWin32(GetLastError());
return STATUS_SUCCESS;

View File

@ -17,26 +17,6 @@
#include <dll/library.h>
/*
* The FspFileSystemOpEnter/FspFileSystemOpLeave functions guard against
* concurrent accesses. Two concurrency models are provided:
*
* 1. A fine-grained concurrency model where file system NAMESPACE accesses
* are guarded using an exclusive-shared (read-write) lock. File I/O is not
* guarded and concurrent reads/writes/etc. are possible. [Note that the FSD
* will still apply an exclusive-shared lock PER INDIVIDUAL FILE, but it will
* not limit I/O operations for different files.]
*
* The fine-grained concurrency model applies the exclusive-shared lock as
* follows:
* - EXCL: SetVolumeLabel, Create, Cleanup(Delete), SetInformation(Rename)
* - SHRD: GetVolumeInfo, Open, SetInformation(Disposition), ReadDirectory
* - NONE: all other operations
*
* 2. A coarse-grained concurrency model where all file system accesses are
* guarded by a mutually exclusive lock.
*/
FSP_API NTSTATUS FspFileSystemOpEnter(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
{
@ -49,7 +29,9 @@ FSP_API NTSTATUS FspFileSystemOpEnter(FSP_FILE_SYSTEM *FileSystem,
Request->Req.Cleanup.Delete) ||
(FspFsctlTransactSetInformationKind == Request->Kind &&
10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
FspFsctlTransactSetVolumeInformationKind == Request->Kind)
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
0 == Request->Req.FlushBuffers.UserContext))
{
AcquireSRWLockExclusive(&FileSystem->OpGuardLock);
}
@ -84,7 +66,9 @@ FSP_API NTSTATUS FspFileSystemOpLeave(FSP_FILE_SYSTEM *FileSystem,
Request->Req.Cleanup.Delete) ||
(FspFsctlTransactSetInformationKind == Request->Kind &&
10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
FspFsctlTransactSetVolumeInformationKind == Request->Kind)
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
0 == Request->Req.FlushBuffers.UserContext))
{
ReleaseSRWLockExclusive(&FileSystem->OpGuardLock);
}
@ -107,13 +91,48 @@ FSP_API NTSTATUS FspFileSystemOpLeave(FSP_FILE_SYSTEM *FileSystem,
return STATUS_SUCCESS;
}
static inline
NTSTATUS FspFileSystemCallResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response,
UINT32 ReparsePointIndex)
{
NTSTATUS Result = STATUS_INVALID_DEVICE_REQUEST;
IO_STATUS_BLOCK IoStatus;
SIZE_T Size;
if (0 != FileSystem->Interface->ResolveReparsePoints)
{
memset(&IoStatus, 0, sizeof IoStatus);
Size = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX;
Result = FileSystem->Interface->ResolveReparsePoints(FileSystem,
(PWSTR)Request->Buffer,
ReparsePointIndex,
!(Request->Req.Create.CreateOptions & FILE_OPEN_REPARSE_POINT),
&IoStatus,
Response->Buffer,
&Size);
if (NT_SUCCESS(Result))
{
Result = STATUS_REPARSE;
Response->IoStatus.Information = (UINT32)IoStatus.Information;
Response->Size = (UINT16)(sizeof *Response + Size);
Response->Rsp.Create.Reparse.Buffer.Offset = 0;
Response->Rsp.Create.Reparse.Buffer.Size = (UINT16)Size;
}
}
return Result;
}
static inline
NTSTATUS FspFileSystemCreateCheck(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response,
BOOLEAN AllowTraverseCheck, PUINT32 PGrantedAccess,
PSECURITY_DESCRIPTOR *PSecurityDescriptor)
{
NTSTATUS Result;
UINT32 GrantedAccess;
/*
* CreateCheck consists of checking the parent directory for the
@ -127,8 +146,10 @@ NTSTATUS FspFileSystemCreateCheck(FSP_FILE_SYSTEM *FileSystem,
Result = FspAccessCheckEx(FileSystem, Request, TRUE, AllowTraverseCheck,
(Request->Req.Create.CreateOptions & FILE_DIRECTORY_FILE) ?
FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE,
PGrantedAccess, PSecurityDescriptor);
if (NT_SUCCESS(Result))
&GrantedAccess, PSecurityDescriptor);
if (STATUS_REPARSE == Result)
Result = FspFileSystemCallResolveReparsePoints(FileSystem, Request, Response, GrantedAccess);
else if (NT_SUCCESS(Result))
{
*PGrantedAccess = (MAXIMUM_ALLOWED & Request->Req.Create.DesiredAccess) ?
FspGetFileGenericMapping()->GenericAll : Request->Req.Create.DesiredAccess;
@ -139,10 +160,11 @@ NTSTATUS FspFileSystemCreateCheck(FSP_FILE_SYSTEM *FileSystem,
static inline
NTSTATUS FspFileSystemOpenCheck(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response,
BOOLEAN AllowTraverseCheck, PUINT32 PGrantedAccess)
{
NTSTATUS Result;
UINT32 GrantedAccess;
/*
* OpenCheck consists of checking the file for the desired access,
@ -157,9 +179,12 @@ NTSTATUS FspFileSystemOpenCheck(FSP_FILE_SYSTEM *FileSystem,
Result = FspAccessCheck(FileSystem, Request, FALSE, AllowTraverseCheck,
Request->Req.Create.DesiredAccess |
((Request->Req.Create.CreateOptions & FILE_DELETE_ON_CLOSE) ? DELETE : 0),
PGrantedAccess);
if (NT_SUCCESS(Result))
&GrantedAccess);
if (STATUS_REPARSE == Result)
Result = FspFileSystemCallResolveReparsePoints(FileSystem, Request, Response, GrantedAccess);
else if (NT_SUCCESS(Result))
{
*PGrantedAccess = GrantedAccess;
if (0 == (Request->Req.Create.DesiredAccess & MAXIMUM_ALLOWED))
*PGrantedAccess &= ~DELETE | (Request->Req.Create.DesiredAccess & DELETE);
}
@ -169,10 +194,11 @@ NTSTATUS FspFileSystemOpenCheck(FSP_FILE_SYSTEM *FileSystem,
static inline
NTSTATUS FspFileSystemOverwriteCheck(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response,
BOOLEAN AllowTraverseCheck, PUINT32 PGrantedAccess)
{
NTSTATUS Result;
UINT32 GrantedAccess;
BOOLEAN Supersede = FILE_SUPERSEDE == ((Request->Req.Create.CreateOptions >> 24) & 0xff);
/*
@ -190,9 +216,12 @@ NTSTATUS FspFileSystemOverwriteCheck(FSP_FILE_SYSTEM *FileSystem,
Request->Req.Create.DesiredAccess |
(Supersede ? DELETE : FILE_WRITE_DATA) |
((Request->Req.Create.CreateOptions & FILE_DELETE_ON_CLOSE) ? DELETE : 0),
PGrantedAccess);
if (NT_SUCCESS(Result))
&GrantedAccess);
if (STATUS_REPARSE == Result)
Result = FspFileSystemCallResolveReparsePoints(FileSystem, Request, Response, GrantedAccess);
else if (NT_SUCCESS(Result))
{
*PGrantedAccess = GrantedAccess;
if (0 == (Request->Req.Create.DesiredAccess & MAXIMUM_ALLOWED))
*PGrantedAccess &= ~(DELETE | FILE_WRITE_DATA) |
(Request->Req.Create.DesiredAccess & (DELETE | FILE_WRITE_DATA));
@ -201,6 +230,29 @@ NTSTATUS FspFileSystemOverwriteCheck(FSP_FILE_SYSTEM *FileSystem,
return Result;
}
static inline
NTSTATUS FspFileSystemOpenTargetDirectoryCheck(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response,
PUINT32 PGrantedAccess)
{
NTSTATUS Result;
UINT32 GrantedAccess;
/*
* OpenTargetDirectoryCheck consists of checking the parent directory
* for the desired access.
*/
Result = FspAccessCheck(FileSystem, Request, TRUE, TRUE, Request->Req.Create.DesiredAccess,
&GrantedAccess);
if (STATUS_REPARSE == Result)
Result = FspFileSystemCallResolveReparsePoints(FileSystem, Request, Response, GrantedAccess);
else if (NT_SUCCESS(Result))
*PGrantedAccess = GrantedAccess;
return Result;
}
static inline
NTSTATUS FspFileSystemRenameCheck(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request)
@ -235,7 +287,9 @@ NTSTATUS FspFileSystemRenameCheck(FSP_FILE_SYSTEM *FileSystem,
CreateRequest->Size = sizeof CreateRequest +
Request->Req.SetInformation.Info.Rename.NewFileName.Size;
CreateRequest->Kind = FspFsctlTransactCreateKind;
CreateRequest->Req.Create.CreateOptions = FILE_DELETE_ON_CLOSE; /* force read-only check! */
CreateRequest->Req.Create.CreateOptions =
FILE_DELETE_ON_CLOSE | /* force read-only check! */
FILE_OPEN_REPARSE_POINT; /* allow rename over reparse point */
CreateRequest->Req.Create.AccessToken = Request->Req.SetInformation.Info.Rename.AccessToken;
CreateRequest->Req.Create.UserMode = TRUE;
CreateRequest->FileName.Offset = 0;
@ -248,6 +302,9 @@ NTSTATUS FspFileSystemRenameCheck(FSP_FILE_SYSTEM *FileSystem,
MemFree(CreateRequest);
if (STATUS_REPARSE == Result)
Result = STATUS_SUCCESS; /* file system should not return STATUS_REPARSE during rename */
return Result;
}
@ -260,8 +317,9 @@ static NTSTATUS FspFileSystemOpCreate_FileCreate(FSP_FILE_SYSTEM *FileSystem,
PVOID FileNode;
FSP_FSCTL_FILE_INFO FileInfo;
Result = FspFileSystemCreateCheck(FileSystem, Request, TRUE, &GrantedAccess, &ParentDescriptor);
if (!NT_SUCCESS(Result))
Result = FspFileSystemCreateCheck(FileSystem, Request, Response, TRUE,
&GrantedAccess, &ParentDescriptor);
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
return Result;
Result = FspCreateSecurityDescriptor(FileSystem, Request, ParentDescriptor, &ObjectDescriptor);
@ -294,8 +352,8 @@ static NTSTATUS FspFileSystemOpCreate_FileOpen(FSP_FILE_SYSTEM *FileSystem,
PVOID FileNode;
FSP_FSCTL_FILE_INFO FileInfo;
Result = FspFileSystemOpenCheck(FileSystem, Request, TRUE, &GrantedAccess);
if (!NT_SUCCESS(Result))
Result = FspFileSystemOpenCheck(FileSystem, Request, Response, TRUE, &GrantedAccess);
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
return Result;
FileNode = 0;
@ -323,8 +381,8 @@ static NTSTATUS FspFileSystemOpCreate_FileOpenIf(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_FILE_INFO FileInfo;
BOOLEAN Create = FALSE;
Result = FspFileSystemOpenCheck(FileSystem, Request, TRUE, &GrantedAccess);
if (!NT_SUCCESS(Result))
Result = FspFileSystemOpenCheck(FileSystem, Request, Response, TRUE, &GrantedAccess);
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
{
if (STATUS_OBJECT_NAME_NOT_FOUND != Result)
return Result;
@ -348,8 +406,9 @@ static NTSTATUS FspFileSystemOpCreate_FileOpenIf(FSP_FILE_SYSTEM *FileSystem,
if (Create)
{
Result = FspFileSystemCreateCheck(FileSystem, Request, FALSE, &GrantedAccess, &ParentDescriptor);
if (!NT_SUCCESS(Result))
Result = FspFileSystemCreateCheck(FileSystem, Request, Response, FALSE,
&GrantedAccess, &ParentDescriptor);
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
return Result;
Result = FspCreateSecurityDescriptor(FileSystem, Request, ParentDescriptor, &ObjectDescriptor);
@ -384,8 +443,8 @@ static NTSTATUS FspFileSystemOpCreate_FileOverwrite(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_FILE_INFO FileInfo;
BOOLEAN Supersede = FILE_SUPERSEDE == ((Request->Req.Create.CreateOptions >> 24) & 0xff);
Result = FspFileSystemOverwriteCheck(FileSystem, Request, TRUE, &GrantedAccess);
if (!NT_SUCCESS(Result))
Result = FspFileSystemOverwriteCheck(FileSystem, Request, Response, TRUE, &GrantedAccess);
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
return Result;
FileNode = 0;
@ -413,8 +472,8 @@ static NTSTATUS FspFileSystemOpCreate_FileOverwriteIf(FSP_FILE_SYSTEM *FileSyste
FSP_FSCTL_FILE_INFO FileInfo;
BOOLEAN Create = FALSE;
Result = FspFileSystemOverwriteCheck(FileSystem, Request, TRUE, &GrantedAccess);
if (!NT_SUCCESS(Result))
Result = FspFileSystemOverwriteCheck(FileSystem, Request, Response, TRUE, &GrantedAccess);
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
{
if (STATUS_OBJECT_NAME_NOT_FOUND != Result)
return Result;
@ -438,8 +497,9 @@ static NTSTATUS FspFileSystemOpCreate_FileOverwriteIf(FSP_FILE_SYSTEM *FileSyste
if (Create)
{
Result = FspFileSystemCreateCheck(FileSystem, Request, FALSE, &GrantedAccess, &ParentDescriptor);
if (!NT_SUCCESS(Result))
Result = FspFileSystemCreateCheck(FileSystem, Request, Response,
FALSE, &GrantedAccess, &ParentDescriptor);
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
return Result;
Result = FspCreateSecurityDescriptor(FileSystem, Request, ParentDescriptor, &ObjectDescriptor);
@ -476,9 +536,8 @@ static NTSTATUS FspFileSystemOpCreate_FileOpenTargetDirectory(FSP_FILE_SYSTEM *F
FSP_FSCTL_FILE_INFO FileInfo;
UINT32 Information;
Result = FspAccessCheck(FileSystem, Request, TRUE, TRUE,
Request->Req.Create.DesiredAccess, &GrantedAccess);
if (!NT_SUCCESS(Result))
Result = FspFileSystemOpenTargetDirectoryCheck(FileSystem, Request, Response, &GrantedAccess);
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
return Result;
FileNode = 0;
@ -819,6 +878,65 @@ FSP_API NTSTATUS FspFileSystemOpQueryDirectory(FSP_FILE_SYSTEM *FileSystem,
return Result;
}
FSP_API NTSTATUS FspFileSystemOpFileSystemControl(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
{
NTSTATUS Result;
PREPARSE_DATA_BUFFER ReparseData;
SIZE_T Size;
Result = STATUS_INVALID_DEVICE_REQUEST;
switch (Request->Req.FileSystemControl.FsControlCode)
{
case FSCTL_GET_REPARSE_POINT:
if (0 != FileSystem->Interface->GetReparsePoint)
{
ReparseData = (PREPARSE_DATA_BUFFER)Response->Buffer;
memset(ReparseData, 0, sizeof *ReparseData);
Size = FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX;
Result = FileSystem->Interface->GetReparsePoint(FileSystem, Request,
(PVOID)Request->Req.FileSystemControl.UserContext,
(PWSTR)Request->Buffer, ReparseData, &Size);
if (NT_SUCCESS(Result))
{
Response->Size = (UINT16)(sizeof *Response + Size);
Response->Rsp.FileSystemControl.Buffer.Offset = 0;
Response->Rsp.FileSystemControl.Buffer.Size = (UINT16)Size;
}
}
break;
case FSCTL_SET_REPARSE_POINT:
if (0 != FileSystem->Interface->SetReparsePoint)
{
ReparseData = (PREPARSE_DATA_BUFFER)
(Request->Buffer + Request->Req.FileSystemControl.Buffer.Offset);
Result = FileSystem->Interface->SetReparsePoint(FileSystem, Request,
(PVOID)Request->Req.FileSystemControl.UserContext,
(PWSTR)Request->Buffer,
ReparseData,
Request->Req.FileSystemControl.Buffer.Size);
}
break;
case FSCTL_DELETE_REPARSE_POINT:
if (0 != FileSystem->Interface->DeleteReparsePoint)
{
ReparseData = (PREPARSE_DATA_BUFFER)
(Request->Buffer + Request->Req.FileSystemControl.Buffer.Offset);
Result = FileSystem->Interface->DeleteReparsePoint(FileSystem, Request,
(PVOID)Request->Req.FileSystemControl.UserContext,
(PWSTR)Request->Buffer,
ReparseData,
Request->Req.FileSystemControl.Buffer.Size);
}
break;
}
return Result;
}
FSP_API NTSTATUS FspFileSystemOpQuerySecurity(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
{
@ -883,3 +1001,264 @@ FSP_API BOOLEAN FspFileSystemAddDirInfo(FSP_FSCTL_DIR_INFO *DirInfo,
return TRUE;
}
FSP_API BOOLEAN FspFileSystemFindReparsePoint(FSP_FILE_SYSTEM *FileSystem,
NTSTATUS (*GetReparsePointByName)(
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize),
PVOID Context,
PWSTR FileName, PUINT32 PReparsePointIndex)
{
PWSTR RemainderPath, LastPathComponent;
NTSTATUS Result;
RemainderPath = FileName;
for (;;)
{
while (L'\\' == *RemainderPath)
RemainderPath++;
LastPathComponent = RemainderPath;
while (L'\\' != *RemainderPath)
{
if (L'\0' == *RemainderPath)
return FALSE;
RemainderPath++;
}
*RemainderPath = L'\0';
Result = GetReparsePointByName(FileSystem, Context, FileName, TRUE, 0, 0);
*RemainderPath = L'\\';
if (STATUS_NOT_A_REPARSE_POINT == Result)
/* it was not a reparse point; continue */
continue;
else if (!NT_SUCCESS(Result))
return FALSE;
/*
* Found a reparse point!
*/
if (0 != PReparsePointIndex)
*PReparsePointIndex = (ULONG)(LastPathComponent - FileName);
return TRUE;
}
return FALSE;
}
FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem,
NTSTATUS (*GetReparsePointByName)(
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize),
PVOID Context,
PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent0,
PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize)
{
PREPARSE_DATA_BUFFER OutputReparseData;
PWSTR TargetPath, RemainderPath, LastPathComponent, NewRemainderPath, ReparseTargetPath;
WCHAR RemainderChar;
union
{
REPARSE_DATA_BUFFER V;
UINT8 B[FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX];
} ReparseDataBuf;
PREPARSE_DATA_BUFFER ReparseData = &ReparseDataBuf.V;
SIZE_T ReparseDataSize, RemainderPathSize, ReparseTargetPathLength;
BOOLEAN ResolveLastPathComponent;
ULONG MaxTries = 32;
NTSTATUS Result;
RemainderPathSize = (lstrlenW(FileName) + 1) * sizeof(WCHAR);
if (FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) +
RemainderPathSize > *PSize)
return STATUS_REPARSE_POINT_NOT_RESOLVED;
OutputReparseData = Buffer;
memset(OutputReparseData, 0,
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer));
OutputReparseData->ReparseTag = IO_REPARSE_TAG_SYMLINK;
OutputReparseData->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
TargetPath = OutputReparseData->SymbolicLinkReparseBuffer.PathBuffer;
memcpy(TargetPath, FileName, RemainderPathSize);
ResolveLastPathComponent = ResolveLastPathComponent0;
RemainderPath = TargetPath + ReparsePointIndex;
for (;;)
{
while (L'\\' == *RemainderPath)
RemainderPath++;
LastPathComponent = RemainderPath;
while (L'\\' != *RemainderPath)
{
if (L'\0' == *RemainderPath)
{
if (!ResolveLastPathComponent)
goto symlink_exit;
ResolveLastPathComponent = FALSE;
break;
}
RemainderPath++;
}
/* handle dot and dotdot! */
if (L'.' == LastPathComponent[0])
{
if (RemainderPath == LastPathComponent + 1)
{
/* dot */
ReparseTargetPath = 0;
ReparseTargetPathLength = 0;
NewRemainderPath = LastPathComponent;
while (TargetPath < NewRemainderPath)
{
NewRemainderPath--;
if (L'\\' == *NewRemainderPath)
break;
}
goto reparse;
}
if (L'.' == LastPathComponent[1] && RemainderPath == LastPathComponent + 2)
{
/* dotdot */
ReparseTargetPath = 0;
ReparseTargetPathLength = 0;
NewRemainderPath = LastPathComponent;
while (TargetPath < NewRemainderPath)
{
NewRemainderPath--;
if (L'\\' != *NewRemainderPath)
break;
}
while (TargetPath < NewRemainderPath)
{
NewRemainderPath--;
if (L'\\' == *NewRemainderPath)
break;
}
goto reparse;
}
}
RemainderChar = *RemainderPath; *RemainderPath = L'\0';
ReparseDataSize = sizeof ReparseDataBuf;
Result = GetReparsePointByName(FileSystem, Context, TargetPath, '\0' != RemainderChar,
ReparseData, &ReparseDataSize);
*RemainderPath = RemainderChar;
if (STATUS_NOT_A_REPARSE_POINT == Result)
/* it was not a reparse point; continue */
continue;
else if (!NT_SUCCESS(Result))
{
if (STATUS_OBJECT_NAME_NOT_FOUND != Result || '\0' != RemainderChar)
Result = STATUS_OBJECT_PATH_NOT_FOUND;
return Result;
}
/*
* Found a reparse point!
*/
/* if not a symlink return the full reparse point */
if (IO_REPARSE_TAG_SYMLINK != ReparseData->ReparseTag)
goto reparse_data_exit;
if (0 == --MaxTries)
return STATUS_REPARSE_POINT_NOT_RESOLVED;
ReparseTargetPath = ReparseData->SymbolicLinkReparseBuffer.PathBuffer +
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
ReparseTargetPathLength = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
/* if device relative symlink replace whole path; else replace last path component */
NewRemainderPath = ReparseTargetPathLength >= sizeof(WCHAR) && L'\\' == ReparseTargetPath[0] ?
TargetPath : LastPathComponent;
reparse:
RemainderPathSize = (lstrlenW(RemainderPath) + 1) * sizeof(WCHAR);
if ((PUINT8)NewRemainderPath + ReparseTargetPathLength + RemainderPathSize >
(PUINT8)Buffer + *PSize)
return STATUS_REPARSE_POINT_NOT_RESOLVED;
/* move remainder path to its new position */
memmove((PUINT8)NewRemainderPath + ReparseTargetPathLength,
RemainderPath, RemainderPathSize);
/* copy symlink target */
memcpy(NewRemainderPath, ReparseTargetPath, ReparseTargetPathLength);
/* if an absolute (in the NT namespace) symlink exit now */
if (0 != ReparseTargetPath /* ensure we are not doing dot handling */ &&
0 == (ReparseData->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) &&
ReparseTargetPathLength >= sizeof(WCHAR) && L'\\' == ReparseTargetPath[0])
{
OutputReparseData->SymbolicLinkReparseBuffer.Flags = 0;
goto symlink_exit;
}
ResolveLastPathComponent = ResolveLastPathComponent0;
RemainderPath = NewRemainderPath;
}
symlink_exit:
OutputReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength =
OutputReparseData->SymbolicLinkReparseBuffer.PrintNameLength =
(USHORT)lstrlenW(OutputReparseData->SymbolicLinkReparseBuffer.PathBuffer) * sizeof(WCHAR);
OutputReparseData->ReparseDataLength =
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) -
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer) +
OutputReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
*PSize = FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer) +
OutputReparseData->ReparseDataLength;
PIoStatus->Status = STATUS_REPARSE;
PIoStatus->Information = ReparseData->ReparseTag;
return STATUS_REPARSE;
reparse_data_exit:
if (ReparseDataSize > *PSize)
return IO_REPARSE_TAG_SYMLINK != ReparseData->ReparseTag ?
STATUS_IO_REPARSE_DATA_INVALID : STATUS_REPARSE_POINT_NOT_RESOLVED;
*PSize = ReparseDataSize;
memcpy(Buffer, ReparseData, ReparseDataSize);
PIoStatus->Status = STATUS_REPARSE;
PIoStatus->Information = ReparseData->ReparseTag;
return STATUS_REPARSE;
}
FSP_API NTSTATUS FspFileSystemCanReplaceReparsePoint(
PVOID CurrentReparseData, SIZE_T CurrentReparseDataSize,
PVOID ReplaceReparseData, SIZE_T ReplaceReparseDataSize)
{
if (sizeof(ULONG) > CurrentReparseDataSize ||
sizeof(ULONG) > ReplaceReparseDataSize)
return STATUS_IO_REPARSE_DATA_INVALID; /* should not happen! */
else if (*(PULONG)CurrentReparseData != *(PULONG)ReplaceReparseData)
return STATUS_IO_REPARSE_TAG_MISMATCH;
else if (!IsReparseTagMicrosoft(*(PULONG)CurrentReparseData) && (
(SIZE_T)REPARSE_GUID_DATA_BUFFER_HEADER_SIZE > CurrentReparseDataSize ||
(SIZE_T)REPARSE_GUID_DATA_BUFFER_HEADER_SIZE > ReplaceReparseDataSize ||
*(PUINT32)&((PREPARSE_GUID_DATA_BUFFER)CurrentReparseData)->ReparseGuid.Data1 !=
*(PUINT32)&((PREPARSE_GUID_DATA_BUFFER)ReplaceReparseData)->ReparseGuid.Data1 ||
*(PUINT32)&((PREPARSE_GUID_DATA_BUFFER)CurrentReparseData)->ReparseGuid.Data2 !=
*(PUINT32)&((PREPARSE_GUID_DATA_BUFFER)ReplaceReparseData)->ReparseGuid.Data2 ||
*(PUINT32)&((PREPARSE_GUID_DATA_BUFFER)CurrentReparseData)->ReparseGuid.Data4[0] !=
*(PUINT32)&((PREPARSE_GUID_DATA_BUFFER)ReplaceReparseData)->ReparseGuid.Data4[0] ||
*(PUINT32)&((PREPARSE_GUID_DATA_BUFFER)CurrentReparseData)->ReparseGuid.Data4[4] !=
*(PUINT32)&((PREPARSE_GUID_DATA_BUFFER)ReplaceReparseData)->ReparseGuid.Data4[4]))
return STATUS_REPARSE_ATTRIBUTE_CONFLICT;
else
return STATUS_SUCCESS;
}

View File

@ -37,10 +37,12 @@ struct fsp_fuse_core_opt_data
set_umask, umask,
set_uid, uid,
set_gid, gid,
set_attr_timeout, attr_timeout;
set_attr_timeout, attr_timeout,
rellinks;
int set_FileInfoTimeout;
int CaseInsensitiveSearch, ReparsePoints,
NamedStreams, ReadOnlyVolume;
int CaseInsensitiveSearch,
NamedStreams,
ReadOnlyVolume;
FSP_FSCTL_VOLUME_PARAMS VolumeParams;
};
@ -78,6 +80,9 @@ static struct fuse_opt fsp_fuse_core_opts[] =
FUSE_OPT_KEY("intr_signal=", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("modules=", FUSE_OPT_KEY_DISCARD),
FSP_FUSE_CORE_OPT("rellinks", rellinks, 1),
FSP_FUSE_CORE_OPT("norellinks", rellinks, 0),
FSP_FUSE_CORE_OPT("SectorSize=%hu", VolumeParams.SectorSize, 4096),
FSP_FUSE_CORE_OPT("SectorsPerAllocationUnit=%hu", VolumeParams.SectorsPerAllocationUnit, 1),
FSP_FUSE_CORE_OPT("MaxComponentLength=%hu", VolumeParams.MaxComponentLength, 0),
@ -89,11 +94,11 @@ static struct fuse_opt fsp_fuse_core_opts[] =
FSP_FUSE_CORE_OPT("FileInfoTimeout=", set_FileInfoTimeout, 1),
FSP_FUSE_CORE_OPT("FileInfoTimeout=%d", VolumeParams.FileInfoTimeout, 0),
FSP_FUSE_CORE_OPT("CaseInsensitiveSearch", CaseInsensitiveSearch, 1),
FSP_FUSE_CORE_OPT("ReparsePoints", ReparsePoints, 1),
FSP_FUSE_CORE_OPT("NamedStreams", NamedStreams, 1),
FSP_FUSE_CORE_OPT("ReadOnlyVolume", ReadOnlyVolume, 1),
FUSE_OPT_KEY("ReparsePoints", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("HardLinks", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("ExtendedAttributes", FUSE_OPT_KEY_DISCARD),
FSP_FUSE_CORE_OPT("ReadOnlyVolume", ReadOnlyVolume, 1),
FUSE_OPT_KEY("--UNC=", 'U'),
FUSE_OPT_KEY("--VolumePrefix=", 'U'),
@ -449,7 +454,6 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
" -o VolumeSerialNumber=N 32-bit wide\n"
" -o FileInfoTimeout=N FileInfo/Security/VolumeInfo timeout (millisec)\n"
" -o CaseInsensitiveSearch file system supports case-insensitive file names\n"
//" -o ReparsePoints file system supports reparse points\n"
//" -o NamedStreams file system supports named streams\n"
//" -o ReadOnlyVolume file system is read only\n"
" --UNC=U --VolumePrefix=U UNC prefix (\\Server\\Share)\n");
@ -463,9 +467,9 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
return 1;
case 'U':
if ('U' == arg[2])
arg += sizeof "--UNC" - 1;
arg += sizeof "--UNC=" - 1;
else if ('V' == arg[2])
arg += sizeof "--VolumePrefix" - 1;
arg += sizeof "--VolumePrefix=" - 1;
if (0 == MultiByteToWideChar(CP_UTF8, 0, arg, -1,
opt_data->VolumeParams.Prefix, sizeof opt_data->VolumeParams.Prefix / sizeof(WCHAR)))
return -1;
@ -498,7 +502,8 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env,
opt_data.VolumeParams.FileInfoTimeout = opt_data.set_attr_timeout * 1000;
opt_data.VolumeParams.CaseSensitiveSearch = !opt_data.CaseInsensitiveSearch;
opt_data.VolumeParams.PersistentAcls = TRUE;
opt_data.VolumeParams.ReparsePoints = !!opt_data.ReparsePoints;
opt_data.VolumeParams.ReparsePoints = TRUE;
opt_data.VolumeParams.ReparsePointsAccessCheck = FALSE;
opt_data.VolumeParams.NamedStreams = !!opt_data.NamedStreams;
opt_data.VolumeParams.ReadOnlyVolume = !!opt_data.ReadOnlyVolume;
@ -507,6 +512,10 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env,
goto fail;
f->env = env;
f->set_umask = opt_data.set_umask; f->umask = opt_data.umask;
f->set_uid = opt_data.set_uid; f->uid = opt_data.uid;
f->set_gid = opt_data.set_gid; f->gid = opt_data.gid;
f->rellinks = opt_data.rellinks;
memcpy(&f->ops, ops, opsize);
f->data = data;
f->DebugLog = opt_data.debug ? -1 : 0;

View File

@ -17,6 +17,90 @@
#include <dll/fuse/library.h>
static inline
VOID fsp_fuse_op_enter_lock(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
{
switch (FileSystem->OpGuardStrategy)
{
case FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FINE:
if ((FspFsctlTransactCreateKind == Request->Kind &&
FILE_OPEN != ((Request->Req.Create.CreateOptions >> 24) & 0xff)) ||
(FspFsctlTransactCleanupKind == Request->Kind &&
Request->Req.Cleanup.Delete) ||
(FspFsctlTransactSetInformationKind == Request->Kind &&
10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
0 == Request->Req.FlushBuffers.UserContext) ||
/* FSCTL_SET_REPARSE_POINT manipulates namespace */
(FspFsctlTransactFileSystemControlKind == Request->Kind &&
FSCTL_SET_REPARSE_POINT == Request->Req.FileSystemControl.FsControlCode))
{
AcquireSRWLockExclusive(&FileSystem->OpGuardLock);
}
else
if (FspFsctlTransactCreateKind == Request->Kind ||
(FspFsctlTransactSetInformationKind == Request->Kind &&
13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
FspFsctlTransactQueryDirectoryKind == Request->Kind ||
FspFsctlTransactQueryVolumeInformationKind == Request->Kind ||
/* FSCTL_GET_REPARSE_POINT may access namespace */
(FspFsctlTransactFileSystemControlKind == Request->Kind &&
FSCTL_GET_REPARSE_POINT == Request->Req.FileSystemControl.FsControlCode))
{
AcquireSRWLockShared(&FileSystem->OpGuardLock);
}
break;
case FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE:
AcquireSRWLockExclusive(&FileSystem->OpGuardLock);
break;
}
}
static inline
VOID fsp_fuse_op_leave_unlock(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
{
switch (FileSystem->OpGuardStrategy)
{
case FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FINE:
if ((FspFsctlTransactCreateKind == Request->Kind &&
FILE_OPEN != ((Request->Req.Create.CreateOptions >> 24) & 0xff)) ||
(FspFsctlTransactCleanupKind == Request->Kind &&
Request->Req.Cleanup.Delete) ||
(FspFsctlTransactSetInformationKind == Request->Kind &&
10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
FspFsctlTransactSetVolumeInformationKind == Request->Kind ||
(FspFsctlTransactFlushBuffersKind == Request->Kind &&
0 == Request->Req.FlushBuffers.UserContext) ||
/* FSCTL_SET_REPARSE_POINT manipulates namespace */
(FspFsctlTransactFileSystemControlKind == Request->Kind &&
FSCTL_SET_REPARSE_POINT == Request->Req.FileSystemControl.FsControlCode))
{
ReleaseSRWLockExclusive(&FileSystem->OpGuardLock);
}
else
if (FspFsctlTransactCreateKind == Request->Kind ||
(FspFsctlTransactSetInformationKind == Request->Kind &&
13/*FileDispositionInformation*/ == Request->Req.SetInformation.FileInformationClass) ||
FspFsctlTransactQueryDirectoryKind == Request->Kind ||
FspFsctlTransactQueryVolumeInformationKind == Request->Kind ||
/* FSCTL_GET_REPARSE_POINT may access namespace */
(FspFsctlTransactFileSystemControlKind == Request->Kind &&
FSCTL_GET_REPARSE_POINT == Request->Req.FileSystemControl.FsControlCode))
{
ReleaseSRWLockShared(&FileSystem->OpGuardLock);
}
break;
case FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE:
ReleaseSRWLockExclusive(&FileSystem->OpGuardLock);
break;
}
}
NTSTATUS fsp_fuse_op_enter(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response)
{
@ -131,9 +215,7 @@ NTSTATUS fsp_fuse_op_enter(FSP_FILE_SYSTEM *FileSystem,
goto exit;
}
Result = FspFileSystemOpEnter(FileSystem, Request, Response);
if (!NT_SUCCESS(Result))
goto exit;
fsp_fuse_op_enter_lock(FileSystem, Request, Response);
context->fuse = f;
context->private_data = f->data;
@ -167,7 +249,7 @@ NTSTATUS fsp_fuse_op_leave(FSP_FILE_SYSTEM *FileSystem,
struct fuse_context *context;
struct fsp_fuse_context_header *contexthdr;
FspFileSystemOpLeave(FileSystem, Request, Response);
fsp_fuse_op_leave_unlock(FileSystem, Request, Response);
context = fsp_fuse_get_context(f->env);
context->fuse = 0;
@ -183,9 +265,123 @@ NTSTATUS fsp_fuse_op_leave(FSP_FILE_SYSTEM *FileSystem,
return STATUS_SUCCESS;
}
static NTSTATUS fsp_fuse_intf_GetFileInfoEx(FSP_FILE_SYSTEM *FileSystem,
static NTSTATUS fsp_fuse_intf_NewHiddenName(FSP_FILE_SYSTEM *FileSystem,
char *PosixPath, char **PPosixHiddenPath)
{
struct fuse *f = FileSystem->UserContext;
NTSTATUS Result;
char *PosixHiddenPath = 0;
char *p, *lastp;
size_t Size;
RPC_STATUS RpcStatus;
union
{
struct { UINT32 V[4]; } Values;
UUID Uuid;
} UuidBuf;
struct fuse_stat stbuf;
int err, maxtries = 3;
*PPosixHiddenPath = 0;
p = PosixPath;
for (;;)
{
while ('/' == *p)
p++;
lastp = p;
while ('/' != *p)
{
if ('\0' == *p)
goto loopend;
p++;
}
}
loopend:;
Size = lastp - PosixPath + sizeof ".fuse_hidden0123456789abcdef";
PosixHiddenPath = MemAlloc(Size);
if (0 == PosixHiddenPath)
{
Result = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
memcpy(PosixHiddenPath, PosixPath, lastp - PosixPath);
do
{
RpcStatus = UuidCreate(&UuidBuf.Uuid);
if (S_OK != RpcStatus && RPC_S_UUID_LOCAL_ONLY != RpcStatus)
{
Result = STATUS_INTERNAL_ERROR;
goto exit;
}
wsprintfA(PosixHiddenPath + (lastp - PosixPath),
".fuse_hidden%08lx%08lx",
UuidBuf.Values.V[0] ^ UuidBuf.Values.V[2],
UuidBuf.Values.V[1] ^ UuidBuf.Values.V[3]);
memset(&stbuf, 0, sizeof stbuf);
if (0 != f->ops.getattr)
err = f->ops.getattr(PosixHiddenPath, (void *)&stbuf);
else
err = -ENOSYS;
} while (0 == err && 0 < --maxtries);
if (0 == err)
{
Result = STATUS_DEVICE_BUSY; // current EBUSY mapping
goto exit;
}
*PPosixHiddenPath = PosixHiddenPath;
Result = STATUS_SUCCESS;
exit:
if (!NT_SUCCESS(Result))
MemFree(PosixHiddenPath);
return Result;
}
static BOOLEAN fsp_fuse_intf_CheckSymlinkDirectory(FSP_FILE_SYSTEM *FileSystem,
const char *PosixPath)
{
struct fuse *f = FileSystem->UserContext;
char *PosixDotPath = 0;
size_t Length;
struct fuse_stat stbuf;
int err;
BOOLEAN Result = FALSE;
Length = lstrlenA(PosixPath);
PosixDotPath = MemAlloc(Length + 3);
if (0 != PosixDotPath)
{
memcpy(PosixDotPath, PosixPath, Length);
PosixDotPath[Length + 0] = '/';
PosixDotPath[Length + 1] = '.';
PosixDotPath[Length + 2] = '\0';
if (0 != f->ops.getattr)
err = f->ops.getattr(PosixDotPath, (void *)&stbuf);
else
err = -ENOSYS;
MemFree(PosixDotPath);
Result = 0 == err && 0040000 == (stbuf.st_mode & 0170000);
}
return Result;
}
#define fsp_fuse_intf_GetFileInfoEx(FileSystem, PosixPath, fi, PUid, PGid, PMode, FileInfo)\
fsp_fuse_intf_GetFileInfoFunnel(FileSystem, PosixPath, fi, PUid, PGid, PMode, 0, FileInfo)
static NTSTATUS fsp_fuse_intf_GetFileInfoFunnel(FSP_FILE_SYSTEM *FileSystem,
const char *PosixPath, struct fuse_file_info *fi,
PUINT32 PUid, PUINT32 PGid, PUINT32 PMode,
PUINT32 PUid, PUINT32 PGid, PUINT32 PMode, PUINT32 PDev,
FSP_FSCTL_FILE_INFO *FileInfo)
{
struct fuse *f = FileSystem->UserContext;
@ -205,14 +401,49 @@ static NTSTATUS fsp_fuse_intf_GetFileInfoEx(FSP_FILE_SYSTEM *FileSystem,
if (0 != err)
return fsp_fuse_ntstatus_from_errno(f->env, err);
if (f->set_umask)
stbuf.st_mode = (stbuf.st_mode & 0170000) | (0777 & ~f->umask);
if (f->set_uid)
stbuf.st_uid = f->uid;
if (f->set_gid)
stbuf.st_gid = f->gid;
*PUid = stbuf.st_uid;
*PGid = stbuf.st_gid;
*PMode = stbuf.st_mode;
if (0 != PDev)
*PDev = stbuf.st_rdev;
AllocationUnit = (UINT64)f->VolumeParams.SectorSize *
(UINT64)f->VolumeParams.SectorsPerAllocationUnit;
FileInfo->FileAttributes = (stbuf.st_mode & 0040000) ? FILE_ATTRIBUTE_DIRECTORY : 0;
FileInfo->ReparseTag = 0;
switch (stbuf.st_mode & 0170000)
{
case 0040000: /* S_IFDIR */
FileInfo->FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
FileInfo->ReparseTag = 0;
break;
case 0010000: /* S_IFIFO */
case 0020000: /* S_IFCHR */
case 0060000: /* S_IFBLK */
case 0140000: /* S_IFSOCK */
FileInfo->FileAttributes = FILE_ATTRIBUTE_REPARSE_POINT;
FileInfo->ReparseTag = IO_REPARSE_TAG_NFS;
break;
case 0120000: /* S_IFLNK */
if (FSP_FUSE_HAS_SYMLINKS(f))
{
FileInfo->FileAttributes = FILE_ATTRIBUTE_REPARSE_POINT;
FileInfo->ReparseTag = IO_REPARSE_TAG_SYMLINK;
if (fsp_fuse_intf_CheckSymlinkDirectory(FileSystem, PosixPath))
FileInfo->FileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
break;
}
/* fall through */
default:
FileInfo->FileAttributes = 0;
FileInfo->ReparseTag = 0;
break;
}
FileInfo->FileSize = stbuf.st_size;
FileInfo->AllocationSize =
(FileInfo->FileSize + AllocationUnit - 1) / AllocationUnit * AllocationUnit;
@ -282,6 +513,183 @@ exit:
return Result;
}
static NTSTATUS fsp_fuse_intf_GetReparsePointSymlink(FSP_FILE_SYSTEM *FileSystem,
const char *PosixPath, PVOID Buffer, PSIZE_T PSize)
{
struct fuse *f = FileSystem->UserContext;
char PosixTargetPath[FSP_FSCTL_TRANSACT_PATH_SIZEMAX / sizeof(WCHAR)];
PWSTR TargetPath = 0;
ULONG TargetPathLength;
int err;
NTSTATUS Result;
err = f->ops.readlink(PosixPath, PosixTargetPath, sizeof PosixTargetPath);
if (-EINVAL/* same on MSVC and Cygwin */ == err)
{
Result = STATUS_NOT_A_REPARSE_POINT;
goto exit;
}
else if (0 != err)
{
Result = fsp_fuse_ntstatus_from_errno(f->env, err);
goto exit;
}
/* is this an absolute path? */
if ('/' == PosixTargetPath[0])
{
/* we do not support absolute paths without the rellinks option */
if (!f->rellinks)
{
Result = STATUS_ACCESS_DENIED;
goto exit;
}
}
Result = FspPosixMapPosixToWindowsPath(PosixTargetPath, &TargetPath);
if (!NT_SUCCESS(Result))
goto exit;
TargetPathLength = lstrlenW(TargetPath) * sizeof(WCHAR);
if (TargetPathLength > *PSize)
{
Result = STATUS_BUFFER_TOO_SMALL;
goto exit;
}
*PSize = TargetPathLength;
memcpy(Buffer, TargetPath, TargetPathLength);
Result = STATUS_SUCCESS;
exit:
if (0 != TargetPath)
FspPosixDeletePath(TargetPath);
return Result;
}
static NTSTATUS fsp_fuse_intf_GetReparsePointEx(FSP_FILE_SYSTEM *FileSystem,
const char *PosixPath, struct fuse_file_info *fi,
PVOID Buffer, PSIZE_T PSize)
{
struct fuse *f = FileSystem->UserContext;
UINT32 Uid, Gid, Mode, Dev;
FSP_FSCTL_FILE_INFO FileInfo;
PREPARSE_DATA_BUFFER ReparseData;
USHORT ReparseDataLength;
SIZE_T Size;
NTSTATUS Result;
Result = fsp_fuse_intf_GetFileInfoFunnel(FileSystem, PosixPath, fi,
&Uid, &Gid, &Mode, &Dev, &FileInfo);
if (!NT_SUCCESS(Result))
return Result;
if (0 == (FILE_ATTRIBUTE_REPARSE_POINT & FileInfo.FileAttributes))
return STATUS_NOT_A_REPARSE_POINT;
if (0 == Buffer)
return STATUS_SUCCESS;
switch (Mode & 0170000)
{
case 0010000: /* S_IFIFO */
ReparseDataLength = (USHORT)(
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) -
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) +
8);
break;
case 0020000: /* S_IFCHR */
ReparseDataLength = (USHORT)(
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) -
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) +
16);
break;
case 0060000: /* S_IFBLK */
ReparseDataLength = (USHORT)(
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) -
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) +
16);
break;
case 0140000: /* S_IFSOCK */
ReparseDataLength = (USHORT)(
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) -
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) +
8);
break;
case 0120000: /* S_IFLNK */
ReparseDataLength = (USHORT)(
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) -
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer));
break;
default:
/* cannot happen! */
return STATUS_NOT_A_REPARSE_POINT;
}
if ((SIZE_T)FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) + ReparseDataLength > *PSize)
return STATUS_BUFFER_TOO_SMALL;
ReparseData = (PREPARSE_DATA_BUFFER)Buffer;
ReparseData->ReparseTag = FileInfo.ReparseTag;
ReparseData->ReparseDataLength = ReparseDataLength;
switch (Mode & 0170000)
{
case 0010000: /* S_IFIFO */
*(PUINT64)(ReparseData->GenericReparseBuffer.DataBuffer + 0) = NFS_SPECFILE_FIFO;
break;
case 0020000: /* S_IFCHR */
*(PUINT64)(ReparseData->GenericReparseBuffer.DataBuffer + 0) = NFS_SPECFILE_CHR;
*(PUINT32)(ReparseData->GenericReparseBuffer.DataBuffer + 8) = (Dev >> 16) & 0xffff;
*(PUINT32)(ReparseData->GenericReparseBuffer.DataBuffer + 12) = Dev & 0xffff;
break;
case 0060000: /* S_IFBLK */
*(PUINT64)(ReparseData->GenericReparseBuffer.DataBuffer + 0) = NFS_SPECFILE_BLK;
*(PUINT32)(ReparseData->GenericReparseBuffer.DataBuffer + 8) = (Dev >> 16) & 0xffff;
*(PUINT32)(ReparseData->GenericReparseBuffer.DataBuffer + 12) = Dev & 0xffff;
break;
case 0140000: /* S_IFSOCK */
*(PUINT64)(ReparseData->GenericReparseBuffer.DataBuffer + 0) = NFS_SPECFILE_SOCK;
break;
case 0120000: /* S_IFLNK */
Size = *PSize -
FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer);
Result = fsp_fuse_intf_GetReparsePointSymlink(FileSystem, PosixPath,
ReparseData->SymbolicLinkReparseBuffer.PathBuffer, &Size);
if (!NT_SUCCESS(Result))
return Result;
ReparseData->ReparseDataLength += (USHORT)Size;
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength = (USHORT)Size;
ReparseData->SymbolicLinkReparseBuffer.PrintNameOffset = 0;
ReparseData->SymbolicLinkReparseBuffer.PrintNameLength = (USHORT)Size;
ReparseData->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE;
break;
default:
/* cannot happen! */
return STATUS_NOT_A_REPARSE_POINT;
}
*PSize = FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) + ReparseData->ReparseDataLength;
return STATUS_SUCCESS;
}
static NTSTATUS fsp_fuse_intf_GetReparsePointByName(
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize);
static NTSTATUS fsp_fuse_intf_GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
FSP_FSCTL_VOLUME_INFO *VolumeInfo)
@ -335,7 +743,12 @@ static NTSTATUS fsp_fuse_intf_GetSecurityByName(FSP_FILE_SYSTEM *FileSystem,
if (!NT_SUCCESS(Result))
goto exit;
Result = STATUS_SUCCESS;
if (FSP_FUSE_HAS_SYMLINKS(f) &&
FspFileSystemFindReparsePoint(FileSystem, fsp_fuse_intf_GetReparsePointByName, 0,
FileName, PFileAttributes))
Result = STATUS_REPARSE;
else
Result = STATUS_SUCCESS;
exit:
if (0 != PosixPath)
@ -468,6 +881,7 @@ static NTSTATUS fsp_fuse_intf_Create(FSP_FILE_SYSTEM *FileSystem,
filedesc->PosixPath = contexthdr->PosixPath;
filedesc->IsDirectory = !!(FileInfoBuf.FileAttributes & FILE_ATTRIBUTE_DIRECTORY);
filedesc->IsReparsePoint = FALSE;
filedesc->OpenFlags = fi.flags;
filedesc->FileHandle = fi.fh;
filedesc->DirBuffer = 0;
@ -555,6 +969,11 @@ static NTSTATUS fsp_fuse_intf_Open(FSP_FILE_SYSTEM *FileSystem,
Result = STATUS_SUCCESS;
}
}
else if (FileInfoBuf.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
{
fi.fh = -1;
Result = STATUS_SUCCESS;
}
else
{
if (0 != f->ops.open)
@ -582,6 +1001,7 @@ static NTSTATUS fsp_fuse_intf_Open(FSP_FILE_SYSTEM *FileSystem,
filedesc->PosixPath = contexthdr->PosixPath;
filedesc->IsDirectory = !!(FileInfoBuf.FileAttributes & FILE_ATTRIBUTE_DIRECTORY);
filedesc->IsReparsePoint = !!(FileInfoBuf.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT);
filedesc->OpenFlags = fi.flags;
filedesc->FileHandle = fi.fh;
filedesc->DirBuffer = 0;
@ -611,6 +1031,9 @@ static NTSTATUS fsp_fuse_intf_Overwrite(FSP_FILE_SYSTEM *FileSystem,
int err;
NTSTATUS Result;
if (filedesc->IsDirectory || filedesc->IsReparsePoint)
return STATUS_ACCESS_DENIED;
if (0 != f->ops.ftruncate)
{
memset(&fi, 0, sizeof fi);
@ -660,7 +1083,7 @@ static VOID fsp_fuse_intf_Cleanup(FSP_FILE_SYSTEM *FileSystem,
*/
if (Delete)
if (filedesc->IsDirectory)
if (filedesc->IsDirectory && !filedesc->IsReparsePoint)
{
if (0 != f->ops.rmdir)
f->ops.rmdir(filedesc->PosixPath);
@ -690,6 +1113,10 @@ static VOID fsp_fuse_intf_Close(FSP_FILE_SYSTEM *FileSystem,
if (0 != f->ops.releasedir)
f->ops.releasedir(filedesc->PosixPath, &fi);
}
else if (filedesc->IsReparsePoint)
{
/* reparse points are not opened, nothing to do! */
}
else
{
if (0 != f->ops.flush)
@ -715,6 +1142,9 @@ static NTSTATUS fsp_fuse_intf_Read(FSP_FILE_SYSTEM *FileSystem,
int bytes;
NTSTATUS Result;
if (filedesc->IsDirectory || filedesc->IsReparsePoint)
return STATUS_ACCESS_DENIED;
if (0 == f->ops.read)
return STATUS_INVALID_DEVICE_REQUEST;
@ -752,6 +1182,9 @@ static NTSTATUS fsp_fuse_intf_Write(FSP_FILE_SYSTEM *FileSystem,
int bytes;
NTSTATUS Result;
if (filedesc->IsDirectory || filedesc->IsReparsePoint)
return STATUS_ACCESS_DENIED;
if (0 == f->ops.write)
return STATUS_INVALID_DEVICE_REQUEST;
@ -824,6 +1257,8 @@ static NTSTATUS fsp_fuse_intf_Flush(FSP_FILE_SYSTEM *FileSystem,
Result = fsp_fuse_ntstatus_from_errno(f->env, err);
}
}
else if (filedesc->IsReparsePoint)
Result = STATUS_ACCESS_DENIED;
else
{
if (0 != f->ops.fsync)
@ -950,6 +1385,9 @@ static NTSTATUS fsp_fuse_intf_SetFileSize(FSP_FILE_SYSTEM *FileSystem,
int err;
NTSTATUS Result;
if (filedesc->IsDirectory || filedesc->IsReparsePoint)
return STATUS_ACCESS_DENIED;
if (0 == f->ops.ftruncate && 0 == f->ops.truncate)
return STATUS_INVALID_DEVICE_REQUEST;
@ -1028,7 +1466,7 @@ static NTSTATUS fsp_fuse_intf_CanDelete(FSP_FILE_SYSTEM *FileSystem,
struct fuse_dirhandle dh;
int err;
if (filedesc->IsDirectory)
if (filedesc->IsDirectory && !filedesc->IsReparsePoint)
{
/* check that directory is empty! */
@ -1265,6 +1703,9 @@ static NTSTATUS fsp_fuse_intf_ReadDirectory(FSP_FILE_SYSTEM *FileSystem,
int err;
NTSTATUS Result;
if (!filedesc->IsDirectory)
return STATUS_ACCESS_DENIED;
memset(&dh, 0, sizeof dh);
if (0 == filedesc->DirBuffer)
@ -1415,6 +1856,257 @@ exit:
return Result;
}
static NTSTATUS fsp_fuse_intf_ResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem,
PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent,
PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize)
{
return FspFileSystemResolveReparsePoints(FileSystem, fsp_fuse_intf_GetReparsePointByName, 0,
FileName, ReparsePointIndex, ResolveLastPathComponent,
PIoStatus, Buffer, PSize);
}
static NTSTATUS fsp_fuse_intf_GetReparsePointByName(
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize)
{
struct fuse *f = FileSystem->UserContext;
char *PosixPath = 0;
NTSTATUS Result;
Result = FspPosixMapWindowsToPosixPath(FileName, &PosixPath);
if (!NT_SUCCESS(Result))
goto exit;
Result = fsp_fuse_intf_GetReparsePointEx(FileSystem, PosixPath, 0, Buffer, PSize);
exit:
if (0 != PosixPath)
FspPosixDeletePath(PosixPath);
return Result;
}
static NTSTATUS fsp_fuse_intf_GetReparsePoint(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
PVOID FileNode,
PWSTR FileName, PVOID Buffer, PSIZE_T PSize)
{
struct fsp_fuse_file_desc *filedesc =
(PVOID)(UINT_PTR)Request->Req.FileSystemControl.UserContext2;
struct fuse_file_info fi;
memset(&fi, 0, sizeof fi);
fi.flags = filedesc->OpenFlags;
fi.fh = filedesc->FileHandle;
return fsp_fuse_intf_GetReparsePointEx(FileSystem, filedesc->PosixPath, &fi, Buffer, PSize);
}
static NTSTATUS fsp_fuse_intf_SetReparsePoint(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
PVOID FileNode,
PWSTR FileName, PVOID Buffer, SIZE_T Size)
{
struct fuse *f = FileSystem->UserContext;
struct fuse_context *context = fsp_fuse_get_context(f->env);
struct fsp_fuse_file_desc *filedesc =
(PVOID)(UINT_PTR)Request->Req.FileSystemControl.UserContext2;
struct fuse_file_info fi;
UINT32 Uid, Gid, Mode, Dev;
FSP_FSCTL_FILE_INFO FileInfo;
PREPARSE_DATA_BUFFER ReparseData;
PWSTR ReparseTargetPath;
SIZE_T ReparseTargetPathLength;
WCHAR TargetPath[FSP_FSCTL_TRANSACT_PATH_SIZEMAX / sizeof(WCHAR)];
char *PosixTargetPath = 0, *PosixHiddenPath = 0;
BOOLEAN IsSymlink;
int err;
NTSTATUS Result;
/*
* How we implement this is probably one of the worst aspects of this FUSE implementation.
*
* In Windows a CreateSymbolicLink is the sequence of the following:
* Create
* DeviceIoControl(FSCTL_SET_REPARSE_POINT)
* Cleanup
* Close
*
* The Create call creates the new file and the DeviceIoControl(FSCTL_SET_REPARSE_POINT)
* call is supposed to convert it into a reparse point. However FUSE mknod/symlink will
* fail with -EEXIST in this case.
*
* We must therefore find a solution using rename, which is unreliable and error-prone.
* Note that this will also result in a change of the inode number for the reparse point!
*/
if (0 == f->ops.rename || 0 == f->ops.unlink)
return STATUS_INVALID_DEVICE_REQUEST;
ReparseData = (PREPARSE_DATA_BUFFER)Buffer;
if (IO_REPARSE_TAG_SYMLINK == ReparseData->ReparseTag || (
IO_REPARSE_TAG_NFS == ReparseData->ReparseTag &&
NFS_SPECFILE_LNK == *(PUINT64)ReparseData->GenericReparseBuffer.DataBuffer))
{
if (0 == f->ops.symlink)
return STATUS_INVALID_DEVICE_REQUEST;
IsSymlink = TRUE;
}
else if (IO_REPARSE_TAG_NFS == ReparseData->ReparseTag)
{
if (0 == f->ops.mknod)
return STATUS_INVALID_DEVICE_REQUEST;
IsSymlink = FALSE;
}
else
return STATUS_IO_REPARSE_TAG_MISMATCH;
/* FUSE cannot make a directory into a reparse point */
if (filedesc->IsDirectory)
return STATUS_ACCESS_DENIED;
memset(&fi, 0, sizeof fi);
fi.flags = filedesc->OpenFlags;
fi.fh = filedesc->FileHandle;
Result = fsp_fuse_intf_GetFileInfoEx(FileSystem, filedesc->PosixPath, &fi,
&Uid, &Gid, &Mode, &FileInfo);
if (!NT_SUCCESS(Result))
return Result;
if (IsSymlink)
{
if (IO_REPARSE_TAG_SYMLINK == ReparseData->ReparseTag)
{
ReparseTargetPath = ReparseData->SymbolicLinkReparseBuffer.PathBuffer +
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
ReparseTargetPathLength = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
/* is this an absolute path? */
if (0 == (ReparseData->SymbolicLinkReparseBuffer.Flags & SYMLINK_FLAG_RELATIVE) &&
ReparseTargetPathLength >= sizeof(WCHAR) && L'\\' == ReparseTargetPath[0])
{
/* we do not support absolute paths that point outside this file system */
if (0 == Request->Req.FileSystemControl.TargetOnFileSystem)
return STATUS_ACCESS_DENIED;
ReparseTargetPath += Request->Req.FileSystemControl.TargetOnFileSystem / sizeof(WCHAR);
ReparseTargetPathLength -= Request->Req.FileSystemControl.TargetOnFileSystem;
}
}
else
{
/* the PATH is in POSIX format (UTF-16 encoding) */
ReparseTargetPath = (PVOID)(ReparseData->GenericReparseBuffer.DataBuffer + 8);
ReparseTargetPathLength = ReparseData->ReparseDataLength - 8;
}
memcpy(TargetPath, ReparseTargetPath, ReparseTargetPathLength);
TargetPath[ReparseTargetPathLength / sizeof(WCHAR)] = L'\0';
/*
* From this point forward we must jump to the EXIT label on failure.
*/
Result = FspPosixMapWindowsToPosixPathEx(TargetPath, &PosixTargetPath,
IO_REPARSE_TAG_SYMLINK == ReparseData->ReparseTag);
if (!NT_SUCCESS(Result))
goto exit;
Result = fsp_fuse_intf_NewHiddenName(FileSystem, filedesc->PosixPath, &PosixHiddenPath);
if (!NT_SUCCESS(Result))
goto exit;
context->uid = Uid, context->gid = Gid;
err = f->ops.symlink(PosixTargetPath, PosixHiddenPath);
context->uid = -1, context->gid = -1;
if (0 != err)
{
Result = fsp_fuse_ntstatus_from_errno(f->env, err);
goto exit;
}
}
else
{
switch (*(PUINT64)ReparseData->GenericReparseBuffer.DataBuffer)
{
case NFS_SPECFILE_FIFO:
Mode = (Mode & ~0170000) | 0010000;
Dev = 0;
break;
case NFS_SPECFILE_SOCK:
Mode = (Mode & ~0170000) | 0140000;
Dev = 0;
break;
case NFS_SPECFILE_CHR:
Mode = (Mode & ~0170000) | 0020000;
Dev =
(*(PUINT32)(ReparseData->GenericReparseBuffer.DataBuffer + 8) << 16) |
(*(PUINT32)(ReparseData->GenericReparseBuffer.DataBuffer + 12));
break;
case NFS_SPECFILE_BLK:
Mode = (Mode & ~0170000) | 0060000;
Dev =
(*(PUINT32)(ReparseData->GenericReparseBuffer.DataBuffer + 8) << 16) |
(*(PUINT32)(ReparseData->GenericReparseBuffer.DataBuffer + 12));
break;
default:
return STATUS_IO_REPARSE_DATA_INVALID;
}
/*
* From this point forward we must jump to the EXIT label on failure.
*/
Result = fsp_fuse_intf_NewHiddenName(FileSystem, filedesc->PosixPath, &PosixHiddenPath);
if (!NT_SUCCESS(Result))
goto exit;
context->uid = Uid, context->gid = Gid;
err = f->ops.mknod(PosixHiddenPath, Mode, Dev);
context->uid = -1, context->gid = -1;
if (0 != err)
{
Result = fsp_fuse_ntstatus_from_errno(f->env, err);
goto exit;
}
}
err = f->ops.rename(PosixHiddenPath, filedesc->PosixPath);
if (0 != err)
{
/* on failure unlink "hidden" symlink */
f->ops.unlink(PosixHiddenPath);
Result = fsp_fuse_ntstatus_from_errno(f->env, err);
goto exit;
}
filedesc->IsReparsePoint = TRUE;
Result = STATUS_SUCCESS;
exit:
MemFree(PosixHiddenPath);
if (0 != PosixTargetPath)
FspPosixDeletePath(PosixTargetPath);
return Result;
}
static NTSTATUS fsp_fuse_intf_DeleteReparsePoint(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
PVOID FileNode,
PWSTR FileName, PVOID Buffer, SIZE_T Size)
{
/* we were asked to delete the reparse point? no can do! */
return STATUS_ACCESS_DENIED;
}
FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf =
{
fsp_fuse_intf_GetVolumeInfo,
@ -1436,4 +2128,8 @@ FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf =
fsp_fuse_intf_GetSecurity,
fsp_fuse_intf_SetSecurity,
fsp_fuse_intf_ReadDirectory,
fsp_fuse_intf_ResolveReparsePoints,
fsp_fuse_intf_GetReparsePoint,
fsp_fuse_intf_SetReparsePoint,
fsp_fuse_intf_DeleteReparsePoint,
};

View File

@ -29,9 +29,15 @@
#define FSP_FUSE_CONTEXT_FROM_HDR(h) \
(struct fuse_context *)((PUINT8)(h) + sizeof(struct fsp_fuse_context_header))
#define FSP_FUSE_HAS_SYMLINKS(f) (0 != (f)->ops.readlink)
struct fuse
{
struct fsp_fuse_env *env;
int set_umask, umask;
int set_uid, uid;
int set_gid, gid;
int rellinks;
struct fuse_operations ops;
void *data;
UINT32 DebugLog;
@ -54,7 +60,7 @@ struct fsp_fuse_context_header
struct fsp_fuse_file_desc
{
char *PosixPath;
BOOLEAN IsDirectory;
BOOLEAN IsDirectory, IsReparsePoint;
int OpenFlags;
UINT64 FileHandle;
PVOID DirBuffer;
@ -86,4 +92,12 @@ NTSTATUS fsp_fuse_op_leave(FSP_FILE_SYSTEM *FileSystem,
extern FSP_FILE_SYSTEM_INTERFACE fsp_fuse_intf;
/* NFS reparse points */
#define NFS_SPECFILE_FIFO 0x000000004F464946
#define NFS_SPECFILE_CHR 0x0000000000524843
#define NFS_SPECFILE_BLK 0x00000000004b4c42
#define NFS_SPECFILE_LNK 0x00000000014b4e4c
#define NFS_SPECFILE_SOCK 0x000000004B434F53
#endif

View File

@ -18,10 +18,29 @@
#include <dll/library.h>
#include <launcher/launcher.h>
#include <npapi.h>
#include <wincred.h>
#define FSP_NP_NAME LIBRARY_NAME ".Np"
#define FSP_NP_TYPE ' spF' /* pick a value hopefully not in use */
/*
* Define the following macro to use CredUIPromptForWindowsCredentials.
* Otherwise CredUIPromptForCredentials will be used.
*/
#define FSP_NP_CREDUI_PROMPT_NEW
/*
* Define the following macro to include support for the credential manager.
*/
#define FSP_NP_CREDENTIAL_MANAGER
enum
{
FSP_NP_CREDENTIALS_NONE = 0,
FSP_NP_CREDENTIALS_PASSWORD = 1,
FSP_NP_CREDENTIALS_USERPASS = 3,
};
DWORD APIENTRY NPGetCaps(DWORD Index)
{
switch (Index)
@ -102,6 +121,9 @@ static inline BOOLEAN FspNpParseRemoteName(PWSTR RemoteName,
PWSTR ClassName, InstanceName, P;
ULONG ClassNameLen, InstanceNameLen;
if (!FspNpCheckRemoteName(RemoteName))
return FALSE;
ClassName = RemoteName + 2; /* skip \\ */
for (P = ClassName; *P; P++)
if (L'\\' == *P)
@ -129,6 +151,27 @@ static inline BOOLEAN FspNpParseRemoteName(PWSTR RemoteName,
return TRUE;
}
static inline BOOLEAN FspNpParseUserName(PWSTR RemoteName,
PWSTR UserName, ULONG UserNameSize/* in chars */)
{
PWSTR ClassName, InstanceName, P;
ULONG ClassNameLen, InstanceNameLen;
if (FspNpParseRemoteName(RemoteName,
&ClassName, &ClassNameLen, &InstanceName, &InstanceNameLen))
{
for (P = InstanceName; *P; P++)
if ('@' == *P && (ULONG)(P - InstanceName) < UserNameSize)
{
memcpy(UserName, InstanceName, (P - InstanceName) * sizeof(WCHAR));
UserName[P - InstanceName] = L'\0';
return TRUE;
}
}
return FALSE;
}
static inline DWORD FspNpCallLauncherPipe(PWSTR PipeBuf, ULONG SendSize, ULONG RecvSize)
{
DWORD NpResult;
@ -225,6 +268,144 @@ static WCHAR FspNpGetDriveLetter(PDWORD PLogicalDrives, PWSTR VolumeName)
return 0;
}
static DWORD FspNpGetCredentialsKind(PWSTR RemoteName, PDWORD PCredentialsKind)
{
HKEY RegKey = 0;
DWORD NpResult, RegSize;
DWORD Credentials;
PWSTR ClassName, InstanceName;
ULONG ClassNameLen, InstanceNameLen;
WCHAR ClassNameBuf[sizeof(((FSP_FSCTL_VOLUME_PARAMS *)0)->Prefix) / sizeof(WCHAR)];
*PCredentialsKind = FSP_NP_CREDENTIALS_NONE;
if (!FspNpParseRemoteName(RemoteName,
&ClassName, &ClassNameLen, &InstanceName, &InstanceNameLen))
return WN_BAD_NETNAME;
if (ClassNameLen > sizeof ClassNameBuf / sizeof ClassNameBuf[0] - 1)
ClassNameLen = sizeof ClassNameBuf / sizeof ClassNameBuf[0] - 1;
memcpy(ClassNameBuf, ClassName, ClassNameLen * sizeof(WCHAR));
ClassNameBuf[ClassNameLen] = '\0';
NpResult = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"" LAUNCHER_REGKEY, 0, KEY_READ, &RegKey);
if (ERROR_SUCCESS != NpResult)
goto exit;
RegSize = sizeof Credentials;
Credentials = 0; /* default is NO credentials */
NpResult = RegGetValueW(RegKey, ClassNameBuf, L"Credentials", RRF_RT_REG_DWORD, 0,
&Credentials, &RegSize);
if (ERROR_SUCCESS != NpResult && ERROR_FILE_NOT_FOUND != NpResult)
goto exit;
switch (Credentials)
{
case FSP_NP_CREDENTIALS_NONE:
case FSP_NP_CREDENTIALS_PASSWORD:
case FSP_NP_CREDENTIALS_USERPASS:
*PCredentialsKind = Credentials;
break;
}
NpResult = ERROR_SUCCESS;
exit:
if (0 != RegKey)
RegCloseKey(RegKey);
return NpResult;
}
static DWORD FspNpGetCredentials(
HWND hwndOwner, PWSTR Caption, DWORD PrevNpResult,
DWORD CredentialsKind,
PBOOL PSave,
PWSTR UserName, ULONG UserNameSize/* in chars */,
PWSTR Password, ULONG PasswordSize/* in chars */)
{
DWORD NpResult;
CREDUI_INFOW UiInfo;
memset(&UiInfo, 0, sizeof UiInfo);
UiInfo.cbSize = sizeof UiInfo;
UiInfo.hwndParent = hwndOwner;
UiInfo.pszCaptionText = Caption;
UiInfo.pszMessageText = L"Enter credentials to unlock this file system.";
#if !defined(FSP_NP_CREDUI_PROMPT_NEW)
NpResult = CredUIPromptForCredentialsW(&UiInfo, L"NONE", 0, 0,
UserName, UserNameSize,
Password, PasswordSize,
PSave,
CREDUI_FLAGS_GENERIC_CREDENTIALS |
CREDUI_FLAGS_DO_NOT_PERSIST |
CREDUI_FLAGS_ALWAYS_SHOW_UI |
(0 != PrevNpResult ? CREDUI_FLAGS_INCORRECT_PASSWORD : 0) |
(0 != PSave ? CREDUI_FLAGS_SHOW_SAVE_CHECK_BOX : 0) |
(FSP_NP_CREDENTIALS_PASSWORD == CredentialsKind ? 0/*CREDUI_FLAGS_KEEP_USERNAME*/ : 0));
#else
WCHAR Domain[CREDUI_MAX_DOMAIN_TARGET_LENGTH + 1];
ULONG AuthPackage = 0;
PVOID InAuthBuf = 0, OutAuthBuf = 0;
ULONG InAuthSize, OutAuthSize, DomainSize;
InAuthSize = 0;
if (!CredPackAuthenticationBufferW(
CRED_PACK_GENERIC_CREDENTIALS, UserName, Password, 0, &InAuthSize) &&
ERROR_INSUFFICIENT_BUFFER != GetLastError())
{
NpResult = GetLastError();
goto exit;
}
InAuthBuf = MemAlloc(InAuthSize);
if (0 == InAuthBuf)
{
NpResult = ERROR_NO_SYSTEM_RESOURCES;
goto exit;
}
if (!CredPackAuthenticationBufferW(
CRED_PACK_GENERIC_CREDENTIALS, UserName, Password, InAuthBuf, &InAuthSize))
{
NpResult = GetLastError();
goto exit;
}
NpResult = CredUIPromptForWindowsCredentialsW(&UiInfo, PrevNpResult,
&AuthPackage, InAuthBuf, InAuthSize, &OutAuthBuf, &OutAuthSize, PSave,
CREDUIWIN_GENERIC | (0 != PSave ? CREDUIWIN_CHECKBOX : 0));
if (ERROR_SUCCESS != NpResult)
goto exit;
DomainSize = sizeof Domain / sizeof Domain[0];
if (!CredUnPackAuthenticationBufferW(0, OutAuthBuf, OutAuthSize,
UserName, &UserNameSize, Domain, &DomainSize, Password, &PasswordSize))
{
NpResult = GetLastError();
goto exit;
}
NpResult = ERROR_SUCCESS;
exit:
if (0 != OutAuthBuf)
{
SecureZeroMemory(OutAuthBuf, OutAuthSize);
CoTaskMemFree(OutAuthBuf);
}
if (0 != InAuthBuf)
{
SecureZeroMemory(InAuthBuf, InAuthSize);
MemFree(InAuthBuf);
}
#endif
return NpResult;
}
DWORD APIENTRY NPGetConnection(
LPWSTR lpLocalName, LPWSTR lpRemoteName, LPDWORD lpnBufferLen)
{
@ -303,12 +484,6 @@ DWORD APIENTRY NPGetConnection(
}
DWORD APIENTRY NPAddConnection(LPNETRESOURCEW lpNetResource, LPWSTR lpPassword, LPWSTR lpUserName)
{
return NPAddConnection3(0, lpNetResource, lpPassword, lpUserName, 0);
}
DWORD APIENTRY NPAddConnection3(HWND hwndOwner,
LPNETRESOURCEW lpNetResource, LPWSTR lpPassword, LPWSTR lpUserName, DWORD dwFlags)
{
DWORD NpResult;
DWORD dwType = lpNetResource->dwType;
@ -317,14 +492,15 @@ DWORD APIENTRY NPAddConnection3(HWND hwndOwner,
WCHAR LocalNameBuf[3];
PWSTR ClassName, InstanceName, RemoteName, P;
ULONG ClassNameLen, InstanceNameLen;
DWORD CredentialsKind;
PWSTR PipeBuf = 0;
#if defined(FSP_NP_CREDENTIAL_MANAGER)
PCREDENTIALW Credential = 0;
#endif
if (dwType & RESOURCETYPE_PRINT)
return WN_BAD_VALUE;
if (!FspNpCheckRemoteName(lpRemoteName))
return WN_BAD_NETNAME;
if (!FspNpParseRemoteName(lpRemoteName,
&ClassName, &ClassNameLen, &InstanceName, &InstanceNameLen))
return WN_BAD_NETNAME;
@ -344,22 +520,75 @@ DWORD APIENTRY NPAddConnection3(HWND hwndOwner,
return WN_ALREADY_CONNECTED;
}
FspNpGetCredentialsKind(lpRemoteName, &CredentialsKind);
#if defined(FSP_NP_CREDENTIAL_MANAGER)
/* if we need credentials and none were passed check with the credential manager */
if (FSP_NP_CREDENTIALS_NONE != CredentialsKind && 0 == lpPassword &&
CredReadW(lpRemoteName, CRED_TYPE_GENERIC, 0, &Credential))
{
if (sizeof(WCHAR) <= Credential->CredentialBlobSize &&
L'\0' == ((PWSTR)(Credential->CredentialBlob))
[(Credential->CredentialBlobSize / sizeof(WCHAR)) - 1])
{
lpUserName = Credential->UserName;
lpPassword = (PVOID)Credential->CredentialBlob;
}
}
#endif
/* if we need credentials and we don't have any return ACCESS DENIED */
if (FSP_NP_CREDENTIALS_NONE != CredentialsKind)
{
int Length;
if (0 == lpPassword ||
(0 == (Length = lstrlenW(lpPassword))) || CREDUI_MAX_PASSWORD_LENGTH < Length)
{
NpResult = WN_ACCESS_DENIED;
goto exit;
}
}
if (FSP_NP_CREDENTIALS_USERPASS == CredentialsKind)
{
int Length;
if (0 == lpUserName ||
(0 == (Length = lstrlenW(lpUserName))) || CREDUI_MAX_USERNAME_LENGTH < Length)
{
NpResult = WN_ACCESS_DENIED;
goto exit;
}
}
PipeBuf = MemAlloc(LAUNCHER_PIPE_BUFFER_SIZE);
if (0 == PipeBuf)
return WN_OUT_OF_MEMORY;
{
NpResult = WN_OUT_OF_MEMORY;
goto exit;
}
/* we do not explicitly check, but assumption is it all fits in LAUNCHER_PIPE_BUFFER_SIZE */
P = PipeBuf;
*P++ = LauncherSvcInstanceStart;
*P++ = FSP_NP_CREDENTIALS_NONE != CredentialsKind ?
LauncherSvcInstanceStartWithSecret : LauncherSvcInstanceStart;
memcpy(P, ClassName, ClassNameLen * sizeof(WCHAR)); P += ClassNameLen; *P++ = L'\0';
memcpy(P, InstanceName, InstanceNameLen * sizeof(WCHAR)); P += InstanceNameLen; *P++ = L'\0';
lstrcpyW(P, RemoteName); P += lstrlenW(RemoteName) + 1;
lstrcpyW(P, LocalNameBuf); P += lstrlenW(LocalNameBuf) + 1;
if (FSP_NP_CREDENTIALS_USERPASS == CredentialsKind)
{
lstrcpyW(P, lpUserName); P += lstrlenW(lpUserName) + 1;
}
if (FSP_NP_CREDENTIALS_NONE != CredentialsKind)
{
lstrcpyW(P, lpPassword); P += lstrlenW(lpPassword) + 1;
}
NpResult = FspNpCallLauncherPipe(
PipeBuf, (ULONG)(P - PipeBuf) * sizeof(WCHAR), LAUNCHER_PIPE_BUFFER_SIZE);
switch (NpResult)
{
case WN_SUCCESS:
case WN_ACCESS_DENIED:
break;
case ERROR_ALREADY_EXISTS:
/*
@ -393,8 +622,89 @@ DWORD APIENTRY NPAddConnection3(HWND hwndOwner,
break;
}
exit:
MemFree(PipeBuf);
#if defined(FSP_NP_CREDENTIAL_MANAGER)
if (0 != Credential)
CredFree(Credential);
#endif
return NpResult;
}
DWORD APIENTRY NPAddConnection3(HWND hwndOwner,
LPNETRESOURCEW lpNetResource, LPWSTR lpPassword, LPWSTR lpUserName, DWORD dwFlags)
{
DWORD NpResult;
PWSTR RemoteName = lpNetResource->lpRemoteName;
DWORD CredentialsKind;
WCHAR UserName[CREDUI_MAX_USERNAME_LENGTH + 1], Password[CREDUI_MAX_PASSWORD_LENGTH + 1];
#if defined(FSP_NP_CREDENTIAL_MANAGER)
BOOL Save = TRUE;
#endif
//dwFlags |= CONNECT_INTERACTIVE | CONNECT_PROMPT; /* TESTING ONLY! */
/* CONNECT_PROMPT is only valid if CONNECT_INTERACTIVE is also set */
if (CONNECT_PROMPT == (dwFlags & (CONNECT_INTERACTIVE | CONNECT_PROMPT)))
return WN_BAD_VALUE;
/* if not CONNECT_PROMPT go ahead and attempt to NPAddConnection once */
if (0 == (dwFlags & CONNECT_PROMPT))
{
NpResult = NPAddConnection(lpNetResource, lpPassword, lpUserName);
if (WN_ACCESS_DENIED != NpResult || 0 == (dwFlags & CONNECT_INTERACTIVE))
return NpResult;
}
FspNpGetCredentialsKind(RemoteName, &CredentialsKind);
if (FSP_NP_CREDENTIALS_NONE == CredentialsKind)
return WN_CANCEL;
/* if CONNECT_INTERACTIVE keep asking the user for valid credentials or cancel */
NpResult = WN_SUCCESS;
lstrcpyW(UserName, L"UNSPECIFIED");
Password[0] = L'\0';
if (FSP_NP_CREDENTIALS_PASSWORD == CredentialsKind)
FspNpParseUserName(RemoteName, UserName, sizeof UserName / sizeof UserName[0]);
do
{
NpResult = FspNpGetCredentials(
hwndOwner, RemoteName, NpResult,
CredentialsKind,
#if defined(FSP_NP_CREDENTIAL_MANAGER)
&Save,
#else
0,
#endif
UserName, sizeof UserName / sizeof UserName[0],
Password, sizeof Password / sizeof Password[0]);
if (WN_SUCCESS != NpResult)
break;
NpResult = NPAddConnection(lpNetResource, Password, UserName);
} while (WN_ACCESS_DENIED == NpResult);
#if defined(FSP_NP_CREDENTIAL_MANAGER)
if (WN_SUCCESS == NpResult && Save)
{
CREDENTIALW Credential;
memset(&Credential, 0, sizeof Credential);
Credential.Type = CRED_TYPE_GENERIC;
Credential.Persist = CRED_PERSIST_LOCAL_MACHINE;
Credential.TargetName = RemoteName;
Credential.UserName = UserName;
Credential.CredentialBlobSize = (lstrlenW(Password) + 1) * sizeof(WCHAR);
Credential.CredentialBlob = (PVOID)Password;
CredWriteW(&Credential, 0);
}
#endif
SecureZeroMemory(Password, sizeof Password);
return NpResult;
}

View File

@ -42,16 +42,16 @@ union
UINT8 B[sizeof(SID) - sizeof(DWORD) + (1 * sizeof(DWORD))];
} FspUnmappedSidBuf =
{
/* S-1-5-7 (Anonymous) */
/* S-1-0-65534 */
.V.Revision = SID_REVISION,
.V.SubAuthorityCount = 1,
.V.IdentifierAuthority.Value[5] = 5,
.V.SubAuthority[0] = 7,
.V.IdentifierAuthority.Value[5] = 0,
.V.SubAuthority[0] = 65534,
};
static PISID FspAccountDomainSid, FspPrimaryDomainSid;
#define FspUnmappedSid (&FspUnmappedSidBuf.V)
#define FspUnmappedUid (7)
#define FspUnmappedUid (65534)
static BOOL WINAPI FspPosixInitialize(
PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context)
@ -223,7 +223,7 @@ FSP_API NTSTATUS FspPosixMapUidToSid(UINT32 Uid, PSID *PSid)
* Other well-known SIDs in the NT_AUTHORITY domain (S-1-5-X-RID):
* S-1-5-X-RID <=> uid/gid: 0x1000 * X + RID
*/
else if (0x1000 <= Uid && Uid < 0x100000)
else if (FspUnmappedUid != Uid && 0x1000 <= Uid && Uid < 0x100000)
*PSid = FspPosixCreateSid(5, 2, Uid >> 12, Uid & 0xfff);
if (0 == *PSid)
@ -325,7 +325,9 @@ FSP_API NTSTATUS FspPosixMapSidToUid(PSID Sid, PUINT32 PUid)
*/
*PUid = 0x60000 + Rid;
}
else
else if (
FspUnmappedSid->IdentifierAuthority.Value[5] != Authority ||
FspUnmappedSid->SubAuthority[0] != Rid)
{
/* [IDMAP]
* Other well-known SIDs:
@ -803,7 +805,8 @@ static UINT32 FspPosixInvalidPathChars[4] =
0x00000008,
};
FSP_API NTSTATUS FspPosixMapWindowsToPosixPath(PWSTR WindowsPath, char **PPosixPath)
FSP_API NTSTATUS FspPosixMapWindowsToPosixPathEx(PWSTR WindowsPath, char **PPosixPath,
BOOLEAN Translate)
{
NTSTATUS Result;
ULONG Size;
@ -826,25 +829,28 @@ FSP_API NTSTATUS FspPosixMapWindowsToPosixPath(PWSTR WindowsPath, char **PPosixP
if (0 == Size)
goto lasterror;
for (p = PosixPath, q = p; *p; p++)
if (Translate)
{
unsigned char c = *p;
if ('\\' == c)
*q++ = '/';
/* encode characters in the Unicode private use area: U+F0XX -> XX */
else if (0xef == c && 0x80 == (0xfc & p[1]) && 0x80 == (0xc0 & p[2]))
for (p = PosixPath, q = p; *p; p++)
{
c = ((p[1] & 0x3) << 6) | (p[2] & 0x3f);
if (128 > c && (FspPosixInvalidPathChars[c >> 5] & (0x80000000 >> (c & 0x1f))))
*q++ = c, p += 2;
unsigned char c = *p;
if ('\\' == c)
*q++ = '/';
/* encode characters in the Unicode private use area: U+F0XX -> XX */
else if (0xef == c && 0x80 == (0xfc & p[1]) && 0x80 == (0xc0 & p[2]))
{
c = ((p[1] & 0x3) << 6) | (p[2] & 0x3f);
if (128 > c && (FspPosixInvalidPathChars[c >> 5] & (0x80000000 >> (c & 0x1f))))
*q++ = c, p += 2;
else
*q++ = *p++, *q++ = *p++, *q++ = *p;
}
else
*q++ = *p++, *q++ = *p++, *q++ = *p;
*q++ = c;
}
else
*q++ = c;
*q = '\0';
}
*q = '\0';
*PPosixPath = PosixPath;
@ -861,7 +867,8 @@ lasterror:
goto exit;
}
FSP_API NTSTATUS FspPosixMapPosixToWindowsPath(const char *PosixPath, PWSTR *PWindowsPath)
FSP_API NTSTATUS FspPosixMapPosixToWindowsPathEx(const char *PosixPath, PWSTR *PWindowsPath,
BOOLEAN Translate)
{
NTSTATUS Result;
ULONG Size;
@ -884,14 +891,17 @@ FSP_API NTSTATUS FspPosixMapPosixToWindowsPath(const char *PosixPath, PWSTR *PWi
if (0 == Size)
goto lasterror;
for (p = WindowsPath; *p; p++)
if (Translate)
{
WCHAR c = *p;
for (p = WindowsPath; *p; p++)
{
WCHAR c = *p;
if (L'/' == c)
*p = L'\\';
else if (128 > c && (FspPosixInvalidPathChars[c >> 5] & (0x80000000 >> (c & 0x1f))))
*p |= 0xf000;
if (L'/' == c)
*p = L'\\';
else if (128 > c && (FspPosixInvalidPathChars[c >> 5] & (0x80000000 >> (c & 0x1f))))
*p |= 0xf000;
}
}
*PWindowsPath = WindowsPath;

View File

@ -48,6 +48,19 @@ static NTSTATUS FspGetSecurityByName(FSP_FILE_SYSTEM *FileSystem,
}
}
static inline ULONG FspPathSuffixIndex(PWSTR FileName)
{
WCHAR Root[2] = L"\\";
PWSTR Remain, Suffix;
ULONG Result;
FspPathSuffix(FileName, &Remain, &Suffix, Root);
Result = Remain == Root ? 0 : (ULONG)(Suffix - Remain);
FspPathCombine(FileName, Suffix);
return Result;
}
FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
BOOLEAN CheckParentDirectory, BOOLEAN AllowTraverseCheck,
@ -76,7 +89,7 @@ FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem,
NTSTATUS Result;
WCHAR Root[2] = L"\\", TraverseCheckRoot[2] = L"\\";
PWSTR FileName, Suffix, Prefix, Remain;
UINT32 FileAttributes;
UINT32 FileAttributes = 0;
PSECURITY_DESCRIPTOR SecurityDescriptor = 0;
SIZE_T SecurityDescriptorSize;
UINT8 PrivilegeSetBuf[sizeof(PRIVILEGE_SET) + 15 * sizeof(LUID_AND_ATTRIBUTES)];
@ -111,18 +124,54 @@ FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem,
break;
}
Result = FspGetSecurityByName(FileSystem, Prefix, 0,
FileAttributes = 0;
Result = FspGetSecurityByName(FileSystem, Prefix, &FileAttributes,
&SecurityDescriptor, &SecurityDescriptorSize);
/* compute the ReparsePointIndex and place it in FileAttributes now */
if (NT_SUCCESS(Result) && STATUS_REPARSE != Result &&
(FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
FileAttributes = FspPathSuffixIndex(Prefix);
FspPathCombine(FileName, Remain);
if (!NT_SUCCESS(Result))
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
{
if (STATUS_OBJECT_NAME_NOT_FOUND == Result)
Result = STATUS_OBJECT_PATH_NOT_FOUND;
goto exit;
}
/*
* We check to see if this is a reparse point and then immediately return
* STATUS_REPARSE. We do this check BEFORE the directory check, because
* contrary to NTFS we want to allow non-directory symlinks to directories.
*
* Note that this effectively turns off traverse checking a path comprised of
* reparse points even when the originating process does not have the Traverse
* privilege. [I am not sure what NTFS does in this case, but POSIX symlinks
* behave similarly.] We will still traverse check the reparsed path when
* the FSD sends it back to us though!
*
* Now if the reparse points are not symlinks (or symlink-like) things
* get even more complicated. Argh! Windows!
*/
if (FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
{
/* ReparsePointIndex already computed after FspGetSecurityByName call above */
Result = STATUS_REPARSE;
goto exit;
}
/*
* Check if this is a directory, otherwise the path is invalid.
*/
if (0 == (FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
Result = STATUS_OBJECT_PATH_NOT_FOUND; /* use STATUS_OBJECT_PATH_INVALID? */
goto exit;
}
if (0 < SecurityDescriptorSize)
{
if (AccessCheck(SecurityDescriptor, (HANDLE)Request->Req.Create.AccessToken, FILE_TRAVERSE,
@ -136,95 +185,120 @@ FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem,
}
}
FileAttributes = 0;
Result = FspGetSecurityByName(FileSystem, FileName, &FileAttributes,
&SecurityDescriptor, &SecurityDescriptorSize);
if (!NT_SUCCESS(Result))
if (!NT_SUCCESS(Result) || STATUS_REPARSE == Result)
goto exit;
if (Request->Req.Create.UserMode && 0 < SecurityDescriptorSize)
{
if (AccessCheck(SecurityDescriptor, (HANDLE)Request->Req.Create.AccessToken, DesiredAccess,
&FspFileGenericMapping, PrivilegeSet, &PrivilegeSetLength, PGrantedAccess, &AccessStatus))
Result = AccessStatus ? STATUS_SUCCESS : STATUS_ACCESS_DENIED;
else
Result = FspNtStatusFromWin32(GetLastError());
if (!NT_SUCCESS(Result))
{
/*
* If the desired access includes the DELETE or FILE_READ_ATTRIBUTES
* (or MAXIMUM_ALLOWED) rights we must still check with our parent to
* see if it gives us access (through the FILE_DELETE_CHILD and
* FILE_LIST_DIRECTORY rights).
*
* Does the Windows security model suck? Ermmmm...
*/
if (STATUS_ACCESS_DENIED != Result ||
0 == ((MAXIMUM_ALLOWED | DELETE | FILE_READ_ATTRIBUTES) & DesiredAccess))
goto exit;
Result = FspAccessCheck(FileSystem, Request, TRUE, FALSE,
(MAXIMUM_ALLOWED & DesiredAccess) ? (FILE_DELETE_CHILD | FILE_LIST_DIRECTORY) :
(
((DELETE & DesiredAccess) ? FILE_DELETE_CHILD : 0) |
((FILE_READ_ATTRIBUTES & DesiredAccess) ? FILE_LIST_DIRECTORY : 0)
),
&ParentAccess);
if (!NT_SUCCESS(Result))
{
/* any failure just becomes ACCESS DENIED at this point */
Result = STATUS_ACCESS_DENIED;
goto exit;
}
/* redo the access check but remove the DELETE and/or FILE_READ_ATTRIBUTES rights */
DesiredAccess2 = DesiredAccess & ~(
((FILE_DELETE_CHILD & ParentAccess) ? DELETE : 0) |
((FILE_LIST_DIRECTORY & ParentAccess) ? FILE_READ_ATTRIBUTES : 0));
if (0 != DesiredAccess2)
{
if (AccessCheck(SecurityDescriptor, (HANDLE)Request->Req.Create.AccessToken, DesiredAccess2,
&FspFileGenericMapping, PrivilegeSet, &PrivilegeSetLength, PGrantedAccess, &AccessStatus))
Result = AccessStatus ? STATUS_SUCCESS : STATUS_ACCESS_DENIED;
else
/* any failure just becomes ACCESS DENIED at this point */
Result = STATUS_ACCESS_DENIED;
if (!NT_SUCCESS(Result))
goto exit;
}
if (FILE_DELETE_CHILD & ParentAccess)
*PGrantedAccess |= DELETE;
if (FILE_LIST_DIRECTORY & ParentAccess)
*PGrantedAccess |= FILE_READ_ATTRIBUTES;
}
}
if (CheckParentDirectory)
{
/*
* We check to see if this is a reparse point and then immediately return
* STATUS_REPARSE. We do this check BEFORE the directory check, because
* contrary to NTFS we want to allow non-directory symlinks to directories.
*/
if (FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
{
FileAttributes = FspPathSuffixIndex(FileName);
Result = STATUS_REPARSE;
goto exit;
}
if (0 == (FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
Result = STATUS_NOT_A_DIRECTORY;
goto exit;
}
}
else
{
/*
* We check to see if this is a reparse point and FILE_OPEN_REPARSE_POINT
* was not specified, in which case we return STATUS_REPARSE.
*/
if (0 != (FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
0 == (Request->Req.Create.CreateOptions & FILE_OPEN_REPARSE_POINT))
{
FileAttributes = FspPathSuffixIndex(FileName);
Result = STATUS_REPARSE;
goto exit;
}
if ((Request->Req.Create.CreateOptions & FILE_DIRECTORY_FILE) &&
0 == (FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
Result = STATUS_NOT_A_DIRECTORY;
goto exit;
}
if ((Request->Req.Create.CreateOptions & FILE_NON_DIRECTORY_FILE) &&
0 != (FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
Result = STATUS_FILE_IS_A_DIRECTORY;
goto exit;
}
}
if (Request->Req.Create.UserMode)
{
if (0 < SecurityDescriptorSize)
{
if (AccessCheck(SecurityDescriptor, (HANDLE)Request->Req.Create.AccessToken, DesiredAccess,
&FspFileGenericMapping, PrivilegeSet, &PrivilegeSetLength, PGrantedAccess, &AccessStatus))
Result = AccessStatus ? STATUS_SUCCESS : STATUS_ACCESS_DENIED;
else
Result = FspNtStatusFromWin32(GetLastError());
if (!NT_SUCCESS(Result))
{
/*
* If the desired access includes the DELETE or FILE_READ_ATTRIBUTES
* (or MAXIMUM_ALLOWED) rights we must still check with our parent to
* see if it gives us access (through the FILE_DELETE_CHILD and
* FILE_LIST_DIRECTORY rights).
*
* Does the Windows security model suck? Ermmmm...
*/
if (STATUS_ACCESS_DENIED != Result ||
0 == ((MAXIMUM_ALLOWED | DELETE | FILE_READ_ATTRIBUTES) & DesiredAccess))
goto exit;
Result = FspAccessCheck(FileSystem, Request, TRUE, FALSE,
(MAXIMUM_ALLOWED & DesiredAccess) ? (FILE_DELETE_CHILD | FILE_LIST_DIRECTORY) :
(
((DELETE & DesiredAccess) ? FILE_DELETE_CHILD : 0) |
((FILE_READ_ATTRIBUTES & DesiredAccess) ? FILE_LIST_DIRECTORY : 0)
),
&ParentAccess);
if (!NT_SUCCESS(Result))
{
/* any failure just becomes ACCESS DENIED at this point */
Result = STATUS_ACCESS_DENIED;
goto exit;
}
/* redo the access check but remove the DELETE and/or FILE_READ_ATTRIBUTES rights */
DesiredAccess2 = DesiredAccess & ~(
((FILE_DELETE_CHILD & ParentAccess) ? DELETE : 0) |
((FILE_LIST_DIRECTORY & ParentAccess) ? FILE_READ_ATTRIBUTES : 0));
if (0 != DesiredAccess2)
{
if (AccessCheck(SecurityDescriptor, (HANDLE)Request->Req.Create.AccessToken, DesiredAccess2,
&FspFileGenericMapping, PrivilegeSet, &PrivilegeSetLength, PGrantedAccess, &AccessStatus))
Result = AccessStatus ? STATUS_SUCCESS : STATUS_ACCESS_DENIED;
else
/* any failure just becomes ACCESS DENIED at this point */
Result = STATUS_ACCESS_DENIED;
if (!NT_SUCCESS(Result))
goto exit;
}
if (FILE_DELETE_CHILD & ParentAccess)
*PGrantedAccess |= DELETE;
if (FILE_LIST_DIRECTORY & ParentAccess)
*PGrantedAccess |= FILE_READ_ATTRIBUTES;
}
}
if (CheckParentDirectory)
{
if (0 == (FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
Result = STATUS_NOT_A_DIRECTORY;
goto exit;
}
}
else
{
if ((Request->Req.Create.CreateOptions & FILE_DIRECTORY_FILE) &&
0 == (FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
Result = STATUS_NOT_A_DIRECTORY;
goto exit;
}
if ((Request->Req.Create.CreateOptions & FILE_NON_DIRECTORY_FILE) &&
0 != (FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
Result = STATUS_FILE_IS_A_DIRECTORY;
goto exit;
}
}
if (0 != (FileAttributes & FILE_ATTRIBUTE_READONLY))
{
if (DesiredAccess &
@ -256,7 +330,9 @@ FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem,
Result = STATUS_SUCCESS;
exit:
if (0 != PSecurityDescriptor && 0 < SecurityDescriptorSize && NT_SUCCESS(Result))
if (STATUS_REPARSE == Result)
*PGrantedAccess = FileAttributes; /* FileAttributes contains ReparsePointIndex */
else if (0 != PSecurityDescriptor && 0 < SecurityDescriptorSize && NT_SUCCESS(Result))
*PSecurityDescriptor = SecurityDescriptor;
else
MemFree(SecurityDescriptor);

View File

@ -54,9 +54,10 @@ static void usage(void)
"usage: %s COMMAND ARGS\n"
"\n"
"commands:\n"
" start ClassName InstanceName Args...\n"
" stop ClassName InstanceName\n"
" info ClassName InstanceName\n"
" start ClassName InstanceName Args...\n"
" startWithSecret ClassName InstanceName Args... Secret\n"
" stop ClassName InstanceName\n"
" info ClassName InstanceName\n"
" list\n",
PROGNAME);
}
@ -115,7 +116,8 @@ static int call_pipe_and_report(PWSTR PipeBuf, ULONG SendSize, ULONG RecvSize)
}
int start(PWSTR PipeBuf, ULONG PipeBufSize,
PWSTR ClassName, PWSTR InstanceName, DWORD Argc, PWSTR *Argv)
PWSTR ClassName, PWSTR InstanceName, DWORD Argc, PWSTR *Argv,
BOOLEAN HasSecret)
{
PWSTR P;
DWORD ClassNameSize, InstanceNameSize, ArgvSize;
@ -130,7 +132,7 @@ int start(PWSTR PipeBuf, ULONG PipeBufSize,
return ERROR_INVALID_PARAMETER;
P = PipeBuf;
*P++ = LauncherSvcInstanceStart;
*P++ = HasSecret ? LauncherSvcInstanceStartWithSecret : LauncherSvcInstanceStart;
memcpy(P, ClassName, ClassNameSize * sizeof(WCHAR)); P += ClassNameSize;
memcpy(P, InstanceName, InstanceNameSize * sizeof(WCHAR)); P += InstanceNameSize;
for (DWORD Argi = 0; Argc > Argi; Argi++)
@ -230,7 +232,17 @@ int wmain(int argc, wchar_t **argv)
if (3 > argc || argc > 12)
usage();
return start(PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE, argv[1], argv[2], argc - 3, argv + 3);
return start(PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE, argv[1], argv[2], argc - 3, argv + 3,
FALSE);
}
else
if (0 == lstrcmpW(L"startWithSecret", argv[0]))
{
if (4 > argc || argc > 13)
usage();
return start(PipeBuf, LAUNCHER_PIPE_BUFFER_SIZE, argv[1], argv[2], argc - 3, argv + 3,
TRUE);
}
else
if (0 == lstrcmpW(L"stop", argv[0]))

View File

@ -19,7 +19,54 @@
#include <sddl.h>
#define PROGNAME "WinFsp.Launcher"
#define REGKEY "SYSTEM\\CurrentControlSet\\Services\\" PROGNAME "\\Services"
#define REGKEY LAUNCHER_REGKEY
BOOL CreateOverlappedPipe(
PHANDLE PReadPipe, PHANDLE PWritePipe, PSECURITY_ATTRIBUTES SecurityAttributes, DWORD Size,
DWORD ReadMode, DWORD WriteMode)
{
RPC_STATUS RpcStatus;
UUID Uuid;
WCHAR PipeNameBuf[MAX_PATH];
HANDLE ReadPipe, WritePipe;
DWORD LastError;
RpcStatus = UuidCreate(&Uuid);
if (S_OK != RpcStatus && RPC_S_UUID_LOCAL_ONLY != RpcStatus)
{
SetLastError(ERROR_INTERNAL_ERROR);
return FALSE;
}
wsprintfW(PipeNameBuf, L"\\\\.\\pipe\\"
"%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
Uuid.Data1, Uuid.Data2, Uuid.Data3,
Uuid.Data4[0], Uuid.Data4[1], Uuid.Data4[2], Uuid.Data4[3],
Uuid.Data4[4], Uuid.Data4[5], Uuid.Data4[6], Uuid.Data4[7]);
ReadPipe = CreateNamedPipeW(PipeNameBuf,
PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | ReadMode,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS,
1, Size, Size, 120 * 1000, SecurityAttributes);
if (INVALID_HANDLE_VALUE == ReadPipe)
return FALSE;
WritePipe = CreateFileW(PipeNameBuf,
GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
SecurityAttributes, OPEN_EXISTING, WriteMode, 0);
if (INVALID_HANDLE_VALUE == WritePipe)
{
LastError = GetLastError();
CloseHandle(ReadPipe);
SetLastError(LastError);
return FALSE;
}
*PReadPipe = ReadPipe;
*PWritePipe = WritePipe;
return TRUE;
}
typedef struct
{
@ -80,6 +127,7 @@ static VOID CALLBACK KillProcessWait(PVOID Context, BOOLEAN Timeout)
typedef struct
{
LONG RefCount;
PWSTR ClassName;
PWSTR InstanceName;
PWSTR CommandLine;
@ -87,6 +135,7 @@ typedef struct
DWORD ProcessId;
HANDLE Process;
HANDLE ProcessWait;
HANDLE StdioHandles[2];
LIST_ENTRY ListEntry;
WCHAR Buffer[];
} SVC_INSTANCE;
@ -108,8 +157,8 @@ static SVC_INSTANCE *SvcInstanceLookup(PWSTR ClassName, PWSTR InstanceName)
{
SvcInstance = CONTAINING_RECORD(ListEntry, SVC_INSTANCE, ListEntry);
if (0 == lstrcmpW(ClassName, SvcInstance->ClassName) &&
0 == lstrcmpW(InstanceName, SvcInstance->InstanceName))
if (0 == lstrcmpiW(ClassName, SvcInstance->ClassName) &&
0 == lstrcmpiW(InstanceName, SvcInstance->InstanceName))
return SvcInstance;
}
@ -231,8 +280,132 @@ static NTSTATUS SvcInstanceAccessCheck(HANDLE ClientToken, ULONG DesiredAccess,
return Result;
}
NTSTATUS SvcInstanceCreateProcess(PWSTR Executable, PWSTR CommandLine,
HANDLE StdioHandles[2],
PPROCESS_INFORMATION ProcessInfo)
{
STARTUPINFOEXW StartupInfoEx;
HANDLE ChildHandles[3] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, 0/* DO NOT CLOSE!*/ };
HANDLE ParentHandles[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
SECURITY_ATTRIBUTES PipeAttributes = { sizeof(SECURITY_ATTRIBUTES), 0, TRUE };
PPROC_THREAD_ATTRIBUTE_LIST AttrList = 0;
SIZE_T Size;
NTSTATUS Result;
memset(&StartupInfoEx, 0, sizeof StartupInfoEx);
StartupInfoEx.StartupInfo.cb = sizeof StartupInfoEx.StartupInfo;
if (0 != StdioHandles)
{
/*
* Create child process and redirect stdin/stdout. Do *not* inherit other handles.
*
* For explanation see:
* https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873/
*/
/* create stdin read/write ends; make them inheritable */
if (!CreateOverlappedPipe(&ChildHandles[0], &ParentHandles[0], &PipeAttributes, 0,
0, 0))
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
/* create stdout read/write ends; make them inheritable */
if (!CreateOverlappedPipe(&ParentHandles[1], &ChildHandles[1], &PipeAttributes, 0,
FILE_FLAG_OVERLAPPED, 0))
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
ChildHandles[2] = GetStdHandle(STD_ERROR_HANDLE);
Size = 0;
if (!InitializeProcThreadAttributeList(0, 1, 0, &Size) &&
ERROR_INSUFFICIENT_BUFFER != GetLastError())
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
AttrList = MemAlloc(Size);
if (0 == AttrList)
{
Result = STATUS_INSUFFICIENT_RESOURCES;
goto exit;
}
if (!InitializeProcThreadAttributeList(AttrList, 1, 0, &Size))
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
/* only the child ends of stdin/stdout are actually inherited */
if (!UpdateProcThreadAttribute(AttrList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
ChildHandles, sizeof ChildHandles, 0, 0))
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
StartupInfoEx.StartupInfo.cb = sizeof StartupInfoEx;
StartupInfoEx.lpAttributeList = AttrList;
StartupInfoEx.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
StartupInfoEx.StartupInfo.hStdInput = ChildHandles[0];
StartupInfoEx.StartupInfo.hStdOutput = ChildHandles[1];
StartupInfoEx.StartupInfo.hStdError = ChildHandles[2];
if (!CreateProcessW(Executable, CommandLine, 0, 0, TRUE,
CREATE_SUSPENDED | CREATE_NEW_PROCESS_GROUP | EXTENDED_STARTUPINFO_PRESENT, 0, 0,
&StartupInfoEx.StartupInfo, ProcessInfo))
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
}
else
{
if (!CreateProcessW(Executable, CommandLine, 0, 0, FALSE,
CREATE_SUSPENDED | CREATE_NEW_PROCESS_GROUP, 0, 0,
&StartupInfoEx.StartupInfo, ProcessInfo))
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
}
Result = STATUS_SUCCESS;
exit:
if (!NT_SUCCESS(Result))
{
if (INVALID_HANDLE_VALUE != ParentHandles[0])
CloseHandle(ParentHandles[0]);
if (INVALID_HANDLE_VALUE != ParentHandles[0])
CloseHandle(ParentHandles[1]);
}
else if (0 != StdioHandles)
{
StdioHandles[0] = ParentHandles[0];
StdioHandles[1] = ParentHandles[1];
}
if (INVALID_HANDLE_VALUE != ChildHandles[0])
CloseHandle(ChildHandles[0]);
if (INVALID_HANDLE_VALUE != ChildHandles[1])
CloseHandle(ChildHandles[1]);
MemFree(AttrList);
return Result;
}
NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv0, HANDLE Job,
BOOLEAN RedirectStdio,
SVC_INSTANCE **PSvcInstance)
{
SVC_INSTANCE *SvcInstance = 0;
@ -241,10 +414,9 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
DWORD ClassNameSize, InstanceNameSize;
WCHAR Executable[MAX_PATH], CommandLineBuf[512], SecurityBuf[512];
PWSTR CommandLine, Security;
DWORD JobControl;
DWORD JobControl, Credentials;
PSECURITY_DESCRIPTOR SecurityDescriptor = 0;
PWSTR Argv[10];
STARTUPINFOW StartupInfo;
PROCESS_INFORMATION ProcessInfo;
NTSTATUS Result;
@ -259,7 +431,6 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
Argv[0] = 0;
Argc++;
memset(&StartupInfo, 0, sizeof StartupInfo);
memset(&ProcessInfo, 0, sizeof ProcessInfo);
EnterCriticalSection(&SvcInstanceLock);
@ -277,6 +448,22 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
goto exit;
}
RegSize = sizeof Credentials;
Credentials = 0;
RegResult = RegGetValueW(RegKey, ClassName, L"Credentials", RRF_RT_REG_DWORD, 0,
&Credentials, &RegSize);
if (ERROR_SUCCESS != RegResult && ERROR_FILE_NOT_FOUND != RegResult)
{
Result = FspNtStatusFromWin32(RegResult);
goto exit;
}
if ((!RedirectStdio && 0 != Credentials) ||
( RedirectStdio && 0 == Credentials))
{
Result = STATUS_DEVICE_CONFIGURATION_ERROR;
goto exit;
}
RegSize = sizeof Executable;
Executable[0] = L'\0';
RegResult = RegGetValueW(RegKey, ClassName, L"Executable", RRF_RT_REG_SZ, 0,
@ -336,7 +523,7 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
goto exit;
}
FspDebugLogSD(__FUNCTION__ ": SDDL = %s\n", SecurityDescriptor);
//FspDebugLogSD(__FUNCTION__ ": SDDL = %s\n", SecurityDescriptor);
Result = SvcInstanceAccessCheck(ClientToken, SERVICE_START, SecurityDescriptor);
if (!NT_SUCCESS(Result))
@ -353,23 +540,23 @@ NTSTATUS SvcInstanceCreate(HANDLE ClientToken,
}
memset(SvcInstance, 0, sizeof *SvcInstance);
SvcInstance->RefCount = 2;
memcpy(SvcInstance->Buffer, ClassName, ClassNameSize);
memcpy(SvcInstance->Buffer + ClassNameSize / sizeof(WCHAR), InstanceName, InstanceNameSize);
SvcInstance->ClassName = SvcInstance->Buffer;
SvcInstance->InstanceName = SvcInstance->Buffer + ClassNameSize / sizeof(WCHAR);
SvcInstance->SecurityDescriptor = SecurityDescriptor;
SvcInstance->StdioHandles[0] = INVALID_HANDLE_VALUE;
SvcInstance->StdioHandles[1] = INVALID_HANDLE_VALUE;
Result = SvcInstanceReplaceArguments(CommandLine, Argc, Argv, &SvcInstance->CommandLine);
if (!NT_SUCCESS(Result))
goto exit;
StartupInfo.cb = sizeof StartupInfo;
if (!CreateProcessW(Executable, SvcInstance->CommandLine, 0, 0, FALSE,
CREATE_SUSPENDED | CREATE_NEW_PROCESS_GROUP, 0, 0, &StartupInfo, &ProcessInfo))
{
Result = FspNtStatusFromWin32(GetLastError());
Result = SvcInstanceCreateProcess(Executable, SvcInstance->CommandLine,
RedirectStdio ? SvcInstance->StdioHandles : 0, &ProcessInfo);
if (!NT_SUCCESS(Result))
goto exit;
}
SvcInstance->ProcessId = ProcessInfo.dwProcessId;
SvcInstance->Process = ProcessInfo.hProcess;
@ -413,6 +600,11 @@ exit:
if (0 != SvcInstance)
{
if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[0])
CloseHandle(SvcInstance->StdioHandles[0]);
if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[1])
CloseHandle(SvcInstance->StdioHandles[1]);
if (0 != SvcInstance->ProcessWait)
UnregisterWaitEx(SvcInstance->ProcessWait, 0);
@ -435,13 +627,21 @@ exit:
return Result;
}
VOID SvcInstanceDelete(SVC_INSTANCE *SvcInstance)
static VOID SvcInstanceRelease(SVC_INSTANCE *SvcInstance)
{
if (0 != InterlockedDecrement(&SvcInstance->RefCount))
return;
EnterCriticalSection(&SvcInstanceLock);
if (RemoveEntryList(&SvcInstance->ListEntry))
SetEvent(SvcInstanceEvent);
LeaveCriticalSection(&SvcInstanceLock);
if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[0])
CloseHandle(SvcInstance->StdioHandles[0]);
if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[1])
CloseHandle(SvcInstance->StdioHandles[1]);
if (0 != SvcInstance->ProcessWait)
UnregisterWaitEx(SvcInstance->ProcessWait, 0);
if (0 != SvcInstance->Process)
@ -457,15 +657,102 @@ static VOID CALLBACK SvcInstanceTerminated(PVOID Context, BOOLEAN Timeout)
{
SVC_INSTANCE *SvcInstance = Context;
SvcInstanceDelete(SvcInstance);
SvcInstanceRelease(SvcInstance);
}
NTSTATUS SvcInstanceStart(HANDLE ClientToken,
PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv, HANDLE Job)
PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv, HANDLE Job,
BOOLEAN HasSecret)
{
SVC_INSTANCE *SvcInstance;
NTSTATUS Result;
return SvcInstanceCreate(ClientToken, ClassName, InstanceName, Argc, Argv, Job, &SvcInstance);
if (HasSecret && (0 == Argc || L'\0' == Argv[Argc - 1][0]))
return STATUS_INVALID_PARAMETER;
HasSecret = !!HasSecret;
Result = SvcInstanceCreate(ClientToken, ClassName, InstanceName,
Argc - HasSecret, Argv, Job, HasSecret,
&SvcInstance);
if (!NT_SUCCESS(Result))
return Result;
if (!HasSecret)
Result = STATUS_SUCCESS;
else
{
PWSTR Secret = Argv[Argc - 1];
UINT8 ReqBuf[256];
UINT8 RspBuf[2];
DWORD BytesTransferred;
OVERLAPPED Overlapped;
DWORD WaitResult;
if (0 == (BytesTransferred =
WideCharToMultiByte(CP_UTF8, 0, Secret, lstrlenW(Secret), ReqBuf, sizeof ReqBuf, 0, 0)))
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
if (!WriteFile(SvcInstance->StdioHandles[0], ReqBuf, BytesTransferred, &BytesTransferred, 0))
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
CloseHandle(SvcInstance->StdioHandles[0]);
SvcInstance->StdioHandles[0] = INVALID_HANDLE_VALUE;
memset(&Overlapped, 0, sizeof Overlapped);
if (!ReadFile(SvcInstance->StdioHandles[1], RspBuf, sizeof RspBuf, 0, &Overlapped) &&
ERROR_IO_PENDING != GetLastError())
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
/*
* We need to perform a GetOverlappedResult with a timeout. GetOverlappedResultEx would
* be perfect except that it is a Windows 8 and above API. We will therefore replace with
* WaitForSingleObject followed by GetOverlappedResult on success.
*/
WaitResult = WaitForSingleObject(SvcInstance->StdioHandles[1],
LAUNCHER_START_WITH_SECRET_TIMEOUT);
if (WAIT_OBJECT_0 == WaitResult)
Result = GetOverlappedResult(SvcInstance->StdioHandles[1], &Overlapped, &BytesTransferred, TRUE) ?
STATUS_SUCCESS : FspNtStatusFromWin32(GetLastError());
else if (WAIT_TIMEOUT == WaitResult)
Result = STATUS_TIMEOUT;
else
Result = FspNtStatusFromWin32(GetLastError());
if (!NT_SUCCESS(Result) || STATUS_TIMEOUT == Result)
{
CancelIoEx(SvcInstance->StdioHandles[1], &Overlapped);
goto exit;
}
if (sizeof RspBuf <= BytesTransferred && 'O' == RspBuf[0] && 'K' == RspBuf[1])
Result = STATUS_SUCCESS;
else
Result = STATUS_ACCESS_DENIED;
}
exit:
if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[0])
{
CloseHandle(SvcInstance->StdioHandles[0]);
SvcInstance->StdioHandles[0] = INVALID_HANDLE_VALUE;
}
if (INVALID_HANDLE_VALUE != SvcInstance->StdioHandles[1])
{
CloseHandle(SvcInstance->StdioHandles[1]);
SvcInstance->StdioHandles[1] = INVALID_HANDLE_VALUE;
}
SvcInstanceRelease(SvcInstance);
return Result;
}
NTSTATUS SvcInstanceStop(HANDLE ClientToken,
@ -625,7 +912,7 @@ static NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv)
&SecurityAttributes.lpSecurityDescriptor, 0))
goto fail;
FspDebugLogSD(__FUNCTION__ ": SDDL = %s\n", SecurityAttributes.lpSecurityDescriptor);
//FspDebugLogSD(__FUNCTION__ ": SDDL = %s\n", SecurityAttributes.lpSecurityDescriptor);
SvcInstanceEvent = CreateEventW(0, TRUE, TRUE, 0);
if (0 == SvcInstanceEvent)
@ -909,12 +1196,16 @@ static VOID SvcPipeTransact(HANDLE ClientToken, PWSTR PipeBuf, PULONG PSize)
PWSTR P = PipeBuf, PipeBufEnd = PipeBuf + *PSize / sizeof(WCHAR);
PWSTR ClassName, InstanceName;
ULONG Argc; PWSTR Argv[9];
BOOLEAN HasSecret = FALSE;
NTSTATUS Result;
*PSize = 0;
switch (*P++)
{
case LauncherSvcInstanceStartWithSecret:
HasSecret = TRUE;
/* fall through! */
case LauncherSvcInstanceStart:
ClassName = SvcPipeTransactGetPart(&P, PipeBufEnd);
InstanceName = SvcPipeTransactGetPart(&P, PipeBufEnd);
@ -924,7 +1215,8 @@ static VOID SvcPipeTransact(HANDLE ClientToken, PWSTR PipeBuf, PULONG PSize)
Result = STATUS_INVALID_PARAMETER;
if (0 != ClassName && 0 != InstanceName)
Result = SvcInstanceStart(ClientToken, ClassName, InstanceName, Argc, Argv, SvcJob);
Result = SvcInstanceStart(ClientToken, ClassName, InstanceName, Argc, Argv, SvcJob,
HasSecret);
SvcPipeTransactResult(Result, PipeBuf, PSize);
break;

View File

@ -21,13 +21,17 @@
#include <winfsp/winfsp.h>
#include <shared/minimal.h>
#define LAUNCHER_REGKEY "SYSTEM\\CurrentControlSet\\Services\\WinFsp.Launcher\\Services"
#define LAUNCHER_STOP_TIMEOUT 5500
#define LAUNCHER_KILL_TIMEOUT 5000
#define LAUNCHER_PIPE_NAME "\\\\.\\pipe\\WinFsp.{14E7137D-22B4-437A-B0C1-D21D1BDF3767}"
#define LAUNCHER_PIPE_BUFFER_SIZE 2048
#define LAUNCHER_PIPE_BUFFER_SIZE 4096
#define LAUNCHER_PIPE_DEFAULT_TIMEOUT 3000
#define LAUNCHER_START_WITH_SECRET_TIMEOUT 15000
/*
* The launcher named pipe SDDL gives full access to LocalSystem and Administrators and
* GENERIC_READ and FILE_WRITE_DATA access to Everyone. We are careful not to give the
@ -53,6 +57,7 @@
enum
{
LauncherSvcInstanceStart = 'S', /* requires: SERVICE_START */
LauncherSvcInstanceStartWithSecret = 'X', /* requires: SERVICE_START */
LauncherSvcInstanceStop = 'T', /* requires: SERVICE_STOP */
LauncherSvcInstanceInfo = 'I', /* requires: SERVICE_QUERY_STATUS */
LauncherSvcInstanceList = 'L', /* requires: none*/

View File

@ -26,6 +26,7 @@
* - "C/C++ > Code Generation > Basic Runtime Checks" must be set to "Default"
* - "C/C++ > Code Generation > Runtime Library" must be set to "Multi-threaded (/MT)".
* - "C/C++ > Code Generation > Security Check" must be disabled (/GS-).
* - "C/C++ > Command Line > Additional Options" add "/Gs16384" to disable __chkstk probes.
* - "Linker > Input > Ignore All Default Libraries" must be "Yes".
*
*
@ -50,8 +51,14 @@
NTSYSAPI VOID NTAPI RtlFillMemory(VOID *Destination, DWORD Length, BYTE Fill);
NTSYSAPI VOID NTAPI RtlMoveMemory(VOID *Destination, CONST VOID *Source, DWORD Length);
#pragma function(memcpy)
#pragma function(memset)
#pragma function(memcpy)
static inline
void *memset(void *dst, int val, size_t siz)
{
RtlFillMemory(dst, (DWORD)siz, val);
return dst;
}
static inline
void *memcpy(void *dst, const void *src, size_t siz)
{
@ -59,9 +66,9 @@ void *memcpy(void *dst, const void *src, size_t siz)
return dst;
}
static inline
void *memset(void *dst, int val, size_t siz)
void *memmove(void *dst, const void *src, size_t siz)
{
RtlFillMemory(dst, (DWORD)siz, val);
RtlMoveMemory(dst, src, (DWORD)siz);
return dst;
}

View File

@ -268,18 +268,22 @@ static NTSTATUS FspFsvolCreateNoLock(
/* check and remove any volume prefix */
if (0 == RelatedFileObject && 0 < FsvolDeviceExtension->VolumePrefix.Length)
{
if (FileNode->FileName.Length <= FsvolDeviceExtension->VolumePrefix.Length ||
!RtlEqualMemory(FileNode->FileName.Buffer, FsvolDeviceExtension->VolumePrefix.Buffer,
FsvolDeviceExtension->VolumePrefix.Length) ||
'\\' != FileNode->FileName.Buffer[FsvolDeviceExtension->VolumePrefix.Length / sizeof(WCHAR)])
if (!FspFsvolDeviceVolumePrefixInString(FsvolDeviceObject, &FileNode->FileName) ||
(FileNode->FileName.Length > FsvolDeviceExtension->VolumePrefix.Length &&
'\\' != FileNode->FileName.Buffer[FsvolDeviceExtension->VolumePrefix.Length / sizeof(WCHAR)]))
{
FspFileNodeDereference(FileNode);
return STATUS_OBJECT_PATH_NOT_FOUND;
}
FileNode->FileName.Length -= FsvolDeviceExtension->VolumePrefix.Length;
FileNode->FileName.MaximumLength -= FsvolDeviceExtension->VolumePrefix.Length;
FileNode->FileName.Buffer += FsvolDeviceExtension->VolumePrefix.Length / sizeof(WCHAR);
if (FileNode->FileName.Length > FsvolDeviceExtension->VolumePrefix.Length)
{
FileNode->FileName.Length -= FsvolDeviceExtension->VolumePrefix.Length;
FileNode->FileName.MaximumLength -= FsvolDeviceExtension->VolumePrefix.Length;
FileNode->FileName.Buffer += FsvolDeviceExtension->VolumePrefix.Length / sizeof(WCHAR);
}
else
FileNode->FileName.Length = sizeof(WCHAR);
}
ASSERT(sizeof(WCHAR) <= FileNode->FileName.Length && L'\\' == FileNode->FileName.Buffer[0]);
@ -335,7 +339,8 @@ static NTSTATUS FspFsvolCreateNoLock(
}
/* fix FileAttributes */
ClearFlag(FileAttributes, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY);
ClearFlag(FileAttributes,
FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT);
if (CreateOptions & FILE_DIRECTORY_FILE)
SetFlag(FileAttributes, FILE_ATTRIBUTE_DIRECTORY);
@ -485,7 +490,8 @@ NTSTATUS FspFsvolCreateComplete(
FSP_FILE_DESC *FileDesc = FspIopRequestContext(Request, RequestFileDesc);
FSP_FILE_NODE *FileNode = FileDesc->FileNode;
FSP_FILE_NODE *OpenedFileNode;
UNICODE_STRING ReparseFileName;
PREPARSE_DATA_BUFFER ReparseData;
UNICODE_STRING ReparseTargetPrefix0, ReparseTargetPrefix1, ReparseTargetPath;
if (FspFsctlTransactCreateKind == Request->Kind)
{
@ -498,32 +504,100 @@ NTSTATUS FspFsvolCreateComplete(
}
/* special case STATUS_REPARSE */
if (STATUS_REPARSE == Result)
if (STATUS_REPARSE == Response->IoStatus.Status)
{
ReparseFileName.Buffer =
(PVOID)(Response->Buffer + Response->Rsp.Create.Reparse.FileName.Offset);
ReparseFileName.Length = ReparseFileName.MaximumLength =
Response->Rsp.Create.Reparse.FileName.Size;
Result = STATUS_ACCESS_DENIED;
if (IO_REPARSE == Response->IoStatus.Information)
if (IO_REMOUNT == Response->IoStatus.Information)
{
if (0 == ReparseFileName.Length ||
(PUINT8)ReparseFileName.Buffer + ReparseFileName.Length >
(PUINT8)Response + Response->Size)
FSP_RETURN();
Irp->IoStatus.Information = IO_REMOUNT;
FSP_RETURN(Result = STATUS_REPARSE);
}
else
if (IO_REPARSE == Response->IoStatus.Information ||
IO_REPARSE_TAG_SYMLINK == Response->IoStatus.Information)
{
/*
* IO_REPARSE means that the user-mode file system has returned an absolute (in
* the device namespace) path. Send it as is to the IO Manager.
*
* IO_REPARSE_TAG_SYMLINK means that the user-mode file system has returned a full
* symbolic link reparse buffer. If the symbolic link is absolute send it to the
* IO Manager as is. If the symbolic link is device-relative (absolute in the
* device namespace) prefix it with our volume name/prefix and then send it to the
* IO Manager.
*
* We do our own handling of IO_REPARSE_TAG_SYMLINK reparse points because
* experimentation with handing them directly to the IO Manager revealed problems
* with UNC paths (\??\UNC\{VolumePrefix}\{FilePath}).
*/
if (ReparseFileName.Length > FileObject->FileName.MaximumLength)
if (IO_REPARSE == Response->IoStatus.Information)
{
PVOID Buffer = FspAllocExternal(ReparseFileName.Length);
RtlZeroMemory(&ReparseTargetPrefix0, sizeof ReparseTargetPrefix0);
RtlZeroMemory(&ReparseTargetPrefix1, sizeof ReparseTargetPrefix1);
ReparseTargetPath.Length = ReparseTargetPath.MaximumLength =
Response->Rsp.Create.Reparse.Buffer.Size;
ReparseTargetPath.Buffer =
(PVOID)(Response->Buffer + Response->Rsp.Create.Reparse.Buffer.Offset);
if ((PUINT8)ReparseTargetPath.Buffer + ReparseTargetPath.Length >
(PUINT8)Response + Response->Size ||
ReparseTargetPath.Length < sizeof(WCHAR) || L'\\' != ReparseTargetPath.Buffer[0])
FSP_RETURN(Result = STATUS_REPARSE_POINT_NOT_RESOLVED);
}
else
{
ASSERT(IO_REPARSE_TAG_SYMLINK == Response->IoStatus.Information);
ReparseData = (PVOID)(Response->Buffer + Response->Rsp.Create.Reparse.Buffer.Offset);
if ((PUINT8)ReparseData + Response->Rsp.Create.Reparse.Buffer.Size >
(PUINT8)Response + Response->Size)
FSP_RETURN(Result = STATUS_REPARSE_POINT_NOT_RESOLVED);
Result = FsRtlValidateReparsePointBuffer(Response->Rsp.Create.Reparse.Buffer.Size,
ReparseData);
if (!NT_SUCCESS(Result))
FSP_RETURN(Result = STATUS_REPARSE_POINT_NOT_RESOLVED);
if (!FlagOn(ReparseData->SymbolicLinkReparseBuffer.Flags, SYMLINK_FLAG_RELATIVE))
{
RtlZeroMemory(&ReparseTargetPrefix0, sizeof ReparseTargetPrefix0);
RtlZeroMemory(&ReparseTargetPrefix1, sizeof ReparseTargetPrefix1);
}
else
{
RtlCopyMemory(&ReparseTargetPrefix0, &FsvolDeviceExtension->VolumeName,
sizeof ReparseTargetPrefix0);
RtlCopyMemory(&ReparseTargetPrefix1, &FsvolDeviceExtension->VolumePrefix,
sizeof ReparseTargetPrefix1);
}
ReparseTargetPath.Buffer = ReparseData->SymbolicLinkReparseBuffer.PathBuffer +
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
ReparseTargetPath.Length = ReparseTargetPath.MaximumLength =
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
if (ReparseTargetPath.Length < sizeof(WCHAR) || L'\\' != ReparseTargetPath.Buffer[0])
FSP_RETURN(Result = STATUS_REPARSE_POINT_NOT_RESOLVED);
}
if (ReparseTargetPrefix0.Length + ReparseTargetPrefix1.Length + ReparseTargetPath.Length >
FileObject->FileName.MaximumLength)
{
PVOID Buffer = FspAllocExternal(
ReparseTargetPrefix0.Length + ReparseTargetPrefix1.Length + ReparseTargetPath.Length);
if (0 == Buffer)
FSP_RETURN(Result = STATUS_INSUFFICIENT_RESOURCES);
FspFreeExternal(FileObject->FileName.Buffer);
FileObject->FileName.MaximumLength = ReparseFileName.Length;
FileObject->FileName.MaximumLength =
ReparseTargetPrefix0.Length + ReparseTargetPrefix1.Length + ReparseTargetPath.Length;
FileObject->FileName.Buffer = Buffer;
}
FileObject->FileName.Length = 0;
RtlCopyUnicodeString(&FileObject->FileName, &ReparseFileName);
RtlAppendUnicodeStringToString(&FileObject->FileName, &ReparseTargetPrefix0);
RtlAppendUnicodeStringToString(&FileObject->FileName, &ReparseTargetPrefix1);
RtlAppendUnicodeStringToString(&FileObject->FileName, &ReparseTargetPath);
/*
* The RelatedFileObject does not need to be changed according to:
@ -539,19 +613,35 @@ NTSTATUS FspFsvolCreateComplete(
* STATUS_REPARSE status returned by the filter. Therefore, it is not
* the responsibility of the filter to free that file object.
*/
}
else
if (IO_REMOUNT == Response->IoStatus.Information)
{
if (0 != ReparseFileName.Length)
FSP_RETURN();
}
else
FSP_RETURN();
Irp->IoStatus.Information = (ULONG_PTR)Response->IoStatus.Information;
Result = Response->IoStatus.Status;
FSP_RETURN();
Irp->IoStatus.Information = IO_REPARSE;
FSP_RETURN(Result = STATUS_REPARSE);
}
else
{
ReparseData = (PVOID)(Response->Buffer + Response->Rsp.Create.Reparse.Buffer.Offset);
if ((PUINT8)ReparseData + Response->Rsp.Create.Reparse.Buffer.Size >
(PUINT8)Response + Response->Size)
FSP_RETURN(Result = STATUS_IO_REPARSE_DATA_INVALID);
Result = FsRtlValidateReparsePointBuffer(Response->Rsp.Create.Reparse.Buffer.Size,
ReparseData);
if (!NT_SUCCESS(Result))
FSP_RETURN();
ASSERT(0 == Irp->Tail.Overlay.AuxiliaryBuffer);
Irp->Tail.Overlay.AuxiliaryBuffer = FspAllocNonPagedExternal(
Response->Rsp.Create.Reparse.Buffer.Size);
if (0 == Irp->Tail.Overlay.AuxiliaryBuffer)
FSP_RETURN(Result = STATUS_INSUFFICIENT_RESOURCES);
RtlCopyMemory(Irp->Tail.Overlay.AuxiliaryBuffer, ReparseData,
Response->Rsp.Create.Reparse.Buffer.Size);
Irp->IoStatus.Information = ReparseData->ReparseTag;
FSP_RETURN(Result = STATUS_REPARSE);
}
}
/* fix FileNode->FileName if we were doing SL_OPEN_TARGET_DIRECTORY */

View File

@ -45,6 +45,7 @@
#define FSP_ALLOC_INTERNAL_TAG 'IpsF'
#define FSP_ALLOC_EXTERNAL_TAG 'XpsF'
#define FSP_IO_INCREMENT IO_NETWORK_INCREMENT
#define FSP_VOLUME_PREFIX_CASE_INS TRUE
/* debug */
#if DBG
@ -427,6 +428,8 @@ PVOID FspAllocateIrpMustSucceed(CCHAR StackSize);
BOOLEAN FspUnicodePathIsValid(PUNICODE_STRING Path, BOOLEAN AllowStreams);
VOID FspUnicodePathSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix);
NTSTATUS FspCreateGuid(GUID *Guid);
NTSTATUS FspGetDeviceObjectPointer(PUNICODE_STRING ObjectName, ACCESS_MASK DesiredAccess,
PULONG PFileNameIndex, PFILE_OBJECT *PFileObject, PDEVICE_OBJECT *PDeviceObject);
NTSTATUS FspSendSetInformationIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
FILE_INFORMATION_CLASS FileInformationClass, PVOID FileInformation, ULONG Length);
NTSTATUS FspBufferUserBuffer(PIRP Irp, ULONG Length, LOCK_OPERATION Operation);
@ -655,13 +658,14 @@ enum
#define FspIopPostWorkRequestBestEffort(D, R)\
FspIopPostWorkRequestFunnel(D, R, TRUE)
#define FspIopCompleteIrp(I, R) FspIopCompleteIrpEx(I, R, TRUE)
#define REQ_ALIGN_SIZE 16
typedef VOID FSP_IOP_REQUEST_FINI(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Context[4]);
typedef struct
{
FSP_IOP_REQUEST_FINI *RequestFini;
PVOID Context[4];
FSP_FSCTL_TRANSACT_RSP *Response;
__declspec(align(MEMORY_ALLOCATION_ALIGNMENT)) UINT8 RequestBuf[];
__declspec(align(REQ_ALIGN_SIZE)) UINT8 RequestBuf[];
} FSP_FSCTL_TRANSACT_REQ_HEADER;
static inline
PVOID *FspIopRequestContextAddress(FSP_FSCTL_TRANSACT_REQ *Request, ULONG I)
@ -814,6 +818,12 @@ VOID FspFsvolDeviceGetVolumeInfo(PDEVICE_OBJECT DeviceObject, FSP_FSCTL_VOLUME_I
BOOLEAN FspFsvolDeviceTryGetVolumeInfo(PDEVICE_OBJECT DeviceObject, FSP_FSCTL_VOLUME_INFO *VolumeInfo);
VOID FspFsvolDeviceSetVolumeInfo(PDEVICE_OBJECT DeviceObject, const FSP_FSCTL_VOLUME_INFO *VolumeInfo);
VOID FspFsvolDeviceInvalidateVolumeInfo(PDEVICE_OBJECT DeviceObject);
static inline
BOOLEAN FspFsvolDeviceVolumePrefixInString(PDEVICE_OBJECT DeviceObject, PUNICODE_STRING String)
{
return RtlPrefixUnicodeString(&FspFsvolDeviceExtension(DeviceObject)->VolumePrefix, String,
FSP_VOLUME_PREFIX_CASE_INS);
}
NTSTATUS FspDeviceCopyList(
PDEVICE_OBJECT **PDeviceObjects, PULONG PDeviceObjectCount);
VOID FspDeviceDeleteList(
@ -1034,4 +1044,26 @@ extern WCHAR FspFileDescDirectoryPatternMatchAll[];
extern FSP_MV_CcCoherencyFlushAndPurgeCache *FspMvCcCoherencyFlushAndPurgeCache;
extern ULONG FspMvMdlMappingNoWrite;
/*
* Fixes
*/
/* ObCloseHandle: add missing prototype */
#if (NTDDI_VERSION < NTDDI_WIN7)
NTKERNELAPI
NTSTATUS
ObCloseHandle(
_In_ HANDLE Handle,
_In_ KPROCESSOR_MODE PreviousMode
);
#endif
/* RtlEqualMemory: this is defined as memcmp, which does not exist on Win7 x86! */
#undef RtlEqualMemory
static inline
LOGICAL RtlEqualMemory(const VOID *Source1, const VOID *Source2, SIZE_T Length)
{
return Length == RtlCompareMemory(Source1, Source2, Length);
}
#endif

View File

@ -18,21 +18,36 @@
#include <sys/driver.h>
static NTSTATUS FspFsctlFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
static NTSTATUS FspFsvolFileSystemControlReparsePoint(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
BOOLEAN IsWrite);
static NTSTATUS FspFsvolFileSystemControlReparsePointComplete(
PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response,
BOOLEAN IsWrite);
static NTSTATUS FspFsvolFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp);
FSP_IOCMPL_DISPATCH FspFsvolFileSystemControlComplete;
static FSP_IOP_REQUEST_FINI FspFsvolFileSystemControlRequestFini;
FSP_DRIVER_DISPATCH FspFileSystemControl;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FspFsctlFileSystemControl)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlReparsePoint)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlReparsePointComplete)
#pragma alloc_text(PAGE, FspFsvolFileSystemControl)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlComplete)
#pragma alloc_text(PAGE, FspFsvolFileSystemControlRequestFini)
#pragma alloc_text(PAGE, FspFileSystemControl)
#endif
enum
{
RequestFileNode = 0,
};
static NTSTATUS FspFsctlFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
PDEVICE_OBJECT FsctlDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
@ -44,32 +59,247 @@ static NTSTATUS FspFsctlFileSystemControl(
{
case FSP_FSCTL_VOLUME_NAME:
if (0 != IrpSp->FileObject->FsContext2)
Result = FspVolumeGetName(DeviceObject, Irp, IrpSp);
Result = FspVolumeGetName(FsctlDeviceObject, Irp, IrpSp);
break;
case FSP_FSCTL_VOLUME_LIST:
Result = FspVolumeGetNameList(DeviceObject, Irp, IrpSp);
Result = FspVolumeGetNameList(FsctlDeviceObject, Irp, IrpSp);
break;
case FSP_FSCTL_TRANSACT:
case FSP_FSCTL_TRANSACT_BATCH:
if (0 != IrpSp->FileObject->FsContext2)
Result = FspVolumeTransact(DeviceObject, Irp, IrpSp);
Result = FspVolumeTransact(FsctlDeviceObject, Irp, IrpSp);
break;
case FSP_FSCTL_STOP:
if (0 != IrpSp->FileObject->FsContext2)
Result = FspVolumeStop(DeviceObject, Irp, IrpSp);
Result = FspVolumeStop(FsctlDeviceObject, Irp, IrpSp);
break;
}
break;
case IRP_MN_MOUNT_VOLUME:
Result = FspVolumeMount(DeviceObject, Irp, IrpSp);
Result = FspVolumeMount(FsctlDeviceObject, Irp, IrpSp);
break;
}
return Result;
}
static NTSTATUS FspFsvolFileSystemControlReparsePoint(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp,
BOOLEAN IsWrite)
{
PAGED_CODE();
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(FsvolDeviceObject);
PFILE_OBJECT FileObject = IrpSp->FileObject;
/* do we support reparse points? */
if (!FsvolDeviceExtension->VolumeParams.ReparsePoints)
return STATUS_INVALID_DEVICE_REQUEST;
/* is this a valid FileObject? */
if (!FspFileNodeIsValid(FileObject->FsContext))
return STATUS_INVALID_DEVICE_REQUEST;
NTSTATUS Result;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
FSP_FILE_DESC *FileDesc = FileObject->FsContext2;
ULONG FsControlCode = IrpSp->Parameters.FileSystemControl.FsControlCode;
PVOID InputBuffer = Irp->AssociatedIrp.SystemBuffer;
PVOID OutputBuffer = Irp->UserBuffer;
ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
PREPARSE_DATA_BUFFER ReparseData;
PWSTR ReparseTargetPath;
USHORT ReparseTargetPathLength;
UINT16 TargetOnFileSystem = 0;
FSP_FSCTL_TRANSACT_REQ *Request;
ASSERT(FileNode == FileDesc->FileNode);
if (IsWrite)
{
if (0 == InputBuffer || 0 == InputBufferLength ||
FSP_FSCTL_TRANSACT_REQ_BUFFER_SIZEMAX - (FileNode->FileName.Length + sizeof(WCHAR)) <
InputBufferLength)
return STATUS_INVALID_PARAMETER;
Result = FsRtlValidateReparsePointBuffer(InputBufferLength, InputBuffer);
if (!NT_SUCCESS(Result))
return Result;
ReparseData = (PREPARSE_DATA_BUFFER)InputBuffer;
if (IO_REPARSE_TAG_SYMLINK == ReparseData->ReparseTag)
{
/* NTFS severely limits symbolic links; we will not do that unless our file system asks */
if (FsvolDeviceExtension->VolumeParams.ReparsePointsAccessCheck)
{
if (KernelMode != Irp->RequestorMode &&
!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_CREATE_SYMBOLIC_LINK_PRIVILEGE),
UserMode))
return STATUS_PRIVILEGE_NOT_HELD;
}
ReparseTargetPath = ReparseData->SymbolicLinkReparseBuffer.PathBuffer +
ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR);
ReparseTargetPathLength = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
/* is this an absolute path? determine if target resides on same device as link */
if (!FlagOn(ReparseData->SymbolicLinkReparseBuffer.Flags, SYMLINK_FLAG_RELATIVE) &&
ReparseTargetPathLength >= sizeof(WCHAR) && L'\\' == ReparseTargetPath[0])
{
UNICODE_STRING TargetObjectName;
PDEVICE_OBJECT TargetDeviceObject;
PFILE_OBJECT TargetFileObject;
ULONG TargetFileNameIndex;
ULONG32 TargetProviderId;
FSRTL_MUP_PROVIDER_INFO_LEVEL_1 ProviderInfo;
ULONG ProviderInfoSize;
TargetObjectName.Length = TargetObjectName.MaximumLength = ReparseTargetPathLength;
TargetObjectName.Buffer = ReparseTargetPath;
/* get a pointer to the target device */
Result = FspGetDeviceObjectPointer(&TargetObjectName, FILE_READ_DATA,
&TargetFileNameIndex, &TargetFileObject, &TargetDeviceObject);
if (!NT_SUCCESS(Result))
goto target_check_exit;
/* is the target device the same as ours? */
if (TargetFileNameIndex < ReparseTargetPathLength &&
IoGetRelatedDeviceObject(FileObject) == TargetDeviceObject)
{
if (0 == FsvolDeviceExtension->VolumePrefix.Length)
/* not going thru MUP: DONE! */
TargetOnFileSystem = (UINT16)TargetFileNameIndex;
else
{
/* going thru MUP cases: \Device\Volume{GUID} and \??\UNC\{VolumePrefix} */
ProviderInfoSize = sizeof ProviderInfo;
Result = FsRtlMupGetProviderInfoFromFileObject(TargetFileObject, 1,
&ProviderInfo, &ProviderInfoSize);
if (NT_SUCCESS(Result))
{
/* case \Device\Volume{GUID}: is the targer provider id same as ours? */
TargetProviderId = ProviderInfo.ProviderId;
ProviderInfoSize = sizeof ProviderInfo;
Result = FsRtlMupGetProviderInfoFromFileObject(FileObject, 1,
&ProviderInfo, &ProviderInfoSize);
if (!NT_SUCCESS(Result))
goto target_check_exit;
if (ProviderInfo.ProviderId == TargetProviderId)
TargetOnFileSystem = (UINT16)TargetFileNameIndex;
}
else
{
/* case \??\UNC\{VolumePrefix}: is the target volume prefix same as ours? */
TargetObjectName.Length = TargetObjectName.MaximumLength =
FsvolDeviceExtension->VolumePrefix.Length;
TargetObjectName.Buffer = ReparseTargetPath +
TargetFileNameIndex / sizeof(WCHAR);
TargetFileNameIndex += FsvolDeviceExtension->VolumePrefix.Length;
if (TargetFileNameIndex < ReparseTargetPathLength &&
RtlEqualUnicodeString(&FsvolDeviceExtension->VolumePrefix,
&TargetObjectName,
FSP_VOLUME_PREFIX_CASE_INS))
TargetOnFileSystem = (UINT16)TargetFileNameIndex;
}
}
}
ObDereferenceObject(TargetFileObject);
target_check_exit:
;
}
}
FspFileNodeAcquireExclusive(FileNode, Full);
}
else
{
if (0 == OutputBuffer || 0 == OutputBufferLength)
return STATUS_INVALID_PARAMETER;
/*
* NtFsControlFile (IopXxxControlFile) will setup Irp->AssociatedIrp.SystemBuffer
* with enough space for either InputBufferLength or OutputBufferLength. There is
* no need to call FspBufferUserBuffer ourselves.
*/
FspFileNodeAcquireShared(FileNode, Full);
}
Result = FspIopCreateRequestEx(Irp, &FileNode->FileName, IsWrite ? InputBufferLength : 0,
FspFsvolFileSystemControlRequestFini, &Request);
if (!NT_SUCCESS(Result))
{
FspFileNodeRelease(FileNode, Full);
return Result;
}
Request->Kind = FspFsctlTransactFileSystemControlKind;
Request->Req.FileSystemControl.UserContext = FileNode->UserContext;
Request->Req.FileSystemControl.UserContext2 = FileDesc->UserContext2;
Request->Req.FileSystemControl.FsControlCode = FsControlCode;
if (IsWrite)
{
Request->Req.FileSystemControl.Buffer.Offset = Request->FileName.Size;
Request->Req.FileSystemControl.Buffer.Size = (UINT16)InputBufferLength;
RtlCopyMemory(Request->Buffer + Request->Req.FileSystemControl.Buffer.Offset,
InputBuffer, InputBufferLength);
Request->Req.FileSystemControl.TargetOnFileSystem = TargetOnFileSystem;
}
FspFileNodeSetOwner(FileNode, Full, Request);
FspIopRequestContext(Request, RequestFileNode) = FileNode;
return FSP_STATUS_IOQ_POST;
}
static NTSTATUS FspFsvolFileSystemControlReparsePointComplete(
PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response,
BOOLEAN IsWrite)
{
PAGED_CODE();
if (IsWrite)
return STATUS_SUCCESS;
NTSTATUS Result;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PVOID OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
if (Response->Buffer + Response->Rsp.FileSystemControl.Buffer.Offset +
Response->Rsp.FileSystemControl.Buffer.Size > (PUINT8)Response + Response->Size)
return STATUS_IO_REPARSE_DATA_INVALID;
Result = FsRtlValidateReparsePointBuffer(Response->Rsp.FileSystemControl.Buffer.Size,
(PVOID)(Response->Buffer + Response->Rsp.FileSystemControl.Buffer.Offset));
if (!NT_SUCCESS(Result))
return Result;
if (Response->Rsp.FileSystemControl.Buffer.Size > OutputBufferLength)
return STATUS_BUFFER_TOO_SMALL;
RtlCopyMemory(OutputBuffer, Response->Buffer + Response->Rsp.FileSystemControl.Buffer.Offset,
Response->Rsp.FileSystemControl.Buffer.Size);
Irp->IoStatus.Information = Response->Rsp.FileSystemControl.Buffer.Size;
return STATUS_SUCCESS;
}
static NTSTATUS FspFsvolFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
{
PAGED_CODE();
@ -81,7 +311,14 @@ static NTSTATUS FspFsvolFileSystemControl(
{
case FSP_FSCTL_WORK:
case FSP_FSCTL_WORK_BEST_EFFORT:
Result = FspVolumeWork(DeviceObject, Irp, IrpSp);
Result = FspVolumeWork(FsvolDeviceObject, Irp, IrpSp);
break;
case FSCTL_GET_REPARSE_POINT:
Result = FspFsvolFileSystemControlReparsePoint(FsvolDeviceObject, Irp, IrpSp, FALSE);
break;
case FSCTL_SET_REPARSE_POINT:
case FSCTL_DELETE_REPARSE_POINT:
Result = FspFsvolFileSystemControlReparsePoint(FsvolDeviceObject, Irp, IrpSp, TRUE);
break;
}
break;
@ -95,6 +332,43 @@ NTSTATUS FspFsvolFileSystemControlComplete(
{
FSP_ENTER_IOC(PAGED_CODE());
/* exit now if we do not have a FileObject (FSP_FSCTL_WORK*) */
if (0 == IrpSp->FileObject)
FSP_RETURN();
if (!NT_SUCCESS(Response->IoStatus.Status))
{
Irp->IoStatus.Information = 0;
Result = Response->IoStatus.Status;
FSP_RETURN();
}
PFILE_OBJECT FileObject = IrpSp->FileObject;
FSP_FILE_NODE *FileNode = FileObject->FsContext;
FSP_FSCTL_TRANSACT_REQ *Request = FspIrpRequest(Irp);
Result = STATUS_INVALID_DEVICE_REQUEST;
switch (IrpSp->MinorFunction)
{
case IRP_MN_USER_FS_REQUEST:
switch (IrpSp->Parameters.FileSystemControl.FsControlCode)
{
case FSCTL_GET_REPARSE_POINT:
Result = FspFsvolFileSystemControlReparsePointComplete(Irp, Response, FALSE);
break;
case FSCTL_SET_REPARSE_POINT:
case FSCTL_DELETE_REPARSE_POINT:
Result = FspFsvolFileSystemControlReparsePointComplete(Irp, Response, TRUE);
break;
}
break;
}
ASSERT(STATUS_INVALID_DEVICE_REQUEST != Result);
FspIopRequestContext(Request, RequestFileNode) = 0;
FspFileNodeReleaseOwner(FileNode, Full, Request);
FSP_LEAVE_IOC(
"%s%sFileObject=%p",
IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction ?
@ -103,6 +377,16 @@ NTSTATUS FspFsvolFileSystemControlComplete(
IrpSp->FileObject);
}
static VOID FspFsvolFileSystemControlRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PVOID Context[4])
{
PAGED_CODE();
FSP_FILE_NODE *FileNode = Context[RequestFileNode];
if (0 != FileNode)
FspFileNodeReleaseOwner(FileNode, Full, Request);
}
NTSTATUS FspFileSystemControl(
PDEVICE_OBJECT DeviceObject, PIRP Irp)
{

View File

@ -89,82 +89,82 @@ SYM(FSCTL_DFSR_SET_GHOST_HANDLE_STATE)
SYM(FSCTL_TXFS_LIST_TRANSACTIONS)
SYM(FSCTL_QUERY_PAGEFILE_ENCRYPTION)
SYM(FSCTL_RESET_VOLUME_ALLOCATION_HINTS)
SYM(FSCTL_QUERY_DEPENDENT_VOLUME)
SYM(FSCTL_SD_GLOBAL_CHANGE)
//SYM(FSCTL_QUERY_DEPENDENT_VOLUME)
//SYM(FSCTL_SD_GLOBAL_CHANGE)
SYM(FSCTL_TXFS_READ_BACKUP_INFORMATION2)
SYM(FSCTL_LOOKUP_STREAM_FROM_CLUSTER)
SYM(FSCTL_TXFS_WRITE_BACKUP_INFORMATION2)
SYM(FSCTL_FILE_TYPE_NOTIFICATION)
SYM(FSCTL_FILE_LEVEL_TRIM)
SYM(FSCTL_GET_BOOT_AREA_INFO)
SYM(FSCTL_GET_RETRIEVAL_POINTER_BASE)
SYM(FSCTL_SET_PERSISTENT_VOLUME_STATE)
SYM(FSCTL_QUERY_PERSISTENT_VOLUME_STATE)
SYM(FSCTL_REQUEST_OPLOCK)
SYM(FSCTL_CSV_TUNNEL_REQUEST)
SYM(FSCTL_IS_CSV_FILE)
SYM(FSCTL_QUERY_FILE_SYSTEM_RECOGNITION)
SYM(FSCTL_CSV_GET_VOLUME_PATH_NAME)
SYM(FSCTL_CSV_GET_VOLUME_NAME_FOR_VOLUME_MOUNT_POINT)
SYM(FSCTL_CSV_GET_VOLUME_PATH_NAMES_FOR_VOLUME_NAME)
SYM(FSCTL_IS_FILE_ON_CSV_VOLUME)
SYM(FSCTL_CORRUPTION_HANDLING)
SYM(FSCTL_OFFLOAD_READ)
SYM(FSCTL_OFFLOAD_WRITE)
SYM(FSCTL_CSV_INTERNAL)
SYM(FSCTL_SET_PURGE_FAILURE_MODE)
SYM(FSCTL_QUERY_FILE_LAYOUT)
SYM(FSCTL_IS_VOLUME_OWNED_BYCSVFS)
SYM(FSCTL_GET_INTEGRITY_INFORMATION)
SYM(FSCTL_SET_INTEGRITY_INFORMATION)
SYM(FSCTL_QUERY_FILE_REGIONS)
SYM(FSCTL_DEDUP_FILE)
SYM(FSCTL_DEDUP_QUERY_FILE_HASHES)
SYM(FSCTL_DEDUP_QUERY_RANGE_STATE)
SYM(FSCTL_DEDUP_QUERY_REPARSE_INFO)
SYM(FSCTL_RKF_INTERNAL)
SYM(FSCTL_SCRUB_DATA)
SYM(FSCTL_REPAIR_COPIES)
SYM(FSCTL_DISABLE_LOCAL_BUFFERING)
SYM(FSCTL_CSV_MGMT_LOCK)
SYM(FSCTL_CSV_QUERY_DOWN_LEVEL_FILE_SYSTEM_CHARACTERISTICS)
SYM(FSCTL_ADVANCE_FILE_ID)
SYM(FSCTL_CSV_SYNC_TUNNEL_REQUEST)
SYM(FSCTL_CSV_QUERY_VETO_FILE_DIRECT_IO)
SYM(FSCTL_WRITE_USN_REASON)
SYM(FSCTL_CSV_CONTROL)
SYM(FSCTL_GET_REFS_VOLUME_DATA)
SYM(FSCTL_CSV_H_BREAKING_SYNC_TUNNEL_REQUEST)
SYM(FSCTL_QUERY_STORAGE_CLASSES)
SYM(FSCTL_QUERY_REGION_INFO)
SYM(FSCTL_USN_TRACK_MODIFIED_RANGES)
SYM(FSCTL_QUERY_SHARED_VIRTUAL_DISK_SUPPORT)
SYM(FSCTL_SVHDX_SYNC_TUNNEL_REQUEST)
SYM(FSCTL_SVHDX_SET_INITIATOR_INFORMATION)
SYM(FSCTL_SET_EXTERNAL_BACKING)
SYM(FSCTL_GET_EXTERNAL_BACKING)
SYM(FSCTL_DELETE_EXTERNAL_BACKING)
SYM(FSCTL_ENUM_EXTERNAL_BACKING)
SYM(FSCTL_ENUM_OVERLAY)
SYM(FSCTL_ADD_OVERLAY)
SYM(FSCTL_REMOVE_OVERLAY)
SYM(FSCTL_UPDATE_OVERLAY)
SYM(FSCTL_DUPLICATE_EXTENTS_TO_FILE)
SYM(FSCTL_SPARSE_OVERALLOCATE)
SYM(FSCTL_STORAGE_QOS_CONTROL)
SYM(FSCTL_INITIATE_FILE_METADATA_OPTIMIZATION)
SYM(FSCTL_QUERY_FILE_METADATA_OPTIMIZATION)
SYM(FSCTL_SVHDX_ASYNC_TUNNEL_REQUEST)
SYM(FSCTL_GET_WOF_VERSION)
SYM(FSCTL_HCS_SYNC_TUNNEL_REQUEST)
SYM(FSCTL_HCS_ASYNC_TUNNEL_REQUEST)
SYM(FSCTL_QUERY_EXTENT_READ_CACHE_INFO)
SYM(FSCTL_QUERY_REFS_VOLUME_COUNTER_INFO)
SYM(FSCTL_CLEAN_VOLUME_METADATA)
SYM(FSCTL_SET_INTEGRITY_INFORMATION_EX)
SYM(FSCTL_SUSPEND_OVERLAY)
SYM(FSCTL_VIRTUAL_STORAGE_QUERY_PROPERTY)
SYM(FSCTL_FILESYSTEM_GET_STATISTICS_EX)
//SYM(FSCTL_LOOKUP_STREAM_FROM_CLUSTER)
//SYM(FSCTL_TXFS_WRITE_BACKUP_INFORMATION2)
//SYM(FSCTL_FILE_TYPE_NOTIFICATION)
//SYM(FSCTL_FILE_LEVEL_TRIM)
//SYM(FSCTL_GET_BOOT_AREA_INFO)
//SYM(FSCTL_GET_RETRIEVAL_POINTER_BASE)
//SYM(FSCTL_SET_PERSISTENT_VOLUME_STATE)
//SYM(FSCTL_QUERY_PERSISTENT_VOLUME_STATE)
//SYM(FSCTL_REQUEST_OPLOCK)
//SYM(FSCTL_CSV_TUNNEL_REQUEST)
//SYM(FSCTL_IS_CSV_FILE)
//SYM(FSCTL_QUERY_FILE_SYSTEM_RECOGNITION)
//SYM(FSCTL_CSV_GET_VOLUME_PATH_NAME)
//SYM(FSCTL_CSV_GET_VOLUME_NAME_FOR_VOLUME_MOUNT_POINT)
//SYM(FSCTL_CSV_GET_VOLUME_PATH_NAMES_FOR_VOLUME_NAME)
//SYM(FSCTL_IS_FILE_ON_CSV_VOLUME)
//SYM(FSCTL_CORRUPTION_HANDLING)
//SYM(FSCTL_OFFLOAD_READ)
//SYM(FSCTL_OFFLOAD_WRITE)
//SYM(FSCTL_CSV_INTERNAL)
//SYM(FSCTL_SET_PURGE_FAILURE_MODE)
//SYM(FSCTL_QUERY_FILE_LAYOUT)
//SYM(FSCTL_IS_VOLUME_OWNED_BYCSVFS)
//SYM(FSCTL_GET_INTEGRITY_INFORMATION)
//SYM(FSCTL_SET_INTEGRITY_INFORMATION)
//SYM(FSCTL_QUERY_FILE_REGIONS)
//SYM(FSCTL_DEDUP_FILE)
//SYM(FSCTL_DEDUP_QUERY_FILE_HASHES)
//SYM(FSCTL_DEDUP_QUERY_RANGE_STATE)
//SYM(FSCTL_DEDUP_QUERY_REPARSE_INFO)
//SYM(FSCTL_RKF_INTERNAL)
//SYM(FSCTL_SCRUB_DATA)
//SYM(FSCTL_REPAIR_COPIES)
//SYM(FSCTL_DISABLE_LOCAL_BUFFERING)
//SYM(FSCTL_CSV_MGMT_LOCK)
//SYM(FSCTL_CSV_QUERY_DOWN_LEVEL_FILE_SYSTEM_CHARACTERISTICS)
//SYM(FSCTL_ADVANCE_FILE_ID)
//SYM(FSCTL_CSV_SYNC_TUNNEL_REQUEST)
//SYM(FSCTL_CSV_QUERY_VETO_FILE_DIRECT_IO)
//SYM(FSCTL_WRITE_USN_REASON)
//SYM(FSCTL_CSV_CONTROL)
//SYM(FSCTL_GET_REFS_VOLUME_DATA)
//SYM(FSCTL_CSV_H_BREAKING_SYNC_TUNNEL_REQUEST)
//SYM(FSCTL_QUERY_STORAGE_CLASSES)
//SYM(FSCTL_QUERY_REGION_INFO)
//SYM(FSCTL_USN_TRACK_MODIFIED_RANGES)
//SYM(FSCTL_QUERY_SHARED_VIRTUAL_DISK_SUPPORT)
//SYM(FSCTL_SVHDX_SYNC_TUNNEL_REQUEST)
//SYM(FSCTL_SVHDX_SET_INITIATOR_INFORMATION)
//SYM(FSCTL_SET_EXTERNAL_BACKING)
//SYM(FSCTL_GET_EXTERNAL_BACKING)
//SYM(FSCTL_DELETE_EXTERNAL_BACKING)
//SYM(FSCTL_ENUM_EXTERNAL_BACKING)
//SYM(FSCTL_ENUM_OVERLAY)
//SYM(FSCTL_ADD_OVERLAY)
//SYM(FSCTL_REMOVE_OVERLAY)
//SYM(FSCTL_UPDATE_OVERLAY)
//SYM(FSCTL_DUPLICATE_EXTENTS_TO_FILE)
//SYM(FSCTL_SPARSE_OVERALLOCATE)
//SYM(FSCTL_STORAGE_QOS_CONTROL)
//SYM(FSCTL_INITIATE_FILE_METADATA_OPTIMIZATION)
//SYM(FSCTL_QUERY_FILE_METADATA_OPTIMIZATION)
//SYM(FSCTL_SVHDX_ASYNC_TUNNEL_REQUEST)
//SYM(FSCTL_GET_WOF_VERSION)
//SYM(FSCTL_HCS_SYNC_TUNNEL_REQUEST)
//SYM(FSCTL_HCS_ASYNC_TUNNEL_REQUEST)
//SYM(FSCTL_QUERY_EXTENT_READ_CACHE_INFO)
//SYM(FSCTL_QUERY_REFS_VOLUME_COUNTER_INFO)
//SYM(FSCTL_CLEAN_VOLUME_METADATA)
//SYM(FSCTL_SET_INTEGRITY_INFORMATION_EX)
//SYM(FSCTL_SUSPEND_OVERLAY)
//SYM(FSCTL_VIRTUAL_STORAGE_QUERY_PROPERTY)
//SYM(FSCTL_FILESYSTEM_GET_STATISTICS_EX)
SYM(FSCTL_LMR_GET_LINK_TRACKING_INFORMATION)
SYM(FSCTL_LMR_SET_LINK_TRACKING_INFORMATION)
SYM(IOCTL_LMR_ARE_FILE_OBJECTS_ON_SAME_SERVER)

View File

@ -48,10 +48,12 @@ NTSTATUS FspIopDispatchComplete(PIRP Irp, const FSP_FSCTL_TRANSACT_RSP *Response
#endif
/* Requests (and RequestHeaders) must be 16-byte aligned, because we use the low 4 bits for flags */
#if 16 != MEMORY_ALLOCATION_ALIGNMENT
#define REQ_HEADER_ALIGNMASK 15
#if REQ_ALIGN_SIZE <= MEMORY_ALLOCATION_ALIGNMENT
#define REQ_HEADER_ALIGN_MASK 0
#define REQ_HEADER_ALIGN_OVERHEAD 0
#else
#define REQ_HEADER_ALIGNMASK 0
#define REQ_HEADER_ALIGN_MASK (REQ_ALIGN_SIZE - 1)
#define REQ_HEADER_ALIGN_OVERHEAD (sizeof(PVOID) + REQ_HEADER_ALIGN_MASK)
#endif
NTSTATUS FspIopCreateRequestFunnel(
@ -74,20 +76,23 @@ NTSTATUS FspIopCreateRequestFunnel(
if (FlagOn(Flags, FspIopRequestMustSucceed))
RequestHeader = FspAllocatePoolMustSucceed(
FlagOn(Flags, FspIopRequestNonPaged) ? NonPagedPool : PagedPool,
sizeof *RequestHeader + sizeof *Request + ExtraSize + REQ_HEADER_ALIGNMASK,
sizeof *RequestHeader + sizeof *Request + ExtraSize + REQ_HEADER_ALIGN_OVERHEAD,
FSP_ALLOC_INTERNAL_TAG);
else
{
RequestHeader = ExAllocatePoolWithTag(
FlagOn(Flags, FspIopRequestNonPaged) ? NonPagedPool : PagedPool,
sizeof *RequestHeader + sizeof *Request + ExtraSize + REQ_HEADER_ALIGNMASK,
sizeof *RequestHeader + sizeof *Request + ExtraSize + REQ_HEADER_ALIGN_OVERHEAD,
FSP_ALLOC_INTERNAL_TAG);
if (0 == RequestHeader)
return STATUS_INSUFFICIENT_RESOURCES;
}
#if 0 != REQ_HEADER_ALIGNMASK
RequestHeader = (PVOID)(((UINT_PTR)RequestHeader + REQ_HEADER_ALIGNMASK) & REQ_HEADER_ALIGNMASK);
#if 0 != REQ_HEADER_ALIGN_MASK
PVOID Allocation = RequestHeader;
RequestHeader = (PVOID)(((UINT_PTR)RequestHeader + REQ_HEADER_ALIGN_OVERHEAD) &
~REQ_HEADER_ALIGN_MASK);
((PVOID *)RequestHeader)[-1] = Allocation;
#endif
RtlZeroMemory(RequestHeader, sizeof *RequestHeader + sizeof *Request + ExtraSize);
@ -127,6 +132,10 @@ VOID FspIopDeleteRequest(FSP_FSCTL_TRANSACT_REQ *Request)
if (0 != RequestHeader->Response)
FspFree(RequestHeader->Response);
#if 0 != REQ_HEADER_ALIGN_MASK
RequestHeader = ((PVOID *)RequestHeader)[-1];
#endif
FspFree(RequestHeader);
}
@ -218,7 +227,7 @@ VOID FspIopCompleteIrpEx(PIRP Irp, NTSTATUS Result, BOOLEAN DeviceDereference)
/* get the device object out of the IRP before completion */
PDEVICE_OBJECT DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject;
if (STATUS_SUCCESS != Result && STATUS_BUFFER_OVERFLOW != Result)
if (STATUS_SUCCESS != Result && STATUS_REPARSE != Result && STATUS_BUFFER_OVERFLOW != Result)
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = Result;
IoCompleteRequest(Irp, FSP_IO_INCREMENT);

View File

@ -20,6 +20,11 @@
/*
* Overview
*
* [NOTE: this comment no longer describes accurately an FSP_IOQ. The main
* difference is that an FSP_IOQ now has a third queue which is used to
* retry IRP completions. However the main ideas below are still valid, so
* I am leaving the rest of the comment intact.]
*
* An FSP_IOQ encapsulates the main FSP mechanism for handling IRP's.
* It has two queues: a "Pending" queue for managing newly arrived IRP's
* and a "Processing" queue for managing IRP's currently being processed

View File

@ -20,6 +20,8 @@
BOOLEAN FspUnicodePathIsValid(PUNICODE_STRING Path, BOOLEAN AllowStreams);
VOID FspUnicodePathSuffix(PUNICODE_STRING Path, PUNICODE_STRING Remain, PUNICODE_STRING Suffix);
NTSTATUS FspCreateGuid(GUID *Guid);
NTSTATUS FspGetDeviceObjectPointer(PUNICODE_STRING ObjectName, ACCESS_MASK DesiredAccess,
PULONG PFileNameIndex, PFILE_OBJECT *PFileObject, PDEVICE_OBJECT *PDeviceObject);
NTSTATUS FspSendSetInformationIrp(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject,
FILE_INFORMATION_CLASS FileInformationClass, PVOID FileInformation, ULONG Length);
static NTSTATUS FspSendSetInformationIrpCompletion(
@ -88,6 +90,7 @@ NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context);
#pragma alloc_text(PAGE, FspUnicodePathIsValid)
#pragma alloc_text(PAGE, FspUnicodePathSuffix)
#pragma alloc_text(PAGE, FspCreateGuid)
#pragma alloc_text(PAGE, FspGetDeviceObjectPointer)
#pragma alloc_text(PAGE, FspSendSetInformationIrp)
#pragma alloc_text(PAGE, FspBufferUserBuffer)
#pragma alloc_text(PAGE, FspLockUserBuffer)
@ -243,6 +246,54 @@ NTSTATUS FspCreateGuid(GUID *Guid)
return Result;
}
NTSTATUS FspGetDeviceObjectPointer(PUNICODE_STRING ObjectName, ACCESS_MASK DesiredAccess,
PULONG PFileNameIndex, PFILE_OBJECT *PFileObject, PDEVICE_OBJECT *PDeviceObject)
{
PAGED_CODE();
UNICODE_STRING PartialName;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE Handle;
NTSTATUS Result;
PartialName.Length = 0;
PartialName.MaximumLength = ObjectName->Length;
PartialName.Buffer = ObjectName->Buffer;
Result = STATUS_NO_SUCH_DEVICE;
while (PartialName.MaximumLength > PartialName.Length)
{
while (PartialName.MaximumLength > PartialName.Length &&
L'\\' == PartialName.Buffer[PartialName.Length / sizeof(WCHAR)])
PartialName.Length += sizeof(WCHAR);
while (PartialName.MaximumLength > PartialName.Length &&
L'\\' != PartialName.Buffer[PartialName.Length / sizeof(WCHAR)])
PartialName.Length += sizeof(WCHAR);
Result = IoGetDeviceObjectPointer(&PartialName, DesiredAccess, PFileObject, PDeviceObject);
if (NT_SUCCESS(Result))
{
*PFileNameIndex = PartialName.Length;
break;
}
InitializeObjectAttributes(&ObjectAttributes, &PartialName, OBJ_KERNEL_HANDLE, 0, 0);
Result = ZwOpenDirectoryObject(&Handle, 0, &ObjectAttributes);
if (!NT_SUCCESS(Result))
{
Result = ZwOpenSymbolicLinkObject(&Handle, 0, &ObjectAttributes);
if (!NT_SUCCESS(Result))
{
Result = STATUS_NO_SUCH_DEVICE;
break;
}
}
ZwClose(Handle);
}
return Result;
}
typedef struct
{
IO_STATUS_BLOCK IoStatus;

View File

@ -505,9 +505,7 @@ NTSTATUS FspVolumeRedirQueryPathEx(
if (!FspIoqStopped(FsvolDeviceExtension->Ioq))
{
if (0 < FsvolDeviceExtension->VolumePrefix.Length &&
QueryPathRequest->PathName.Length >= FsvolDeviceExtension->VolumePrefix.Length &&
RtlEqualMemory(QueryPathRequest->PathName.Buffer,
FsvolDeviceExtension->VolumePrefix.Buffer, FsvolDeviceExtension->VolumePrefix.Length) &&
FspFsvolDeviceVolumePrefixInString(FsvolDeviceObject, &QueryPathRequest->PathName) &&
(QueryPathRequest->PathName.Length == FsvolDeviceExtension->VolumePrefix.Length ||
'\\' == QueryPathRequest->PathName.Buffer[FsvolDeviceExtension->VolumePrefix.Length / sizeof(WCHAR)]))
{

View File

@ -151,8 +151,9 @@ static NTSTATUS FspFsvolWriteCached(
ASSERT(FspTimeoutInfinity32 ==
FspFsvolDeviceExtension(FsvolDeviceObject)->VolumeParams.FileInfoTimeout);
FspFileNodeGetFileInfo(FileNode, &FileInfo);
WriteEndOffset = WriteToEndOfFile ?
FileInfo.FileSize + WriteLength : WriteOffset.QuadPart + WriteLength;
if (WriteToEndOfFile)
WriteOffset.QuadPart = FileInfo.FileSize;
WriteEndOffset = WriteOffset.QuadPart + WriteLength;
ExtendingFile = FileInfo.FileSize < WriteEndOffset;
if (ExtendingFile && !CanWait)
{

33
tools/diag.bat Normal file
View File

@ -0,0 +1,33 @@
@echo off
setlocal
echo WINFSP INSTALLATION DIRECTORY
reg query HKLM\SOFTWARE\WinFsp /reg:32
echo.
echo WINFSP DLL REGISTRATIONS
reg query HKLM\SYSTEM\CurrentControlSet\Control\NetworkProvider\Order
reg query HKLM\SYSTEM\CurrentControlSet\Services\WinFsp.Np\NetworkProvider
reg query HKLM\SYSTEM\CurrentControlSet\Services\EventLog\Application\WinFsp
echo.
echo WINFSP LAUNCHER REGISTRATIONS
reg query HKLM\SYSTEM\CurrentControlSet\Services\WinFsp.Launcher\Services /s
echo.
echo WINFSP FSD CONFIGURATION AND STATUS
sc query WinFsp
sc qc WinFsp
sc sdshow WinFsp
echo.
echo WINFSP LAUNCHER SERVICE CONFIGURATION AND STATUS
sc query WinFsp.Launcher
sc qc WinFsp.Launcher
sc sdshow WinFsp.Launcher
echo.
echo OS INFORMATION
systeminfo
echo.

View File

@ -1,6 +1,7 @@
@echo off
setlocal
setlocal EnableDelayedExpansion
set Configuration=Release
if not X%1==X set Configuration=%1
@ -36,7 +37,7 @@ for %%f in (winfsp-tests-x64 winfsp-tests-x86 :fsx-memfs-x64 :fsx-memfs-x86 :win
call %%f
popd
if errorlevel 1 (
if !ERRORLEVEL! neq 0 (
set /a testfail=testfail+1
echo === Failed %%f
@ -90,18 +91,18 @@ exit /b 0
:winfstest-memfs-x64
M:
call "%ProjRoot%\ext\test\winfstest\run-winfstest.bat" base
call "%ProjRoot%\ext\test\winfstest\run-winfstest.bat" base reparse
if errorlevel 1 goto fail
N:
call "%ProjRoot%\ext\test\winfstest\run-winfstest.bat" base
call "%ProjRoot%\ext\test\winfstest\run-winfstest.bat" base reparse
if errorlevel 1 goto fail
exit /b 0
:winfstest-memfs-x86
O:
call "%ProjRoot%\ext\test\winfstest\run-winfstest.bat" base
call "%ProjRoot%\ext\test\winfstest\run-winfstest.bat" base reparse
if errorlevel 1 goto fail
P:
call "%ProjRoot%\ext\test\winfstest\run-winfstest.bat" base
call "%ProjRoot%\ext\test\winfstest\run-winfstest.bat" base reparse
if errorlevel 1 goto fail
exit /b 0

View File

@ -20,6 +20,7 @@
#include <sddl.h>
#include <map>
#include <cassert>
#include <VersionHelpers.h>
/*
* Define the DEBUG_BUFFER_CHECK macro on Windows 8 or above. This includes
@ -59,6 +60,8 @@ typedef struct _MEMFS_FILE_NODE
SIZE_T FileSecuritySize;
PVOID FileSecurity;
PVOID FileData;
SIZE_T ReparseDataSize;
PVOID ReparseData;
ULONG RefCount;
} MEMFS_FILE_NODE;
@ -112,6 +115,7 @@ NTSTATUS MemfsFileNodeCreate(PWSTR FileName, MEMFS_FILE_NODE **PFileNode)
static inline
VOID MemfsFileNodeDelete(MEMFS_FILE_NODE *FileNode)
{
free(FileNode->ReparseData);
free(FileNode->FileData);
free(FileNode->FileSecurity);
free(FileNode);
@ -271,6 +275,10 @@ BOOLEAN MemfsFileNodeMapEnumerateDescendants(MEMFS_FILE_NODE_MAP *FileNodeMap, M
return TRUE;
}
static NTSTATUS GetReparsePointByName(
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize);
static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
PVOID FileNode0, UINT64 NewSize, BOOLEAN SetAllocationSize,
@ -324,7 +332,13 @@ static NTSTATUS GetSecurityByName(FSP_FILE_SYSTEM *FileSystem,
if (0 == FileNode)
{
Result = STATUS_OBJECT_NAME_NOT_FOUND;
MemfsFileNodeMapGetParent(Memfs->FileNodeMap, FileName, &Result);
if (FspFileSystemFindReparsePoint(FileSystem, GetReparsePointByName, 0,
FileName, PFileAttributes))
Result = STATUS_REPARSE;
else
MemfsFileNodeMapGetParent(Memfs->FileNodeMap, FileName, &Result);
return Result;
}
@ -539,7 +553,8 @@ static NTSTATUS Write(FSP_FILE_SYSTEM *FileSystem,
__try
{
*P = *P | 0;
assert(0);
assert(!IsWindows8OrGreater());
/* only on Windows 8 we can make the buffer read-only! */
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
@ -930,6 +945,125 @@ static NTSTATUS ReadDirectory(FSP_FILE_SYSTEM *FileSystem,
return STATUS_SUCCESS;
}
static NTSTATUS ResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem,
PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent,
PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize)
{
return FspFileSystemResolveReparsePoints(FileSystem, GetReparsePointByName, 0,
FileName, ReparsePointIndex, ResolveLastPathComponent,
PIoStatus, Buffer, PSize);
}
static NTSTATUS GetReparsePointByName(
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize)
{
MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
MEMFS_FILE_NODE *FileNode;
FileNode = MemfsFileNodeMapGet(Memfs->FileNodeMap, FileName);
if (0 == FileNode)
return STATUS_OBJECT_NAME_NOT_FOUND;
if (0 == (FileNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
return STATUS_NOT_A_REPARSE_POINT;
if (0 != Buffer)
{
if (FileNode->ReparseDataSize > *PSize)
return STATUS_BUFFER_TOO_SMALL;
*PSize = FileNode->ReparseDataSize;
memcpy(Buffer, FileNode->ReparseData, FileNode->ReparseDataSize);
}
return STATUS_SUCCESS;
}
static NTSTATUS GetReparsePoint(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
PVOID FileNode0,
PWSTR FileName, PVOID Buffer, PSIZE_T PSize)
{
MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
if (0 == (FileNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
return STATUS_NOT_A_REPARSE_POINT;
if (FileNode->ReparseDataSize > *PSize)
return STATUS_BUFFER_TOO_SMALL;
*PSize = FileNode->ReparseDataSize;
memcpy(Buffer, FileNode->ReparseData, FileNode->ReparseDataSize);
return STATUS_SUCCESS;
}
static NTSTATUS SetReparsePoint(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
PVOID FileNode0,
PWSTR FileName, PVOID Buffer, SIZE_T Size)
{
MEMFS *Memfs = (MEMFS *)FileSystem->UserContext;
MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
PVOID ReparseData;
NTSTATUS Result;
if (MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode))
return STATUS_DIRECTORY_NOT_EMPTY;
if (0 != FileNode->ReparseData)
{
Result = FspFileSystemCanReplaceReparsePoint(
FileNode->ReparseData, FileNode->ReparseDataSize,
Buffer, Size);
if (!NT_SUCCESS(Result))
return Result;
}
ReparseData = realloc(FileNode->ReparseData, Size);
if (0 == ReparseData && 0 != Size)
return STATUS_INSUFFICIENT_RESOURCES;
FileNode->FileInfo.FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT;
FileNode->FileInfo.ReparseTag = *(PULONG)Buffer;
/* the first field in a reparse buffer is the reparse tag */
FileNode->ReparseDataSize = Size;
FileNode->ReparseData = ReparseData;
memcpy(FileNode->ReparseData, Buffer, Size);
return STATUS_SUCCESS;
}
static NTSTATUS DeleteReparsePoint(FSP_FILE_SYSTEM *FileSystem,
FSP_FSCTL_TRANSACT_REQ *Request,
PVOID FileNode0,
PWSTR FileName, PVOID Buffer, SIZE_T Size)
{
MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0;
NTSTATUS Result;
if (0 != FileNode->ReparseData)
{
Result = FspFileSystemCanReplaceReparsePoint(
FileNode->ReparseData, FileNode->ReparseDataSize,
Buffer, Size);
if (!NT_SUCCESS(Result))
return Result;
}
else
return STATUS_NOT_A_REPARSE_POINT;
free(FileNode->ReparseData);
FileNode->FileInfo.FileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT;
FileNode->FileInfo.ReparseTag = 0;
FileNode->ReparseDataSize = 0;
FileNode->ReparseData = 0;
return STATUS_SUCCESS;
}
static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
{
GetVolumeInfo,
@ -951,6 +1085,10 @@ static FSP_FILE_SYSTEM_INTERFACE MemfsInterface =
GetSecurity,
SetSecurity,
ReadDirectory,
ResolveReparsePoints,
GetReparsePoint,
SetReparsePoint,
DeleteReparsePoint,
};
NTSTATUS MemfsCreate(
@ -1011,6 +1149,8 @@ NTSTATUS MemfsCreate(
VolumeParams.CasePreservedNames = 1;
VolumeParams.UnicodeOnDisk = 1;
VolumeParams.PersistentAcls = 1;
VolumeParams.ReparsePoints = 1;
VolumeParams.ReparsePointsAccessCheck = 0;
if (0 != VolumePrefix)
wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix);

32
tst/secret/secret.c Normal file
View File

@ -0,0 +1,32 @@
/*
* Compile:
* - cl secret.c
*
* Register:
* - secret.reg (fix Executable path first)
*
* Run:
* - launchctl-x64 startWithSecret secret 1 nopass
* - launchctl-x64 startWithSecret secret 1 foobar
*/
#include <stdio.h>
int main()
{
char pass[256];
gets(pass);
if (0 == strcmp("foobar", pass))
{
puts("OK");
fprintf(stderr, "OK secret=\"%s\"\n", pass);
}
else
{
puts("KO");
fprintf(stderr, "KO secret=\"%s\"\n", pass);
}
return 0;
}

BIN
tst/secret/secret.reg Normal file

Binary file not shown.

View File

@ -4,13 +4,7 @@
#include <strsafe.h>
#include "memfs.h"
void *memfs_start(ULONG Flags);
void memfs_stop(void *data);
PWSTR memfs_volumename(void *data);
extern int NtfsTests;
extern int WinFspDiskTests;
extern int WinFspNetTests;
#include "winfsp-tests.h"
void create_dotest(ULONG Flags, PWSTR Prefix)
{
@ -424,10 +418,48 @@ void create_share_test(void)
create_share_dotest(MemfsNet, L"\\\\memfs\\share");
}
void create_curdir_dotest(ULONG Flags, PWSTR Prefix)
{
void *memfs = memfs_start(Flags);
WCHAR CurrentDirectory[MAX_PATH], FilePath[MAX_PATH];
BOOL Success;
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s%s",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs),
Prefix ? L"" : L"\\");
Success = GetCurrentDirectoryW(MAX_PATH, CurrentDirectory);
ASSERT(Success);
Success = SetCurrentDirectoryW(FilePath);
ASSERT(Success);
Success = SetCurrentDirectoryW(CurrentDirectory);
ASSERT(Success);
memfs_stop(memfs);
}
void create_curdir_test(void)
{
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH] = L"\\\\?\\";
GetCurrentDirectoryW(MAX_PATH - 4, DirBuf + 4);
create_curdir_dotest(-1, DirBuf);
}
if (WinFspDiskTests)
create_curdir_dotest(MemfsDisk, 0);
if (WinFspNetTests)
create_curdir_dotest(MemfsNet, L"\\\\memfs\\share");
}
void create_tests(void)
{
TEST(create_test);
TEST(create_related_test);
TEST(create_sd_test);
TEST(create_share_test);
TEST(create_curdir_test);
}

View File

@ -5,14 +5,7 @@
#include <strsafe.h>
#include "memfs.h"
void *memfs_start_ex(ULONG Flags, ULONG FileInfoTimeout);
void *memfs_start(ULONG Flags);
void memfs_stop(void *data);
PWSTR memfs_volumename(void *data);
extern int NtfsTests;
extern int WinFspDiskTests;
extern int WinFspNetTests;
#include "winfsp-tests.h"
static void querydir_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout, ULONG SleepTimeout)
{

View File

@ -1,6 +1,8 @@
#include <winfsp/winfsp.h>
#include <tlib/testsuite.h>
#include "winfsp-tests.h"
void eventlog_test(void)
{
/* this is not a real test! */

View File

@ -5,14 +5,7 @@
#include <VersionHelpers.h>
#include "memfs.h"
void *memfs_start_ex(ULONG Flags, ULONG FileInfoTimeout);
void *memfs_start(ULONG Flags);
void memfs_stop(void *data);
PWSTR memfs_volumename(void *data);
extern int NtfsTests;
extern int WinFspDiskTests;
extern int WinFspNetTests;
#include "winfsp-tests.h"
static void flush_dotest(ULONG Flags, PWSTR VolPrefix, PWSTR Prefix, ULONG FileInfoTimeout, DWORD CreateFlags,
BOOLEAN FlushVolume)

View File

@ -3,6 +3,8 @@
#include <stddef.h>
#include <string.h>
#include "winfsp-tests.h"
struct data
{
int fortytwo;

34
tst/winfsp-tests/hook.c Normal file
View File

@ -0,0 +1,34 @@
#include <winfsp/winfsp.h>
HANDLE HookCreateFileW(
LPCWSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile)
{
HANDLE h = CreateFileW(
lpFileName,
dwDesiredAccess,
dwShareMode,
lpSecurityAttributes,
dwCreationDisposition,
dwFlagsAndAttributes,
hTemplateFile);
DWORD LastError = GetLastError();
FspDebugLog("CreateFileW(\"%S\", %#lx, %#lx, %p, %#lx, %#lx, %p) = %p[%#lx]\n",
lpFileName,
dwDesiredAccess,
dwShareMode,
lpSecurityAttributes,
dwCreationDisposition,
dwFlagsAndAttributes,
hTemplateFile,
h, INVALID_HANDLE_VALUE != h ? 0 : LastError);
SetLastError(LastError);
return h;
}

View File

@ -4,14 +4,7 @@
#include <strsafe.h>
#include "memfs.h"
void *memfs_start_ex(ULONG Flags, ULONG FileInfoTimeout);
void *memfs_start(ULONG Flags);
void memfs_stop(void *data);
PWSTR memfs_volumename(void *data);
extern int NtfsTests;
extern int WinFspDiskTests;
extern int WinFspNetTests;
#include "winfsp-tests.h"
void getfileinfo_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{

View File

@ -5,14 +5,7 @@
#include <VersionHelpers.h>
#include "memfs.h"
void *memfs_start_ex(ULONG Flags, ULONG FileInfoTimeout);
void *memfs_start(ULONG Flags);
void memfs_stop(void *data);
PWSTR memfs_volumename(void *data);
extern int NtfsTests;
extern int WinFspDiskTests;
extern int WinFspNetTests;
#include "winfsp-tests.h"
static void lock_dotest(ULONG Flags, PWSTR VolPrefix, PWSTR Prefix, ULONG FileInfoTimeout, DWORD CreateFlags)
{

View File

@ -3,8 +3,7 @@
#include <process.h>
#include "memfs.h"
extern int WinFspDiskTests;
extern int WinFspNetTests;
#include "winfsp-tests.h"
void *memfs_start_ex(ULONG Flags, ULONG FileInfoTimeout)
{

View File

@ -3,8 +3,7 @@
#include <process.h>
#include <strsafe.h>
extern int WinFspDiskTests;
extern int WinFspNetTests;
#include "winfsp-tests.h"
void mount_invalid_test(void)
{

View File

@ -1,6 +1,8 @@
#include <winfsp/winfsp.h>
#include <tlib/testsuite.h>
#include "winfsp-tests.h"
void path_prefix_test(void)
{
PWSTR ipaths[] =

View File

@ -2,6 +2,8 @@
#include <tlib/testsuite.h>
#include <sddl.h>
#include "winfsp-tests.h"
void posix_map_sid_test(void)
{
struct
@ -10,6 +12,7 @@ void posix_map_sid_test(void)
UINT32 Uid;
} map[] =
{
{ L"S-1-0-65534", 65534 },
{ L"S-1-0-0", 0x10000 },
{ L"S-1-1-0", 0x10100 },
{ L"S-1-2-0", 0x10200 },

View File

@ -5,14 +5,7 @@
#include <VersionHelpers.h>
#include "memfs.h"
void *memfs_start_ex(ULONG Flags, ULONG FileInfoTimeout);
void *memfs_start(ULONG Flags);
void memfs_stop(void *data);
PWSTR memfs_volumename(void *data);
extern int NtfsTests;
extern int WinFspDiskTests;
extern int WinFspNetTests;
#include "winfsp-tests.h"
static void rdwr_dotest(ULONG Flags, PWSTR VolPrefix, PWSTR Prefix, ULONG FileInfoTimeout, DWORD CreateFlags)
{
@ -192,6 +185,90 @@ static void rdwr_dotest(ULONG Flags, PWSTR VolPrefix, PWSTR Prefix, ULONG FileIn
memfs_stop(memfs);
}
static void rdwr_append_dotest(ULONG Flags, PWSTR VolPrefix, PWSTR Prefix, ULONG FileInfoTimeout, DWORD CreateFlags)
{
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
HANDLE Handle;
BOOL Success;
WCHAR FilePath[MAX_PATH];
SYSTEM_INFO SystemInfo;
DWORD SectorsPerCluster;
DWORD BytesPerSector;
DWORD FreeClusters;
DWORD TotalClusters;
PVOID AllocBuffer[2], Buffer[2];
ULONG AllocBufferSize;
DWORD BytesTransferred;
DWORD FilePointer;
GetSystemInfo(&SystemInfo);
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\",
VolPrefix ? L"" : L"\\\\?\\GLOBALROOT", VolPrefix ? VolPrefix : memfs_volumename(memfs));
Success = GetDiskFreeSpaceW(FilePath, &SectorsPerCluster, &BytesPerSector, &FreeClusters, &TotalClusters);
ASSERT(Success);
AllocBufferSize = 16 * SystemInfo.dwPageSize;
AllocBuffer[0] = _aligned_malloc(AllocBufferSize, SystemInfo.dwPageSize);
AllocBuffer[1] = _aligned_malloc(AllocBufferSize, SystemInfo.dwPageSize);
ASSERT(0 != AllocBuffer[0] && 0 != AllocBuffer[1]);
srand((unsigned)time(0));
for (PUINT8 Bgn = AllocBuffer[0], End = Bgn + AllocBufferSize; End > Bgn; Bgn++)
*Bgn = rand();
Buffer[0] = (PVOID)((PUINT8)AllocBuffer[0] + BytesPerSector);
Buffer[1] = (PVOID)((PUINT8)AllocBuffer[1] + BytesPerSector);
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\file0",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
Handle = CreateFileW(FilePath,
FILE_APPEND_DATA, FILE_SHARE_READ, 0,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL | CreateFlags, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
Success = WriteFile(Handle, Buffer[0], BytesPerSector, &BytesTransferred, 0);
ASSERT(Success);
ASSERT(BytesPerSector == BytesTransferred);
Success = WriteFile(Handle, (PUINT8)Buffer[0] + BytesPerSector, BytesPerSector, &BytesTransferred, 0);
ASSERT(Success);
ASSERT(BytesPerSector == BytesTransferred);
Success = CloseHandle(Handle);
ASSERT(Success);
Handle = CreateFileW(FilePath,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | CreateFlags | FILE_FLAG_DELETE_ON_CLOSE, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
FilePointer = SetFilePointer(Handle, 0, 0, FILE_BEGIN);
ASSERT(0 == FilePointer);
memset(AllocBuffer[1], 0, AllocBufferSize);
Success = ReadFile(Handle, Buffer[1], 2 * BytesPerSector, &BytesTransferred, 0);
ASSERT(Success);
ASSERT(2 * BytesPerSector == BytesTransferred);
ASSERT(0 == memcmp(Buffer[0], Buffer[1], BytesTransferred));
Success = CloseHandle(Handle);
ASSERT(Success);
Handle = CreateFileW(FilePath,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
OPEN_EXISTING, 0, 0);
ASSERT(INVALID_HANDLE_VALUE == Handle);
ASSERT(ERROR_FILE_NOT_FOUND == GetLastError());
_aligned_free(AllocBuffer[0]);
_aligned_free(AllocBuffer[1]);
memfs_stop(memfs);
}
static void rdwr_overlapped_dotest(ULONG Flags, PWSTR VolPrefix, PWSTR Prefix, ULONG FileInfoTimeout, DWORD CreateFlags)
{
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
@ -742,6 +819,32 @@ void rdwr_noncached_test(void)
}
}
/*
* According to NtCreateFile documentation the FILE_NO_INTERMEDIATE_BUFFERING flag
* "is incompatible with the DesiredAccess FILE_APPEND_DATA flag".
*/
#if 0
void rdwr_noncached_append_test(void)
{
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH] = L"\\\\?\\";
GetCurrentDirectoryW(MAX_PATH - 4, DirBuf + 4);
rdwr_append_dotest(-1, L"C:", DirBuf, 0, FILE_FLAG_NO_BUFFERING);
}
if (WinFspDiskTests)
{
rdwr_append_dotest(MemfsDisk, 0, 0, 1000, FILE_FLAG_NO_BUFFERING);
rdwr_append_dotest(MemfsDisk, 0, 0, INFINITE, FILE_FLAG_NO_BUFFERING);
}
if (WinFspNetTests)
{
rdwr_append_dotest(MemfsNet, L"\\\\memfs\\share", L"\\\\memfs\\share", 1000, FILE_FLAG_NO_BUFFERING);
rdwr_append_dotest(MemfsNet, L"\\\\memfs\\share", L"\\\\memfs\\share", INFINITE, FILE_FLAG_NO_BUFFERING);
}
}
#endif
void rdwr_noncached_overlapped_test(void)
{
if (NtfsTests)
@ -782,6 +885,26 @@ void rdwr_cached_test(void)
}
}
void rdwr_cached_append_test(void)
{
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH] = L"\\\\?\\";
GetCurrentDirectoryW(MAX_PATH - 4, DirBuf + 4);
rdwr_append_dotest(-1, L"C:", DirBuf, 0, 0);
}
if (WinFspDiskTests)
{
rdwr_append_dotest(MemfsDisk, 0, 0, 1000, 0);
rdwr_append_dotest(MemfsDisk, 0, 0, INFINITE, 0);
}
if (WinFspNetTests)
{
rdwr_append_dotest(MemfsNet, L"\\\\memfs\\share", L"\\\\memfs\\share", 1000, 0);
rdwr_append_dotest(MemfsNet, L"\\\\memfs\\share", L"\\\\memfs\\share", INFINITE, 0);
}
}
void rdwr_cached_overlapped_test(void)
{
if (NtfsTests)
@ -822,6 +945,26 @@ void rdwr_writethru_test(void)
}
}
void rdwr_writethru_append_test(void)
{
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH] = L"\\\\?\\";
GetCurrentDirectoryW(MAX_PATH - 4, DirBuf + 4);
rdwr_append_dotest(-1, L"C:", DirBuf, 0, FILE_FLAG_WRITE_THROUGH);
}
if (WinFspDiskTests)
{
rdwr_append_dotest(MemfsDisk, 0, 0, 1000, FILE_FLAG_WRITE_THROUGH);
rdwr_append_dotest(MemfsDisk, 0, 0, INFINITE, FILE_FLAG_WRITE_THROUGH);
}
if (WinFspNetTests)
{
rdwr_append_dotest(MemfsNet, L"\\\\memfs\\share", L"\\\\memfs\\share", 1000, FILE_FLAG_WRITE_THROUGH);
rdwr_append_dotest(MemfsNet, L"\\\\memfs\\share", L"\\\\memfs\\share", INFINITE, FILE_FLAG_WRITE_THROUGH);
}
}
void rdwr_writethru_overlapped_test(void)
{
if (NtfsTests)
@ -940,10 +1083,19 @@ void rdwr_mixed_test(void)
void rdwr_tests(void)
{
TEST(rdwr_noncached_test);
#if 0
/*
* According to NtCreateFile documentation the FILE_NO_INTERMEDIATE_BUFFERING flag
* "is incompatible with the DesiredAccess FILE_APPEND_DATA flag".
*/
TEST(rdwr_noncached_append_test);
#endif
TEST(rdwr_noncached_overlapped_test);
TEST(rdwr_cached_test);
TEST(rdwr_cached_append_test);
TEST(rdwr_cached_overlapped_test);
TEST(rdwr_writethru_test);
TEST(rdwr_writethru_append_test);
TEST(rdwr_writethru_overlapped_test);
TEST(rdwr_mmap_test);
TEST(rdwr_mixed_test);

View File

@ -0,0 +1,521 @@
#include <winfsp/winfsp.h>
#include <tlib/testsuite.h>
#include <strsafe.h>
#include "memfs.h"
#include "winfsp-tests.h"
static void reparse_guid_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
static const GUID reparse_guid =
{ 0x895fc61e, 0x7b91, 0x4677, { 0xba, 0x3e, 0x79, 0x34, 0xed, 0xf2, 0xb7, 0x43 } };
HANDLE Handle;
BOOL Success;
WCHAR FilePath[MAX_PATH];
union
{
//REPARSE_DATA_BUFFER D;
REPARSE_GUID_DATA_BUFFER G;
UINT8 B[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
} ReparseDataBuf;
DWORD Bytes;
static const char *datstr = "foobar";
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\file0",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
Handle = CreateFileW(FilePath,
FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
ReparseDataBuf.G.ReparseTag = 0x1234;
ReparseDataBuf.G.ReparseDataLength = (USHORT)strlen(datstr);
ReparseDataBuf.G.Reserved = 0;
memcpy(&ReparseDataBuf.G.ReparseGuid, &reparse_guid, sizeof reparse_guid);
memcpy(ReparseDataBuf.G.GenericReparseBuffer.DataBuffer, datstr, strlen(datstr));
Success = DeviceIoControl(Handle, FSCTL_SET_REPARSE_POINT,
&ReparseDataBuf, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE + ReparseDataBuf.G.ReparseDataLength,
0, 0,
&Bytes, 0);
ASSERT(Success);
Success = DeviceIoControl(Handle, FSCTL_GET_REPARSE_POINT,
0, 0,
&ReparseDataBuf, sizeof ReparseDataBuf,
&Bytes, 0);
ASSERT(Success);
ASSERT(ReparseDataBuf.G.ReparseTag == 0x1234);
ASSERT(ReparseDataBuf.G.ReparseDataLength == strlen(datstr));
ASSERT(ReparseDataBuf.G.Reserved == 0);
ASSERT(0 == memcmp(&ReparseDataBuf.G.ReparseGuid, &reparse_guid, sizeof reparse_guid));
ASSERT(0 == memcmp(ReparseDataBuf.G.GenericReparseBuffer.DataBuffer, datstr, strlen(datstr)));
CloseHandle(Handle);
Handle = CreateFileW(FilePath,
FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0, OPEN_EXISTING, 0, 0);
ASSERT(INVALID_HANDLE_VALUE == Handle);
ASSERT(ERROR_CANT_ACCESS_FILE == GetLastError());
Handle = CreateFileW(FilePath,
FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
ReparseDataBuf.G.ReparseDataLength = 0;
Success = DeviceIoControl(Handle, FSCTL_DELETE_REPARSE_POINT,
&ReparseDataBuf, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE + ReparseDataBuf.G.ReparseDataLength,
0, 0,
&Bytes, 0);
ASSERT(Success);
Success = DeviceIoControl(Handle, FSCTL_GET_REPARSE_POINT,
0, 0,
&ReparseDataBuf, sizeof ReparseDataBuf,
&Bytes, 0);
ASSERT(!Success);
ASSERT(ERROR_NOT_A_REPARSE_POINT == GetLastError());
CloseHandle(Handle);
Success = DeleteFileW(FilePath);
ASSERT(Success);
memfs_stop(memfs);
}
void reparse_guid_test(void)
{
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH] = L"\\\\?\\";
GetCurrentDirectoryW(MAX_PATH - 4, DirBuf + 4);
reparse_guid_dotest(-1, DirBuf, 0);
}
if (WinFspDiskTests)
{
reparse_guid_dotest(MemfsDisk, 0, 0);
}
if (WinFspNetTests)
{
reparse_guid_dotest(MemfsNet, L"\\\\memfs\\share", 0);
}
}
static void reparse_nfs_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
HANDLE Handle;
BOOL Success;
WCHAR FilePath[MAX_PATH];
union
{
REPARSE_DATA_BUFFER D;
//REPARSE_GUID_DATA_BUFFER G;
UINT8 B[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
} ReparseDataBuf;
DWORD Bytes;
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\file0",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
Handle = CreateFileW(FilePath,
FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
ReparseDataBuf.D.ReparseTag = IO_REPARSE_TAG_NFS;
ReparseDataBuf.D.ReparseDataLength = 16;
ReparseDataBuf.D.Reserved = 0;
*(PUINT64)(ReparseDataBuf.D.GenericReparseBuffer.DataBuffer + 0) = 0x524843;/* NFS_SPECFILE_CHR */
*(PUINT32)(ReparseDataBuf.D.GenericReparseBuffer.DataBuffer + 8) = 0x42; /* major */
*(PUINT32)(ReparseDataBuf.D.GenericReparseBuffer.DataBuffer + 12) = 0x62; /* minor */
Success = DeviceIoControl(Handle, FSCTL_SET_REPARSE_POINT,
&ReparseDataBuf, REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseDataBuf.D.ReparseDataLength,
0, 0,
&Bytes, 0);
ASSERT(Success);
Success = DeviceIoControl(Handle, FSCTL_GET_REPARSE_POINT,
0, 0,
&ReparseDataBuf, sizeof ReparseDataBuf,
&Bytes, 0);
ASSERT(Success);
ASSERT(ReparseDataBuf.D.ReparseTag == IO_REPARSE_TAG_NFS);
ASSERT(ReparseDataBuf.D.ReparseDataLength == 16);
ASSERT(ReparseDataBuf.D.Reserved == 0);
ASSERT(*(PUINT64)(ReparseDataBuf.D.GenericReparseBuffer.DataBuffer + 0) == 0x524843);
ASSERT(*(PUINT32)(ReparseDataBuf.D.GenericReparseBuffer.DataBuffer + 8) == 0x42);
ASSERT(*(PUINT32)(ReparseDataBuf.D.GenericReparseBuffer.DataBuffer + 12) == 0x62);
CloseHandle(Handle);
Handle = CreateFileW(FilePath,
FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0, OPEN_EXISTING, 0, 0);
ASSERT(INVALID_HANDLE_VALUE == Handle);
ASSERT(ERROR_CANT_ACCESS_FILE == GetLastError());
Handle = CreateFileW(FilePath,
FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
ReparseDataBuf.D.ReparseDataLength = 0;
Success = DeviceIoControl(Handle, FSCTL_DELETE_REPARSE_POINT,
&ReparseDataBuf, REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseDataBuf.D.ReparseDataLength,
0, 0,
&Bytes, 0);
ASSERT(Success);
Success = DeviceIoControl(Handle, FSCTL_GET_REPARSE_POINT,
0, 0,
&ReparseDataBuf, sizeof ReparseDataBuf,
&Bytes, 0);
ASSERT(!Success);
ASSERT(ERROR_NOT_A_REPARSE_POINT == GetLastError());
CloseHandle(Handle);
Success = DeleteFileW(FilePath);
ASSERT(Success);
memfs_stop(memfs);
}
void reparse_nfs_test(void)
{
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH] = L"\\\\?\\";
GetCurrentDirectoryW(MAX_PATH - 4, DirBuf + 4);
reparse_nfs_dotest(-1, DirBuf, 0);
}
if (WinFspDiskTests)
{
reparse_nfs_dotest(MemfsDisk, 0, 0);
}
if (WinFspNetTests)
{
reparse_nfs_dotest(MemfsNet, L"\\\\memfs\\share", 0);
}
}
static void reparse_symlink_dotest0(ULONG Flags, PWSTR Prefix,
PWSTR FilePath, PWSTR LinkPath, PWSTR TargetPath)
{
HANDLE Handle;
BOOL Success;
PUINT8 NameInfoBuf[sizeof(FILE_NAME_INFO) + MAX_PATH];
PFILE_NAME_INFO PNameInfo = (PVOID)NameInfoBuf;
Success = CreateSymbolicLinkW(LinkPath, TargetPath, 0);
if (Success)
{
Handle = CreateFileW(FilePath,
FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
CloseHandle(Handle);
Handle = CreateFileW(LinkPath,
FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0, OPEN_EXISTING, 0, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
Success = GetFileInformationByHandleEx(Handle, FileNameInfo, PNameInfo, sizeof NameInfoBuf);
ASSERT(Success);
if (-1 == Flags)
ASSERT(PNameInfo->FileNameLength == wcslen(FilePath + 6) * sizeof(WCHAR));
else if (0 == Prefix)
ASSERT(PNameInfo->FileNameLength == wcslen(L"\\file0") * sizeof(WCHAR));
else
ASSERT(PNameInfo->FileNameLength == wcslen(FilePath + 1) * sizeof(WCHAR));
if (-1 == Flags)
ASSERT(0 == memcmp(FilePath + 6, PNameInfo->FileName, PNameInfo->FileNameLength));
else if (0 == Prefix)
ASSERT(0 == memcmp(L"\\file0", PNameInfo->FileName, PNameInfo->FileNameLength));
else
ASSERT(0 == memcmp(FilePath + 1, PNameInfo->FileName, PNameInfo->FileNameLength));
CloseHandle(Handle);
Success = DeleteFileW(LinkPath);
ASSERT(Success);
Success = DeleteFileW(FilePath);
ASSERT(Success);
}
else
{
ASSERT(ERROR_PRIVILEGE_NOT_HELD == GetLastError());
FspDebugLog(__FUNCTION__ ": need SE_CREATE_SYMBOLIC_LINK_PRIVILEGE\n");
}
}
static void reparse_symlink_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
WCHAR FilePath[MAX_PATH], LinkPath[MAX_PATH], TargetPath[MAX_PATH];
PUINT8 NameInfoBuf[sizeof(FILE_NAME_INFO) + MAX_PATH];
PFILE_NAME_INFO PNameInfo = (PVOID)NameInfoBuf;
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\file0",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
StringCbPrintfW(LinkPath, sizeof LinkPath, L"%s%s\\link0",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
reparse_symlink_dotest0(Flags, Prefix, FilePath, LinkPath, FilePath);
StringCbPrintfW(TargetPath, sizeof TargetPath, L"%s\\file0",
-1 == Flags ? Prefix + 6 : L"");
reparse_symlink_dotest0(Flags, Prefix, FilePath, LinkPath, TargetPath);
reparse_symlink_dotest0(Flags, Prefix, FilePath, LinkPath, L"file0");
memfs_stop(memfs);
}
void reparse_symlink_test(void)
{
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH] = L"\\\\?\\";
GetCurrentDirectoryW(MAX_PATH - 4, DirBuf + 4);
reparse_symlink_dotest(-1, DirBuf, 0);
}
if (WinFspDiskTests)
{
reparse_symlink_dotest(MemfsDisk, 0, 0);
}
if (WinFspNetTests)
{
reparse_symlink_dotest(MemfsNet, L"\\\\memfs\\share", 0);
}
}
static BOOL my_mkdir_fn(PWSTR Prefix, void *memfs, PWSTR FileName)
{
WCHAR FilePath[MAX_PATH];
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s%s",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs), FileName);
return CreateDirectoryW(FilePath, 0);
}
static BOOL my_rmdir_fn(PWSTR Prefix, void *memfs, PWSTR FileName)
{
WCHAR FilePath[MAX_PATH];
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s%s",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs), FileName);
return RemoveDirectoryW(FilePath);
}
static BOOL my_make_fn(PWSTR Prefix, void *memfs, PWSTR FileName)
{
WCHAR FilePath[MAX_PATH];
HANDLE Handle;
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s%s",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs), FileName);
Handle = CreateFileW(FilePath, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
if (INVALID_HANDLE_VALUE == Handle)
return FALSE;
CloseHandle(Handle);
return TRUE;
}
static BOOL my_unlink_fn(PWSTR Prefix, void *memfs, PWSTR FileName)
{
WCHAR FilePath[MAX_PATH];
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s%s",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs), FileName);
return DeleteFileW(FilePath);
}
static BOOL my_symlink_fn(ULONG Flags, PWSTR Prefix, void *memfs, PWSTR LinkName, PWSTR FileName,
DWORD SymlinkFlags)
{
WCHAR LinkPath[MAX_PATH], FilePath[MAX_PATH];
StringCbPrintfW(LinkPath, sizeof LinkPath, L"%s%s%s",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs), LinkName);
if (-1 == Flags && L'\\' == FileName[0])
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s",
Prefix ? Prefix : L"", FileName);
else
StringCbPrintfW(FilePath, sizeof FilePath, L"%s",
FileName);
return CreateSymbolicLinkW(LinkPath,
-1 == Flags && L'\\' == FileName[0] ? FilePath + 6 : FilePath,
SymlinkFlags);
}
static BOOL my_namecheck_fn(ULONG Flags, PWSTR Prefix, void *memfs, PWSTR FileName, PWSTR Expected)
{
WCHAR FilePath[MAX_PATH], ExpectedPath[MAX_PATH];
HANDLE Handle;
PUINT8 NameInfoBuf[sizeof(FILE_NAME_INFO) + MAX_PATH];
PFILE_NAME_INFO PNameInfo = (PVOID)NameInfoBuf;
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s%s",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs), FileName);
StringCbPrintfW(ExpectedPath, sizeof ExpectedPath, L"%s%s",
Prefix ? Prefix : L"", Expected);
Handle = CreateFileW(FilePath, FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (INVALID_HANDLE_VALUE == Handle)
return FALSE;
if (GetFileInformationByHandleEx(Handle, FileNameInfo, PNameInfo, sizeof NameInfoBuf))
{
if (-1 == Flags)
ASSERT(PNameInfo->FileNameLength == wcslen(ExpectedPath + 6) * sizeof(WCHAR));
else if (0 == Prefix)
ASSERT(PNameInfo->FileNameLength == wcslen(ExpectedPath) * sizeof(WCHAR));
else
ASSERT(PNameInfo->FileNameLength == wcslen(ExpectedPath + 1) * sizeof(WCHAR));
if (-1 == Flags)
ASSERT(0 == memcmp(ExpectedPath + 6, PNameInfo->FileName, PNameInfo->FileNameLength));
else if (0 == Prefix)
ASSERT(0 == memcmp(ExpectedPath, PNameInfo->FileName, PNameInfo->FileNameLength));
else
ASSERT(0 == memcmp(ExpectedPath + 1, PNameInfo->FileName, PNameInfo->FileNameLength));
}
CloseHandle(Handle);
return TRUE;
}
#define my_mkdir(FileName) ASSERT(my_mkdir_fn(Prefix, memfs, FileName))
#define my_rmdir(FileName) ASSERT(my_rmdir_fn(Prefix, memfs, FileName))
#define my_make(FileName) ASSERT(my_make_fn(Prefix, memfs, FileName))
#define my_unlink(FileName) ASSERT(my_unlink_fn(Prefix, memfs, FileName))
#define my_symlinkd(LinkName, FileName) ASSERT(my_symlink_fn(Flags, Prefix, memfs, LinkName, FileName,\
SYMBOLIC_LINK_FLAG_DIRECTORY))
#define my_symlink(LinkName, FileName) ASSERT(my_symlink_fn(Flags, Prefix, memfs, LinkName, FileName, 0))
#define my_namecheck(FileName, Expected)ASSERT(my_namecheck_fn(Flags, Prefix, memfs, FileName, Expected))
#define my_failcheck(FileName) ASSERT(!my_namecheck_fn(Flags, Prefix, memfs, FileName, L""))
#define my_symlink_noassert(LinkName, FileName)\
my_symlink_fn(Flags, Prefix, memfs, LinkName, FileName, 0)
static void reparse_symlink_relative_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
if (my_symlink_noassert(L"\\l0", L"NON-EXISTANT"))
my_unlink(L"\\l0");
else
{
ASSERT(ERROR_PRIVILEGE_NOT_HELD == GetLastError());
FspDebugLog(__FUNCTION__ ": need SE_CREATE_SYMBOLIC_LINK_PRIVILEGE\n");
return;
}
my_mkdir(L"\\1");
my_mkdir(L"\\1\\1.1");
my_make(L"\\1\\1.2");
my_make(L"\\1\\1.3");
my_make(L"\\1\\1.1\\1.1.1");
my_mkdir(L"\\2");
my_make(L"\\2\\2.1");
my_symlink(L"\\l0", L"NON-EXISTANT");
my_symlink(L"\\loop", L"loop");
my_symlink(L"\\lf", L"\\1");
my_symlinkd(L"\\ld", L"\\1\\1.1\\1.1.1");
my_symlink(L"\\1\\1.1\\l1.1.1", L"1.1.1");
my_symlinkd(L"\\2\\l1", L"..\\1\\.");
my_symlinkd(L"\\1\\l2", L"..\\.\\2");
my_symlinkd(L"\\2\\a1", L"\\1");
my_symlinkd(L"\\1\\a2", L"\\2");
my_failcheck(L"\\l0");
ASSERT(ERROR_FILE_NOT_FOUND == GetLastError());
my_failcheck(L"\\loop");
ASSERT(ERROR_CANT_RESOLVE_FILENAME == GetLastError());
/* NTFS open with FILE_FLAG_BACKUP_SEMANTICS does not care about SYMLINK/SYMLINKD difference! */
my_namecheck(L"\\lf", L"\\1");
my_namecheck(L"\\ld", L"\\1\\1.1\\1.1.1");
my_namecheck(L"\\1\\1.1\\l1.1.1", L"\\1\\1.1\\1.1.1");
my_namecheck(L"\\2\\l1\\1.1\\l1.1.1", L"\\1\\1.1\\1.1.1");
my_namecheck(L"\\1\\l2\\l1\\1.1\\l1.1.1", L"\\1\\1.1\\1.1.1");
my_namecheck(L"\\2\\a1\\1.1\\l1.1.1", L"\\1\\1.1\\1.1.1");
my_namecheck(L"\\2\\l1\\1.1\\l1.1.1", L"\\1\\1.1\\1.1.1");
my_namecheck(L"\\1\\a2\\l1\\1.1\\l1.1.1", L"\\1\\1.1\\1.1.1");
my_namecheck(L"\\1\\a2\\a1\\1.1\\l1.1.1", L"\\1\\1.1\\1.1.1");
my_namecheck(L"\\1\\a2\\a1\\l2\\l1\\1.1\\l1.1.1", L"\\1\\1.1\\1.1.1");
my_rmdir(L"\\ld");
my_unlink(L"\\lf");
my_rmdir(L"\\1\\a2");
my_rmdir(L"\\2\\a1");
my_rmdir(L"\\1\\l2");
my_rmdir(L"\\2\\l1");
my_unlink(L"\\1\\1.1\\l1.1.1");
my_unlink(L"\\loop");
my_unlink(L"\\l0");
my_unlink(L"\\2\\2.1");
my_rmdir(L"\\2");
my_unlink(L"\\1\\1.1\\1.1.1");
my_unlink(L"\\1\\1.3");
my_unlink(L"\\1\\1.2");
my_rmdir(L"\\1\\1.1");
my_rmdir(L"\\1");
memfs_stop(memfs);
}
void reparse_symlink_relative_test(void)
{
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH] = L"\\\\?\\";
GetCurrentDirectoryW(MAX_PATH - 4, DirBuf + 4);
reparse_symlink_relative_dotest(-1, DirBuf, 0);
}
if (WinFspDiskTests)
{
reparse_symlink_relative_dotest(MemfsDisk, 0, 0);
}
if (WinFspNetTests)
{
reparse_symlink_relative_dotest(MemfsNet, L"\\\\memfs\\share", 0);
}
}
void reparse_tests(void)
{
TEST(reparse_guid_test);
TEST(reparse_nfs_test);
TEST(reparse_symlink_test);
TEST(reparse_symlink_relative_test);
}

View File

@ -4,14 +4,7 @@
#include <strsafe.h>
#include "memfs.h"
void *memfs_start_ex(ULONG Flags, ULONG FileInfoTimeout);
void *memfs_start(ULONG Flags);
void memfs_stop(void *data);
PWSTR memfs_volumename(void *data);
extern int NtfsTests;
extern int WinFspDiskTests;
extern int WinFspNetTests;
#include "winfsp-tests.h"
void getsecurity_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{

View File

@ -3,8 +3,7 @@
#include <process.h>
#include <strsafe.h>
extern int WinFspDiskTests;
extern int WinFspNetTests;
#include "winfsp-tests.h"
static unsigned __stdcall timeout_pending_dotest_thread(void *FilePath)
{

View File

@ -1,5 +1,7 @@
#include <tlib/testsuite.h>
#include "winfsp-tests.h"
int NtfsTests = 0;
int WinFspDiskTests = 1;
int WinFspNetTests = 1;
@ -20,6 +22,7 @@ int main(int argc, char *argv[])
TESTSUITE(flush_tests);
TESTSUITE(lock_tests);
TESTSUITE(dirctl_tests);
TESTSUITE(reparse_tests);
tlib_run_tests(argc, argv);
return 0;

View File

@ -0,0 +1,20 @@
#include <windows.h>
void *memfs_start_ex(ULONG Flags, ULONG FileInfoTimeout);
void *memfs_start(ULONG Flags);
void memfs_stop(void *data);
PWSTR memfs_volumename(void *data);
extern int NtfsTests;
extern int WinFspDiskTests;
extern int WinFspNetTests;
//#define CreateFileW HookCreateFileW
HANDLE HookCreateFileW(
LPCWSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);