Compare commits

...

58 Commits

Author SHA1 Message Date
ba68ab845b update Changelog 2017-03-13 15:28:16 -07:00
db0566701e dll: fuse: add fuse_* symbols for programs using FFI 2017-03-13 12:30:25 -07:00
433e62f813 dll: do not export FspFileSystemResolveReparsePointsInternal 2017-03-13 11:46:36 -07:00
6777de6569 update Changelog for v1.0 2017-03-11 16:45:40 -08:00
63ee24235b update Changelog for v1.0 2017-03-11 16:37:55 -08:00
c3eb46813e update Changelog for v1.0 2017-03-11 16:21:46 -08:00
bee0fa79b2 build: choco: rename chocolateyUninstall.ps1 to chocolateyBeforeModify.ps1 to properly handle upgrades 2017-03-11 15:06:48 -08:00
d907bf2769 inc: winfsp: RegGetValueW prototype and disable macro redef warnings 2017-03-11 14:32:12 -08:00
38bcece5ce build: choco: remove MSI after installation 2017-03-10 22:14:16 -08:00
193de36301 dll: fuse: debug output goes to stderr; configurable with -oDebugLog=FILE 2017-03-10 21:57:27 -08:00
c080e86f11 build: choco: add admin tag as per chocolatey validator 2017-03-10 15:23:54 -08:00
79811a2faa appveyor: add -y switch for chocolatey 2017-03-10 10:56:21 -08:00
b1e287727d appveyor: test chocolatey uninstall 2017-03-10 10:54:22 -08:00
2def18e271 build: choco: add chocolateyUninstall.ps1 2017-03-10 10:44:56 -08:00
3ddc2249f6 build: choco: add chocolateyUninstall.ps1 2017-03-10 10:37:19 -08:00
709ead9e06 build: choco: initial chocolatey package implementation 2017-03-09 22:14:50 -08:00
7c574da261 build: version.properties: 2017 Gold 2017-03-09 15:54:46 -08:00
0980365082 dll: fuse: fuse_intf_GetVolumeInfo: return default info when statfs unimplemented 2017-03-09 14:59:01 -08:00
ba46d9fef8 update Changelog, README, etc. 2017-02-25 17:59:37 -08:00
4c0386bf8d sys: hardcode AlwaysUseDoubleBuffering for Reads 2017-02-25 11:22:22 -08:00
6774c34422 sys: FspProcessBufferAcquire: fix silly mistake when using ZwAllocateVirtualMemory 2017-02-23 22:24:32 -08:00
3215d8e26a sys: ProcessBuffer: DEBUGTEST 2017-02-23 20:26:14 -08:00
7911f1354f sys: ProcessBuffer: improvements 2017-02-23 17:39:28 -08:00
28f97c2619 sys: ProcessBuffer: conditional usage 2017-02-23 16:36:21 -08:00
4b43cc590f sys: initial ProcessBuffer implementation 2017-02-23 16:06:55 -08:00
441c45c77f sys: fix a couple of static analysis issues 2017-02-21 15:07:06 -08:00
c246acb2d3 dll: FspVersion 2017-02-17 15:22:31 -08:00
50ed020e16 dll: FspVersion 2017-02-17 15:20:57 -08:00
12704a3a4a dll: FspFileSystemSetMountPointEx and related refactoring 2017-02-16 20:30:39 -08:00
186f7cd9ee dll: fuse: minor fix 2017-02-16 17:26:07 -08:00
50830f0bf2 bump version to 2017 RC3 2017-02-16 17:25:54 -08:00
86025aa30b dll: fuse: fuse_mount: now also accepts cygwin paths 2017-02-16 16:53:06 -08:00
31e6f15eaf tst: passthrough-fuse: FSP_FUSE_CAP_*: testing 2017-02-15 18:07:25 -08:00
5ce6d74e90 tst: passthrough-fuse: FSP_FUSE_CAP_*: testing 2017-02-15 17:26:37 -08:00
02ffa6afb2 tst: passthrough-fuse: ptfs_init 2017-02-13 22:03:39 -08:00
712870ddd9 tst: passthrough-fuse: FSP_FUSE_CAP_* 2017-02-13 21:53:37 -08:00
35d1adb360 dll: fuse: improve SectorSize: handling 2017-02-13 16:21:26 -08:00
3b9f9ce93b dll: fuse: discard unused options 2017-02-13 15:11:09 -08:00
a7c5e4f80c dll: fuse: default FileInfoTimeout is now 1000ms 2017-02-12 13:25:43 -08:00
0b826f04c2 tst: winfsp-tests: exec-test: disable test that fails under shares 2017-02-12 11:49:46 -08:00
69d53b7a28 tst: winfsp-tests: exec-test: disable test that fails under shares 2017-02-12 11:33:56 -08:00
877bd97ba8 tst: winfsp-tests: exec-test 2017-02-12 10:42:14 -08:00
559b7e80a3 tst: winfsp-tests: exec-test 2017-02-12 10:29:30 -08:00
495c78eb25 tst: winfsp-tests: exec-test 2017-02-12 00:26:20 -08:00
cf69d6a08d tst: winfsp-tests: delete_standby_test 2017-02-11 10:07:03 -08:00
0d95bb9b14 tst: winfsp-tests: delete_standby_test: disable parts that fail with shares and passthrough 2017-02-10 23:24:05 -08:00
cf48d19759 tst: winfsp-tests: delete_standby_test: disable portion that fails under a share 2017-02-10 22:19:25 -08:00
14c602ccd8 tst: winfsp-tests: delete_standby_test 2017-02-10 21:31:55 -08:00
2d45082d80 tst: winfsp-tests: delete_standby_test 2017-02-10 19:50:16 -08:00
25cf527af4 tst: winfsp-tests: rename_standby_test 2017-02-10 15:47:44 -08:00
76d081937e tst: winfsp-tests: rename_standby_test 2017-02-10 15:25:36 -08:00
ccd4bb869c Experimental fix for github issue #45 2017-02-10 12:39:53 -08:00
46d5c98926 tst: winfsp-tests: rename_standby_test 2017-02-09 17:38:24 -08:00
1726ca8ccb Update README as per issue #48 2017-02-09 12:06:54 -08:00
b8c257f996 Update README as per issue #48 2017-02-09 12:04:51 -08:00
53e6894298 doc: use asciidoc for winfsp.h reference 2017-02-08 17:36:17 -08:00
10d1ba707e Update README.md 2017-02-08 13:42:49 -08:00
c234e2af2a Update README.md 2017-02-08 13:42:30 -08:00
58 changed files with 4100 additions and 2184 deletions

View File

@ -1,6 +1,29 @@
= Changelog
v1.0::
This is the WinFsp 2017 release! :tada:
- The API is now *FROZEN*. Breaking API changes will receive a major version update (`2.0`). Incremental API changes will receive a minor version update (`1.x`).
- Adds chocolatey package. Try `choco install winfsp` (note: pending approval from chocolatey.org).
- FUSE `-d` output now always goes to stderr. There is also a new `-oDebugLog=FILE` switch to specify a debug output file.
- FUSE now provides a default `statfs` implementation if a file system does not provide one.
- The WinFsp DLL now exports `fuse_*` symbols in addition to the `fsp_fuse_*` symbols. These symbols are for use with programs that use FFI technology such as jnr-fuse and fusepy *ONLY*. They are not supposed to be used by native C/C++ programs. Such programs are supposed to include the `<fuse.h>` headers.
v1.0RC3::
This is the WinFsp 2017 Release Candidate 3, which should be the last Release Candidate according to the current plan. This release fixes a major issue with some file systems and includes a few smaller changes:
- Fixes GitHub issue #55. Prior to this fix it was possible for a rogue process (or faulty file system) to crash Windows using WinFsp. For full details read http://www.osronline.com/showthread.cfm?link=282037[this thread].
- Introduces the `FspFileSystemSetMountPointEx` API, which allows the specification of a security descriptor when mounting over a directory.
- Introduces the `FspVersion` API, which allows the retrieval of the WinFsp DLL version. Currently this reports `0x00010000` (version `1.0`).
- Introduces the `FSP_FUSE_CAP_CASE_INSENSITIVE` and `FSP_FUSE_CAP_READDIR_PLUS` WinFsp-FUSE flags. The `FSP_FUSE_CAP_CASE_INSENSITIVE` flag allows a file system to mark itself as case-insensitive. The `FSP_FUSE_CAP_READDIR_PLUS` flag allows a file system to include full `stat` details when responding to the `readdir` operation (thus avoiding extraneous `getattr` calls).
- When using WinFsp-FUSE over Cygwin, POSIX paths can be used as mountpoints.
- Fixes GitHub issue #45. Prior to this fix, file systems that do not properly implement `Cleanup` (including FUSE file systems) would at times disallow renaming of directories.
v1.0RC2::
This is the WinFsp 2017 Release Candidate 2. Some important changes included below:

View File

@ -3,7 +3,7 @@
![WinFsp Demo](http://www.secfs.net/winfsp/files/cap.gif)
<a href="https://github.com/billziss-gh/winfsp/releases/download/v1.0RC1/winfsp-1.0.17009.msi"><img src="http://www.secfs.net/winfsp/resources/Download-WinFsp.png" alt="Download WinFsp Installer" width="244" height="34"></a>
<a href="https://github.com/billziss-gh/winfsp/releases"><img src="http://www.secfs.net/winfsp/resources/Download-WinFsp.png" alt="Download WinFsp Installer" width="244" height="34"></a>
@ -45,19 +45,22 @@ The project source code is organized as follows:
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/).
To fully build WinFsp (including the installer) you must use `tools\build.bat`. By default it builds a Release build, but you can choose either the Debug or Release configuration by using the syntax:
WinFsp is designed to run on Vista and above. It has been tested on the following platforms so far:
tools\build.bat CONFIGURATION
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 on`. For more information see this [document](http://www.secfs.net/winfsp/develop/debug/).
WinFsp is designed to run on Windows 7 and above. It has been tested on the following platforms:
* Windows 7 Enterprise
* Windows 8 Pro
* Windows 10 Pro
* Windows Server 2012
* Windows 10 Pro
* Windows Server 2016
## How to Help

View File

@ -23,7 +23,7 @@ build_script:
- tools\build.bat %CONFIGURATION%
test_script:
- for %%f in ("build\VStudio\build\%CONFIGURATION%\winfsp-*.msi") do start /wait msiexec /i %%f /qn INSTALLLEVEL=1000
- choco install winfsp -s build\VStudio\build\%CONFIGURATION% -y
- if %TESTING%==Func appveyor DownloadFile http://www.secfs.net/winfsp/resources/Test.Filter.Driver.zip && 7z x Test.Filter.Driver.zip
- if %TESTING%==Func start /wait msiexec /i "Test.Filter.Driver\HCK Filter.Driver Content-x86_en-us.msi" /qn
- if %TESTING%==Func tools\nmake-ext-test.bat %CONFIGURATION%
@ -31,6 +31,7 @@ test_script:
- if %TESTING%==Func tools\run-tests.bat %CONFIGURATION% ifstest
- if %TESTING%==Func tools\run-tests.bat %CONFIGURATION% sample
- if %TESTING%==Perf tools\run-perf-tests.bat %CONFIGURATION% baseline > perf-tests.csv && type perf-tests.csv & appveyor PushArtifact perf-tests.csv
- choco uninstall winfsp -y
- if exist %SystemRoot%\memory.dmp exit 1
on_finish:

View File

@ -185,6 +185,7 @@
<ClCompile Include="..\..\..\tst\winfsp-tests\dirbuf-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\dirctl-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\eventlog-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\exec-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\flush-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\fuse-opt-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\hooks.c" />
@ -201,6 +202,7 @@
<ClCompile Include="..\..\..\tst\winfsp-tests\security-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\stream-tests.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\timeout-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\version-test.c" />
<ClCompile Include="..\..\..\tst\winfsp-tests\winfsp-tests.c" />
</ItemGroup>
<ItemGroup>
@ -213,6 +215,9 @@
<Project>{4a7c0b21-9e10-4c81-92de-1493efcf24eb}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\..\..\tst\winfsp-tests\helper\winfsp-tests-helper.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>

View File

@ -79,6 +79,12 @@
<ClCompile Include="..\..\..\tst\winfsp-tests\dirbuf-test.c">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="..\..\..\tst\winfsp-tests\exec-test.c">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="..\..\..\tst\winfsp-tests\version-test.c">
<Filter>Source</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\ext\tlib\testsuite.h">
@ -91,4 +97,9 @@
<Filter>Source</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\..\..\tst\winfsp-tests\helper\winfsp-tests-helper.rc">
<Filter>Source</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View File

@ -17,8 +17,8 @@
<MyCanonicalVersion>1.0</MyCanonicalVersion>
<MyProductVersion>2017 RC2</MyProductVersion>
<MyProductStage>RC</MyProductStage>
<MyProductVersion>2017</MyProductVersion>
<MyProductStage>Gold</MyProductStage>
<MyVersion>$(MyCanonicalVersion).$(MyBuildNumber)</MyVersion>
<MyVersionWithCommas>$(MyVersion.Replace('.',',')),0</MyVersionWithCommas>

View File

@ -34,6 +34,7 @@
<ClCompile Include="..\..\src\dll\dirbuf.c" />
<ClCompile Include="..\..\src\dll\eventlog.c" />
<ClCompile Include="..\..\src\dll\fuse\fuse.c" />
<ClCompile Include="..\..\src\dll\fuse\fuse_compat.c" />
<ClCompile Include="..\..\src\dll\fuse\fuse_intf.c" />
<ClCompile Include="..\..\src\dll\fuse\fuse_main.c" />
<ClCompile Include="..\..\src\dll\fuse\fuse_opt.c" />

View File

@ -106,6 +106,9 @@
<ClCompile Include="..\..\src\dll\dirbuf.c">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="..\..\src\dll\fuse\fuse_compat.c">
<Filter>Source\fuse</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\..\src\dll\library.def">

View File

@ -174,6 +174,7 @@
<ClCompile Include="..\..\src\sys\lockctl.c" />
<ClCompile Include="..\..\src\sys\meta.c" />
<ClCompile Include="..\..\src\sys\name.c" />
<ClCompile Include="..\..\src\sys\psbuffer.c" />
<ClCompile Include="..\..\src\sys\read.c" />
<ClCompile Include="..\..\src\sys\security.c" />
<ClCompile Include="..\..\src\sys\shutdown.c" />

View File

@ -98,6 +98,9 @@
<ClCompile Include="..\..\src\sys\statistics.c">
<Filter>Source</Filter>
</ClCompile>
<ClCompile Include="..\..\src\sys\psbuffer.c">
<Filter>Source</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\sys\driver.h">

View File

@ -0,0 +1,36 @@
$ErrorActionPreference = 'Stop';
$packageName = 'winfsp'
$softwareName = 'WinFsp*'
$installerType = 'msi'
$silentArgs = '/qn /norestart'
$validExitCodes = @(0, 3010, 1605, 1614, 1641)
[array]$key = Get-UninstallRegistryKey -SoftwareName $softwareName
if ($key.Count -eq 1) {
$key | % {
# The Product Code GUID is all that should be passed for MSI, and very
# FIRST, because it comes directly after /x, which is already set in the
# Uninstall-ChocolateyPackage msiargs (facepalm).
$silentArgs = "$($_.PSChildName) $silentArgs"
# Don't pass anything for file, it is ignored for msi (facepalm number 2)
# Alternatively if you need to pass a path to an msi, determine that and
# use it instead of the above in silentArgs, still very first
$file = ''
Uninstall-ChocolateyPackage `
-PackageName $packageName `
-FileType $installerType `
-SilentArgs "$silentArgs" `
-ValidExitCodes $validExitCodes `
-File "$file"
}
} elseif ($key.Count -eq 0) {
Write-Warning "$packageName has already been uninstalled by other means."
} elseif ($key.Count -gt 1) {
Write-Warning "Too many matching packages found! Package may not be uninstalled."
Write-Warning "Please alert package maintainer the following packages were matched:"
$key | % {Write-Warning "- $_"}
}

View File

@ -0,0 +1,16 @@
$ErrorActionPreference = 'Stop';
$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$fileLocation = @(Get-ChildItem $toolsDir -filter winfsp-*.msi)[0].FullName
$packageArgs = @{
packageName = 'winfsp'
fileType = 'msi'
file = $fileLocation
silentArgs = "/qn /norestart INSTALLLEVEL=1000"
validExitCodes = @(0, 3010, 1641)
}
Install-ChocolateyInstallPackage @packageArgs
Remove-Item -Force $packageArgs.file

47
build/choco/winfsp.nuspec Normal file
View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
<metadata>
<id>winfsp</id>
<version>$version$</version>
<packageSourceUrl>https://github.com/billziss-gh/winfsp/tree/master/build/choco</packageSourceUrl>
<owners>Bill Zissimopoulos &lt;billziss at navimatics.com&gt;</owners>
<title>WinFsp</title>
<authors>Bill Zissimopoulos &lt;billziss at navimatics.com&gt;</authors>
<projectUrl>https://github.com/billziss-gh/winfsp</projectUrl>
<iconUrl>https://github.com/billziss-gh/winfsp/raw/master/art/winfsp-solid.png</iconUrl>
<copyright>Bill Zissimopoulos</copyright>
<licenseUrl>https://github.com/billziss-gh/winfsp/blob/master/License.txt</licenseUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<projectSourceUrl>https://github.com/billziss-gh/winfsp</projectSourceUrl>
<docsUrl>https://github.com/billziss-gh/winfsp/tree/master/doc</docsUrl>
<mailingListUrl>https://groups.google.com/forum/#!forum/winfsp</mailingListUrl>
<bugTrackerUrl>https://github.com/billziss-gh/winfsp/issues</bugTrackerUrl>
<tags>driver filesystem fuse gplv3 windows-kernel admin</tags>
<summary>Windows File System Proxy - FUSE for Windows</summary>
<description>
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 of using WinFsp are listed below:
* Very well-tested and stable.
* Very fast.
* Strives for compatibility with NTFS.
* Easy to understand but comprehensive API.
* FUSE compatibility layer for native Windows and Cygwin.
* Signed drivers provided on every release.
* Available under the GPLv3 license with a special exception for Free/Libre and Open Source Software.
</description>
<releaseNotes>https://github.com/billziss-gh/winfsp/blob/master/Changelog.asciidoc</releaseNotes>
<!--<dependencies>
<dependency id="chocolatey-uninstall.extension" />
</dependencies>-->
</metadata>
<files>
<file src="chocolateyInstall.ps1" target="tools" />
<file src="chocolateyBeforeModify.ps1" target="tools" />
<file src="winfsp-$version$.msi" target="tools" />
</files>
</package>

View File

@ -112,7 +112,7 @@ usage: passthrough OPTIONS
options:
-d DebugFlags [-1: enable all debug logs]
-D DebugLogFile [file path; use - for stdout]
-D DebugLogFile [file path; use - for stderr]
-u \Server\Share [UNC prefix (single backslash)]
-p Directory [directory to expose as pass through file system]
-m MountPoint [X:|*|directory]
@ -138,7 +138,7 @@ The variable `DebugLogFile` is used to control the WinFsp debug logging mechanis
if (0 != DebugLogFile)
{
if (0 == wcscmp(L"-", DebugLogFile))
DebugLogHandle = GetStdHandle(STD_OUTPUT_HANDLE);
DebugLogHandle = GetStdHandle(STD_ERROR_HANDLE);
else
DebugLogHandle = CreateFileW(
DebugLogFile,

1741
doc/winfsp.h.asciidoc Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -41,6 +41,15 @@ extern "C" {
#define FUSE_CAP_EXPORT_SUPPORT (1 << 4)
#define FUSE_CAP_BIG_WRITES (1 << 5)
#define FUSE_CAP_DONT_MASK (1 << 6)
#define FUSE_CAP_ALLOCATE (1 << 27) /* reserved (OSXFUSE) */
#define FUSE_CAP_EXCHANGE_DATA (1 << 28) /* reserved (OSXFUSE) */
#define FUSE_CAP_CASE_INSENSITIVE (1 << 29) /* file system is case insensitive */
#define FUSE_CAP_VOL_RENAME (1 << 30) /* reserved (OSXFUSE) */
#define FUSE_CAP_XTIMES (1 << 31) /* reserved (OSXFUSE) */
#define FSP_FUSE_CAP_READDIR_PLUS (1 << 21) /* file system supports enhanced readdir */
#define FSP_FUSE_CAP_READ_ONLY (1 << 22) /* file system is marked read-only */
#define FSP_FUSE_CAP_CASE_INSENSITIVE FUSE_CAP_CASE_INSENSITIVE
#define FUSE_IOCTL_COMPAT (1 << 0)
#define FUSE_IOCTL_UNRESTRICTED (1 << 1)

View File

@ -177,6 +177,7 @@ struct fuse_flock
MemAlloc, MemFree, \
fsp_fuse_daemonize, \
fsp_fuse_set_signal_handlers, \
0/*conv_to_win_path*/, \
}
#else
#define FSP_FUSE_ENV_INIT \
@ -185,6 +186,7 @@ struct fuse_flock
malloc, free, \
fsp_fuse_daemonize, \
fsp_fuse_set_signal_handlers, \
0/*conv_to_win_path*/, \
}
#endif
@ -226,6 +228,7 @@ struct fuse_flock
malloc, free, \
fsp_fuse_daemonize, \
fsp_fuse_set_signal_handlers, \
fsp_fuse_conv_to_win_path, \
}
/*
@ -244,7 +247,8 @@ struct fsp_fuse_env
void (*memfree)(void *);
int (*daemonize)(int);
int (*set_signal_handlers)(void *);
void (*reserved[4])();
char *(*conv_to_win_path)(const char *);
void (*reserved[3])();
};
FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_signal_handler)(int sig);
@ -348,6 +352,13 @@ static inline int fsp_fuse_set_signal_handlers(void *se)
#undef FSP_FUSE_SET_SIGNAL_HANDLER
}
static inline char *fsp_fuse_conv_to_win_path(const char *path)
{
void *cygwin_create_path(unsigned, const void *);
return cygwin_create_path(
0/*CCP_POSIX_TO_WIN_A*/ | 0x100/*CCP_RELATIVE*/,
path);
}
#endif

View File

@ -148,7 +148,8 @@ typedef struct
/* kernel-mode flags */
UINT32 PostCleanupWhenModifiedOnly:1; /* post Cleanup when a file was modified/deleted */
UINT32 PassQueryDirectoryPattern:1; /* pass Pattern during QueryDirectory operations */
UINT32 KmReservedFlags:4;
UINT32 AlwaysUseDoubleBuffering:1;
UINT32 KmReservedFlags:3;
/* user-mode flags */
UINT32 UmFileContextIsUserContext2:1; /* user mode: FileContext parameter is UserContext2 */
UINT32 UmFileContextIsFullContext:1; /* user mode: FileContext parameter is FullContext */

View File

@ -26,7 +26,10 @@
#include <windows.h>
#undef WIN32_NO_STATUS
#include <winternl.h>
#pragma warning(push)
#pragma warning(disable:4005) /* macro redefinition */
#include <ntstatus.h>
#pragma warning(pop)
#if defined(WINFSP_DLL_INTERNAL)
#define FSP_API __declspec(dllexport)
@ -893,6 +896,8 @@ FSP_API VOID FspFileSystemDelete(FSP_FILE_SYSTEM *FileSystem);
* STATUS_SUCCESS or error code.
*/
FSP_API NTSTATUS FspFileSystemSetMountPoint(FSP_FILE_SYSTEM *FileSystem, PWSTR MountPoint);
FSP_API NTSTATUS FspFileSystemSetMountPointEx(FSP_FILE_SYSTEM *FileSystem, PWSTR MountPoint,
PSECURITY_DESCRIPTOR SecurityDescriptor);
/**
* Remove the mount point for a file system.
*
@ -1625,6 +1630,7 @@ FSP_API NTSTATUS FspCallNamedPipeSecurely(PWSTR PipeName,
PVOID InBuffer, ULONG InBufferSize, PVOID OutBuffer, ULONG OutBufferSize,
PULONG PBytesTransferred, ULONG Timeout,
PSID Sid);
FSP_API NTSTATUS FspVersion(PUINT32 PVersion);
/*
* Delay load
@ -1639,6 +1645,18 @@ NTSTATUS FspLoad(PVOID *PModule)
#endif
#define FSP_DLLPATH "bin\\" FSP_DLLNAME
WINADVAPI
LSTATUS
APIENTRY
RegGetValueW(
HKEY hkey,
LPCWSTR lpSubKey,
LPCWSTR lpValue,
DWORD dwFlags,
LPDWORD pdwType,
PVOID pvData,
LPDWORD pcbData);
WCHAR PathBuf[MAX_PATH];
DWORD Size;
HKEY RegKey;

View File

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

View File

@ -187,20 +187,63 @@ FSP_API VOID FspFileSystemDelete(FSP_FILE_SYSTEM *FileSystem)
MemFree(FileSystem);
}
static NTSTATUS FspFileSystemSetMountPoint_CreateDirectory(PWSTR MountPoint, PWSTR VolumeName)
static NTSTATUS FspFileSystemSetMountPoint_Drive(PWSTR MountPoint, PWSTR VolumeName,
PHANDLE PMountHandle)
{
*PMountHandle = 0;
if (!DefineDosDeviceW(DDD_RAW_TARGET_PATH, MountPoint, VolumeName))
return FspNtStatusFromWin32(GetLastError());
if (0 != FspNtOpenSymbolicLinkObject)
{
NTSTATUS Result;
WCHAR SymlinkBuf[6];
UNICODE_STRING Symlink;
OBJECT_ATTRIBUTES Obja;
memcpy(SymlinkBuf, L"\\??\\X:", sizeof SymlinkBuf);
SymlinkBuf[4] = MountPoint[0];
Symlink.Length = Symlink.MaximumLength = sizeof SymlinkBuf;
Symlink.Buffer = SymlinkBuf;
memset(&Obja, 0, sizeof Obja);
Obja.Length = sizeof Obja;
Obja.ObjectName = &Symlink;
Obja.Attributes = OBJ_CASE_INSENSITIVE;
Result = FspNtOpenSymbolicLinkObject(PMountHandle, DELETE, &Obja);
if (NT_SUCCESS(Result))
{
Result = FspNtMakeTemporaryObject(*PMountHandle);
if (!NT_SUCCESS(Result))
{
FspNtClose(*PMountHandle);
*PMountHandle = 0;
}
}
}
return STATUS_SUCCESS;
}
static NTSTATUS FspFileSystemSetMountPoint_Directory(PWSTR MountPoint, PWSTR VolumeName,
PSECURITY_DESCRIPTOR SecurityDescriptor, PHANDLE PMountHandle)
{
NTSTATUS Result;
HANDLE DirHandle;
BOOL Success;
SECURITY_ATTRIBUTES SecurityAttributes;
HANDLE MountHandle = INVALID_HANDLE_VALUE;
DWORD Backslashes, Bytes;
USHORT VolumeNameLength, BackslashLength, ReparseDataLength;
PREPARSE_DATA_BUFFER ReparseData = 0;
PWSTR P, PathBuffer;
*PMountHandle = 0;
/*
* Windows does not allow mount points (junctions) to point to network file systems.
*
* Count how many backslashes our VolumeName. If it is 3 or more this is a network
* Count how many backslashes our VolumeName has. If it is 3 or more this is a network
* file system. Preemptively return STATUS_NETWORK_ACCESS_DENIED.
*/
for (P = VolumeName, Backslashes = 0; *P; P++)
@ -211,25 +254,24 @@ static NTSTATUS FspFileSystemSetMountPoint_CreateDirectory(PWSTR MountPoint, PWS
goto exit;
}
if (!CreateDirectoryW(MountPoint, 0))
memset(&SecurityAttributes, 0, sizeof SecurityAttributes);
SecurityAttributes.nLength = sizeof SecurityAttributes;
SecurityAttributes.lpSecurityDescriptor = SecurityDescriptor;
MountHandle = CreateFileW(MountPoint,
FILE_WRITE_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
&SecurityAttributes,
CREATE_NEW,
FILE_ATTRIBUTE_DIRECTORY |
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE,
0);
if (INVALID_HANDLE_VALUE == MountHandle)
{
Result = FspNtStatusFromWin32(GetLastError());
goto exit;
}
DirHandle = CreateFileW(MountPoint,
FILE_WRITE_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
0);
if (INVALID_HANDLE_VALUE == DirHandle)
{
Result = FspNtStatusFromWin32(GetLastError());
goto rmdir_and_exit;
}
VolumeNameLength = (USHORT)lstrlenW(VolumeName);
BackslashLength = 0 == VolumeNameLength || L'\\' != VolumeName[VolumeNameLength - 1];
VolumeNameLength *= sizeof(WCHAR);
@ -243,7 +285,7 @@ static NTSTATUS FspFileSystemSetMountPoint_CreateDirectory(PWSTR MountPoint, PWS
if (0 == ReparseData)
{
Result = STATUS_INSUFFICIENT_RESOURCES;
goto rmdir_and_exit;
goto exit;
}
ReparseData->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
@ -270,86 +312,35 @@ static NTSTATUS FspFileSystemSetMountPoint_CreateDirectory(PWSTR MountPoint, PWS
PathBuffer[VolumeNameLength / sizeof(WCHAR)] = L'\\';
PathBuffer[(VolumeNameLength + BackslashLength) / sizeof(WCHAR)] = L'\0';
Success = DeviceIoControl(DirHandle, FSCTL_SET_REPARSE_POINT,
if (!DeviceIoControl(MountHandle, FSCTL_SET_REPARSE_POINT,
ReparseData, REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseData->ReparseDataLength,
0, 0,
&Bytes, 0);
CloseHandle(DirHandle);
if (!Success)
&Bytes, 0))
{
Result = FspNtStatusFromWin32(GetLastError());
goto rmdir_and_exit;
goto exit;
}
*PMountHandle = MountHandle;
Result = STATUS_SUCCESS;
exit:
if (!NT_SUCCESS(Result) && INVALID_HANDLE_VALUE != MountHandle)
CloseHandle(MountHandle);
MemFree(ReparseData);
return Result;
rmdir_and_exit:
RemoveDirectoryW(MountPoint);
goto exit;
}
static NTSTATUS FspFileSystemSetMountPoint_MakeTemporary(PWSTR MountPoint, PHANDLE PMountHandle)
{
NTSTATUS Result = STATUS_SUCCESS;
HANDLE MountHandle = 0;
if (FspPathIsDrive(MountPoint))
{
if (0 != FspNtOpenSymbolicLinkObject)
{
WCHAR SymlinkBuf[6];
UNICODE_STRING Symlink;
OBJECT_ATTRIBUTES Obja;
memcpy(SymlinkBuf, L"\\??\\X:", sizeof SymlinkBuf);
SymlinkBuf[4] = MountPoint[0];
Symlink.Length = Symlink.MaximumLength = sizeof SymlinkBuf;
Symlink.Buffer = SymlinkBuf;
memset(&Obja, 0, sizeof Obja);
Obja.Length = sizeof Obja;
Obja.ObjectName = &Symlink;
Obja.Attributes = OBJ_CASE_INSENSITIVE;
Result = FspNtOpenSymbolicLinkObject(&MountHandle, DELETE, &Obja);
if (NT_SUCCESS(Result))
{
Result = FspNtMakeTemporaryObject(MountHandle);
if (!NT_SUCCESS(Result))
{
FspNtClose(MountHandle);
MountHandle = 0;
}
}
}
}
else
{
/* open the directory for DELETE_ON_CLOSE; closing it will remove the directory */
MountHandle = CreateFileW(MountPoint,
FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
0,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_DELETE_ON_CLOSE,
0);
if (INVALID_HANDLE_VALUE == MountHandle)
{
MountHandle = 0;
Result = FspNtStatusFromWin32(GetLastError());
}
}
*PMountHandle = MountHandle;
return Result;
}
FSP_API NTSTATUS FspFileSystemSetMountPoint(FSP_FILE_SYSTEM *FileSystem, PWSTR MountPoint)
{
return FspFileSystemSetMountPointEx(FileSystem, MountPoint, 0);
}
FSP_API NTSTATUS FspFileSystemSetMountPointEx(FSP_FILE_SYSTEM *FileSystem, PWSTR MountPoint,
PSECURITY_DESCRIPTOR SecurityDescriptor)
{
if (0 != FileSystem->MountPoint)
return STATUS_INVALID_PARAMETER;
@ -375,11 +366,10 @@ FSP_API NTSTATUS FspFileSystemSetMountPoint(FSP_FILE_SYSTEM *FileSystem, PWSTR M
if (0 == (Drives & (1 << (Drive - 'A'))))
{
MountPoint[0] = Drive;
if (DefineDosDeviceW(DDD_RAW_TARGET_PATH, MountPoint, FileSystem->VolumeName))
{
Result = STATUS_SUCCESS;
Result = FspFileSystemSetMountPoint_Drive(MountPoint, FileSystem->VolumeName,
&MountHandle);
if (NT_SUCCESS(Result))
goto exit;
}
}
Result = STATUS_NO_SUCH_DEVICE;
}
@ -400,22 +390,16 @@ FSP_API NTSTATUS FspFileSystemSetMountPoint(FSP_FILE_SYSTEM *FileSystem, PWSTR M
MountPoint = P;
if (FspPathIsDrive(MountPoint))
{
if (DefineDosDeviceW(DDD_RAW_TARGET_PATH, MountPoint, FileSystem->VolumeName))
Result = STATUS_SUCCESS;
else
Result = FspNtStatusFromWin32(GetLastError());
}
Result = FspFileSystemSetMountPoint_Drive(MountPoint, FileSystem->VolumeName,
&MountHandle);
else
Result = FspFileSystemSetMountPoint_CreateDirectory(MountPoint, FileSystem->VolumeName);
Result = FspFileSystemSetMountPoint_Directory(MountPoint, FileSystem->VolumeName,
SecurityDescriptor, &MountHandle);
}
exit:
if (NT_SUCCESS(Result))
{
FspFileSystemSetMountPoint_MakeTemporary(MountPoint, &MountHandle);
/* ignore result; this path always considered successful */
FileSystem->MountPoint = MountPoint;
FileSystem->MountHandle = MountHandle;
}
@ -425,33 +409,35 @@ exit:
return Result;
}
static VOID FspFileSystemRemoveMountPoint_Drive(PWSTR MountPoint, PWSTR VolumeName, HANDLE MountHandle)
{
DefineDosDeviceW(DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE,
MountPoint, VolumeName);
if (0 != MountHandle)
FspNtClose(MountHandle);
}
static VOID FspFileSystemRemoveMountPoint_Directory(HANDLE MountHandle)
{
/* directory is marked DELETE_ON_CLOSE */
CloseHandle(MountHandle);
}
FSP_API VOID FspFileSystemRemoveMountPoint(FSP_FILE_SYSTEM *FileSystem)
{
BOOLEAN IsDrive;
if (0 == FileSystem->MountPoint)
return;
IsDrive = FspPathIsDrive(FileSystem->MountPoint);
if (IsDrive)
DefineDosDeviceW(DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE,
FileSystem->MountPoint, FileSystem->VolumeName);
if (FspPathIsDrive(FileSystem->MountPoint))
FspFileSystemRemoveMountPoint_Drive(FileSystem->MountPoint, FileSystem->VolumeName,
FileSystem->MountHandle);
else
/* nothing to do! directory will be deleted when the MountHandle is closed */;
FspFileSystemRemoveMountPoint_Directory(FileSystem->MountHandle);
MemFree(FileSystem->MountPoint);
FileSystem->MountPoint = 0;
if (0 != FileSystem->MountHandle)
{
if (IsDrive)
FspNtClose(FileSystem->MountHandle);
else
/* CloseHandle really calls NtClose, but I like being defensive when programming */
CloseHandle(FileSystem->MountHandle);
FileSystem->MountHandle = 0;
}
FileSystem->MountHandle = 0;
}
static DWORD WINAPI FspFileSystemDispatcherThread(PVOID FileSystem0)

View File

@ -1354,7 +1354,7 @@ FSP_API BOOLEAN FspFileSystemFindReparsePoint(FSP_FILE_SYSTEM *FileSystem,
return FALSE;
}
FSP_API NTSTATUS FspFileSystemResolveReparsePointsInternal(FSP_FILE_SYSTEM *FileSystem,
static NTSTATUS FspFileSystemResolveReparsePointsInternal(FSP_FILE_SYSTEM *FileSystem,
NTSTATUS (*GetReparsePointByName)(
FSP_FILE_SYSTEM *FileSystem, PVOID Context,
PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize),

View File

@ -32,16 +32,13 @@ struct fsp_fuse_core_opt_data
{
struct fsp_fuse_env *env;
int help, debug;
int hard_remove,
use_ino, readdir_ino,
set_umask, umask,
HANDLE DebugLogHandle;
int set_umask, umask,
set_uid, uid,
set_gid, gid,
set_attr_timeout, attr_timeout,
rellinks;
int set_FileInfoTimeout;
int CaseInsensitiveSearch,
ReadOnlyVolume;
FSP_FSCTL_VOLUME_PARAMS VolumeParams;
};
@ -56,9 +53,11 @@ static struct fuse_opt fsp_fuse_core_opts[] =
FSP_FUSE_CORE_OPT("-d", debug, 1),
FSP_FUSE_CORE_OPT("debug", debug, 1),
FSP_FUSE_CORE_OPT("hard_remove", hard_remove, 1),
FSP_FUSE_CORE_OPT("use_ino", use_ino, 1),
FSP_FUSE_CORE_OPT("readdir_ino", readdir_ino, 1),
FUSE_OPT_KEY("DebugLog=", 'D'),
FUSE_OPT_KEY("hard_remove", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("use_ino", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("readdir_ino", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("direct_io", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("kernel_cache", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("auto_cache", FUSE_OPT_KEY_DISCARD),
@ -89,17 +88,8 @@ static struct fuse_opt fsp_fuse_core_opts[] =
FSP_FUSE_CORE_OPT("MaxComponentLength=%hu", VolumeParams.MaxComponentLength, 0),
FSP_FUSE_CORE_OPT("VolumeCreationTime=%lli", VolumeParams.VolumeCreationTime, 0),
FSP_FUSE_CORE_OPT("VolumeSerialNumber=%lx", VolumeParams.VolumeSerialNumber, 0),
FSP_FUSE_CORE_OPT("TransactTimeout=%u", VolumeParams.TransactTimeout, 0),
FSP_FUSE_CORE_OPT("IrpTimeout=%u", VolumeParams.IrpTimeout, 0),
FSP_FUSE_CORE_OPT("IrpCapacity=%u", VolumeParams.IrpCapacity, 0),
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("ReadOnlyVolume", ReadOnlyVolume, 1),
FUSE_OPT_KEY("ReparsePoints", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("NamedStreams", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("HardLinks", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("ExtendedAttributes", FUSE_OPT_KEY_DISCARD),
FUSE_OPT_KEY("--UNC=", 'U'),
FUSE_OPT_KEY("--VolumePrefix=", 'U'),
FUSE_OPT_KEY("--FileSystemName=", 'F'),
@ -131,6 +121,9 @@ static inline void *fsp_fuse_obj_alloc(struct fsp_fuse_env *env, size_t size)
static inline void fsp_fuse_obj_free(void *obj)
{
if (0 == obj)
return;
struct fsp_fuse_obj_hdr *hdr = (PVOID)((PUINT8)obj - sizeof(struct fsp_fuse_obj_hdr));
hdr->dtor(hdr);
@ -190,23 +183,55 @@ FSP_FUSE_API struct fuse_chan *fsp_fuse_mount(struct fsp_fuse_env *env,
const char *mountpoint, struct fuse_args *args)
{
struct fuse_chan *ch = 0;
WCHAR TempMountPointBuf[MAX_PATH], MountPointBuf[MAX_PATH];
int Size;
if (0 == mountpoint)
mountpoint = "";
if (0 == mountpoint || '\0' == mountpoint[0] ||
('*' == mountpoint[0] && '\0' == mountpoint[1]))
{
MountPointBuf[0] = L'*';
MountPointBuf[1] = L'\0';
Size = 2 * sizeof(WCHAR);
}
else if (
(
('A' <= mountpoint[0] && mountpoint[0] <= 'Z') ||
('a' <= mountpoint[0] && mountpoint[0] <= 'z')
) &&
':' == mountpoint[1] && '\0' == mountpoint[2])
{
MountPointBuf[0] = mountpoint[0];
MountPointBuf[1] = ':';
MountPointBuf[2] = '\0';
Size = 3 * sizeof(WCHAR);
}
else
{
char *win_mountpoint = 0;
Size = MultiByteToWideChar(CP_UTF8, 0, mountpoint, -1, 0, 0);
if (0 == Size)
goto fail;
if (0 != env->conv_to_win_path)
mountpoint = win_mountpoint = env->conv_to_win_path(mountpoint);
ch = fsp_fuse_obj_alloc(env, sizeof *ch + Size * sizeof(WCHAR));
Size = 0;
if (0 != mountpoint &&
0 != MultiByteToWideChar(CP_UTF8, 0, mountpoint, -1, TempMountPointBuf, MAX_PATH))
Size = GetFullPathNameW(TempMountPointBuf, MAX_PATH, MountPointBuf, 0);
env->memfree(win_mountpoint);
if (0 == Size || MAX_PATH <= Size)
goto fail;
mountpoint = 0;
Size = (Size + 1) * sizeof(WCHAR);
}
ch = fsp_fuse_obj_alloc(env, sizeof *ch + Size);
if (0 == ch)
goto fail;
ch->MountPoint = (PVOID)ch->Buffer;
Size = MultiByteToWideChar(CP_UTF8, 0, mountpoint, -1, ch->MountPoint, Size);
if (0 == Size)
goto fail;
memcpy(ch->MountPoint, MountPointBuf, Size);
return ch;
@ -261,9 +286,17 @@ static NTSTATUS fsp_fuse_svcstart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv)
//FUSE_CAP_ATOMIC_O_TRUNC | /* due to Windows/WinFsp design, no support */
//FUSE_CAP_EXPORT_SUPPORT | /* not needed in Windows/WinFsp */
FUSE_CAP_BIG_WRITES |
FUSE_CAP_DONT_MASK;
FUSE_CAP_DONT_MASK |
FSP_FUSE_CAP_READDIR_PLUS |
FSP_FUSE_CAP_READ_ONLY |
FSP_FUSE_CAP_CASE_INSENSITIVE;
if (0 != f->ops.init)
{
context->private_data = f->data = f->ops.init(&conn);
f->VolumeParams.ReadOnlyVolume = 0 != (conn.want & FSP_FUSE_CAP_READ_ONLY);
f->VolumeParams.CaseSensitiveSearch = 0 == (conn.want & FSP_FUSE_CAP_CASE_INSENSITIVE);
f->conn_want = conn.want;
}
f->fsinit = TRUE;
if (0 != f->ops.statfs)
{
@ -278,10 +311,12 @@ static NTSTATUS fsp_fuse_svcstart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv)
goto fail;
}
if (stbuf.f_frsize > FSP_FUSE_SECTORSIZE_MAX)
stbuf.f_frsize = FSP_FUSE_SECTORSIZE_MAX;
if (0 == f->VolumeParams.SectorSize)
if (0 == f->VolumeParams.SectorSize && 0 != stbuf.f_frsize)
f->VolumeParams.SectorSize = (UINT16)stbuf.f_frsize;
#if 0
if (0 == f->VolumeParams.SectorsPerAllocationUnit && 0 != stbuf.f_frsize)
f->VolumeParams.SectorsPerAllocationUnit = (UINT16)(stbuf.f_bsize / stbuf.f_frsize);
#endif
if (0 == f->VolumeParams.MaxComponentLength)
f->VolumeParams.MaxComponentLength = (UINT16)stbuf.f_namemax;
}
@ -313,9 +348,8 @@ static NTSTATUS fsp_fuse_svcstart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv)
}
/* the FSD does not currently limit these VolumeParams fields; do so here! */
if (f->VolumeParams.SectorSize < FSP_FUSE_SECTORSIZE_MIN)
f->VolumeParams.SectorSize = FSP_FUSE_SECTORSIZE_MIN;
if (f->VolumeParams.SectorSize > FSP_FUSE_SECTORSIZE_MAX)
if (f->VolumeParams.SectorSize < FSP_FUSE_SECTORSIZE_MIN ||
f->VolumeParams.SectorSize > FSP_FUSE_SECTORSIZE_MAX)
f->VolumeParams.SectorSize = FSP_FUSE_SECTORSIZE_MAX;
if (f->VolumeParams.SectorsPerAllocationUnit == 0)
f->VolumeParams.SectorsPerAllocationUnit = 1;
@ -419,14 +453,13 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
case 'h':
FspServiceLog(EVENTLOG_ERROR_TYPE, L""
FSP_FUSE_LIBRARY_NAME " options:\n"
" -o SectorSize=N sector size for Windows (512-4096, deflt: 512)\n"
" -o SectorsPerAllocationUnit=N allocation unit size (deflt: 1*SectorSize)\n"
" -o DebugLog=FILE debug log file (deflt: stderr)\n"
" -o SectorSize=N sector size for Windows (512-4096, deflt: 4096)\n"
" -o SectorsPerAllocationUnit=N sectors per allocation unit (deflt: 1)\n"
" -o MaxComponentLength=N max file name component length (deflt: 255)\n"
" -o VolumeCreationTime=T volume creation time (FILETIME hex format)\n"
" -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 ReadOnlyVolume file system is read only\n"
" --UNC=U --VolumePrefix=U UNC prefix (\\Server\\Share)\n"
" --FileSystemName=FSN Name of user mode file system\n");
opt_data->help = 1;
@ -437,6 +470,17 @@ static int fsp_fuse_core_opt_proc(void *opt_data0, const char *arg, int key,
FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION);
opt_data->help = 1;
return 1;
case 'D':
arg += sizeof "DebugLog=" - 1;
opt_data->DebugLogHandle = CreateFileA(
arg,
FILE_APPEND_DATA,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
0);
return 0;
case 'U':
if ('U' == arg[2])
arg += sizeof "--UNC=" - 1;
@ -479,12 +523,24 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env,
memset(&opt_data, 0, sizeof opt_data);
opt_data.env = env;
opt_data.DebugLogHandle = GetStdHandle(STD_ERROR_HANDLE);
opt_data.VolumeParams.FileInfoTimeout = 1000; /* default FileInfoTimeout for FUSE file systems */
if (-1 == fsp_fuse_opt_parse(env, args, &opt_data, fsp_fuse_core_opts, fsp_fuse_core_opt_proc))
return 0;
if (opt_data.help)
return 0;
if (opt_data.debug)
{
if (INVALID_HANDLE_VALUE == opt_data.DebugLogHandle)
{
ErrorMessage = L": cannot open debug log file.";
goto fail;
}
FspDebugLogSetHandle(opt_data.DebugLogHandle);
}
if ((opt_data.set_uid && -1 == opt_data.uid) ||
(opt_data.set_gid && -1 == opt_data.gid))
{
@ -509,12 +565,12 @@ FSP_FUSE_API struct fuse *fsp_fuse_new(struct fsp_fuse_env *env,
if (!opt_data.set_FileInfoTimeout && opt_data.set_attr_timeout)
opt_data.VolumeParams.FileInfoTimeout = opt_data.set_attr_timeout * 1000;
opt_data.VolumeParams.CaseSensitiveSearch = !opt_data.CaseInsensitiveSearch;
opt_data.VolumeParams.CaseSensitiveSearch = TRUE;
opt_data.VolumeParams.PersistentAcls = TRUE;
opt_data.VolumeParams.ReparsePoints = TRUE;
opt_data.VolumeParams.ReparsePointsAccessCheck = FALSE;
opt_data.VolumeParams.NamedStreams = FALSE;
opt_data.VolumeParams.ReadOnlyVolume = !!opt_data.ReadOnlyVolume;
opt_data.VolumeParams.ReadOnlyVolume = FALSE;
opt_data.VolumeParams.PostCleanupWhenModifiedOnly = TRUE;
opt_data.VolumeParams.UmFileContextIsUserContext2 = TRUE;
if (L'\0' == opt_data.VolumeParams.FileSystemName[0])

View File

@ -0,0 +1,35 @@
/**
* @file dll/fuse/fuse_compat.c
*
* @copyright 2015-2017 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
*
* You can redistribute it and/or modify it under the terms of the GNU
* General Public License version 3 as published by the Free Software
* Foundation.
*
* Licensees holding a valid commercial license may use this file in
* accordance with the commercial license agreement provided with the
* software.
*/
#include <dll/library.h>
/*
* This file provides an implementation of the `fuse_*` symbols. This
* implementation is a simple shim that forwards `fuse_*` calls to the
* equivalent `fsp_fuse_*` ones using a default `fsp_fuse_env`.
*
* These symbols should *not* be used by C/C++ programs. For this reason
* the `fuse.h` headers only expose the `fsp_fuse_*` symbols, wrapped
* with macros. These symbols are for use only from programs using FFI
* technology to access FUSE symbols (e.g. fusepy, jnr-fuse).
*/
#define FSP_FUSE_API
#define FSP_FUSE_SYM(proto, ...) __declspec(dllexport) proto { __VA_ARGS__ }
#include <fuse/fuse_common.h>
#include <fuse/fuse.h>
#include <fuse/fuse_opt.h>

View File

@ -311,28 +311,34 @@ static BOOLEAN fsp_fuse_intf_CheckSymlinkDirectory(FSP_FILE_SYSTEM *FileSystem,
}
#define fsp_fuse_intf_GetFileInfoEx(FileSystem, PosixPath, fi, PUid, PGid, PMode, FileInfo)\
fsp_fuse_intf_GetFileInfoFunnel(FileSystem, PosixPath, fi, PUid, PGid, PMode, 0, FileInfo)
fsp_fuse_intf_GetFileInfoFunnel(FileSystem, PosixPath, fi, 0, PUid, PGid, PMode, 0, FileInfo)
static NTSTATUS fsp_fuse_intf_GetFileInfoFunnel(FSP_FILE_SYSTEM *FileSystem,
const char *PosixPath, struct fuse_file_info *fi,
const char *PosixPath, struct fuse_file_info *fi, const struct fuse_stat *stbufp,
PUINT32 PUid, PUINT32 PGid, PUINT32 PMode, PUINT32 PDev,
FSP_FSCTL_FILE_INFO *FileInfo)
{
struct fuse *f = FileSystem->UserContext;
UINT64 AllocationUnit;
struct fuse_stat stbuf;
int err;
memset(&stbuf, 0, sizeof stbuf);
if (0 != f->ops.fgetattr && 0 != fi && -1 != fi->fh)
err = f->ops.fgetattr(PosixPath, (void *)&stbuf, fi);
else if (0 != f->ops.getattr)
err = f->ops.getattr(PosixPath, (void *)&stbuf);
if (0 != stbufp)
memcpy(&stbuf, stbufp, sizeof stbuf);
else
return STATUS_INVALID_DEVICE_REQUEST;
{
int err;
if (0 != err)
return fsp_fuse_ntstatus_from_errno(f->env, err);
memset(&stbuf, 0, sizeof stbuf);
if (0 != f->ops.fgetattr && 0 != fi && -1 != fi->fh)
err = f->ops.fgetattr(PosixPath, (void *)&stbuf, fi);
else if (0 != f->ops.getattr)
err = f->ops.getattr(PosixPath, (void *)&stbuf);
else
return STATUS_INVALID_DEVICE_REQUEST;
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);
@ -513,7 +519,7 @@ static NTSTATUS fsp_fuse_intf_GetReparsePointEx(FSP_FILE_SYSTEM *FileSystem,
SIZE_T Size;
NTSTATUS Result;
Result = fsp_fuse_intf_GetFileInfoFunnel(FileSystem, PosixPath, fi,
Result = fsp_fuse_intf_GetFileInfoFunnel(FileSystem, PosixPath, fi, 0,
&Uid, &Gid, &Mode, &Dev, &FileInfo);
if (!NT_SUCCESS(Result))
return Result;
@ -630,13 +636,13 @@ static NTSTATUS fsp_fuse_intf_GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem,
struct fuse_statvfs stbuf;
int err;
if (0 == f->ops.statfs)
return STATUS_INVALID_DEVICE_REQUEST;
memset(&stbuf, 0, sizeof stbuf);
err = f->ops.statfs("/", &stbuf);
if (0 != err)
return fsp_fuse_ntstatus_from_errno(f->env, err);
if (0 != f->ops.statfs)
{
err = f->ops.statfs("/", &stbuf);
if (0 != err)
return fsp_fuse_ntstatus_from_errno(f->env, err);
}
VolumeInfo->TotalSize = (UINT64)stbuf.f_blocks * (UINT64)stbuf.f_frsize;
VolumeInfo->FreeSize = (UINT64)stbuf.f_bfree * (UINT64)stbuf.f_frsize;
@ -1576,6 +1582,17 @@ static int fsp_fuse_intf_AddDirInfo(void *buf, const char *name,
memset(DirInfo, 0, sizeof *DirInfo);
DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + SizeW * sizeof(WCHAR));
if (dh->ReaddirPlus && 0 != stbuf)
{
UINT32 Uid, Gid, Mode;
NTSTATUS Result0;
Result0 = fsp_fuse_intf_GetFileInfoFunnel(dh->FileSystem, 0, 0, stbuf,
&Uid, &Gid, &Mode, 0, &DirInfo->FileInfo);
if (NT_SUCCESS(Result0))
DirInfo->Padding[0] = 1; /* HACK: remember that the FileInfo is valid */
}
return !FspFileSystemFillDirectoryBuffer(&filedesc->DirBuffer, DirInfo, &dh->Result);
}
@ -1619,44 +1636,53 @@ static NTSTATUS fsp_fuse_intf_FixDirInfo(FSP_FILE_SYSTEM *FileSystem,
DirInfo = (FSP_FSCTL_DIR_INFO *)(Buffer + *Index);
SizeW = (DirInfo->Size - sizeof *DirInfo) / sizeof(WCHAR);
if (1 == SizeW && L'.' == DirInfo->FileNameBuf[0])
if (DirInfo->Padding[0])
{
PosixPathEnd = 1 < PosixName - PosixPath ? PosixName - 1 : PosixName;
SavedPathChar = *PosixPathEnd;
*PosixPathEnd = '\0';
}
else
if (2 == SizeW && L'.' == DirInfo->FileNameBuf[0] && L'.' == DirInfo->FileNameBuf[1])
{
PosixPathEnd = 1 < PosixName - PosixPath ? PosixName - 2 : PosixName;
while (PosixPath < PosixPathEnd && '/' != *PosixPathEnd)
PosixPathEnd--;
if (PosixPath == PosixPathEnd)
PosixPathEnd++;
SavedPathChar = *PosixPathEnd;
*PosixPathEnd = '\0';
/* DirInfo has been filled already! */
DirInfo->Padding[0] = 0;
}
else
{
PosixPathEnd = 0;
SizeA = WideCharToMultiByte(CP_UTF8, 0, DirInfo->FileNameBuf, SizeW, PosixName, 255, 0, 0);
if (0 == SizeA)
if (1 == SizeW && L'.' == DirInfo->FileNameBuf[0])
{
/* this should never happen because we just converted using MultiByteToWideChar */
Result = STATUS_OBJECT_NAME_INVALID;
goto exit;
PosixPathEnd = 1 < PosixName - PosixPath ? PosixName - 1 : PosixName;
SavedPathChar = *PosixPathEnd;
*PosixPathEnd = '\0';
}
PosixName[SizeA] = '\0';
else
if (2 == SizeW && L'.' == DirInfo->FileNameBuf[0] && L'.' == DirInfo->FileNameBuf[1])
{
PosixPathEnd = 1 < PosixName - PosixPath ? PosixName - 2 : PosixName;
while (PosixPath < PosixPathEnd && '/' != *PosixPathEnd)
PosixPathEnd--;
if (PosixPath == PosixPathEnd)
PosixPathEnd++;
SavedPathChar = *PosixPathEnd;
*PosixPathEnd = '\0';
}
else
{
PosixPathEnd = 0;
SizeA = WideCharToMultiByte(CP_UTF8, 0, DirInfo->FileNameBuf, SizeW, PosixName, 255, 0, 0);
if (0 == SizeA)
{
/* this should never happen because we just converted using MultiByteToWideChar */
Result = STATUS_OBJECT_NAME_INVALID;
goto exit;
}
PosixName[SizeA] = '\0';
}
Result = fsp_fuse_intf_GetFileInfoEx(FileSystem, PosixPath, 0,
&Uid, &Gid, &Mode, &DirInfo->FileInfo);
if (!NT_SUCCESS(Result))
goto exit;
if (0 != PosixPathEnd)
*PosixPathEnd = SavedPathChar;
}
Result = fsp_fuse_intf_GetFileInfoEx(FileSystem, PosixPath, 0,
&Uid, &Gid, &Mode, &DirInfo->FileInfo);
if (!NT_SUCCESS(Result))
goto exit;
if (0 != PosixPathEnd)
*PosixPathEnd = SavedPathChar;
FspPosixDecodeWindowsPath(DirInfo->FileNameBuf, SizeW);
}
@ -1683,6 +1709,8 @@ static NTSTATUS fsp_fuse_intf_ReadDirectory(FSP_FILE_SYSTEM *FileSystem,
{
memset(&dh, 0, sizeof dh);
dh.filedesc = filedesc;
dh.FileSystem = FileSystem;
dh.ReaddirPlus = 0 != (f->conn_want & FSP_FUSE_CAP_READDIR_PLUS);
dh.Result = STATUS_SUCCESS;
if (0 != f->ops.readdir)

View File

@ -40,12 +40,13 @@ struct fuse
int rellinks;
struct fuse_operations ops;
void *data;
unsigned conn_want;
BOOLEAN fsinit;
UINT32 DebugLog;
FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY OpGuardStrategy;
FSP_FSCTL_VOLUME_PARAMS VolumeParams;
PWSTR MountPoint;
FSP_FILE_SYSTEM *FileSystem;
BOOLEAN fsinit;
FSP_SERVICE *Service; /* weak */
};
@ -66,8 +67,12 @@ struct fsp_fuse_file_desc
struct fuse_dirhandle
{
/* ReadDirectory */
struct fsp_fuse_file_desc *filedesc;
FSP_FILE_SYSTEM *FileSystem;
BOOLEAN ReaddirPlus;
NTSTATUS Result;
/* CanDelete */
BOOLEAN DotFiles, HasChild;
};

View File

@ -163,3 +163,47 @@ exit:
return Result;
}
FSP_API NTSTATUS FspVersion(PUINT32 PVersion)
{
static UINT32 Version;
if (0 == Version)
{
/*
* This check is not thread-safe, but that should be ok.
* Two threads competing to read the version will read
* the same value from the Version resource.
*/
extern HINSTANCE DllInstance;
WCHAR ModuleFileName[MAX_PATH];
PVOID VersionInfo;
DWORD Size;
VS_FIXEDFILEINFO *FixedFileInfo = 0;
if (0 != GetModuleFileNameW(DllInstance, ModuleFileName, MAX_PATH))
{
Size = GetFileVersionInfoSizeW(ModuleFileName, &Size/*dummy*/);
if (0 < Size)
{
VersionInfo = MemAlloc(Size);
if (0 != VersionInfo &&
GetFileVersionInfoW(ModuleFileName, 0, Size, VersionInfo) &&
VerQueryValueW(VersionInfo, L"\\", &FixedFileInfo, &Size))
{
/* 32-bit store should be atomic! */
Version = FixedFileInfo->dwFileVersionMS;
}
MemFree(VersionInfo);
}
}
if (0 == FixedFileInfo)
return STATUS_UNSUCCESSFUL;
}
*PVersion = Version;
return STATUS_SUCCESS;
}

View File

@ -80,8 +80,18 @@ static NTSTATUS FspFsvolClose(
FspFileDescDelete(FileDesc); /* this will also close the MainFileObject if any */
FspFileNodeDereference(FileNode);
/* if we are closing files in the context of a rename make it synchronous */
if (FspFsvolDeviceFileRenameIsAcquiredExclusive(FsvolDeviceObject))
{
/* acquire ownership of the Request */
Request->Hint = (UINT_PTR)Irp;
FspIrpSetRequest(Irp, Request);
return FSP_STATUS_IOQ_POST_BEST_EFFORT;
}
/*
* Post as a BestEffort work request. This allows us to complete our own IRP
* Post as a BestEffort WORK request. This allows us to complete our own IRP
* and return immediately.
*/
FspIopPostWorkRequestBestEffort(FsvolDeviceObject, Request);

View File

@ -41,6 +41,7 @@ VOID FspFsvolDeviceFileRenameAcquireExclusive(PDEVICE_OBJECT DeviceObject);
VOID FspFsvolDeviceFileRenameSetOwner(PDEVICE_OBJECT DeviceObject, PVOID Owner);
VOID FspFsvolDeviceFileRenameRelease(PDEVICE_OBJECT DeviceObject);
VOID FspFsvolDeviceFileRenameReleaseOwner(PDEVICE_OBJECT DeviceObject, PVOID Owner);
BOOLEAN FspFsvolDeviceFileRenameIsAcquiredExclusive(PDEVICE_OBJECT DeviceObject);
VOID FspFsvolDeviceLockContextTable(PDEVICE_OBJECT DeviceObject);
VOID FspFsvolDeviceUnlockContextTable(PDEVICE_OBJECT DeviceObject);
NTSTATUS FspFsvolDeviceCopyContextList(PDEVICE_OBJECT DeviceObject,
@ -80,6 +81,7 @@ VOID FspDeviceDeleteAll(VOID);
#pragma alloc_text(PAGE, FspFsvolDeviceFileRenameSetOwner)
#pragma alloc_text(PAGE, FspFsvolDeviceFileRenameRelease)
#pragma alloc_text(PAGE, FspFsvolDeviceFileRenameReleaseOwner)
#pragma alloc_text(PAGE, FspFsvolDeviceFileRenameIsAcquiredExclusive)
#pragma alloc_text(PAGE, FspFsvolDeviceLockContextTable)
#pragma alloc_text(PAGE, FspFsvolDeviceUnlockContextTable)
#pragma alloc_text(PAGE, FspFsvolDeviceCopyContextList)
@ -574,6 +576,15 @@ VOID FspFsvolDeviceFileRenameReleaseOwner(PDEVICE_OBJECT DeviceObject, PVOID Own
ExReleaseResourceForThreadLite(&FsvolDeviceExtension->FileRenameResource, (ERESOURCE_THREAD)Owner);
}
BOOLEAN FspFsvolDeviceFileRenameIsAcquiredExclusive(PDEVICE_OBJECT DeviceObject)
{
PAGED_CODE();
FSP_FSVOL_DEVICE_EXTENSION *FsvolDeviceExtension = FspFsvolDeviceExtension(DeviceObject);
return ExIsResourceAcquiredExclusiveLite(&FsvolDeviceExtension->FileRenameResource);
}
VOID FspFsvolDeviceLockContextTable(PDEVICE_OBJECT DeviceObject)
{
PAGED_CODE();

View File

@ -78,6 +78,7 @@ enum
{
/* QueryDirectory */
RequestIrp = 0,
RequestCookie = 1,
RequestMdl = 1,
RequestAddress = 2,
RequestProcess = 3,
@ -88,6 +89,7 @@ enum
/* DirectoryControlComplete retry */
RequestDirInfoChangeNumber = 0,
};
FSP_FSCTL_STATIC_ASSERT(RequestCookie == RequestMdl, "");
static NTSTATUS FspFsvolQueryDirectoryCopy(
PUNICODE_STRING DirectoryPattern, BOOLEAN CaseInsensitive,
@ -833,41 +835,67 @@ NTSTATUS FspFsvolDirectoryControlPrepare(
{
PAGED_CODE();
NTSTATUS Result;
PMDL Mdl = 0;
PVOID Address;
PEPROCESS Process;
Mdl = IoAllocateMdl(
Irp->AssociatedIrp.SystemBuffer,
Request->Req.QueryDirectory.Length,
FALSE, FALSE, 0);
if (0 == Mdl)
return STATUS_INSUFFICIENT_RESOURCES;
MmBuildMdlForNonPagedPool(Mdl);
/* map the MDL into user-mode */
Result = FspMapLockedPagesInUserMode(Mdl, &Address, 0);
if (!NT_SUCCESS(Result))
if (FspQueryDirectoryIrpShouldUseProcessBuffer(Irp, Request->Req.QueryDirectory.Length))
{
if (0 != Mdl)
IoFreeMdl(Mdl);
NTSTATUS Result;
PVOID Cookie;
PVOID Address;
PEPROCESS Process;
return Result;
Result = FspProcessBufferAcquire(Request->Req.QueryDirectory.Length, &Cookie, &Address);
if (!NT_SUCCESS(Result))
return Result;
/* get a pointer to the current process so that we can release the buffer later */
Process = PsGetCurrentProcess();
ObReferenceObject(Process);
Request->Req.QueryDirectory.Address = (UINT64)(UINT_PTR)Address;
FspIopRequestContext(Request, RequestCookie) = (PVOID)((UINT_PTR)Cookie | 1);
FspIopRequestContext(Request, RequestAddress) = Address;
FspIopRequestContext(Request, RequestProcess) = Process;
return STATUS_SUCCESS;
}
else
{
NTSTATUS Result;
PMDL Mdl = 0;
PVOID Address;
PEPROCESS Process;
/* get a pointer to the current process so that we can unmap the address later */
Process = PsGetCurrentProcess();
ObReferenceObject(Process);
Mdl = IoAllocateMdl(
Irp->AssociatedIrp.SystemBuffer,
Request->Req.QueryDirectory.Length,
FALSE, FALSE, 0);
if (0 == Mdl)
return STATUS_INSUFFICIENT_RESOURCES;
Request->Req.QueryDirectory.Address = (UINT64)(UINT_PTR)Address;
MmBuildMdlForNonPagedPool(Mdl);
FspIopRequestContext(Request, RequestMdl) = Mdl;
FspIopRequestContext(Request, RequestAddress) = Address;
FspIopRequestContext(Request, RequestProcess) = Process;
/* map the MDL into user-mode */
Result = FspMapLockedPagesInUserMode(Mdl, &Address, 0);
if (!NT_SUCCESS(Result))
{
if (0 != Mdl)
IoFreeMdl(Mdl);
return STATUS_SUCCESS;
return Result;
}
/* get a pointer to the current process so that we can unmap the address later */
Process = PsGetCurrentProcess();
ObReferenceObject(Process);
Request->Req.QueryDirectory.Address = (UINT64)(UINT_PTR)Address;
FspIopRequestContext(Request, RequestMdl) = Mdl;
FspIopRequestContext(Request, RequestAddress) = Address;
FspIopRequestContext(Request, RequestProcess) = Process;
return STATUS_SUCCESS;
}
}
NTSTATUS FspFsvolDirectoryControlComplete(
@ -910,6 +938,23 @@ NTSTATUS FspFsvolDirectoryControlComplete(
if (FspFsctlTransactQueryDirectoryKind == Request->Kind)
{
if ((UINT_PTR)FspIopRequestContext(Request, RequestCookie) & 1)
{
PVOID Address = FspIopRequestContext(Request, RequestAddress);
ASSERT(0 != Address);
try
{
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, Address, Response->IoStatus.Information);
}
except (EXCEPTION_EXECUTE_HANDLER)
{
Result = GetExceptionCode();
Result = FsRtlIsNtstatusExpected(Result) ? STATUS_INVALID_USER_BUFFER : Result;
FSP_RETURN();
}
}
DirInfoChangeNumber = FspFileNodeDirInfoChangeNumber(FileNode);
Request->Kind = FspFsctlTransactReservedKind;
FspIopResetRequest(Request, 0);
@ -1010,29 +1055,56 @@ static VOID FspFsvolQueryDirectoryRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, P
PAGED_CODE();
PIRP Irp = Context[RequestIrp];
PMDL Mdl = Context[RequestMdl];
PVOID Address = Context[RequestAddress];
PEPROCESS Process = Context[RequestProcess];
if (0 != Address)
if ((UINT_PTR)Context[RequestCookie] & 1)
{
KAPC_STATE ApcState;
BOOLEAN Attach;
PVOID Cookie = (PVOID)((UINT_PTR)Context[RequestCookie] & ~1);
PVOID Address = Context[RequestAddress];
PEPROCESS Process = Context[RequestProcess];
ASSERT(0 != Process);
Attach = Process != PsGetCurrentProcess();
if (0 != Address)
{
KAPC_STATE ApcState;
BOOLEAN Attach;
if (Attach)
KeStackAttachProcess(Process, &ApcState);
MmUnmapLockedPages(Address, Mdl);
if (Attach)
KeUnstackDetachProcess(&ApcState);
ASSERT(0 != Process);
Attach = Process != PsGetCurrentProcess();
ObDereferenceObject(Process);
if (Attach)
KeStackAttachProcess(Process, &ApcState);
FspProcessBufferRelease(Cookie, Address);
if (Attach)
KeUnstackDetachProcess(&ApcState);
ObDereferenceObject(Process);
}
}
else
{
PMDL Mdl = Context[RequestMdl];
PVOID Address = Context[RequestAddress];
PEPROCESS Process = Context[RequestProcess];
if (0 != Mdl)
IoFreeMdl(Mdl);
if (0 != Address)
{
KAPC_STATE ApcState;
BOOLEAN Attach;
ASSERT(0 != Process);
Attach = Process != PsGetCurrentProcess();
if (Attach)
KeStackAttachProcess(Process, &ApcState);
MmUnmapLockedPages(Address, Mdl);
if (Attach)
KeUnstackDetachProcess(&ApcState);
ObDereferenceObject(Process);
}
if (0 != Mdl)
IoFreeMdl(Mdl);
}
if (0 != Irp)
{

View File

@ -46,6 +46,10 @@ NTSTATUS DriverEntry(
FspDriverMultiVersionInitialize();
Result = FspProcessBufferInitialize();
if (!NT_SUCCESS(Result))
FSP_RETURN();
FspDriverObject = DriverObject;
ExInitializeResourceLite(&FspDeviceGlobalResource);
@ -59,14 +63,21 @@ NTSTATUS DriverEntry(
&DeviceSddl, &FspFsctlDeviceClassGuid,
&FspFsctlDiskDeviceObject);
if (!NT_SUCCESS(Result))
{
FspProcessBufferFinalize();
FSP_RETURN();
}
RtlInitUnicodeString(&DeviceName, L"\\Device\\" FSP_FSCTL_NET_DEVICE_NAME);
Result = FspDeviceCreateSecure(FspFsctlDeviceExtensionKind, 0,
&DeviceName, FILE_DEVICE_NETWORK_FILE_SYSTEM, FILE_DEVICE_SECURE_OPEN,
&DeviceSddl, &FspFsctlDeviceClassGuid,
&FspFsctlNetDeviceObject);
if (!NT_SUCCESS(Result))
FSP_RETURN(FspDeviceDelete(FspFsctlDiskDeviceObject));
{
FspDeviceDelete(FspFsctlDiskDeviceObject);
FspProcessBufferFinalize();
FSP_RETURN();
}
Result = FspDeviceInitialize(FspFsctlDiskDeviceObject);
ASSERT(STATUS_SUCCESS == Result);
Result = FspDeviceInitialize(FspFsctlNetDeviceObject);
@ -178,6 +189,7 @@ static VOID FspDriverMultiVersionInitialize(VOID)
{
FspProcessorCount = KeQueryActiveProcessorCount(0);
#pragma prefast(suppress:30035, "FspDriverMultiVersionInitialize is called from DriverEntry")
ExInitializeDriverRuntime(DrvRtPoolNxOptIn);
if (RtlIsNtDdiVersionAvailable(NTDDI_WIN7))
@ -206,6 +218,8 @@ VOID FspUnload(
ExDeleteResourceLite(&FspDeviceGlobalResource);
FspDriverObject = 0;
FspProcessBufferFinalize();
#pragma prefast(suppress:28175, "We are in DriverUnload: ok to access DriverName")
FSP_LEAVE_VOID("DriverName=\"%wZ\"",
&DriverObject->DriverName);

View File

@ -601,6 +601,14 @@ VOID FspIrpHookReset(PIRP Irp);
PVOID FspIrpHookContext(PVOID Context);
NTSTATUS FspIrpHookNext(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context);
/* process buffers */
#define FspProcessBufferSizeMax (64 * 1024)
NTSTATUS FspProcessBufferInitialize(VOID);
VOID FspProcessBufferFinalize(VOID);
VOID FspProcessBufferCollect(HANDLE ProcessId);
NTSTATUS FspProcessBufferAcquire(SIZE_T BufferSize, PVOID *PBufferCookie, PVOID *PBuffer);
VOID FspProcessBufferRelease(PVOID BufferCookie, PVOID Buffer);
/* IRP context */
#define FspIrpTimestampInfinity ((ULONG)-1L)
#define FspIrpTimestamp(Irp) \
@ -1064,6 +1072,7 @@ VOID FspFsvolDeviceFileRenameAcquireExclusive(PDEVICE_OBJECT DeviceObject);
VOID FspFsvolDeviceFileRenameSetOwner(PDEVICE_OBJECT DeviceObject, PVOID Owner);
VOID FspFsvolDeviceFileRenameRelease(PDEVICE_OBJECT DeviceObject);
VOID FspFsvolDeviceFileRenameReleaseOwner(PDEVICE_OBJECT DeviceObject, PVOID Owner);
BOOLEAN FspFsvolDeviceFileRenameIsAcquiredExclusive(PDEVICE_OBJECT DeviceObject);
VOID FspFsvolDeviceLockContextTable(PDEVICE_OBJECT DeviceObject);
VOID FspFsvolDeviceUnlockContextTable(PDEVICE_OBJECT DeviceObject);
NTSTATUS FspFsvolDeviceCopyContextList(PDEVICE_OBJECT DeviceObject,
@ -1112,6 +1121,35 @@ VOID FspDeviceGlobalUnlock(VOID)
//(FILE_DEVICE_DISK_FILE_SYSTEM == (DeviceObject)->DeviceType ?\
// STATUS_VOLUME_DISMOUNTED : STATUS_DEVICE_NOT_CONNECTED)
/* process buffers conditional usage */
static inline
BOOLEAN FspReadIrpShouldUseProcessBuffer(PIRP Irp, SIZE_T BufferSize)
{
ASSERT(0 != Irp);
#if DBG
return DEBUGTEST(50) ||
#else
return FspProcessBufferSizeMax >= BufferSize ||
#endif
FspFsvolDeviceExtension(IoGetCurrentIrpStackLocation(Irp)->DeviceObject)->
VolumeParams.AlwaysUseDoubleBuffering;
}
static inline
BOOLEAN FspWriteIrpShouldUseProcessBuffer(PIRP Irp, SIZE_T BufferSize)
{
ASSERT(0 != Irp);
#if DBG
return DEBUGTEST(50);
#else
return FspProcessBufferSizeMax >= BufferSize;
#endif
}
static inline
BOOLEAN FspQueryDirectoryIrpShouldUseProcessBuffer(PIRP Irp, SIZE_T BufferSize)
{
return FspReadIrpShouldUseProcessBuffer(Irp, BufferSize);
}
/* volume management */
#define FspVolumeTransactEarlyTimeout (1 * 10000ULL)
NTSTATUS FspVolumeCreate(

View File

@ -1243,6 +1243,50 @@ NTSTATUS FspFileNodeRenameCheck(PDEVICE_OBJECT FsvolDeviceObject, PIRP OplockIrp
}
}
/* flush and purge cleaned up but still open files affected by rename (github issue #45) */
{
PIRP TopLevelIrp;
IO_STATUS_BLOCK IoStatus;
/* reset the top-level IRP to avoid deadlock on the FileNodes' resources */
TopLevelIrp = IoGetTopLevelIrp();
IoSetTopLevelIrp(0);
/* enumerate in reverse order so that files are flushed before containing directories */
for (
DescendantFileNodeIndex = DescendantFileNodeCount - 1;
DescendantFileNodeCount > DescendantFileNodeIndex;
DescendantFileNodeIndex--)
{
DescendantFileNode = DescendantFileNodes[DescendantFileNodeIndex];
HasHandles = (UINT_PTR)DescendantFileNode & 1;
DescendantFileNode = (PVOID)((UINT_PTR)DescendantFileNode & ~7);
if (HasHandles)
continue;
if (CheckingOldName &&
(DescendantFileNode->FileName.Length <= FileName->Length ||
L'\\' != DescendantFileNode->FileName.Buffer[FileName->Length / sizeof(WCHAR)]))
continue;
if (MmDoesFileHaveUserWritableReferences(&DescendantFileNode->NonPaged->SectionObjectPointers))
continue;
/*
* There are no handles and no writable user mappings. [Ideally we would want to know
* that there are no handles and no user mappings, period. Is there an DDI/method to
* do that?] There may be a read-only user mapping, but in this case CcFlushCache
* should be a no-op and MmForceSectionClosed will fail (which is fine).
*/
ASSERT(DescendantFileNode != FileNode && DescendantFileNode->MainFileNode != FileNode);
FspCcFlushCache(&DescendantFileNode->NonPaged->SectionObjectPointers, 0, 0, &IoStatus);
MmForceSectionClosed(&DescendantFileNode->NonPaged->SectionObjectPointers, FALSE);
}
IoSetTopLevelIrp(TopLevelIrp);
}
/* break any Batch or Handle oplocks on descendants */
Result = STATUS_SUCCESS;
for (
@ -2073,6 +2117,8 @@ NTSTATUS FspFileDescResetDirectory(FSP_FILE_DESC *FileDesc,
NTSTATUS FspFileDescSetDirectoryMarker(FSP_FILE_DESC *FileDesc,
PUNICODE_STRING FileName)
{
PAGED_CODE();
if (&FileDesc->DirectoryMarker == FileName)
return STATUS_SUCCESS;

277
src/sys/psbuffer.c Normal file
View File

@ -0,0 +1,277 @@
/**
* @file sys/psbuffer.c
*
* @copyright 2015-2017 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
*
* You can redistribute it and/or modify it under the terms of the GNU
* General Public License version 3 as published by the Free Software
* Foundation.
*
* Licensees holding a valid commercial license may use this file in
* accordance with the commercial license agreement provided with the
* software.
*/
#include <sys/driver.h>
#define SafeGetCurrentProcessId() (PsGetProcessId(PsGetCurrentProcess()))
#define FspProcessBufferCountMax (2 >= FspProcessorCount ? 2 : (8 <= FspProcessorCount ? 8 : FspProcessorCount))
#define ProcessBufferBucketCount 61 /* are you going to have that many file systems? */
typedef struct _FSP_PROCESS_BUFFER_ITEM
{
struct _FSP_PROCESS_BUFFER_ITEM *DictNext;
struct _FSP_PROCESS_BUFFER_LIST_ENTRY *BufferList;
ULONG BufferCount;
HANDLE ProcessId;
} FSP_PROCESS_BUFFER_ITEM;
typedef struct _FSP_PROCESS_BUFFER_LIST_ENTRY
{
struct _FSP_PROCESS_BUFFER_LIST_ENTRY *Next;
PVOID Buffer;
} FSP_PROCESS_BUFFER_LIST_ENTRY;
static KSPIN_LOCK ProcessBufferLock;
static FSP_PROCESS_BUFFER_ITEM *ProcessBufferBuckets[ProcessBufferBucketCount];
static VOID FspProcessBufferNotifyRoutine(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create);
static inline FSP_PROCESS_BUFFER_ITEM *FspProcessBufferLookupItemAtDpcLevel(HANDLE ProcessId)
{
FSP_PROCESS_BUFFER_ITEM *Item = 0;
ULONG HashIndex = FspHashMixPointer(ProcessId) % ProcessBufferBucketCount;
for (FSP_PROCESS_BUFFER_ITEM *ItemX = ProcessBufferBuckets[HashIndex]; ItemX; ItemX = ItemX->DictNext)
if (ItemX->ProcessId == ProcessId)
{
Item = ItemX;
break;
}
return Item;
}
static inline VOID FspProcessBufferAddItemAtDpcLevel(FSP_PROCESS_BUFFER_ITEM *Item)
{
ULONG HashIndex = FspHashMixPointer(Item->ProcessId) % ProcessBufferBucketCount;
#if DBG
for (FSP_PROCESS_BUFFER_ITEM *ItemX = ProcessBufferBuckets[HashIndex]; ItemX; ItemX = ItemX->DictNext)
ASSERT(ItemX->ProcessId != Item->ProcessId);
#endif
Item->DictNext = ProcessBufferBuckets[HashIndex];
ProcessBufferBuckets[HashIndex] = Item;
}
static inline FSP_PROCESS_BUFFER_ITEM *FspProcessBufferRemoveItemAtDpcLevel(HANDLE ProcessId)
{
FSP_PROCESS_BUFFER_ITEM *Item = 0;
ULONG HashIndex = FspHashMixPointer(ProcessId) % ProcessBufferBucketCount;
for (FSP_PROCESS_BUFFER_ITEM **P = &ProcessBufferBuckets[HashIndex]; *P; P = &(*P)->DictNext)
if ((*P)->ProcessId == ProcessId)
{
Item = *P;
*P = (*P)->DictNext;
break;
}
return Item;
}
static inline VOID FspProcessBufferReuseEntry(HANDLE ProcessId,
FSP_PROCESS_BUFFER_LIST_ENTRY *BufferEntry)
{
KIRQL Irql;
FSP_PROCESS_BUFFER_ITEM *Item;
KeAcquireSpinLock(&ProcessBufferLock, &Irql);
Item = FspProcessBufferLookupItemAtDpcLevel(ProcessId);
if (0 != Item)
{
BufferEntry->Next = Item->BufferList;
Item->BufferList = BufferEntry;
}
KeReleaseSpinLock(&ProcessBufferLock, Irql);
if (0 == Item)
{
if (0 != BufferEntry->Buffer)
{
SIZE_T BufferSize = 0;
ZwFreeVirtualMemory(ZwCurrentProcess(), &BufferEntry->Buffer, &BufferSize, MEM_RELEASE);
}
FspFree(BufferEntry);
}
}
NTSTATUS FspProcessBufferInitialize(VOID)
{
KeInitializeSpinLock(&ProcessBufferLock);
return PsSetCreateProcessNotifyRoutine(FspProcessBufferNotifyRoutine, FALSE);
}
VOID FspProcessBufferFinalize(VOID)
{
PsSetCreateProcessNotifyRoutine(FspProcessBufferNotifyRoutine, TRUE);
}
static VOID FspProcessBufferNotifyRoutine(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create)
{
if (!Create)
FspProcessBufferCollect(ProcessId);
}
VOID FspProcessBufferCollect(HANDLE ProcessId)
{
KIRQL Irql;
FSP_PROCESS_BUFFER_ITEM *Item = 0;
KeAcquireSpinLock(&ProcessBufferLock, &Irql);
Item = FspProcessBufferRemoveItemAtDpcLevel(ProcessId);
KeReleaseSpinLock(&ProcessBufferLock, Irql);
if (0 != Item)
{
DEBUGLOG("pid=%ld", (ULONG)(UINT_PTR)ProcessId);
for (FSP_PROCESS_BUFFER_LIST_ENTRY *P = Item->BufferList, *Next; P; P = Next)
{
Next = P->Next;
FspFree(P);
}
FspFree(Item);
}
}
NTSTATUS FspProcessBufferAcquire(SIZE_T BufferSize, PVOID *PBufferCookie, PVOID *PBuffer)
{
if (FspProcessBufferSizeMax >= BufferSize)
{
HANDLE ProcessId = SafeGetCurrentProcessId();
KIRQL Irql;
FSP_PROCESS_BUFFER_ITEM *Item, *NewItem;
FSP_PROCESS_BUFFER_LIST_ENTRY *BufferEntry = 0;
BOOLEAN AllocNoReuse;
NTSTATUS Result;
KeAcquireSpinLock(&ProcessBufferLock, &Irql);
Item = FspProcessBufferLookupItemAtDpcLevel(ProcessId);
if (0 != Item)
{
BufferEntry = Item->BufferList;
if (0 != BufferEntry)
Item->BufferList = BufferEntry->Next;
}
AllocNoReuse = 0 == BufferEntry &&
(0 != Item && FspProcessBufferCountMax <= Item->BufferCount);
KeReleaseSpinLock(&ProcessBufferLock, Irql);
if (AllocNoReuse)
goto alloc_no_reuse;
if (0 == BufferEntry)
{
*PBufferCookie = 0;
*PBuffer = 0;
BufferEntry = FspAllocNonPaged(sizeof *BufferEntry);
if (0 == BufferEntry)
return STATUS_INSUFFICIENT_RESOURCES;
RtlZeroMemory(BufferEntry, sizeof *BufferEntry);
NewItem = FspAllocNonPaged(sizeof *NewItem);
if (0 == NewItem)
{
FspFree(BufferEntry);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(NewItem, sizeof *NewItem);
KeAcquireSpinLock(&ProcessBufferLock, &Irql);
Item = FspProcessBufferLookupItemAtDpcLevel(ProcessId);
if (0 == Item)
{
Item = NewItem;
NewItem = 0;
Item->BufferCount = 1;
Item->ProcessId = ProcessId;
FspProcessBufferAddItemAtDpcLevel(Item);
}
else if (FspProcessBufferCountMax > Item->BufferCount)
Item->BufferCount++;
else
AllocNoReuse = TRUE;
KeReleaseSpinLock(&ProcessBufferLock, Irql);
if (0 != NewItem)
FspFree(NewItem);
if (AllocNoReuse)
{
FspFree(BufferEntry);
goto alloc_no_reuse;
}
}
if (0 == BufferEntry->Buffer)
{
BufferSize = FspProcessBufferSizeMax;
Result = ZwAllocateVirtualMemory(ZwCurrentProcess(),
&BufferEntry->Buffer, 0, &BufferSize, MEM_COMMIT, PAGE_READWRITE);
if (!NT_SUCCESS(Result))
{
/* failed to allocate actual buffer; reuse BufferEntry */
FspProcessBufferReuseEntry(ProcessId, BufferEntry);
return Result;
}
}
*PBufferCookie = BufferEntry;
*PBuffer = BufferEntry->Buffer;
return STATUS_SUCCESS;
}
else
{
alloc_no_reuse:
*PBufferCookie = 0;
*PBuffer = 0;
return ZwAllocateVirtualMemory(ZwCurrentProcess(),
PBuffer, 0, &BufferSize, MEM_COMMIT, PAGE_READWRITE);
}
}
VOID FspProcessBufferRelease(PVOID BufferCookie, PVOID Buffer)
{
if (0 != BufferCookie)
{
HANDLE ProcessId = SafeGetCurrentProcessId();
FSP_PROCESS_BUFFER_LIST_ENTRY *BufferEntry = BufferCookie;
ASSERT(Buffer == BufferEntry->Buffer);
FspProcessBufferReuseEntry(ProcessId, BufferEntry);
}
else
{
SIZE_T BufferSize = 0;
ZwFreeVirtualMemory(ZwCurrentProcess(), &Buffer, &BufferSize, MEM_RELEASE);
}
}

View File

@ -44,10 +44,12 @@ enum
{
/* ReadNonCached */
RequestIrp = 0,
RequestCookie = 1,
RequestSafeMdl = 1,
RequestAddress = 2,
RequestProcess = 3,
};
FSP_FSCTL_STATIC_ASSERT(RequestCookie == RequestSafeMdl, "");
static NTSTATUS FspFsvolRead(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
@ -346,41 +348,70 @@ NTSTATUS FspFsvolReadPrepare(
{
PAGED_CODE();
NTSTATUS Result;
FSP_SAFE_MDL *SafeMdl = 0;
PVOID Address;
PEPROCESS Process;
/* create a "safe" MDL if necessary */
if (!FspSafeMdlCheck(Irp->MdlAddress))
if (FspReadIrpShouldUseProcessBuffer(Irp, Request->Req.Read.Length))
{
Result = FspSafeMdlCreate(Irp->MdlAddress, IoWriteAccess, &SafeMdl);
NTSTATUS Result;
PVOID Cookie;
PVOID Address;
PEPROCESS Process;
if (0 == MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority))
return STATUS_INSUFFICIENT_RESOURCES; /* something is seriously screwy! */
Result = FspProcessBufferAcquire(Request->Req.Read.Length, &Cookie, &Address);
if (!NT_SUCCESS(Result))
return Result;
}
/* map the MDL into user-mode */
Result = FspMapLockedPagesInUserMode(
0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress, &Address, 0);
if (!NT_SUCCESS(Result))
/* get a pointer to the current process so that we can release the buffer later */
Process = PsGetCurrentProcess();
ObReferenceObject(Process);
Request->Req.Read.Address = (UINT64)(UINT_PTR)Address;
FspIopRequestContext(Request, RequestCookie) = (PVOID)((UINT_PTR)Cookie | 1);
FspIopRequestContext(Request, RequestAddress) = Address;
FspIopRequestContext(Request, RequestProcess) = Process;
return STATUS_SUCCESS;
}
else
{
if (0 != SafeMdl)
FspSafeMdlDelete(SafeMdl);
NTSTATUS Result;
FSP_SAFE_MDL *SafeMdl = 0;
PVOID Address;
PEPROCESS Process;
return Result;
/* create a "safe" MDL if necessary */
if (!FspSafeMdlCheck(Irp->MdlAddress))
{
Result = FspSafeMdlCreate(Irp->MdlAddress, IoWriteAccess, &SafeMdl);
if (!NT_SUCCESS(Result))
return Result;
}
/* map the MDL into user-mode */
Result = FspMapLockedPagesInUserMode(
0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress, &Address, 0);
if (!NT_SUCCESS(Result))
{
if (0 != SafeMdl)
FspSafeMdlDelete(SafeMdl);
return Result;
}
/* get a pointer to the current process so that we can unmap the address later */
Process = PsGetCurrentProcess();
ObReferenceObject(Process);
Request->Req.Read.Address = (UINT64)(UINT_PTR)Address;
FspIopRequestContext(Request, RequestSafeMdl) = SafeMdl;
FspIopRequestContext(Request, RequestAddress) = Address;
FspIopRequestContext(Request, RequestProcess) = Process;
return STATUS_SUCCESS;
}
/* get a pointer to the current process so that we can unmap the address later */
Process = PsGetCurrentProcess();
ObReferenceObject(Process);
Request->Req.Read.Address = (UINT64)(UINT_PTR)Address;
FspIopRequestContext(Request, RequestSafeMdl) = SafeMdl;
FspIopRequestContext(Request, RequestAddress) = Address;
FspIopRequestContext(Request, RequestProcess) = Process;
return STATUS_SUCCESS;
}
NTSTATUS FspFsvolReadComplete(
@ -400,15 +431,36 @@ NTSTATUS FspFsvolReadComplete(
if (Response->IoStatus.Information > Request->Req.Read.Length)
FSP_RETURN(Result = STATUS_INTERNAL_ERROR);
FSP_SAFE_MDL *SafeMdl = FspIopRequestContext(Request, RequestSafeMdl);
if ((UINT_PTR)FspIopRequestContext(Request, RequestCookie) & 1)
{
PVOID Address = FspIopRequestContext(Request, RequestAddress);
PVOID SystemAddress = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
ASSERT(0 != Address);
try
{
RtlCopyMemory(SystemAddress, Address, Response->IoStatus.Information);
}
except (EXCEPTION_EXECUTE_HANDLER)
{
Result = GetExceptionCode();
Result = FsRtlIsNtstatusExpected(Result) ? STATUS_INVALID_USER_BUFFER : Result;
FSP_RETURN();
}
}
else
{
FSP_SAFE_MDL *SafeMdl = FspIopRequestContext(Request, RequestSafeMdl);
if (0 != SafeMdl)
FspSafeMdlCopyBack(SafeMdl);
}
PFILE_OBJECT FileObject = IrpSp->FileObject;
LARGE_INTEGER ReadOffset = IrpSp->Parameters.Read.ByteOffset;
BOOLEAN PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO);
BOOLEAN SynchronousIo = BooleanFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO);
if (0 != SafeMdl)
FspSafeMdlCopyBack(SafeMdl);
/* if we are top-level */
if (0 == FspIrpTopFlags(Irp))
{
@ -442,29 +494,56 @@ static VOID FspFsvolReadNonCachedRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, PV
PAGED_CODE();
PIRP Irp = Context[RequestIrp];
FSP_SAFE_MDL *SafeMdl = Context[RequestSafeMdl];
PVOID Address = Context[RequestAddress];
PEPROCESS Process = Context[RequestProcess];
if (0 != Address)
if ((UINT_PTR)Context[RequestCookie] & 1)
{
KAPC_STATE ApcState;
BOOLEAN Attach;
PVOID Cookie = (PVOID)((UINT_PTR)Context[RequestCookie] & ~1);
PVOID Address = Context[RequestAddress];
PEPROCESS Process = Context[RequestProcess];
ASSERT(0 != Process);
Attach = Process != PsGetCurrentProcess();
if (0 != Address)
{
KAPC_STATE ApcState;
BOOLEAN Attach;
if (Attach)
KeStackAttachProcess(Process, &ApcState);
MmUnmapLockedPages(Address, 0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress);
if (Attach)
KeUnstackDetachProcess(&ApcState);
ASSERT(0 != Process);
Attach = Process != PsGetCurrentProcess();
ObDereferenceObject(Process);
if (Attach)
KeStackAttachProcess(Process, &ApcState);
FspProcessBufferRelease(Cookie, Address);
if (Attach)
KeUnstackDetachProcess(&ApcState);
ObDereferenceObject(Process);
}
}
else
{
FSP_SAFE_MDL *SafeMdl = Context[RequestSafeMdl];
PVOID Address = Context[RequestAddress];
PEPROCESS Process = Context[RequestProcess];
if (0 != SafeMdl)
FspSafeMdlDelete(SafeMdl);
if (0 != Address)
{
KAPC_STATE ApcState;
BOOLEAN Attach;
ASSERT(0 != Process);
Attach = Process != PsGetCurrentProcess();
if (Attach)
KeStackAttachProcess(Process, &ApcState);
MmUnmapLockedPages(Address, 0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress);
if (Attach)
KeUnstackDetachProcess(&ApcState);
ObDereferenceObject(Process);
}
if (0 != SafeMdl)
FspSafeMdlDelete(SafeMdl);
}
if (0 != Irp)
{

View File

@ -29,6 +29,8 @@ NTSTATUS FspStatisticsCopy(FSP_STATISTICS *Statistics, PVOID Buffer, PULONG PLen
NTSTATUS FspStatisticsCreate(FSP_STATISTICS **PStatistics)
{
PAGED_CODE();
*PStatistics = FspAllocNonPaged(sizeof(FSP_STATISTICS) * FspProcessorCount);
if (0 == *PStatistics)
return STATUS_INSUFFICIENT_RESOURCES;
@ -49,11 +51,15 @@ NTSTATUS FspStatisticsCreate(FSP_STATISTICS **PStatistics)
VOID FspStatisticsDelete(FSP_STATISTICS *Statistics)
{
PAGED_CODE();
FspFree(Statistics);
}
NTSTATUS FspStatisticsCopy(FSP_STATISTICS *Statistics, PVOID Buffer, PULONG PLength)
{
PAGED_CODE();
NTSTATUS Result;
ULONG StatLength;

View File

@ -171,6 +171,16 @@ static NTSTATUS FspVolumeCreateNoLock(
}
VolumeParams.FileSystemName[sizeof VolumeParams.FileSystemName / sizeof(WCHAR) - 1] = L'\0';
#if !DBG
/*
* In Release builds we hardcode AlwaysUseDoubleBuffering for Reads as we do not want someone
* to use WinFsp to crash Windows.
*
* See http://www.osronline.com/showthread.cfm?link=282037
*/
VolumeParams.AlwaysUseDoubleBuffering = 1;
#endif
/* create volume guid */
Result = FspCreateGuid(&Guid);
if (!NT_SUCCESS(Result))

View File

@ -45,10 +45,12 @@ enum
{
/* WriteNonCached */
RequestIrp = 0,
RequestCookie = 1,
RequestSafeMdl = 1,
RequestAddress = 2,
RequestProcess = 3,
};
FSP_FSCTL_STATIC_ASSERT(RequestCookie == RequestSafeMdl, "");
static NTSTATUS FspFsvolWrite(
PDEVICE_OBJECT FsvolDeviceObject, PIRP Irp, PIO_STACK_LOCATION IrpSp)
@ -416,41 +418,86 @@ NTSTATUS FspFsvolWritePrepare(
{
PAGED_CODE();
NTSTATUS Result;
FSP_SAFE_MDL *SafeMdl = 0;
PVOID Address;
PEPROCESS Process;
/* create a "safe" MDL if necessary */
if (!FspSafeMdlCheck(Irp->MdlAddress))
if (FspWriteIrpShouldUseProcessBuffer(Irp, Request->Req.Write.Length))
{
Result = FspSafeMdlCreate(Irp->MdlAddress, IoReadAccess, &SafeMdl);
NTSTATUS Result;
PVOID Cookie;
PVOID Address;
PEPROCESS Process;
PVOID SystemAddress = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
if (0 == SystemAddress)
return STATUS_INSUFFICIENT_RESOURCES; /* something is seriously screwy! */
Result = FspProcessBufferAcquire(Request->Req.Write.Length, &Cookie, &Address);
if (!NT_SUCCESS(Result))
return Result;
}
/* map the MDL into user-mode */
Result = FspMapLockedPagesInUserMode(
0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress, &Address, FspMvMdlMappingNoWrite);
if (!NT_SUCCESS(Result))
ASSERT(0 != Address);
try
{
RtlCopyMemory(Address, SystemAddress, Request->Req.Write.Length);
}
except (EXCEPTION_EXECUTE_HANDLER)
{
Result = GetExceptionCode();
Result = FsRtlIsNtstatusExpected(Result) ? STATUS_INVALID_USER_BUFFER : Result;
FspProcessBufferRelease(Cookie, Address);
return Result;
}
/* get a pointer to the current process so that we can release the buffer later */
Process = PsGetCurrentProcess();
ObReferenceObject(Process);
Request->Req.Write.Address = (UINT64)(UINT_PTR)Address;
FspIopRequestContext(Request, RequestCookie) = (PVOID)((UINT_PTR)Cookie | 1);
FspIopRequestContext(Request, RequestAddress) = Address;
FspIopRequestContext(Request, RequestProcess) = Process;
return STATUS_SUCCESS;
}
else
{
if (0 != SafeMdl)
FspSafeMdlDelete(SafeMdl);
NTSTATUS Result;
FSP_SAFE_MDL *SafeMdl = 0;
PVOID Address;
PEPROCESS Process;
return Result;
/* create a "safe" MDL if necessary */
if (!FspSafeMdlCheck(Irp->MdlAddress))
{
Result = FspSafeMdlCreate(Irp->MdlAddress, IoReadAccess, &SafeMdl);
if (!NT_SUCCESS(Result))
return Result;
}
/* map the MDL into user-mode */
Result = FspMapLockedPagesInUserMode(
0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress, &Address, FspMvMdlMappingNoWrite);
if (!NT_SUCCESS(Result))
{
if (0 != SafeMdl)
FspSafeMdlDelete(SafeMdl);
return Result;
}
/* get a pointer to the current process so that we can unmap the address later */
Process = PsGetCurrentProcess();
ObReferenceObject(Process);
Request->Req.Write.Address = (UINT64)(UINT_PTR)Address;
FspIopRequestContext(Request, RequestSafeMdl) = SafeMdl;
FspIopRequestContext(Request, RequestAddress) = Address;
FspIopRequestContext(Request, RequestProcess) = Process;
return STATUS_SUCCESS;
}
/* get a pointer to the current process so that we can unmap the address later */
Process = PsGetCurrentProcess();
ObReferenceObject(Process);
Request->Req.Write.Address = (UINT64)(UINT_PTR)Address;
FspIopRequestContext(Request, RequestSafeMdl) = SafeMdl;
FspIopRequestContext(Request, RequestAddress) = Address;
FspIopRequestContext(Request, RequestProcess) = Process;
return STATUS_SUCCESS;
}
NTSTATUS FspFsvolWriteComplete(
@ -524,29 +571,56 @@ static VOID FspFsvolWriteNonCachedRequestFini(FSP_FSCTL_TRANSACT_REQ *Request, P
PAGED_CODE();
PIRP Irp = Context[RequestIrp];
FSP_SAFE_MDL *SafeMdl = Context[RequestSafeMdl];
PVOID Address = Context[RequestAddress];
PEPROCESS Process = Context[RequestProcess];
if (0 != Address)
if ((UINT_PTR)Context[RequestCookie] & 1)
{
KAPC_STATE ApcState;
BOOLEAN Attach;
PVOID Cookie = (PVOID)((UINT_PTR)Context[RequestCookie] & ~1);
PVOID Address = Context[RequestAddress];
PEPROCESS Process = Context[RequestProcess];
ASSERT(0 != Process);
Attach = Process != PsGetCurrentProcess();
if (0 != Address)
{
KAPC_STATE ApcState;
BOOLEAN Attach;
if (Attach)
KeStackAttachProcess(Process, &ApcState);
MmUnmapLockedPages(Address, 0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress);
if (Attach)
KeUnstackDetachProcess(&ApcState);
ASSERT(0 != Process);
Attach = Process != PsGetCurrentProcess();
ObDereferenceObject(Process);
if (Attach)
KeStackAttachProcess(Process, &ApcState);
FspProcessBufferRelease(Cookie, Address);
if (Attach)
KeUnstackDetachProcess(&ApcState);
ObDereferenceObject(Process);
}
}
else
{
FSP_SAFE_MDL *SafeMdl = Context[RequestSafeMdl];
PVOID Address = Context[RequestAddress];
PEPROCESS Process = Context[RequestProcess];
if (0 != SafeMdl)
FspSafeMdlDelete(SafeMdl);
if (0 != Address)
{
KAPC_STATE ApcState;
BOOLEAN Attach;
ASSERT(0 != Process);
Attach = Process != PsGetCurrentProcess();
if (Attach)
KeStackAttachProcess(Process, &ApcState);
MmUnmapLockedPages(Address, 0 != SafeMdl ? SafeMdl->Mdl : Irp->MdlAddress);
if (Attach)
KeUnstackDetachProcess(&ApcState);
ObDereferenceObject(Process);
}
if (0 != SafeMdl)
FspSafeMdlDelete(SafeMdl);
}
if (0 != Irp)
{

View File

@ -1,6 +1,7 @@
@echo off
setlocal
setlocal EnableDelayedExpansion
set MsiName="WinFsp - Windows File System Proxy"
set CrossCert="%~dp0DigiCert High Assurance EV Root CA.crt"
@ -19,6 +20,7 @@ if not X%SignedPackage%==X (
if not exist "%~dp0..\build\VStudio\build\%Configuration%\winfsp-*.msi" (echo previous build not found >&2 & exit /b 1)
if not exist "%SignedPackage%" (echo signed package not found >&2 & exit /b 1)
del "%~dp0..\build\VStudio\build\%Configuration%\winfsp-*.msi"
if exist "%~dp0..\build\VStudio\build\%Configuration%\winfsp.*.nupkg" del "%~dp0..\build\VStudio\build\%Configuration%\winfsp.*.nupkg"
for /R "%SignedPackage%" %%f in (*.sys) do (
copy "%%f" "%~dp0..\build\VStudio\build\%Configuration%" >nul
)
@ -81,6 +83,16 @@ for %%f in (build\%Configuration%\winfsp-*.msi) do (
if not %signfail%==0 echo SIGNING FAILED! The product has been successfully built, but not signed.
where /q choco.exe
if %ERRORLEVEL% equ 0 (
for %%f in (build\%Configuration%\winfsp-*.msi) do set Version=%%~nf
set Version=!Version:winfsp-=!
copy ..\choco\* build\%Configuration%
choco pack build\%Configuration%\winfsp.nuspec --version=!Version! --outputdirectory=build\%Configuration%
if errorlevel 1 goto fail
)
exit /b 0
:fail

View File

@ -508,7 +508,8 @@ L:
"%ProjRoot%\build\VStudio\build\%Configuration%\winfsp-tests-%1.exe" ^
--external --resilient --case-insensitive-cmp --share-prefix="\passthrough\%TMP::=$%\passthrough-%1\test" ^
-create_allocation_test -getfileinfo_name_test -rename_flipflop_test -rename_mmap_test -reparse* -stream*
-create_allocation_test -getfileinfo_name_test -rename_flipflop_test -rename_mmap_test -exec_rename_dir_test ^
-reparse* -stream*
if !ERRORLEVEL! neq 0 set SamplePassthroughExit=1
popd
@ -534,7 +535,7 @@ call %ProjRoot%\tools\build-sample %Configuration% %1 passthrough-fuse "%TMP%\pa
if !ERRORLEVEL! neq 0 goto fail
mkdir "%TMP%\passthrough-fuse-%1\test"
call "%ProjRoot%\tools\fsreg" passthrough-fuse "%TMP%\passthrough-fuse-%1\build\%Configuration%\passthrough-fuse-%1.exe" ^
"-ouid=65792,gid=65792 -oCaseInsensitiveSearch --VolumePrefix=%%%%1 %%%%2" "D:P(A;;RPWPLC;;;WD)"
"-ouid=65792,gid=65792 --VolumePrefix=%%%%1 %%%%2" "D:P(A;;RPWPLC;;;WD)"
echo net use L: "\\passthrough-fuse\%TMP::=$%\passthrough-fuse-%1\test"
net use L: "\\passthrough-fuse\%TMP::=$%\passthrough-fuse-%1\test"
if !ERRORLEVEL! neq 0 goto fail
@ -547,7 +548,7 @@ L:
"%ProjRoot%\build\VStudio\build\%Configuration%\winfsp-tests-%1.exe" ^
--external --resilient --case-insensitive-cmp --share-prefix="\passthrough-fuse\%TMP::=$%\passthrough-fuse-%1\test" ^
-create_allocation_test -create_notraverse_test -create_backup_test -create_restore_test -create_namelen_test ^
-getfileinfo_name_test -setfileinfo_test -delete_access_test -delete_mmap_test -rename_flipflop_test -rename_mmap_test -setsecurity_test ^
-getfileinfo_name_test -setfileinfo_test -delete_access_test -delete_mmap_test -rename_flipflop_test -rename_mmap_test -setsecurity_test -exec_rename_dir_test ^
-reparse* -stream*
if !ERRORLEVEL! neq 0 set SamplePassthroughExit=1

View File

@ -106,7 +106,7 @@ NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv)
if (0 != DebugLogFile)
{
if (0 == wcscmp(L"-", DebugLogFile))
DebugLogHandle = GetStdHandle(STD_OUTPUT_HANDLE);
DebugLogHandle = GetStdHandle(STD_ERROR_HANDLE);
else
DebugLogHandle = CreateFileW(
DebugLogFile,
@ -184,7 +184,7 @@ usage:
"\n"
"options:\n"
" -d DebugFlags [-1: enable all debug logs]\n"
" -D DebugLogFile [file path; use - for stdout]\n"
" -D DebugLogFile [file path; use - for stderr]\n"
" -i [case insensitive file system]\n"
" -t FileInfoTimeout [millis]\n"
" -n MaxFileNodes\n"

View File

@ -45,9 +45,11 @@ FSP_FSCTL_STATIC_ASSERT(MEMFS_MAX_PATH > MAX_PATH,
/*
* Define the DEBUG_BUFFER_CHECK macro on Windows 8 or above. This includes
* a check for the Write buffer to ensure that it is read-only.
*
* Since ProcessBuffer support in the FSD, this is no longer a guarantee.
*/
#if !defined(NDEBUG)
#define DEBUG_BUFFER_CHECK
//#define DEBUG_BUFFER_CHECK
#endif
#define MEMFS_SECTOR_SIZE 512

View File

@ -187,7 +187,11 @@ static int ptfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, fus
errno = 0;
if (0 == (de = readdir(dirp)))
break;
#if defined(_WIN64) || defined(_WIN32)
if (0 != filler(buf, de->d_name, &de->d_stat, 0))
#else
if (0 != filler(buf, de->d_name, 0, 0))
#endif
return -ENOMEM;
}
@ -201,6 +205,21 @@ static int ptfs_releasedir(const char *path, struct fuse_file_info *fi)
return -1 != closedir(dirp) ? 0 : -errno;
}
static void *ptfs_init(struct fuse_conn_info *conn)
{
#if defined(_WIN64) || defined(_WIN32)
#if defined(FSP_FUSE_CAP_READDIR_PLUS)
conn->want |= (conn->capable & FSP_FUSE_CAP_READDIR_PLUS);
#endif
#if defined(FSP_FUSE_CAP_CASE_INSENSITIVE)
conn->want |= (conn->capable & FSP_FUSE_CAP_CASE_INSENSITIVE);
#endif
#endif
return fuse_get_context()->private_data;
}
static int ptfs_create(const char *path, fuse_mode_t mode, struct fuse_file_info *fi)
{
ptfs_impl_fullpath(path);
@ -254,7 +273,7 @@ static struct fuse_operations ptfs_ops =
ptfs_readdir,
ptfs_releasedir,
0, //fsyncdir
0, //init
ptfs_init,
0, //destroy
0, //access
ptfs_create,

View File

@ -115,7 +115,7 @@ int statvfs(const char *path, struct fuse_statvfs *stbuf)
memset(stbuf, 0, sizeof *stbuf);
stbuf->f_bsize = SectorsPerCluster * BytesPerSector;
stbuf->f_frsize = BytesPerSector;
stbuf->f_frsize = SectorsPerCluster * BytesPerSector;
stbuf->f_blocks = TotalNumberOfClusters;
stbuf->f_bfree = NumberOfFreeClusters;
stbuf->f_bavail = TotalNumberOfClusters;
@ -388,6 +388,8 @@ void rewinddir(DIR *dirp)
struct dirent *readdir(DIR *dirp)
{
WIN32_FIND_DATAA FindData;
UINT64 CreationTime, LastAccessTime, LastWriteTime;
struct fuse_stat *stbuf = &dirp->de.d_stat;
if (INVALID_HANDLE_VALUE == dirp->fh)
{
@ -405,6 +407,24 @@ struct dirent *readdir(DIR *dirp)
}
}
CreationTime = ((PLARGE_INTEGER)(&FindData.ftCreationTime))->QuadPart - 116444736000000000;
LastAccessTime = ((PLARGE_INTEGER)(&FindData.ftLastAccessTime))->QuadPart - 116444736000000000;
LastWriteTime = ((PLARGE_INTEGER)(&FindData.ftLastWriteTime))->QuadPart - 116444736000000000;
memset(stbuf, 0, sizeof *stbuf);
stbuf->st_mode = 0777 |
((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 0040000/* S_IFDIR */ : 0);
stbuf->st_nlink = 1;
stbuf->st_size = ((UINT64)FindData.nFileSizeHigh << 32) | ((UINT64)FindData.nFileSizeLow);
stbuf->st_atim.tv_sec = LastAccessTime / 10000000;
stbuf->st_atim.tv_nsec = LastAccessTime % 10000000 * 100;
stbuf->st_mtim.tv_sec = LastWriteTime / 10000000;
stbuf->st_mtim.tv_nsec = LastWriteTime % 10000000 * 100;
stbuf->st_ctim.tv_sec = LastWriteTime / 10000000;
stbuf->st_ctim.tv_nsec = LastWriteTime % 10000000 * 100;
stbuf->st_birthtim.tv_sec = CreationTime / 10000000;
stbuf->st_birthtim.tv_nsec = CreationTime % 10000000 * 100;
strcpy(dirp->de.d_name, FindData.cFileName);
return &dirp->de;

View File

@ -31,6 +31,7 @@
typedef struct _DIR DIR;
struct dirent
{
struct fuse_stat d_stat;
char d_name[255];
};

View File

@ -846,7 +846,7 @@ static NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv)
if (0 != DebugLogFile)
{
if (0 == wcscmp(L"-", DebugLogFile))
DebugLogHandle = GetStdHandle(STD_OUTPUT_HANDLE);
DebugLogHandle = GetStdHandle(STD_ERROR_HANDLE);
else
DebugLogHandle = CreateFileW(
DebugLogFile,
@ -903,7 +903,7 @@ usage:
"\n"
"options:\n"
" -d DebugFlags [-1: enable all debug logs]\n"
" -D DebugLogFile [file path; use - for stdout]\n"
" -D DebugLogFile [file path; use - for stderr]\n"
" -u \\Server\\Share [UNC prefix (single backslash)]\n"
" -p Directory [directory to expose as pass through file system]\n"
" -m MountPoint [X:|*|directory]\n";

View File

@ -0,0 +1,352 @@
/**
* @file exec-test.c
*
* @copyright 2015-2017 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
*
* You can redistribute it and/or modify it under the terms of the GNU
* General Public License version 3 as published by the Free Software
* Foundation.
*
* Licensees holding a valid commercial license may use this file in
* accordance with the commercial license agreement provided with the
* software.
*/
#include <winfsp/winfsp.h>
#include <tlib/testsuite.h>
#include <strsafe.h>
#include "memfs.h"
#include "winfsp-tests.h"
static NTSTATUS WriteResource(
HANDLE Handle, HANDLE Module, PWSTR ResourceName, PULONG PBytesTransferred)
{
HRSRC Resource;
HGLOBAL ResourceGlob;
PVOID ResourceData;
DWORD ResourceSize;
if ((Resource = FindResourceW(Module, ResourceName, RT_RCDATA)) &&
(ResourceGlob = LoadResource(Module, Resource)) &&
(ResourceData = LockResource(ResourceGlob)) &&
(ResourceSize = SizeofResource(Module, Resource)) &&
(WriteFile(Handle, ResourceData, ResourceSize, PBytesTransferred, 0)))
return STATUS_SUCCESS;
else
return FspNtStatusFromWin32(GetLastError());
}
static NTSTATUS ExtractHelperProgram(PWSTR FileName)
{
HANDLE Handle;
ULONG BytesTransferred;
NTSTATUS Result;
Handle = CreateFileW(FileName,
FILE_WRITE_DATA, FILE_SHARE_WRITE, 0,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (INVALID_HANDLE_VALUE == Handle)
return FspNtStatusFromWin32(GetLastError());
Result = WriteResource(
Handle,
0,
#if defined(_WIN64)
L"winfsp-tests-helper-x64.exe",
#elif defined(_WIN32)
L"winfsp-tests-helper-x86.exe",
#else
#error
#endif
&BytesTransferred);
CloseHandle(Handle);
return Result;
}
static NTSTATUS CreateHelperProcess(PWSTR FileName, ULONG Timeout, PHANDLE PProcess)
{
HANDLE Event;
SECURITY_ATTRIBUTES EventAttributes;
WCHAR CommandLine[MAX_PATH + 64];
STARTUPINFOW StartupInfo;
PROCESS_INFORMATION ProcessInfo;
DWORD WaitResult;
NTSTATUS Result;
memset(&EventAttributes, 0, sizeof EventAttributes);
EventAttributes.nLength = sizeof EventAttributes;
EventAttributes.bInheritHandle = TRUE;
Event = CreateEventW(&EventAttributes, TRUE, FALSE, 0);
if (0 == Event)
return FspNtStatusFromWin32(GetLastError());
StringCbPrintfW(CommandLine, sizeof CommandLine, L"\"%s\" %lx %lx",
FileName, (ULONG)(UINT_PTR)Event, Timeout);
memset(&StartupInfo, 0, sizeof StartupInfo);
StartupInfo.cb = sizeof StartupInfo;
// !!!: need hook
if (!CreateProcessW(FileName, CommandLine, 0, 0, TRUE, 0, 0, 0, &StartupInfo, &ProcessInfo))
{
Result = FspNtStatusFromWin32(GetLastError());
CloseHandle(Event);
return Result;
}
WaitResult = WaitForSingleObject(Event, 3000);
if (WaitResult == WAIT_FAILED)
Result = FspNtStatusFromWin32(GetLastError());
else if (WaitResult == WAIT_TIMEOUT)
Result = STATUS_UNSUCCESSFUL;
else
Result = STATUS_SUCCESS;
CloseHandle(Event);
CloseHandle(ProcessInfo.hThread);
if (!NT_SUCCESS(Result))
CloseHandle(ProcessInfo.hProcess);
else
*PProcess = ProcessInfo.hProcess;
return Result;
}
static VOID ExecHelper(PWSTR FileName, ULONG Timeout, PHANDLE PProcess)
{
NTSTATUS Result;
Result = ExtractHelperProgram(FileName);
ASSERT(NT_SUCCESS(Result));
Result = CreateHelperProcess(FileName, Timeout, PProcess);
ASSERT(NT_SUCCESS(Result));
}
static VOID WaitHelper(HANDLE Process, ULONG Timeout)
{
DWORD ExitCode;
ASSERT(WAIT_OBJECT_0 == WaitForSingleObject(Process, Timeout + 1000));
ASSERT(GetExitCodeProcess(Process, &ExitCode));
ASSERT(0 == ExitCode);
ASSERT(CloseHandle(Process));
}
static void exec_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
WCHAR FilePath[MAX_PATH];
HANDLE Process;
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\helper.exe",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
ExecHelper(FilePath, 0, &Process);
WaitHelper(Process, 0);
ASSERT(DeleteFileW(FilePath));
memfs_stop(memfs);
}
static void exec_test(void)
{
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH];
GetTestDirectory(DirBuf);
exec_dotest(-1, DirBuf, 0);
}
if (WinFspDiskTests)
{
exec_dotest(MemfsDisk, 0, 0);
exec_dotest(MemfsDisk, 0, 1000);
}
if (WinFspNetTests)
{
exec_dotest(MemfsNet, L"\\\\memfs\\share", 0);
exec_dotest(MemfsNet, L"\\\\memfs\\share", 1000);
}
}
static void exec_delete_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
WCHAR FilePath[MAX_PATH];
HANDLE Process;
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\helper.exe",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
ExecHelper(FilePath, 1000, &Process);
ASSERT(!DeleteFileW(FilePath));
ASSERT(ERROR_ACCESS_DENIED == GetLastError());
WaitHelper(Process, 1000);
ASSERT(DeleteFileW(FilePath));
memfs_stop(memfs);
}
static void exec_delete_test(void)
{
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH];
GetTestDirectory(DirBuf);
exec_delete_dotest(-1, DirBuf, 0);
}
if (WinFspDiskTests)
{
exec_delete_dotest(MemfsDisk, 0, 0);
exec_delete_dotest(MemfsDisk, 0, 1000);
}
if (WinFspNetTests)
{
exec_delete_dotest(MemfsNet, L"\\\\memfs\\share", 0);
exec_delete_dotest(MemfsNet, L"\\\\memfs\\share", 1000);
}
}
static void exec_rename_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
WCHAR FilePath[MAX_PATH], File2Path[MAX_PATH], File3Path[MAX_PATH];
HANDLE Process;
HANDLE Handle;
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\helper.exe",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
StringCbPrintfW(File2Path, sizeof File2Path, L"%s%s\\helper2.exe",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
StringCbPrintfW(File3Path, sizeof File3Path, L"%s%s\\helper3.exe",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
Handle = CreateFileW(File3Path,
FILE_WRITE_DATA, FILE_SHARE_WRITE, 0,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
CloseHandle(Handle);
ExecHelper(FilePath, 1000, &Process);
ASSERT(MoveFileExW(FilePath, File2Path, MOVEFILE_REPLACE_EXISTING));
ASSERT(MoveFileExW(File2Path, FilePath, MOVEFILE_REPLACE_EXISTING));
ASSERT(!MoveFileExW(File3Path, FilePath, MOVEFILE_REPLACE_EXISTING));
ASSERT(ERROR_ACCESS_DENIED == GetLastError());
WaitHelper(Process, 1000);
ASSERT(MoveFileExW(File3Path, FilePath, MOVEFILE_REPLACE_EXISTING));
ASSERT(DeleteFileW(FilePath));
memfs_stop(memfs);
}
static void exec_rename_test(void)
{
if (OptShareName)
return;
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH];
GetTestDirectory(DirBuf);
exec_rename_dotest(-1, DirBuf, 0);
}
if (WinFspDiskTests)
{
exec_rename_dotest(MemfsDisk, 0, 0);
exec_rename_dotest(MemfsDisk, 0, 1000);
}
if (WinFspNetTests)
{
exec_rename_dotest(MemfsNet, L"\\\\memfs\\share", 0);
exec_rename_dotest(MemfsNet, L"\\\\memfs\\share", 1000);
}
}
static void exec_rename_dir_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
WCHAR Dir1Path[MAX_PATH], Dir2Path[MAX_PATH], FilePath[MAX_PATH];
HANDLE Process;
StringCbPrintfW(Dir1Path, sizeof Dir1Path, L"%s%s\\dir1",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
StringCbPrintfW(Dir2Path, sizeof Dir2Path, L"%s%s\\dir2",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
StringCbPrintfW(FilePath, sizeof FilePath, L"%s%s\\dir1\\helper.exe",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
ASSERT(CreateDirectoryW(Dir1Path, 0));
ExecHelper(FilePath, 1000, &Process);
ASSERT(MoveFileExW(Dir1Path, Dir2Path, MOVEFILE_REPLACE_EXISTING));
ASSERT(MoveFileExW(Dir2Path, Dir1Path, MOVEFILE_REPLACE_EXISTING));
WaitHelper(Process, 1000);
ASSERT(DeleteFileW(FilePath));
ASSERT(RemoveDirectoryW(Dir1Path));
memfs_stop(memfs);
}
static void exec_rename_dir_test(void)
{
if (OptShareName)
return;
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH];
GetTestDirectory(DirBuf);
exec_rename_dir_dotest(-1, DirBuf, 0);
}
if (WinFspDiskTests)
{
exec_rename_dir_dotest(MemfsDisk, 0, 0);
exec_rename_dir_dotest(MemfsDisk, 0, 1000);
}
if (WinFspNetTests)
{
exec_rename_dir_dotest(MemfsNet, L"\\\\memfs\\share", 0);
exec_rename_dir_dotest(MemfsNet, L"\\\\memfs\\share", 1000);
}
}
void exec_tests(void)
{
TEST(exec_test);
TEST(exec_delete_test);
if (!OptShareName)
TEST(exec_rename_test);
if (!OptShareName)
TEST(exec_rename_dir_test);
}

View File

@ -0,0 +1,13 @@
@echo off
setlocal
cd %~dp0
call "%VS140COMNTOOLS%\..\..\VC\vcvarsall.bat" x64
cl /Fewinfsp-tests-helper-x64.exe /MT /W2 winfsp-tests-helper.c kernel32.lib shell32.lib /link /subsystem:console /nodefaultlib
del *.obj
call "%VS140COMNTOOLS%\..\..\VC\vcvarsall.bat" x86
cl /Fewinfsp-tests-helper-x86.exe /MT /W2 winfsp-tests-helper.c kernel32.lib shell32.lib /link /subsystem:console
del *.obj

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,104 @@
/**
* @file winfsp-tests-helper.c
*
* @copyright 2015-2017 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
*
* You can redistribute it and/or modify it under the terms of the GNU
* General Public License version 3 as published by the Free Software
* Foundation.
*
* Licensees holding a valid commercial license may use this file in
* accordance with the commercial license agreement provided with the
* software.
*/
#include <windows.h>
/* based on src/dll/fuse/fuse_opt.c */
static long long wcstoint(const wchar_t *p, int base, int is_signed)
{
long long v;
int maxdig, maxalp, sign = +1;
if (is_signed)
{
if ('+' == *p)
p++;
else if ('-' == *p)
p++, sign = -1;
}
if (0 == base)
{
if ('0' == *p)
{
p++;
if ('x' == *p || 'X' == *p)
{
p++;
base = 16;
}
else
base = 8;
}
else
{
base = 10;
}
}
maxdig = 10 < base ? '9' : (base - 1) + '0';
maxalp = 10 < base ? (base - 1 - 10) + 'a' : 0;
for (v = 0; *p; p++)
{
int c = *p;
if ('0' <= c && c <= maxdig)
v = base * v + (c - '0');
else
{
c |= 0x20;
if ('a' <= c && c <= maxalp)
v = base * v + (c - 'a') + 10;
else
break;
}
}
return sign * v;
}
int wmain(int argc, wchar_t **argv)
{
HANDLE Event;
ULONG Timeout;
if (argc != 3)
return 1;
Event = (HANDLE)(UINT_PTR)wcstoint(argv[1], 16, 0);
Timeout = wcstoint(argv[2], 16, 0);
SetEvent(Event);
CloseHandle(Event);
Sleep(Timeout);
return 0;
}
void wmainCRTStartup(void)
{
DWORD Argc;
PWSTR *Argv;
Argv = CommandLineToArgvW(GetCommandLineW(), &Argc);
if (0 == Argv)
ExitProcess(GetLastError());
ExitProcess(wmain(Argc, Argv));
}

View File

@ -0,0 +1,2 @@
winfsp-tests-helper-x64.exe RCDATA "winfsp-tests-helper-x64.exe"
winfsp-tests-helper-x86.exe RCDATA "winfsp-tests-helper-x86.exe"

View File

@ -427,3 +427,35 @@ BOOL WINAPI HookSetCurrentDirectoryW(
MaybeAdjustTraversePrivilege(TRUE);
return Success;
}
BOOL WINAPI HookCreateProcessW(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation)
{
WCHAR FileNameBuf[FILENAMEBUF_SIZE];
BOOL Success;
PrepareFileName(lpApplicationName, FileNameBuf);
MaybeAdjustTraversePrivilege(FALSE);
Success = CreateProcessW(FileNameBuf,
lpCommandLine, /* we should probably change this as well */
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation);
MaybeAdjustTraversePrivilege(TRUE);
return Success;
}

View File

@ -19,6 +19,7 @@
#include <tlib/testsuite.h>
#include <sddl.h>
#include <strsafe.h>
#include <time.h>
#include "memfs.h"
#include "winfsp-tests.h"
@ -604,6 +605,157 @@ void delete_mmap_test(void)
}
}
static void delete_standby_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
HANDLE Handle, Mapping0, Mapping1;
PUINT8 MappedView0, MappedView1;
BOOL Success;
WCHAR Dir1Path[MAX_PATH];
WCHAR File0Path[MAX_PATH];
WCHAR File1Path[MAX_PATH];
SYSTEM_INFO SystemInfo;
unsigned seed = (unsigned)time(0);
GetSystemInfo(&SystemInfo);
StringCbPrintfW(Dir1Path, sizeof Dir1Path, L"%s%s\\dir1",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
StringCbPrintfW(File0Path, sizeof File0Path, L"%s%s\\dir1\\file0",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
StringCbPrintfW(File1Path, sizeof File1Path, L"%s%s\\dir1\\file1",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
Success = CreateDirectoryW(Dir1Path, 0);
ASSERT(Success);
srand(seed);
Handle = CreateFileW(File0Path,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
Mapping0 = CreateFileMappingW(Handle, 0, PAGE_READWRITE,
0, 16 * SystemInfo.dwAllocationGranularity, 0);
ASSERT(0 != Mapping0);
Success = CloseHandle(Handle);
ASSERT(Success);
MappedView0 = MapViewOfFile(Mapping0, FILE_MAP_ALL_ACCESS, 0, 0, 0);
ASSERT(0 != MappedView0);
for (PUINT8 P = MappedView0, EndP = P + 16 * SystemInfo.dwAllocationGranularity; EndP > P; P++)
*P = rand() & 0xff;
Success = UnmapViewOfFile(MappedView0);
ASSERT(Success);
Success = CloseHandle(Mapping0);
ASSERT(Success);
Handle = CreateFileW(File1Path,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
Mapping1 = CreateFileMappingW(Handle, 0, PAGE_READWRITE,
0, 16 * SystemInfo.dwAllocationGranularity, 0);
ASSERT(0 != Mapping1);
Success = CloseHandle(Handle);
ASSERT(Success);
MappedView1 = MapViewOfFile(Mapping1, FILE_MAP_ALL_ACCESS, 0, 0, 0);
ASSERT(0 != MappedView1);
for (PUINT8 P = MappedView1, EndP = P + 16 * SystemInfo.dwAllocationGranularity; EndP > P; P++)
*P = rand() & 0xff;
Success = UnmapViewOfFile(MappedView1);
ASSERT(Success);
Success = CloseHandle(Mapping1);
ASSERT(Success);
Success = DeleteFileW(File0Path);
ASSERT(Success);
Success = DeleteFileW(File1Path);
ASSERT(Success);
Success = RemoveDirectoryW(Dir1Path);
ASSERT(Success);
Success = RemoveDirectoryW(Dir1Path);
ASSERT(!Success);
ASSERT(ERROR_FILE_NOT_FOUND == GetLastError());
Success = CreateDirectoryW(Dir1Path, 0);
ASSERT(Success);
srand(seed);
Handle = CreateFileW(File0Path,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
Mapping0 = CreateFileMappingW(Handle, 0, PAGE_READWRITE,
0, 16 * SystemInfo.dwAllocationGranularity, 0);
ASSERT(0 != Mapping0);
MappedView0 = MapViewOfFile(Mapping0, FILE_MAP_ALL_ACCESS, 0, 0, 0);
ASSERT(0 != MappedView0);
for (PUINT8 P = MappedView0, EndP = P + 16 * SystemInfo.dwAllocationGranularity; EndP > P; P++)
*P = rand() & 0xff;
Success = UnmapViewOfFile(MappedView0);
ASSERT(Success);
Success = CloseHandle(Mapping0);
ASSERT(Success);
Success = CloseHandle(Handle);
ASSERT(Success);
Handle = CreateFileW(File1Path,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
Mapping1 = CreateFileMappingW(Handle, 0, PAGE_READWRITE,
0, 16 * SystemInfo.dwAllocationGranularity, 0);
ASSERT(0 != Mapping1);
MappedView1 = MapViewOfFile(Mapping1, FILE_MAP_ALL_ACCESS, 0, 0, 0);
ASSERT(0 != MappedView1);
for (PUINT8 P = MappedView1, EndP = P + 16 * SystemInfo.dwAllocationGranularity; EndP > P; P++)
*P = rand() & 0xff;
Success = UnmapViewOfFile(MappedView1);
ASSERT(Success);
Success = CloseHandle(Mapping1);
ASSERT(Success);
Success = CloseHandle(Handle);
ASSERT(Success);
Success = RemoveDirectoryW(Dir1Path);
ASSERT(Success);
Success = RemoveDirectoryW(Dir1Path);
ASSERT(!Success);
ASSERT(ERROR_FILE_NOT_FOUND == GetLastError());
memfs_stop(memfs);
}
void delete_standby_test(void)
{
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH];
GetTestDirectory(DirBuf);
delete_standby_dotest(-1, DirBuf, 0);
}
if (WinFspDiskTests)
{
delete_standby_dotest(MemfsDisk, 0, 0);
delete_standby_dotest(MemfsDisk, 0, 1000);
delete_standby_dotest(MemfsDisk, 0, INFINITE);
}
if (WinFspNetTests)
{
delete_standby_dotest(MemfsNet, L"\\\\memfs\\share", 0);
delete_standby_dotest(MemfsNet, L"\\\\memfs\\share", 1000);
delete_standby_dotest(MemfsDisk, 0, INFINITE);
}
}
static void rename_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
@ -1116,6 +1268,250 @@ void rename_mmap_test(void)
}
}
static void rename_standby_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
HANDLE Handle, Mapping0, Mapping1;
PUINT8 MappedView0, MappedView1;
BOOL Success;
WCHAR Dir1Path[MAX_PATH];
WCHAR Dir2Path[MAX_PATH];
WCHAR File0Path[MAX_PATH];
WCHAR File1Path[MAX_PATH];
SYSTEM_INFO SystemInfo;
unsigned seed = (unsigned)time(0);
GetSystemInfo(&SystemInfo);
StringCbPrintfW(Dir1Path, sizeof Dir1Path, L"%s%s\\dir1",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
StringCbPrintfW(Dir2Path, sizeof Dir2Path, L"%s%s\\dir2",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
StringCbPrintfW(File0Path, sizeof File0Path, L"%s%s\\dir1\\file0",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
StringCbPrintfW(File1Path, sizeof File1Path, L"%s%s\\dir1\\file1",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
Success = CreateDirectoryW(Dir1Path, 0);
ASSERT(Success);
srand(seed);
Handle = CreateFileW(File0Path,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
Mapping0 = CreateFileMappingW(Handle, 0, PAGE_READWRITE,
0, 16 * SystemInfo.dwAllocationGranularity, 0);
ASSERT(0 != Mapping0);
Success = CloseHandle(Handle);
ASSERT(Success);
MappedView0 = MapViewOfFile(Mapping0, FILE_MAP_ALL_ACCESS, 0, 0, 0);
ASSERT(0 != MappedView0);
for (PUINT8 P = MappedView0, EndP = P + 16 * SystemInfo.dwAllocationGranularity; EndP > P; P++)
*P = rand() & 0xff;
Success = UnmapViewOfFile(MappedView0);
ASSERT(Success);
Success = CloseHandle(Mapping0);
ASSERT(Success);
Handle = CreateFileW(File1Path,
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
Mapping1 = CreateFileMappingW(Handle, 0, PAGE_READWRITE,
0, 16 * SystemInfo.dwAllocationGranularity, 0);
ASSERT(0 != Mapping1);
Success = CloseHandle(Handle);
ASSERT(Success);
MappedView1 = MapViewOfFile(Mapping1, FILE_MAP_ALL_ACCESS, 0, 0, 0);
ASSERT(0 != MappedView1);
for (PUINT8 P = MappedView1, EndP = P + 16 * SystemInfo.dwAllocationGranularity; EndP > P; P++)
*P = rand() & 0xff;
Success = UnmapViewOfFile(MappedView1);
ASSERT(Success);
Success = CloseHandle(Mapping1);
ASSERT(Success);
Success = MoveFileExW(Dir1Path, Dir2Path, MOVEFILE_REPLACE_EXISTING);
ASSERT(Success);
StringCbPrintfW(File0Path, sizeof File0Path, L"%s%s\\dir2\\file0",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
StringCbPrintfW(File1Path, sizeof File1Path, L"%s%s\\dir2\\file1",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
srand(seed);
Handle = CreateFileW(File0Path,
GENERIC_READ, FILE_SHARE_READ, 0,
OPEN_EXISTING, 0, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
Mapping0 = CreateFileMappingW(Handle, 0, PAGE_READONLY,
0, 16 * SystemInfo.dwAllocationGranularity, 0);
ASSERT(0 != Mapping0);
Success = CloseHandle(Handle);
ASSERT(Success);
MappedView0 = MapViewOfFile(Mapping0, FILE_MAP_READ, 0, 0, 0);
ASSERT(0 != MappedView0);
for (PUINT8 P = MappedView0, EndP = P + 16 * SystemInfo.dwAllocationGranularity; EndP > P; P++)
ASSERT(*P == (rand() & 0xff));
Success = UnmapViewOfFile(MappedView0);
ASSERT(Success);
Success = CloseHandle(Mapping0);
ASSERT(Success);
Handle = CreateFileW(File1Path,
GENERIC_READ, FILE_SHARE_READ, 0,
OPEN_EXISTING, 0, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
Mapping1 = CreateFileMappingW(Handle, 0, PAGE_READONLY,
0, 16 * SystemInfo.dwAllocationGranularity, 0);
ASSERT(0 != Mapping1);
Success = CloseHandle(Handle);
ASSERT(Success);
MappedView1 = MapViewOfFile(Mapping1, FILE_MAP_READ, 0, 0, 0);
ASSERT(0 != MappedView1);
for (PUINT8 P = MappedView1, EndP = P + 16 * SystemInfo.dwAllocationGranularity; EndP > P; P++)
ASSERT(*P == (rand() & 0xff));
Success = UnmapViewOfFile(MappedView1);
ASSERT(Success);
Success = CloseHandle(Mapping1);
ASSERT(Success);
Success = MoveFileExW(Dir2Path, Dir1Path, MOVEFILE_REPLACE_EXISTING);
ASSERT(Success);
StringCbPrintfW(File0Path, sizeof File0Path, L"%s%s\\dir1\\file0",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
StringCbPrintfW(File1Path, sizeof File1Path, L"%s%s\\dir1\\file1",
Prefix ? L"" : L"\\\\?\\GLOBALROOT", Prefix ? Prefix : memfs_volumename(memfs));
srand(seed);
Handle = CreateFileW(File0Path,
GENERIC_READ, FILE_SHARE_READ, 0,
OPEN_EXISTING, 0, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
Mapping0 = CreateFileMappingW(Handle, 0, PAGE_READONLY,
0, 16 * SystemInfo.dwAllocationGranularity, 0);
ASSERT(0 != Mapping0);
Success = CloseHandle(Handle);
ASSERT(Success);
MappedView0 = MapViewOfFile(Mapping0, FILE_MAP_READ, 0, 0, 0);
ASSERT(0 != MappedView0);
for (PUINT8 P = MappedView0, EndP = P + 16 * SystemInfo.dwAllocationGranularity; EndP > P; P++)
ASSERT(*P == (rand() & 0xff));
Success = UnmapViewOfFile(MappedView0);
ASSERT(Success);
Success = CloseHandle(Mapping0);
ASSERT(Success);
Handle = CreateFileW(File1Path,
GENERIC_READ, FILE_SHARE_READ, 0,
OPEN_EXISTING, 0, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
Mapping1 = CreateFileMappingW(Handle, 0, PAGE_READONLY,
0, 16 * SystemInfo.dwAllocationGranularity, 0);
ASSERT(0 != Mapping1);
Success = CloseHandle(Handle);
ASSERT(Success);
MappedView1 = MapViewOfFile(Mapping1, FILE_MAP_READ, 0, 0, 0);
ASSERT(0 != MappedView1);
for (PUINT8 P = MappedView1, EndP = P + 16 * SystemInfo.dwAllocationGranularity; EndP > P; P++)
ASSERT(*P == (rand() & 0xff));
Success = UnmapViewOfFile(MappedView1);
ASSERT(Success);
Success = CloseHandle(Mapping1);
ASSERT(Success);
Success = MoveFileExW(File0Path, File1Path, MOVEFILE_REPLACE_EXISTING);
ASSERT(Success);
srand(seed);
Handle = CreateFileW(File1Path,
GENERIC_READ, FILE_SHARE_READ, 0,
OPEN_EXISTING, 0, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
Mapping1 = CreateFileMappingW(Handle, 0, PAGE_READONLY,
0, 16 * SystemInfo.dwAllocationGranularity, 0);
ASSERT(0 != Mapping1);
Success = CloseHandle(Handle);
ASSERT(Success);
MappedView1 = MapViewOfFile(Mapping1, FILE_MAP_READ, 0, 0, 0);
ASSERT(0 != MappedView1);
for (PUINT8 P = MappedView1, EndP = P + 16 * SystemInfo.dwAllocationGranularity; EndP > P; P++)
ASSERT(*P == (rand() & 0xff));
Success = UnmapViewOfFile(MappedView1);
ASSERT(Success);
Success = CloseHandle(Mapping1);
ASSERT(Success);
Success = MoveFileExW(File1Path, File0Path, MOVEFILE_REPLACE_EXISTING);
ASSERT(Success);
srand(seed);
Handle = CreateFileW(File0Path,
GENERIC_READ, FILE_SHARE_READ, 0,
OPEN_EXISTING, 0, 0);
ASSERT(INVALID_HANDLE_VALUE != Handle);
Mapping0 = CreateFileMappingW(Handle, 0, PAGE_READONLY,
0, 16 * SystemInfo.dwAllocationGranularity, 0);
ASSERT(0 != Mapping0);
Success = CloseHandle(Handle);
ASSERT(Success);
MappedView0 = MapViewOfFile(Mapping0, FILE_MAP_READ, 0, 0, 0);
ASSERT(0 != MappedView0);
for (PUINT8 P = MappedView0, EndP = P + 16 * SystemInfo.dwAllocationGranularity; EndP > P; P++)
ASSERT(*P == (rand() & 0xff));
Success = UnmapViewOfFile(MappedView0);
ASSERT(Success);
Success = CloseHandle(Mapping0);
ASSERT(Success);
Success = DeleteFileW(File0Path);
ASSERT(Success);
Success = RemoveDirectoryW(Dir1Path);
ASSERT(Success);
Success = RemoveDirectoryW(Dir1Path);
ASSERT(!Success);
ASSERT(ERROR_FILE_NOT_FOUND == GetLastError());
memfs_stop(memfs);
}
void rename_standby_test(void)
{
if (NtfsTests)
{
WCHAR DirBuf[MAX_PATH];
GetTestDirectory(DirBuf);
rename_standby_dotest(-1, DirBuf, 0);
}
if (WinFspDiskTests)
{
rename_standby_dotest(MemfsDisk, 0, 0);
rename_standby_dotest(MemfsDisk, 0, 1000);
rename_standby_dotest(MemfsDisk, 0, INFINITE);
}
if (WinFspNetTests)
{
rename_standby_dotest(MemfsNet, L"\\\\memfs\\share", 0);
rename_standby_dotest(MemfsNet, L"\\\\memfs\\share", 1000);
rename_standby_dotest(MemfsDisk, 0, INFINITE);
}
}
void getvolinfo_dotest(ULONG Flags, PWSTR Prefix, ULONG FileInfoTimeout)
{
void *memfs = memfs_start_ex(Flags, FileInfoTimeout);
@ -1331,6 +1727,7 @@ void info_tests(void)
TEST(delete_pending_test);
if (!OptShareName)
TEST(delete_mmap_test);
TEST(delete_standby_test);
TEST(rename_test);
TEST(rename_open_test);
TEST(rename_caseins_test);
@ -1338,6 +1735,7 @@ void info_tests(void)
TEST(rename_flipflop_test);
if (!OptShareName)
TEST(rename_mmap_test);
TEST(rename_standby_test);
TEST(getvolinfo_test);
TEST(setvolinfo_test);
}

View File

@ -0,0 +1,42 @@
/**
* @file version-test.c
*
* @copyright 2015-2017 Bill Zissimopoulos
*/
/*
* This file is part of WinFsp.
*
* You can redistribute it and/or modify it under the terms of the GNU
* General Public License version 3 as published by the Free Software
* Foundation.
*
* Licensees holding a valid commercial license may use this file in
* accordance with the commercial license agreement provided with the
* software.
*/
#include <winfsp/winfsp.h>
#include <tlib/testsuite.h>
#include "winfsp-tests.h"
static void version_test(void)
{
UINT32 Version1, Version2;
NTSTATUS Result;
Result = FspVersion(&Version1);
ASSERT(NT_SUCCESS(Result));
Result = FspVersion(&Version2);
ASSERT(NT_SUCCESS(Result));
ASSERT(Version1 == Version2);
FspDebugLog(__FUNCTION__ ": FspVersion=%d.%d\n", HIWORD(Version1), LOWORD(Version1));
}
void version_tests(void)
{
TEST(version_test);
}

View File

@ -184,6 +184,7 @@ int main(int argc, char *argv[])
TESTSUITE(eventlog_tests);
TESTSUITE(path_tests);
TESTSUITE(dirbuf_tests);
TESTSUITE(version_tests);
TESTSUITE(mount_tests);
TESTSUITE(timeout_tests);
TESTSUITE(memfs_tests);
@ -194,6 +195,7 @@ int main(int argc, char *argv[])
TESTSUITE(flush_tests);
TESTSUITE(lock_tests);
TESTSUITE(dirctl_tests);
TESTSUITE(exec_tests);
TESTSUITE(reparse_tests);
TESTSUITE(stream_tests);
TESTSUITE(oplock_tests);

View File

@ -85,6 +85,17 @@ static inline BOOL RealSetCurrentDirectoryW(
{
return SetCurrentDirectoryW(lpPathName);
}
BOOL WINAPI HookCreateProcessW(
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation);
#if !defined(WINFSP_TESTS_NO_HOOKS)
#define CreateFileW HookCreateFileW
#define CloseHandle HookCloseHandle
@ -99,6 +110,7 @@ static inline BOOL RealSetCurrentDirectoryW(
#define GetVolumeInformationW HookGetVolumeInformationW
#define SetVolumeLabelW HookSetVolumeLabelW
#define SetCurrentDirectoryW HookSetCurrentDirectoryW
#define CreateProcessW HookCreateProcessW
#endif
HANDLE WINAPI ResilientCreateFileW(