├── doc ├── wincap.gif ├── wslcap.gif ├── wslseq.puml ├── winseq.puml ├── component.puml ├── wslseq.svg ├── winseq.svg └── component.svg ├── tst ├── memfs-fuse3 │ ├── .gitignore │ ├── memfs-fuse3.out.vcxproj.filters │ ├── memfs-fuse3.exe.vcxproj.filters │ ├── memfs-fuse3.sln │ ├── compat.h │ ├── memfs-fuse3.out.vcxproj │ └── memfs-fuse3.exe.vcxproj ├── wslfuse-tests │ ├── wslfuse-tests.c │ └── mount-test.c ├── winfuse-tests │ ├── winfuse-tests.c │ └── path-test.c └── lockdly │ └── lockdly.c ├── ext └── tlib │ ├── Commit.txt │ ├── injected │ ├── allfunc.h │ ├── stdfunc.h │ ├── stdfunc.c │ ├── curlfunc.h │ └── curlfunc.c │ ├── callstack.h │ ├── injection.h │ ├── callstack.c │ ├── testsuite.h │ ├── injection.c │ └── testsuite.c ├── .gitmodules ├── tools ├── nmake-ext-test.bat ├── license-headers │ ├── fix.ps1 │ ├── header.c │ ├── header.cpp │ └── header.h ├── debug.bat ├── build-lxdk.bat ├── ioc.c ├── build-winfsp.bat ├── deploy.ps1 ├── gensrc │ ├── errno.sh │ └── errno.txt ├── build-sample.bat ├── build-libfuse.bat ├── DigiCert High Assurance EV Root CA.crt ├── deploy.bat ├── build.bat └── run-tests.bat ├── src ├── winfuse │ ├── driver.inf.in │ ├── version.rc │ ├── driver.c │ ├── driver.h │ └── device.c ├── wslfuse │ ├── driver.inf.in │ ├── driver.h │ ├── version.rc │ └── driver.c ├── fusermount │ ├── uninstall.sh │ ├── install.sh │ └── fusermount-helper.c └── shared │ ├── km │ ├── file.c │ ├── context.c │ ├── path.c │ ├── util.c │ ├── errnosym.i │ ├── coro.h │ ├── errno.i │ ├── ioq.c │ └── instance.c │ └── ku │ └── wslfuse.h ├── appveyor.yml ├── README.md └── Contributors.asciidoc /doc/wincap.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billziss-gh/winfuse/HEAD/doc/wincap.gif -------------------------------------------------------------------------------- /doc/wslcap.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/billziss-gh/winfuse/HEAD/doc/wslcap.gif -------------------------------------------------------------------------------- /tst/memfs-fuse3/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.ncb 3 | *.suo 4 | *.vcproj.* 5 | *.vcxproj.user 6 | *.VC.db 7 | *.VC.opendb 8 | .vs 9 | *.exe 10 | *.install 11 | -------------------------------------------------------------------------------- /ext/tlib/Commit.txt: -------------------------------------------------------------------------------- 1 | Taken from github.com/billziss-gh/winfsp/ext/tlib codebase: 2 | commit 847eab3da4e558e2c88504edfe18a51e4450f9e0 3 | 4 | Originally from secfs.core/src/tlib codebase: 5 | commit 16296d2ac64a7d532b1c486dd3c44af5865d4851 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ext/libfuse"] 2 | path = ext/libfuse 3 | url = https://github.com/billziss-gh/libfuse.git 4 | [submodule "ext/winfsp"] 5 | path = ext/winfsp 6 | url = https://github.com/billziss-gh/winfsp.git 7 | [submodule "ext/lxdk"] 8 | path = ext/lxdk 9 | url = https://github.com/billziss-gh/lxdk.git 10 | -------------------------------------------------------------------------------- /ext/tlib/injected/allfunc.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tlib/injected/allfunc.h 3 | * 4 | * @copyright 2014-2019 Bill Zissimopoulos 5 | */ 6 | 7 | #ifndef TLIB_INJECTED_ALLFUNC_H_INCLUDED 8 | #define TLIB_INJECTED_ALLFUNC_H_INCLUDED 9 | 10 | #include 11 | #include 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /tools/nmake-ext-test.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | setlocal 4 | setlocal EnableDelayedExpansion 5 | 6 | set vswhere="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" 7 | for /f "usebackq tokens=*" %%i in (`%vswhere% -find VC\**\vcvarsall.bat`) do ( 8 | call "%%i" x64 9 | ) 10 | 11 | cd %~dp0..\ext\winfsp\ext\test 12 | nmake /f Nmakefile 13 | -------------------------------------------------------------------------------- /src/winfuse/driver.inf.in: -------------------------------------------------------------------------------- 1 | [Version] 2 | Signature = "$WINDOWS NT$" 3 | Class = Volume 4 | ClassGuid = {71a27cdd-812a-11d0-bec7-08002be2092f} 5 | CatalogFile = !CatalogFile! 6 | Provider = !Provider! 7 | 8 | [DestinationDirs] 9 | DefaultDestDir = 12 10 | 11 | [DefaultInstall] 12 | CopyFiles = Driver.CopyFiles 13 | 14 | [Driver.CopyFiles] 15 | !DriverFile! 16 | 17 | [SourceDisksFiles] 18 | !DriverFile! = 1 19 | 20 | [SourceDisksNames] 21 | 1 = Disk1 22 | -------------------------------------------------------------------------------- /src/wslfuse/driver.inf.in: -------------------------------------------------------------------------------- 1 | [Version] 2 | Signature = "$WINDOWS NT$" 3 | Class = Volume 4 | ClassGuid = {71a27cdd-812a-11d0-bec7-08002be2092f} 5 | CatalogFile = !CatalogFile! 6 | Provider = !Provider! 7 | 8 | [DestinationDirs] 9 | DefaultDestDir = 12 10 | 11 | [DefaultInstall] 12 | CopyFiles = Driver.CopyFiles 13 | 14 | [Driver.CopyFiles] 15 | !DriverFile! 16 | 17 | [SourceDisksFiles] 18 | !DriverFile! = 1 19 | 20 | [SourceDisksNames] 21 | 1 = Disk1 22 | -------------------------------------------------------------------------------- /tools/license-headers/fix.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env powershell 2 | 3 | param ( 4 | [Parameter(Mandatory=$true)][string]$srcfile, 5 | [string]$dstfile = $srcfile 6 | ) 7 | 8 | $content = Get-Content -Raw $srcfile 9 | $fileext = [System.IO.Path]::GetExtension($srcfile) 10 | $lichead = Get-Content -Raw "$PSScriptRoot\header$fileext" 11 | $content = $content -replace "(?sm)/\*((?!\*/).)*This file is part of((?!\*/).)*\*/", $lichead 12 | Set-Content -Path $dstfile -NoNewline -Value $content 13 | -------------------------------------------------------------------------------- /tools/debug.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | setlocal 4 | 5 | set DebugWorkspace=winfuse 6 | set DebugPort=50000 7 | set DebugKey=1.1.1.1 8 | 9 | set RegKey="HKLM\SOFTWARE\Microsoft\Windows Kits\Installed Roots" 10 | set RegVal="KitsRoot10" 11 | reg query %RegKey% /v %RegVal% >nul 2>&1 || (echo Cannot find Windows Kit >&2 & exit /b 1) 12 | for /f "tokens=2,*" %%i in ('reg query %RegKey% /v %RegVal% ^| findstr %RegVal%') do ( 13 | set KitRoot="%%j" 14 | ) 15 | start "%DebugWorkspace%" %KitRoot%\Debuggers\x64\windbg -W %DebugWorkspace% -k net:port=%DebugPort%,key=%DebugKey% 16 | -------------------------------------------------------------------------------- /tst/memfs-fuse3/memfs-fuse3.out.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {642abec8-4756-443b-ac1f-3de5522812c2} 6 | 7 | 8 | 9 | 10 | Source 11 | 12 | 13 | 14 | 15 | Source 16 | 17 | 18 | -------------------------------------------------------------------------------- /ext/tlib/callstack.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tlib/callstack.h 3 | * 4 | * @copyright 2014-2019 Bill Zissimopoulos 5 | */ 6 | 7 | #ifndef TLIB_CALLSTACK_H_INCLUDED 8 | #define TLIB_CALLSTACK_H_INCLUDED 9 | 10 | #include 11 | 12 | enum 13 | { 14 | TLIB_MAX_SYMLEN = 63, 15 | TLIB_MAX_SYMRET = 8, 16 | TLIB_MAX_SYMCAP = 62, /* max number of frames to capture (Windows restriction) */ 17 | }; 18 | 19 | struct tlib_callstack_s 20 | { 21 | const char *syms[TLIB_MAX_SYMRET + 1]; 22 | char symbuf[TLIB_MAX_SYMRET][TLIB_MAX_SYMLEN + 1]; 23 | }; 24 | void tlib_callstack(size_t skip, size_t count, struct tlib_callstack_s *stack); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /ext/tlib/injected/stdfunc.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tlib/injected/stdfunc.h 3 | * 4 | * @copyright 2014-2019 Bill Zissimopoulos 5 | */ 6 | 7 | #ifndef TLIB_INJECTED_STDFUNC_H_INCLUDED 8 | #define TLIB_INJECTED_STDFUNC_H_INCLUDED 9 | 10 | #include 11 | #include 12 | 13 | #if defined(TLIB_INJECTIONS_ENABLED) 14 | #define calloc(count, size) tlib_calloc(count, size) 15 | #define malloc(size) tlib_malloc(size) 16 | #define realloc(ptr, size) tlib_realloc(ptr, size) 17 | #endif 18 | 19 | void *tlib_calloc(size_t count, size_t size); 20 | void *tlib_malloc(size_t size); 21 | void *tlib_realloc(void *ptr, size_t size); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /ext/tlib/injected/stdfunc.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tlib/injected/stdfunc.c 3 | * 4 | * @copyright 2014-2019 Bill Zissimopoulos 5 | */ 6 | 7 | #include 8 | #define TLIB_INJECTIONS_ENABLED 9 | #include 10 | 11 | #undef calloc 12 | #undef malloc 13 | #undef realloc 14 | 15 | void *tlib_calloc(size_t count, size_t size) 16 | { 17 | TLIB_INJECT("calloc", return 0); 18 | return calloc(count, size); 19 | } 20 | void *tlib_malloc(size_t size) 21 | { 22 | TLIB_INJECT("malloc", return 0); 23 | return malloc(size); 24 | } 25 | void *tlib_realloc(void *ptr, size_t size) 26 | { 27 | TLIB_INJECT("realloc", return 0); 28 | return realloc(ptr, size); 29 | } 30 | -------------------------------------------------------------------------------- /tools/build-lxdk.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | setlocal 4 | setlocal EnableDelayedExpansion 5 | 6 | if not X%1==X set Config=%1 7 | if not X%2==X set DestDir=%2 8 | 9 | if X!Config!==X (echo usage: build-lxdk Config [DestDir] >&2 & goto fail) 10 | 11 | if not X!DestDir!==X ( 12 | if exist !DestDir! rmdir /s/q !DestDir! 13 | mkdir !DestDir! 14 | pushd !DestDir! 15 | set DestDir=!cd! 16 | popd 17 | ) 18 | 19 | cd %~dp0.. 20 | call ext\lxdk\tools\build.bat %Config% 21 | if !ERRORLEVEL! neq 0 goto :fail 22 | 23 | if not X!DestDir!==X ( 24 | copy %~dp0..\ext\lxdk\build\VStudio\build\%Config%\lxdk-*.msi !DestDir! 25 | ) 26 | 27 | exit /b 0 28 | 29 | :fail 30 | exit /b 1 31 | -------------------------------------------------------------------------------- /tools/license-headers/header.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of WinFuse. 3 | * 4 | * You can redistribute it and/or modify it under the terms of the GNU 5 | * Affero General Public License version 3 as published by the Free 6 | * Software Foundation. 7 | * 8 | * Licensees holding a valid commercial license may use this software 9 | * in accordance with the commercial license agreement provided in 10 | * conjunction with the software. The terms and conditions of any such 11 | * commercial license agreement shall govern, supersede, and render 12 | * ineffective any application of the AGPLv3 license to this software, 13 | * notwithstanding of any reference thereto in the software or 14 | * associated repository. 15 | */ -------------------------------------------------------------------------------- /tools/license-headers/header.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of WinFuse. 3 | * 4 | * You can redistribute it and/or modify it under the terms of the GNU 5 | * Affero General Public License version 3 as published by the Free 6 | * Software Foundation. 7 | * 8 | * Licensees holding a valid commercial license may use this software 9 | * in accordance with the commercial license agreement provided in 10 | * conjunction with the software. The terms and conditions of any such 11 | * commercial license agreement shall govern, supersede, and render 12 | * ineffective any application of the AGPLv3 license to this software, 13 | * notwithstanding of any reference thereto in the software or 14 | * associated repository. 15 | */ -------------------------------------------------------------------------------- /tools/license-headers/header.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of WinFuse. 3 | * 4 | * You can redistribute it and/or modify it under the terms of the GNU 5 | * Affero General Public License version 3 as published by the Free 6 | * Software Foundation. 7 | * 8 | * Licensees holding a valid commercial license may use this software 9 | * in accordance with the commercial license agreement provided in 10 | * conjunction with the software. The terms and conditions of any such 11 | * commercial license agreement shall govern, supersede, and render 12 | * ineffective any application of the AGPLv3 license to this software, 13 | * notwithstanding of any reference thereto in the software or 14 | * associated repository. 15 | */ -------------------------------------------------------------------------------- /tst/memfs-fuse3/memfs-fuse3.exe.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | 10 | 11 | Source 12 | 13 | 14 | 15 | 16 | Source 17 | 18 | 19 | -------------------------------------------------------------------------------- /tools/ioc.c: -------------------------------------------------------------------------------- 1 | ///usr/bin/gcc -o /tmp/ioc.$$ "$0"; /tmp/ioc.$$ "$@"; rm /tmp/ioc.$$; exit 2 | 3 | // usage: sh ioc.c dir typ num siz 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | unsigned dir, typ, num, siz; 12 | 13 | if (5 != argc) 14 | { 15 | fprintf(stderr, "usage: sh ioc.c dir typ num siz\n"); 16 | exit(2); 17 | } 18 | 19 | dir = strtoul(argv[1], 0, 0); 20 | typ = strtoul(argv[2], 0, 0); 21 | num = strtoul(argv[3], 0, 0); 22 | siz = strtoul(argv[4], 0, 0); 23 | 24 | printf( 25 | "dir=%u typ=%u num=%u siz=%u\n" 26 | "ioc=0x%08x\n", 27 | dir, typ, num, siz, 28 | _IOC(dir, typ, num, siz)); 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /src/fusermount/uninstall.sh: -------------------------------------------------------------------------------- 1 | #set -x 2 | 3 | if [ "$(id -u)" != "0" ]; then 4 | echo "must be run as superuser" 1>&2; exit 1 5 | fi 6 | 7 | cd "$(dirname "$0")" 8 | 9 | # /dev/fuse 10 | rm -f /etc/tmpfiles.d/wslfuse.conf 11 | rm -f /dev/fuse 12 | 13 | # fusermount 14 | uninstall() 15 | { 16 | local src="$1" 17 | local dst="$2" 18 | if cmp -s "$src" "$dst"; then 19 | local bak=$(ls -1rv "$dst".~*~ 2>/dev/null | head -1) 20 | if [ -n "$bak" ]; then 21 | mv -f "$bak" "$dst" 22 | else 23 | rm -f "$dst" 24 | fi 25 | fi 26 | } 27 | uninstall fusermount.out /usr/bin/fusermount 28 | uninstall fusermount.out /usr/bin/fusermount3 29 | rm -f /usr/bin/fusermount-helper.exe 30 | 31 | echo FUSE for WSL1 user space components uninstalled 32 | -------------------------------------------------------------------------------- /ext/tlib/injected/curlfunc.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tlib/injected/curlfunc.h 3 | * 4 | * @copyright 2014-2019 Bill Zissimopoulos 5 | */ 6 | 7 | #ifndef TLIB_INJECTED_CURLFUNC_H_INCLUDED 8 | #define TLIB_INJECTED_CURLFUNC_H_INCLUDED 9 | 10 | #include 11 | 12 | #if defined(TLIB_INJECTIONS_ENABLED) 13 | #define curl_easy_init() tlib_curl_easy_init() 14 | #define curl_multi_init() tlib_curl_multi_init() 15 | #define curl_multi_add_handle(mh, eh) tlib_curl_multi_add_handle(mh, eh) 16 | #define curl_multi_perform(mh, rh) tlib_curl_multi_perform(mh, rh) 17 | #endif 18 | 19 | CURL *tlib_curl_easy_init(void); 20 | CURLM *tlib_curl_multi_init(void); 21 | CURLMcode tlib_curl_multi_add_handle(CURLM *mh, CURL *eh); 22 | CURLMcode tlib_curl_multi_perform(CURLM *mh, int *running_handles); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/fusermount/install.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | 3 | if [ "$(id -u)" != "0" ]; then 4 | echo "must be run as superuser" 1>&2; exit 1 5 | fi 6 | 7 | cd "$(dirname "$0")" 8 | 9 | # fusermount 10 | cmp_install() 11 | { 12 | local src="" 13 | local dst="" 14 | for arg in "$@"; do src="$dst"; dst="$arg"; done 15 | cmp -s "$src" "$dst" || install "$@" 16 | } 17 | cmp_install --backup=numbered -o0 -g0 -mu=srwx,go=rx fusermount.out /usr/bin/fusermount 18 | cmp_install --backup=numbered -o0 -g0 -mu=srwx,go=rx fusermount.out /usr/bin/fusermount3 19 | cmp_install -o0 -g0 -mu=rwx,go=rx fusermount-helper.exe /usr/bin/fusermount-helper.exe 20 | 21 | # /dev/fuse 22 | echo c /dev/fuse 0666 root root - 10:229 > /etc/tmpfiles.d/wslfuse.conf 23 | systemd-tmpfiles --create wslfuse.conf 24 | 25 | echo FUSE for WSL1 user space components installed 26 | -------------------------------------------------------------------------------- /ext/tlib/injection.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tlib/injection.h 3 | * 4 | * @copyright 2014-2019 Bill Zissimopoulos 5 | */ 6 | 7 | /* NOTE: This header may usefully be included multiple times. 8 | * The TLIB_INJECT() macro will be redefined based on whether 9 | * TLIB_INJECTIONS_ENABLED is defined. 10 | */ 11 | 12 | #undef TLIB_INJECT 13 | #if defined(TLIB_INJECTIONS_ENABLED) 14 | #define TLIB_INJECT(name, stmt) \ 15 | do\ 16 | {\ 17 | static void *injection = 0;\ 18 | if (0 == injection)\ 19 | injection = tlib_injection(name);\ 20 | if (tlib_injection_trace(injection))\ 21 | stmt;\ 22 | } while (0) 23 | #else 24 | #define TLIB_INJECT(name, stmt) do {} while (0) 25 | #endif 26 | 27 | void *tlib_injection(const char *name); 28 | int tlib_injection_trace(void *injection); 29 | void tlib_injection_enable(const char *name, const char *sym, unsigned trigger); 30 | void tlib_injection_disable(const char *name, const char *sym); 31 | -------------------------------------------------------------------------------- /doc/wslseq.puml: -------------------------------------------------------------------------------- 1 | skinparam shadowing false 2 | hide footbox 3 | 4 | box "Originating Process Context" 5 | participant "Originating\nProcess" as OP 6 | participant "winfsp" as winfspOP 7 | end box 8 | box "File System Process Context" 9 | participant "winfsp" as winfspFS 10 | participant wslfuse #Salmon 11 | participant "Linux\nFUSE FS" as FS 12 | end box 13 | 14 | activate OP 15 | OP->winfspOP: I/O 16 | deactivate OP 17 | activate winfspOP 18 | winfspOP-->winfspFS: Context Switch 19 | deactivate winfspOP 20 | activate winfspFS 21 | winfspFS->wslfuse: FSP_FSCTL_TRANSACT Req 22 | deactivate winfspFS 23 | activate wslfuse 24 | wslfuse->FS: /dev/fuse read 25 | deactivate wslfuse 26 | activate FS 27 | FS->FS: Process 28 | activate FS 29 | deactivate FS 30 | FS->wslfuse: /dev/fuse write 31 | deactivate FS 32 | activate wslfuse 33 | wslfuse->winfspFS: FSP_FSCTL_TRANSACT Rsp 34 | deactivate wslfuse 35 | activate winfspFS 36 | winfspFS-->OP: Context Switch and Return 37 | deactivate winfspFS 38 | activate OP 39 | -------------------------------------------------------------------------------- /tst/wslfuse-tests/wslfuse-tests.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file wslfuse-tests.c 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #include 23 | 24 | int main(int argc, char *argv[]) 25 | { 26 | TESTSUITE(mount_tests); 27 | 28 | tlib_run_tests(argc, argv); 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /doc/winseq.puml: -------------------------------------------------------------------------------- 1 | skinparam shadowing false 2 | hide footbox 3 | 4 | box "Originating Process Context" 5 | participant "Originating\nProcess" as OP 6 | participant "winfsp" as winfspOP 7 | end box 8 | box "File System Process Context" 9 | participant "winfsp" as winfspFS 10 | participant winfuse #Salmon 11 | participant "Windows\nFUSE FS" as FS 12 | end box 13 | 14 | activate OP 15 | OP->winfspOP: I/O 16 | deactivate OP 17 | activate winfspOP 18 | winfspOP-->winfspFS: Context Switch 19 | deactivate winfspOP 20 | activate winfspFS 21 | winfspFS->winfuse: FSP_FSCTL_TRANSACT Req 22 | deactivate winfspFS 23 | activate winfuse 24 | winfuse->FS: FUSE_FSCTL_TRANSACT Req 25 | deactivate winfuse 26 | activate FS 27 | FS->FS: Process 28 | activate FS 29 | deactivate FS 30 | FS->winfuse: FUSE_FSCTL_TRANSACT Rsp 31 | deactivate FS 32 | activate winfuse 33 | winfuse->winfspFS: FSP_FSCTL_TRANSACT Rsp 34 | deactivate winfuse 35 | activate winfspFS 36 | winfspFS-->OP: Context Switch and Return 37 | deactivate winfspFS 38 | activate OP 39 | -------------------------------------------------------------------------------- /ext/tlib/injected/curlfunc.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tlib/injected/curlfunc.c 3 | * 4 | * @copyright 2014-2019 Bill Zissimopoulos 5 | */ 6 | 7 | #include 8 | #define TLIB_INJECTIONS_ENABLED 9 | #include 10 | 11 | #undef curl_easy_init 12 | #undef curl_multi_init 13 | #undef curl_multi_add_handle 14 | #undef curl_multi_perform 15 | 16 | CURL *tlib_curl_easy_init(void) 17 | { 18 | TLIB_INJECT("curl_easy_init", return 0); 19 | return curl_easy_init(); 20 | } 21 | CURLM *tlib_curl_multi_init(void) 22 | { 23 | TLIB_INJECT("curl_multi_init", return 0); 24 | return curl_multi_init(); 25 | } 26 | CURLMcode tlib_curl_multi_add_handle(CURLM *mh, CURL *eh) 27 | { 28 | TLIB_INJECT("curl_multi_add_handle", return CURLM_INTERNAL_ERROR); 29 | return curl_multi_add_handle(mh, eh); 30 | } 31 | CURLMcode tlib_curl_multi_perform(CURLM *mh, int *running_handles) 32 | { 33 | TLIB_INJECT("curl_multi_perform", return CURLM_INTERNAL_ERROR); 34 | return curl_multi_perform(mh, running_handles); 35 | } 36 | -------------------------------------------------------------------------------- /tools/build-winfsp.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | setlocal 4 | setlocal EnableDelayedExpansion 5 | 6 | if not X%1==X set Config=%1 7 | if not X%2==X set DestDir=%2 8 | 9 | if X!Config!==X (echo usage: build-winfsp Config [DestDir] >&2 & goto fail) 10 | 11 | if not X!DestDir!==X ( 12 | if exist !DestDir! rmdir /s/q !DestDir! 13 | mkdir !DestDir! 14 | pushd !DestDir! 15 | set DestDir=!cd! 16 | popd 17 | ) 18 | 19 | set vswhere="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" 20 | for /f "usebackq tokens=*" %%i in (`%vswhere% -find VC\**\vcvarsall.bat`) do ( 21 | call "%%i" x64 22 | ) 23 | 24 | cd %~dp0.. 25 | set VS140COMNTOOLS= 26 | call ext\winfsp\tools\build.bat %Config% 27 | if !ERRORLEVEL! neq 0 goto :fail 28 | 29 | if not X!DestDir!==X ( 30 | copy %~dp0..\ext\winfsp\build\VStudio\build\%Config%\winfsp-*.msi !DestDir! 31 | copy %~dp0..\ext\winfsp\build\VStudio\build\%Config%\winfsp-tests-x64.exe !DestDir! 32 | ) 33 | 34 | exit /b 0 35 | 36 | :fail 37 | exit /b 1 38 | -------------------------------------------------------------------------------- /src/wslfuse/driver.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file wslfuse/driver.h 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #ifndef WSLFUSE_DRIVER_H_INCLUDED 23 | #define WSLFUSE_DRIVER_H_INCLUDED 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | INT FuseMiscRegister(PLX_INSTANCE Instance); 30 | VOID FuseMiscInitialize(VOID); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/winfuse/version.rc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define STR(x) STR_(x) 4 | #define STR_(x) #x 5 | 6 | VS_VERSION_INFO VERSIONINFO 7 | FILEVERSION MyVersionWithCommas 8 | PRODUCTVERSION MyVersionWithCommas 9 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 10 | #ifdef _DEBUG 11 | FILEFLAGS VS_FF_DEBUG 12 | #else 13 | FILEFLAGS 0 14 | #endif 15 | FILEOS VOS_NT 16 | FILETYPE VFT_DRV 17 | FILESUBTYPE VFT2_DRV_SYSTEM 18 | BEGIN 19 | BLOCK "StringFileInfo" 20 | BEGIN 21 | BLOCK "040904b0" 22 | BEGIN 23 | VALUE "CompanyName", STR(MyCompanyName) 24 | VALUE "FileDescription", STR(MyDescription) 25 | VALUE "FileVersion", STR(MyFullVersion) 26 | VALUE "InternalName", "winfuse.sys" 27 | VALUE "LegalCopyright", STR(MyCopyright) 28 | VALUE "OriginalFilename", "winfuse.sys" 29 | VALUE "ProductName", STR(MyProductName) 30 | VALUE "ProductVersion", STR(MyProductVersion) 31 | END 32 | END 33 | BLOCK "VarFileInfo" 34 | BEGIN 35 | VALUE "Translation", 0x409, 1200 36 | END 37 | END 38 | -------------------------------------------------------------------------------- /src/wslfuse/version.rc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define STR(x) STR_(x) 4 | #define STR_(x) #x 5 | 6 | VS_VERSION_INFO VERSIONINFO 7 | FILEVERSION MyVersionWithCommas 8 | PRODUCTVERSION MyVersionWithCommas 9 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 10 | #ifdef _DEBUG 11 | FILEFLAGS VS_FF_DEBUG 12 | #else 13 | FILEFLAGS 0 14 | #endif 15 | FILEOS VOS_NT 16 | FILETYPE VFT_DRV 17 | FILESUBTYPE VFT2_DRV_SYSTEM 18 | BEGIN 19 | BLOCK "StringFileInfo" 20 | BEGIN 21 | BLOCK "040904b0" 22 | BEGIN 23 | VALUE "CompanyName", STR(MyCompanyName) 24 | VALUE "FileDescription", STR(MyDescription) 25 | VALUE "FileVersion", STR(MyFullVersion) 26 | VALUE "InternalName", "wslfuse.sys" 27 | VALUE "LegalCopyright", STR(MyCopyright) 28 | VALUE "OriginalFilename", "wslfuse.sys" 29 | VALUE "ProductName", STR(MyProductName) 30 | VALUE "ProductVersion", STR(MyProductVersion) 31 | END 32 | END 33 | BLOCK "VarFileInfo" 34 | BEGIN 35 | VALUE "Translation", 0x409, 1200 36 | END 37 | END 38 | -------------------------------------------------------------------------------- /tst/winfuse-tests/winfuse-tests.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file winfuse-tests.c 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #include 23 | #include 24 | 25 | int main(int argc, char *argv[]) 26 | { 27 | FspLoad(0); 28 | 29 | TESTSUITE(coro_tests); 30 | TESTSUITE(path_tests); 31 | TESTSUITE(transact_tests); 32 | 33 | tlib_run_tests(argc, argv); 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /src/winfuse/driver.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file winfuse/driver.c 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #include 23 | 24 | DRIVER_INITIALIZE DriverEntry; 25 | 26 | #ifdef ALLOC_PRAGMA 27 | #pragma alloc_text(INIT, DriverEntry) 28 | #endif 29 | 30 | NTSTATUS DriverEntry( 31 | PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) 32 | { 33 | #if DBG 34 | if (!KD_DEBUGGER_NOT_PRESENT) 35 | DbgBreakPoint(); 36 | #endif 37 | 38 | return FspFsextProviderRegister(&FuseProvider); 39 | } 40 | -------------------------------------------------------------------------------- /tools/deploy.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [Parameter(Mandatory)][string]$Name, 3 | [string]$CheckpointName, 4 | [Parameter(Mandatory)][string[]]$Files, 5 | [Parameter(Mandatory)][string]$Destination 6 | ) 7 | 8 | function Restore-VM ($Name, $CheckpointName) { 9 | $VM = Get-VM -Name $Name 10 | if ($VM.State -eq "Running") { 11 | Stop-VM -Name $Name -TurnOff 12 | } 13 | 14 | if (-not $CheckpointName) { 15 | $Checkpoint = Get-VMCheckpoint -VMName $Name | 16 | Sort-Object -Property CreationTime -Descending | 17 | select -First 1 18 | } else { 19 | $Checkpoint = Get-VMCheckpoint -VMName $Name -Name $CheckpointName 20 | } 21 | Restore-VMCheckpoint -VMCheckpoint $Checkpoint -Confirm:$false 22 | 23 | Start-VM -Name $Name 24 | } 25 | 26 | function Deploy-VMFiles ($Name, $Files, $Destination) { 27 | foreach ($File in $Files) { 28 | $Leaf = Split-Path -Path $File -Leaf 29 | $Dest = Join-Path $Destination $Leaf 30 | Copy-VMFile -Name $Name -SourcePath $File -DestinationPath $Dest -FileSource Host -CreateFullPath -Force 31 | } 32 | } 33 | 34 | Restore-VM -Name $Name -CheckpointName $CheckpointName 35 | Deploy-VMFiles -Name $Name -Files $Files -Destination $Destination 36 | -------------------------------------------------------------------------------- /src/wslfuse/driver.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file wslfuse/driver.c 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #include 23 | 24 | DRIVER_INITIALIZE DriverEntry; 25 | 26 | #ifdef ALLOC_PRAGMA 27 | #pragma alloc_text(INIT, DriverEntry) 28 | #endif 29 | 30 | NTSTATUS DriverEntry( 31 | PDRIVER_OBJECT DriverObject, 32 | PUNICODE_STRING RegistryPath) 33 | { 34 | #if DBG 35 | if (!KD_DEBUGGER_NOT_PRESENT) 36 | DbgBreakPoint(); 37 | #endif 38 | 39 | FuseMiscInitialize(); 40 | 41 | return LxldrRegisterService(DriverObject, TRUE, FuseMiscRegister); 42 | } 43 | -------------------------------------------------------------------------------- /doc/component.puml: -------------------------------------------------------------------------------- 1 | left to right direction 2 | skinparam componentStyle uml2 3 | skinparam nodesep 50 4 | skinparam ranksep 30 5 | skinparam shadowing false 6 | skinparam rectangle { 7 | fontcolor grey 8 | borderColor grey 9 | shadowing false 10 | } 11 | skinparam linetype ortho 12 | 13 | rectangle NTOS { 14 | together dummy { 15 | component winfuse #Salmon 16 | interface "FUSE_FSCTL\nTRANSACT" as FUSE_FSCTL_TRANSACT #Salmon 17 | winfuse -right- FUSE_FSCTL_TRANSACT 18 | 19 | component winfsp 20 | interface "FSP_FSCTL\nTRANSACT" as FSP_FSCTL_TRANSACT 21 | winfsp -right- FSP_FSCTL_TRANSACT 22 | 23 | winfsp .up.> winfuse: loads 24 | winfuse -down-( FSP_FSCTL_TRANSACT 25 | } 26 | 27 | rectangle LXCORE { 28 | component wslfuse #Salmon 29 | interface "/dev/fuse" as devfuse #Salmon 30 | wslfuse -right- devfuse 31 | 32 | component lxldr 33 | 34 | lxldr .up.> wslfuse: loads 35 | } 36 | 37 | winfsp -[hidden]- wslfuse 38 | wslfuse -up-( FSP_FSCTL_TRANSACT 39 | } 40 | 41 | component "Windows\nWinFsp FS" as fspfs 42 | fspfs -left-( FSP_FSCTL_TRANSACT 43 | 44 | component "Windows\nFUSE FS" as winfs 45 | winfs -left-( FUSE_FSCTL_TRANSACT 46 | 47 | component "Linux\nFUSE FS" as wslfs 48 | wslfs -left-( devfuse 49 | -------------------------------------------------------------------------------- /src/winfuse/driver.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file winfuse/driver.h 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #ifndef WINFUSE_DRIVER_H_INCLUDED 23 | #define WINFUSE_DRIVER_H_INCLUDED 24 | 25 | #include 26 | 27 | #define FUSE_FSCTL_TRANSACT \ 28 | CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0xC00 + 'F', METHOD_BUFFERED, FILE_ANY_ACCESS) 29 | 30 | extern FSP_FSEXT_PROVIDER FuseProvider; 31 | static inline 32 | FUSE_INSTANCE *FuseInstanceFromDeviceObject(PDEVICE_OBJECT DeviceObject) 33 | { 34 | return (PVOID)((PUINT8)DeviceObject->DeviceExtension + FuseProvider.DeviceExtensionOffset); 35 | } 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /tools/gensrc/errno.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # run from Cygwin! 4 | 5 | gensrc() { 6 | echo "#if FUSE_ERRNO == 87 /* Windows */" 7 | echo 8 | vcvars="$(/mnt/c/Program\ Files\ \(x86\)/Microsoft\ Visual\ Studio/Installer/vswhere.exe -latest -find 'VC\**\vcvarsall.bat')" 9 | cmd /c "call" "$vcvars" "x64" "&&" cl /nologo /EP /C errno.src 2>/dev/null | sed -e '1,/beginbeginbeginbegin/d' -e 's/\r$//' 10 | echo 11 | echo "#elif FUSE_ERRNO == 67 /* Cygwin */" 12 | echo 13 | cpp -C -P errno.src | sed -e '1,/beginbeginbeginbegin/d' 14 | echo 15 | echo "#elif FUSE_ERRNO == 76 /* Linux */" 16 | echo 17 | # this produces very strange results without the intermediate file (GitHub microsoft/WSL#5063 ?) 18 | wsl -- cpp -C -P errno.src \| sed -e '1,/beginbeginbeginbegin/d' \> errno.out 19 | cat errno.out 20 | echo 21 | echo "#endif" 22 | } 23 | 24 | cd $(dirname "$0") 25 | pwd 26 | 27 | ( 28 | echo '#include ' 29 | echo '/*beginbeginbeginbegin*/' 30 | awk '{ printf "case %s: return %s;\n", $1, $2 }' errno.txt 31 | ) > errno.src 32 | gensrc > ../../src/shared/km/errno.i 33 | 34 | ( 35 | echo '#include ' 36 | echo '/*beginbeginbeginbegin*/' 37 | awk '{ printf "case %s: return \"%s\";\n", $1, $1 }' errno.txt 38 | ) > errno.src 39 | gensrc > ../../src/shared/km/errnosym.i 40 | 41 | rm errno.src errno.out 42 | -------------------------------------------------------------------------------- /tools/build-sample.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | setlocal 4 | setlocal EnableDelayedExpansion 5 | 6 | if not X%1==X set Config=%1 7 | if not X%2==X set Arch=%2 8 | if not X%3==X set Sample=%3 9 | if not X%4==X set DestDir=%4 10 | 11 | if X!Sample!==X (echo usage: build-sample Config Arch Sample [DestDir] >&2 & goto fail) 12 | 13 | set SampleProj= 14 | for /F "tokens=1,2 delims=*" %%k in ("!Sample!") do ( 15 | set Sample=%%k 16 | set SampleProj=%%l 17 | ) 18 | 19 | set SampleDir=%~dp0..\tst 20 | if not exist "!SampleDir!\!Sample!" (echo sample !Sample! not found >&2 & goto fail) 21 | 22 | if not X!DestDir!==X ( 23 | if exist !DestDir! rmdir /s/q !DestDir! 24 | mkdir !DestDir! 25 | cd !DestDir! 26 | xcopy /s/e/q/y "!SampleDir!\!Sample!" . 27 | ) else ( 28 | cd "!SampleDir!\!Sample!" 29 | ) 30 | 31 | set vswhere="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" 32 | for /f "usebackq tokens=*" %%i in (`%vswhere% -find VC\**\vcvarsall.bat`) do ( 33 | call "%%i" x64 34 | ) 35 | 36 | if exist build rmdir /s/q build 37 | if X!SampleProj!==X ( 38 | devenv "!Sample!.sln" /build "!Config!|!Arch!" 39 | ) else ( 40 | devenv "!Sample!.sln" /build "!Config!|!Arch!" /project "!SampleProj!" 41 | ) 42 | if !ERRORLEVEL! neq 0 goto :fail 43 | 44 | exit /b 0 45 | 46 | :fail 47 | exit /b 1 48 | -------------------------------------------------------------------------------- /tools/build-libfuse.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | setlocal 4 | setlocal EnableDelayedExpansion 5 | 6 | if not X%1==X set Config=%1 7 | if not X%2==X set Arch=%2 8 | if not X%3==X set DESTDIR=%3 9 | 10 | if X!Config!==X (echo usage: build-libfuse Config Arch [DestDir] >&2 & goto fail) 11 | 12 | if X!Config!==XDebug set Buildtype=debug 13 | if X!Config!==XRelease set Buildtype=release 14 | 15 | rem ninja uses DESTDIR for installation 16 | if not X!DESTDIR!==X ( 17 | if exist !DESTDIR! rmdir /s/q !DESTDIR! 18 | mkdir !DESTDIR! 19 | pushd !DESTDIR! 20 | set DESTDIR=!cd! 21 | popd 22 | ) 23 | 24 | set vswhere="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" 25 | for /f "usebackq tokens=*" %%i in (`%vswhere% -find VC\**\vcvarsall.bat`) do ( 26 | call "%%i" !Arch! 27 | ) 28 | 29 | rem workaround getting Cygwin's meson/ninja 30 | set PATH=C:\Program Files\Meson;%PATH% 31 | 32 | cd %~dp0..\ext\libfuse 33 | if exist build\!Config!\!Arch! rmdir /s/q build\!Config!\!Arch! 34 | mkdir build\!Config!\!Arch! && cd build\!Config!\!Arch! 35 | 36 | meson --buildtype=!Buildtype! -D examples=false ..\..\.. 37 | if errorlevel 1 goto fail 38 | ninja 39 | if errorlevel 1 goto fail 40 | if not X!DestDir!==X ( 41 | ninja install 42 | if errorlevel 1 goto fail 43 | copy ..\..\..\LGPL2.txt !DestDir!\License.txt 44 | if errorlevel 1 goto fail 45 | ) 46 | 47 | rem The pkgconfig prefix in file !InstallDir!\lib\pkgconfig\fuse3.pc 48 | rem is erroneously set as: prefix=c:/ 49 | rem We should somehow set it to: prefix=${pcfiledir}/../.. 50 | 51 | exit /b 0 52 | 53 | :fail 54 | exit /b 1 55 | -------------------------------------------------------------------------------- /tools/DigiCert High Assurance EV Root CA.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFOzCCAyOgAwIBAgIKYSBNtAAAAAAAJzANBgkqhkiG9w0BAQUFADB/MQswCQYD 3 | VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe 4 | MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQDEyBNaWNyb3Nv 5 | ZnQgQ29kZSBWZXJpZmljYXRpb24gUm9vdDAeFw0xMTA0MTUxOTQ1MzNaFw0yMTA0 6 | MTUxOTU1MzNaMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx 7 | GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNVBAMTIkRpZ2lDZXJ0IEhp 8 | Z2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw 9 | ggEKAoIBAQDGzOVz5vvUu+UtLTKm3+WBP8nNJUm2cSrD1ZQ0Z6IKHLBfaaZAscS3 10 | so/QmKSpQVk609yU1jzbdDikSsxNJYL3SqVTEjju80ltcZF+Y7arpl/DpIT4T2JR 11 | vvjF7Ns4kuMG5QiRDMQoQVX7y1qJFX5x6DW/TXIJPb46OFBbdzEbjbPHJEWap6xt 12 | ABRaBLe6E+tRCphBQSJOZWGHgUFQpnlcid4ZSlfVLuZdHFMsfpjNGgYWpGhz0DQE 13 | E1yhcdNafFXbXmThN4cwVgTlEbQpgBLxeTmIogIRfCdmt4i3ePLKCqg4qwpkwr9m 14 | XZWEwaElHoddGlALIBLMQbtuC1E4uEvLAgMBAAGjgcswgcgwEQYDVR0gBAowCDAG 15 | BgRVHSAAMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSx 16 | PsNpA/i/RwHUmCYaCALvY2QrwzAfBgNVHSMEGDAWgBRi+wohW39DbhHaCVRQa/XS 17 | lnHxnjBVBgNVHR8ETjBMMEqgSKBGhkRodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v 18 | cGtpL2NybC9wcm9kdWN0cy9NaWNyb3NvZnRDb2RlVmVyaWZSb290LmNybDANBgkq 19 | hkiG9w0BAQUFAAOCAgEAIIzBWe1vnGstwUo+dR1FTEFQHL2A6tmwkosGKhM/Uxae 20 | VjlqimO2eCR59X24uUehCpbC9su9omafBuGs0nkJDv083KwCDHCvPxvseH7U60sF 21 | YCbZc2GRIe2waGPglxKrb6AS7dmf0tonPLPkVvnR1IEPcb1CfKaJ3M3VvZWiq/GT 22 | EX3orDEpqF1mcEGd/HXJ1bMaOSrQhQVQi6yRysSTy3GlnaSUb1gM+m4gxAgxtYWd 23 | foH50j3KWxiFbAqG7CIJG6V0NE9/KLyVSqsdtpiwXQmkd3Z+76eOXYT2GCTL0W2m 24 | w6GcwhB1gP+dMv3mz0M6gvfOj+FyKptit1/tlRo5XC+UbUi3AV8zL7vcLXM0iQRC 25 | ChyLefmj+hfv+qEaEN/gssGV61wMBZc7NT4YiE3bbL8kiY3Ivdifezk6JKDV39Hz 26 | ShqX9qZveh+wkKmzrAE5kdNht2TxPlc4A6/OetK1kPWu3DmZ1bY8l+2myxbHfWsq 27 | TJCU5kxU/R7NIOzOaJyHWOlhYL7rDsnVGX2f6Xi9DqwhdQePqW7gjGoqa5zj52W8 28 | vC08bdwE3GdFNjKvBIG8qABuYUyVxVzUjo6fL8EydL29EWUDB83vt14CV9qG1Boo 29 | NK+ISbLPpd2CVm9oqhTiWVT+/+ru7+qScCJggeMlI8CfzA9JsjWqWMM6w9kWlBA= 30 | -----END CERTIFICATE----- 31 | -------------------------------------------------------------------------------- /tst/memfs-fuse3/memfs-fuse3.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30204.135 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "memfs-fuse3.exe", "memfs-fuse3.exe.vcxproj", "{CF538F42-C714-4653-B351-E72FD7B0B217}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "memfs-fuse3.out", "memfs-fuse3.out.vcxproj", "{38AD1D24-7745-4B6E-9928-BD9731FED72A}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x64.ActiveCfg = Debug|x64 19 | {CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x64.Build.0 = Debug|x64 20 | {CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x86.ActiveCfg = Debug|Win32 21 | {CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x86.Build.0 = Debug|Win32 22 | {CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x64.ActiveCfg = Release|x64 23 | {CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x64.Build.0 = Release|x64 24 | {CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x86.ActiveCfg = Release|Win32 25 | {CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x86.Build.0 = Release|Win32 26 | {38AD1D24-7745-4B6E-9928-BD9731FED72A}.Debug|x64.ActiveCfg = Debug|x64 27 | {38AD1D24-7745-4B6E-9928-BD9731FED72A}.Debug|x86.ActiveCfg = Debug|x64 28 | {38AD1D24-7745-4B6E-9928-BD9731FED72A}.Release|x64.ActiveCfg = Release|x64 29 | {38AD1D24-7745-4B6E-9928-BD9731FED72A}.Release|x86.ActiveCfg = Release|x64 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {F8910224-C4C3-4B29-9EB9-AEBDDBFCC250} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /tst/lockdly/lockdly.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Description: 3 | * On debug builds of the WinFuse driver, when the special file \$LOCKDLY is created/opened 4 | * an artificial delay is introduced. This allows enough time for forcible termination of the 5 | * file system (e.g. using Ctrl-C). If using non-alertable locks (kernel ERESOURCE) then the 6 | * file system process should become unkillable and the lockdly.exe process should hang. If our 7 | * custom implementation of alertable read-write locks (using semaphores and alertable waits) 8 | * is used, then this should not happen. 9 | * 10 | * Compile: 11 | * - cl lockdly.c 12 | * 13 | * Reproduce: 14 | * - lockdly.exe 15 | * - Quickly kill the file system process (e.g. switch to its command windows and Ctrl-C). 16 | * 17 | * Correct behavior: 18 | * - Normal program termination. 19 | * 20 | * Incorrect behavior: 21 | * - File system process and/or lockdly.exe process hang and become unkillable. 22 | */ 23 | 24 | #include 25 | 26 | DWORD WINAPI ThreadProc(PVOID Param) 27 | { 28 | HANDLE Handle = CreateFileW( 29 | L"$LOCKDLY", 30 | GENERIC_READ | GENERIC_WRITE, 31 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 32 | 0, 33 | CREATE_ALWAYS, 34 | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, 35 | 0); 36 | if (INVALID_HANDLE_VALUE == Handle) 37 | return 1; 38 | 39 | return 0; 40 | } 41 | 42 | int wmain(int argc, wchar_t *argv[]) 43 | { 44 | HANDLE Thread = CreateThread(0, 0, ThreadProc, 0, 0, 0); 45 | if (0 == Thread) 46 | return 1; 47 | 48 | Sleep(1000); 49 | 50 | HANDLE Handle = CreateFileW( 51 | L"$LOCKDLY", 52 | GENERIC_READ | GENERIC_WRITE, 53 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 54 | 0, 55 | OPEN_EXISTING, 56 | 0, 57 | 0); 58 | if (INVALID_HANDLE_VALUE == Handle) 59 | return 1; 60 | 61 | WaitForSingleObject(Thread, INFINITE); 62 | 63 | DWORD ExitCode; 64 | GetExitCodeThread(Thread, &ExitCode); 65 | return ExitCode; 66 | } 67 | -------------------------------------------------------------------------------- /tools/gensrc/errno.txt: -------------------------------------------------------------------------------- 1 | 0 STATUS_SUCCESS 2 | EPERM STATUS_ACCESS_DENIED 3 | ENOENT STATUS_OBJECT_NAME_NOT_FOUND 4 | ESRCH STATUS_PROCEDURE_NOT_FOUND 5 | EINTR STATUS_CANCELLED 6 | EIO STATUS_IO_DEVICE_ERROR 7 | ENXIO STATUS_FILE_INVALID 8 | E2BIG STATUS_INSUFFICIENT_RESOURCES 9 | ENOEXEC STATUS_INVALID_IMAGE_FORMAT 10 | EBADF STATUS_INVALID_HANDLE 11 | ENOMEM STATUS_INSUFFICIENT_RESOURCES 12 | EACCES STATUS_ACCESS_DENIED 13 | EFAULT STATUS_ACCESS_VIOLATION 14 | EBUSY STATUS_DEVICE_BUSY 15 | EEXIST STATUS_OBJECT_NAME_COLLISION 16 | EXDEV STATUS_NOT_SAME_DEVICE 17 | ENODEV STATUS_NO_SUCH_DEVICE 18 | ENOTDIR STATUS_NOT_A_DIRECTORY 19 | EISDIR STATUS_FILE_IS_A_DIRECTORY 20 | EINVAL STATUS_INVALID_PARAMETER 21 | ENFILE STATUS_TOO_MANY_OPENED_FILES 22 | EMFILE STATUS_TOO_MANY_OPENED_FILES 23 | EFBIG STATUS_DISK_FULL 24 | ENOSPC STATUS_DISK_FULL 25 | ESPIPE STATUS_INVALID_PARAMETER 26 | EROFS STATUS_MEDIA_WRITE_PROTECTED 27 | EMLINK STATUS_TOO_MANY_LINKS 28 | EPIPE STATUS_PIPE_BROKEN 29 | EDOM STATUS_INVALID_PARAMETER 30 | ERANGE STATUS_INVALID_PARAMETER 31 | EDEADLK STATUS_POSSIBLE_DEADLOCK 32 | ENAMETOOLONG STATUS_NAME_TOO_LONG 33 | ENOLCK STATUS_LOCK_NOT_GRANTED 34 | ENOSYS STATUS_INVALID_DEVICE_REQUEST 35 | ENOTEMPTY STATUS_DIRECTORY_NOT_EMPTY 36 | EILSEQ STATUS_INVALID_PARAMETER 37 | EADDRINUSE STATUS_ADDRESS_ALREADY_ASSOCIATED 38 | EALREADY STATUS_CONNECTION_ACTIVE 39 | ECANCELED STATUS_CANCELLED 40 | ECONNABORTED STATUS_CONNECTION_ABORTED 41 | ECONNREFUSED STATUS_CONNECTION_REFUSED 42 | ECONNRESET STATUS_CONNECTION_RESET 43 | EHOSTUNREACH STATUS_HOST_UNREACHABLE 44 | EISCONN STATUS_CONNECTION_ACTIVE 45 | ELOOP STATUS_REPARSE_POINT_NOT_RESOLVED 46 | ENETDOWN STATUS_HOST_DOWN 47 | ENETRESET STATUS_CONNECTION_RESET 48 | ENETUNREACH STATUS_NETWORK_UNREACHABLE 49 | ENOBUFS STATUS_INSUFFICIENT_RESOURCES 50 | ENODATA STATUS_END_OF_FILE 51 | ENOLINK STATUS_CONNECTION_INVALID 52 | ENOTCONN STATUS_CONNECTION_INVALID 53 | ENOTSOCK STATUS_INVALID_HANDLE 54 | ETIMEDOUT STATUS_TRANSACTION_TIMED_OUT 55 | -------------------------------------------------------------------------------- /src/shared/km/file.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file shared/km/file.c 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #include 23 | 24 | VOID FuseFileInstanceInit(FUSE_INSTANCE *Instance) 25 | { 26 | KeInitializeSpinLock(&Instance->FileListLock); 27 | InitializeListHead(&Instance->FileList); 28 | } 29 | 30 | VOID FuseFileInstanceFini(FUSE_INSTANCE *Instance) 31 | { 32 | FUSE_FILE *File; 33 | 34 | for (PLIST_ENTRY Entry = Instance->FileList.Flink; &Instance->FileList != Entry;) 35 | { 36 | File = CONTAINING_RECORD(Entry, FUSE_FILE, ListEntry); 37 | Entry = Entry->Flink; 38 | FuseCacheDereferenceItem(Instance->Cache, File->CacheItem); 39 | FuseFree(File); 40 | } 41 | } 42 | 43 | NTSTATUS FuseFileCreate(FUSE_INSTANCE *Instance, FUSE_FILE **PFile) 44 | { 45 | KIRQL Irql; 46 | FUSE_FILE *File; 47 | 48 | *PFile = 0; 49 | 50 | File = FuseAllocNonPaged(sizeof *File); 51 | /* spinlocks must operate on non-paged memory */ 52 | if (0 == File) 53 | return STATUS_INSUFFICIENT_RESOURCES; 54 | 55 | RtlZeroMemory(File, sizeof *File); 56 | 57 | KeAcquireSpinLock(&Instance->FileListLock, &Irql); 58 | InsertTailList(&Instance->FileList, &File->ListEntry); 59 | KeReleaseSpinLock(&Instance->FileListLock, Irql); 60 | 61 | *PFile = File; 62 | 63 | return STATUS_SUCCESS; 64 | } 65 | 66 | VOID FuseFileDelete(FUSE_INSTANCE *Instance, FUSE_FILE *File) 67 | { 68 | KIRQL Irql; 69 | 70 | KeAcquireSpinLock(&Instance->FileListLock, &Irql); 71 | RemoveEntryList(&File->ListEntry); 72 | KeReleaseSpinLock(&Instance->FileListLock, Irql); 73 | 74 | FuseCacheDereferenceItem(Instance->Cache, File->CacheItem); 75 | 76 | DEBUGFILL(File, sizeof *File); 77 | FuseFree(File); 78 | } 79 | -------------------------------------------------------------------------------- /ext/tlib/callstack.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tlib/callstack.c 3 | * 4 | * @copyright 2014-2019 Bill Zissimopoulos 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #if defined(_WIN32) || defined(_WIN64) 11 | #include 12 | #include 13 | #pragma comment(lib, "dbghelp.lib") 14 | #elif defined(__APPLE__) || defined(__linux__) 15 | #include 16 | #include 17 | #else 18 | #error tlib/callstack.c not implemented for this platform 19 | #endif 20 | 21 | void tlib_callstack(size_t skip, size_t count, struct tlib_callstack_s *stack) 22 | { 23 | if (count > TLIB_MAX_SYMRET) 24 | count = TLIB_MAX_SYMRET; 25 | size_t naddrs = skip + count; 26 | if (naddrs > TLIB_MAX_SYMCAP) 27 | naddrs = TLIB_MAX_SYMCAP; 28 | memset((void *)stack->syms, 0, sizeof stack->syms); 29 | #if defined(_WIN32) || defined(_WIN64) 30 | /* SymInitialize()/SymFromAddr() are not thread-safe. Furthermore SymInitialize() should 31 | * not be called more than once per process. 32 | */ 33 | HANDLE hproc = GetCurrentProcess(); 34 | static SYMBOL_INFO *syminfo = 0; 35 | void *addrs[TLIB_MAX_SYMCAP]; 36 | size_t i = 0; 37 | if (0 == syminfo) 38 | { 39 | syminfo = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO) + TLIB_MAX_SYMLEN); 40 | if (0 == syminfo) 41 | return; 42 | SymInitialize(hproc, 0, TRUE); 43 | } 44 | memset(syminfo, 0, sizeof(SYMBOL_INFO)); 45 | syminfo->SizeOfStruct = sizeof(SYMBOL_INFO); 46 | syminfo->MaxNameLen = TLIB_MAX_SYMLEN; 47 | naddrs = CaptureStackBackTrace(0, naddrs, addrs, 0); 48 | for (void **p = addrs + skip, **endp = addrs + naddrs; endp > p; p++) 49 | { 50 | DWORD64 displacement = 0; 51 | if (SymFromAddr(hproc, (DWORD64)*p, &displacement, syminfo)) 52 | { 53 | stack->syms[i] = stack->symbuf[i]; 54 | strncpy(stack->symbuf[i], syminfo->Name, TLIB_MAX_SYMLEN); 55 | stack->symbuf[i][TLIB_MAX_SYMLEN] = '\0'; 56 | i++; 57 | } 58 | } 59 | #elif defined(__APPLE__) || defined(__linux__) 60 | void *addrs[TLIB_MAX_SYMCAP]; 61 | naddrs = backtrace(addrs, naddrs); 62 | size_t i = 0; 63 | Dl_info syminfo; 64 | for (void **p = addrs + skip, **endp = addrs + naddrs; endp > p; p++) 65 | { 66 | if (dladdr(*p, &syminfo) && 0 != syminfo.dli_sname) 67 | { 68 | stack->syms[i] = stack->symbuf[i]; 69 | strncpy(stack->symbuf[i], syminfo.dli_sname, TLIB_MAX_SYMLEN); 70 | stack->symbuf[i][TLIB_MAX_SYMLEN] = '\0'; 71 | i++; 72 | } 73 | } 74 | #endif 75 | } 76 | -------------------------------------------------------------------------------- /src/shared/km/context.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file shared/km/context.c 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #include 23 | 24 | VOID FuseContextCreate(FUSE_CONTEXT **PContext, 25 | FUSE_INSTANCE *Instance, FSP_FSCTL_TRANSACT_REQ *InternalRequest); 26 | VOID FuseContextDelete(FUSE_CONTEXT *Context); 27 | 28 | #ifdef ALLOC_PRAGMA 29 | #pragma alloc_text(PAGE, FuseContextCreate) 30 | #pragma alloc_text(PAGE, FuseContextDelete) 31 | #endif 32 | 33 | VOID FuseContextCreate(FUSE_CONTEXT **PContext, 34 | FUSE_INSTANCE *Instance, FSP_FSCTL_TRANSACT_REQ *InternalRequest) 35 | { 36 | PAGED_CODE(); 37 | 38 | FUSE_CONTEXT *Context; 39 | UINT32 Kind = 0 == InternalRequest ? 40 | FspFsctlTransactReservedKind : InternalRequest->Kind; 41 | 42 | ASSERT(FspFsctlTransactKindCount > Kind); 43 | if (0 == FuseOperations[Kind].Proc) 44 | { 45 | *PContext = FuseContextStatus(STATUS_INVALID_DEVICE_REQUEST); 46 | return; 47 | } 48 | 49 | Context = FuseAlloc(sizeof *Context); 50 | if (0 == Context) 51 | { 52 | *PContext = FuseContextStatus(STATUS_INSUFFICIENT_RESOURCES); 53 | return; 54 | } 55 | 56 | RtlZeroMemory(Context, sizeof *Context); 57 | Context->Instance = Instance; 58 | Context->InternalRequest = InternalRequest; 59 | Context->InternalResponse = (PVOID)&Context->InternalResponseBuf; 60 | Context->InternalResponse->Size = sizeof(FSP_FSCTL_TRANSACT_RSP); 61 | Context->InternalResponse->Kind = Kind; 62 | Context->InternalResponse->Hint = 0 != InternalRequest ? InternalRequest->Hint : 0; 63 | *PContext = Context; 64 | } 65 | 66 | VOID FuseContextDelete(FUSE_CONTEXT *Context) 67 | { 68 | PAGED_CODE(); 69 | 70 | if (FuseOpGuardTrue == Context->OpGuardResult) 71 | { 72 | UINT32 Kind = 0 == Context->InternalRequest ? 73 | FspFsctlTransactReservedKind : Context->InternalRequest->Kind; 74 | FuseOperations[Kind].Guard(Context, FALSE); 75 | } 76 | 77 | if (0 != Context->Fini) 78 | Context->Fini(Context); 79 | if (0 != Context->InternalRequest) 80 | FuseFree(Context->InternalRequest); 81 | if ((PVOID)&Context->InternalResponseBuf != Context->InternalResponse) 82 | FuseFree(Context->InternalResponse); 83 | 84 | DEBUGFILL(Context, sizeof *Context); 85 | FuseFree(Context); 86 | } 87 | -------------------------------------------------------------------------------- /src/shared/km/path.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file shared/km/path.c 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #include 23 | 24 | VOID FusePosixPathPrefix(PSTRING Path, PSTRING Prefix, PSTRING Remain); 25 | VOID FusePosixPathSuffix(PSTRING Path, PSTRING Remain, PSTRING Suffix); 26 | 27 | #ifdef ALLOC_PRAGMA 28 | #pragma alloc_text(PAGE, FusePosixPathPrefix) 29 | #pragma alloc_text(PAGE, FusePosixPathSuffix) 30 | #endif 31 | 32 | VOID FusePosixPathPrefix(PSTRING Path, PSTRING Prefix, PSTRING Remain) 33 | { 34 | PAGED_CODE(); 35 | 36 | STRING PathBuf, PrefixBuf, RemainBuf; 37 | 38 | PathBuf = *Path; 39 | if (0 == Prefix) 40 | Prefix = &PrefixBuf; 41 | if (0 == Remain) 42 | Remain = &RemainBuf; 43 | 44 | PSTR P = PathBuf.Buffer, EndP = P + PathBuf.Length / sizeof(*P); 45 | 46 | if (EndP > P && '/' == *P) 47 | { 48 | Prefix->Length = 1; 49 | Prefix->Buffer = PathBuf.Buffer; 50 | } 51 | else 52 | { 53 | while (EndP > P && '/' != *P) 54 | P++; 55 | 56 | Prefix->Length = (USHORT)((P - PathBuf.Buffer) * sizeof *P); 57 | Prefix->Buffer = PathBuf.Buffer; 58 | } 59 | 60 | while (EndP > P && '/' == *P) 61 | P++; 62 | 63 | Remain->Length = (USHORT)((EndP - P) * sizeof *P); 64 | Remain->Buffer = P; 65 | 66 | Prefix->MaximumLength = Prefix->Length; 67 | Remain->MaximumLength = Remain->Length; 68 | } 69 | 70 | VOID FusePosixPathSuffix(PSTRING Path, PSTRING Remain, PSTRING Suffix) 71 | { 72 | PAGED_CODE(); 73 | 74 | STRING PathBuf, RemainBuf, SuffixBuf; 75 | 76 | PathBuf = *Path; 77 | if (0 == Remain) 78 | Remain = &RemainBuf; 79 | if (0 == Suffix) 80 | Suffix = &SuffixBuf; 81 | 82 | PSTR P = PathBuf.Buffer, EndP = P + PathBuf.Length / sizeof(*P); 83 | 84 | Remain->Length = PathBuf.Length; 85 | Remain->Buffer = PathBuf.Buffer; 86 | 87 | Suffix->Length = 0; 88 | Suffix->Buffer = PathBuf.Buffer; 89 | 90 | while (EndP > P) 91 | if ('/' == *P) 92 | { 93 | Remain->Length = (USHORT)((P - PathBuf.Buffer) * sizeof *P); 94 | if (0 == Remain->Length) 95 | Remain->Length = 1; 96 | 97 | while (EndP > P && '/' == *P) 98 | P++; 99 | 100 | Suffix->Length = (USHORT)((EndP - P) * sizeof *P); 101 | Suffix->Buffer = P; 102 | } 103 | else 104 | P++; 105 | 106 | Remain->MaximumLength = Remain->Length; 107 | Suffix->MaximumLength = Suffix->Length; 108 | } 109 | -------------------------------------------------------------------------------- /src/shared/km/util.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file shared/km/util.c 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #include 23 | 24 | PVOID FuseAllocatePoolMustSucceed(POOL_TYPE PoolType, SIZE_T Size, ULONG Tag); 25 | NTSTATUS FuseSafeCopyMemory(PVOID Dst, PVOID Src, ULONG Len); 26 | NTSTATUS FuseGetTokenUid(PACCESS_TOKEN Token, TOKEN_INFORMATION_CLASS InfoClass, PUINT32 PUid); 27 | 28 | #ifdef ALLOC_PRAGMA 29 | // !#pragma alloc_text(PAGE, FuseAllocatePoolMustSucceed) 30 | #pragma alloc_text(PAGE, FuseSafeCopyMemory) 31 | #pragma alloc_text(PAGE, FuseGetTokenUid) 32 | #endif 33 | 34 | static const LONG Delays[] = 35 | { 36 | 10/*ms*/ * -10000, 37 | 10/*ms*/ * -10000, 38 | 50/*ms*/ * -10000, 39 | 50/*ms*/ * -10000, 40 | 100/*ms*/ * -10000, 41 | 100/*ms*/ * -10000, 42 | 300/*ms*/ * -10000, 43 | }; 44 | 45 | PVOID FuseAllocatePoolMustSucceed(POOL_TYPE PoolType, SIZE_T Size, ULONG Tag) 46 | { 47 | // !PAGED_CODE(); 48 | 49 | PVOID Result; 50 | LARGE_INTEGER Delay; 51 | 52 | for (ULONG i = 0, n = sizeof(Delays) / sizeof(Delays[0]);; i++) 53 | { 54 | Result = DEBUGTEST(99) ? ExAllocatePoolWithTag(PoolType, Size, Tag) : 0; 55 | if (0 != Result) 56 | return Result; 57 | 58 | Delay.QuadPart = n > i ? Delays[i] : Delays[n - 1]; 59 | KeDelayExecutionThread(KernelMode, FALSE, &Delay); 60 | } 61 | } 62 | 63 | NTSTATUS FuseSafeCopyMemory(PVOID Dst, PVOID Src, ULONG Len) 64 | { 65 | PAGED_CODE(); 66 | 67 | try 68 | { 69 | RtlCopyMemory(Dst, Src, Len); 70 | return STATUS_SUCCESS; 71 | } 72 | except (EXCEPTION_EXECUTE_HANDLER) 73 | { 74 | NTSTATUS Result = GetExceptionCode(); 75 | return FsRtlIsNtstatusExpected(Result) ? STATUS_INVALID_USER_BUFFER : Result; 76 | } 77 | } 78 | 79 | NTSTATUS FuseGetTokenUid(PACCESS_TOKEN Token, TOKEN_INFORMATION_CLASS InfoClass, PUINT32 PUid) 80 | { 81 | PAGED_CODE(); 82 | 83 | NTSTATUS Result; 84 | PVOID Info = 0; 85 | PSID *PSid; 86 | 87 | Result = SeQueryInformationToken(Token, InfoClass, &Info); 88 | if (!NT_SUCCESS(Result)) 89 | goto exit; 90 | 91 | switch (InfoClass) 92 | { 93 | case TokenUser: 94 | PSid = &((PTOKEN_USER)Info)->User.Sid; 95 | break; 96 | case TokenOwner: 97 | PSid = &((PTOKEN_OWNER)Info)->Owner; 98 | break; 99 | case TokenPrimaryGroup: 100 | PSid = &((PTOKEN_PRIMARY_GROUP)Info)->PrimaryGroup; 101 | break; 102 | default: 103 | ASSERT(0); 104 | Result = STATUS_INVALID_PARAMETER; 105 | goto exit; 106 | } 107 | 108 | Result = FspPosixMapSidToUid(*PSid, PUid); 109 | 110 | exit: 111 | if (0 != Info) 112 | FuseFreeExternal(Info); 113 | 114 | return Result; 115 | } 116 | -------------------------------------------------------------------------------- /tst/memfs-fuse3/compat.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file compat.h 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #ifndef COMPAT_H_INCLUDED 23 | #define COMPAT_H_INCLUDED 24 | 25 | #if defined(_WIN32) && defined(FSP_FUSE_SYM) 26 | #include 27 | #undef fuse_main 28 | #define fuse_main(argc, argv, ops, data)\ 29 | (FspLoad(0), fuse_main_real(argc, argv, ops, sizeof *(ops), data)) 30 | #endif 31 | 32 | #if !defined(_WIN32) && !defined(fuse_stat) 33 | 34 | typedef uint64_t fuse_ino_t; 35 | 36 | #define fuse_uid_t uid_t 37 | #define fuse_gid_t gid_t 38 | #define fuse_pid_t pid_t 39 | 40 | #define fuse_dev_t dev_t 41 | #define fuse_mode_t mode_t 42 | #define fuse_nlink_t nlink_t 43 | #define fuse_off_t off_t 44 | 45 | #define fuse_fsblkcnt_t fsblkcnt_t 46 | #define fuse_fsfilcnt_t fsfilcnt_t 47 | #define fuse_blksize_t blksize_t 48 | #define fuse_blkcnt_t blkcnt_t 49 | 50 | #define fuse_timespec timespec 51 | 52 | #define fuse_stat stat 53 | 54 | #define fuse_statvfs statvfs 55 | 56 | #define fuse_flock flock 57 | 58 | #define fuse_iovec iovec 59 | 60 | #endif 61 | 62 | #if !defined(S_IFMT) 63 | #define S_IFMT 0170000 64 | #endif 65 | #if !defined(S_IFDIR) 66 | #define S_IFDIR 0040000 67 | #endif 68 | #if !defined(S_IFCHR) 69 | #define S_IFCHR 0020000 70 | #endif 71 | #if !defined(S_IFBLK) 72 | #define S_IFBLK 0060000 73 | #endif 74 | #if !defined(S_IFREG) 75 | #define S_IFREG 0100000 76 | #endif 77 | #if !defined(S_IFLNK) 78 | #define S_IFLNK 0120000 79 | #endif 80 | #if !defined(S_IFSOCK) 81 | #define S_IFSOCK 0140000 82 | #endif 83 | #if !defined(S_IFIFO) 84 | #define S_IFIFO 0010000 85 | #endif 86 | 87 | #if defined(__APPLE__) 88 | #define st_atim st_atimespec 89 | #define st_ctim st_ctimespec 90 | #define st_mtim st_mtimespec 91 | #endif 92 | 93 | #if defined(__APPLE__) || defined(__linux__) || defined(__CYGWIN__) 94 | #include 95 | #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(_WIN32) 96 | #define XATTR_CREATE 1 97 | #define XATTR_REPLACE 2 98 | #endif 99 | 100 | #if !defined(ENOATTR) 101 | #define ENOATTR ENODATA 102 | #elif !defined(ENODATA) 103 | #define ENODATA ENOATTR 104 | #endif 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /ext/tlib/testsuite.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tlib/testsuite.h 3 | * 4 | * @copyright 2014-2019 Bill Zissimopoulos 5 | */ 6 | 7 | #ifndef TLIB_TESTSUITE_H_INCLUDED 8 | #define TLIB_TESTSUITE_H_INCLUDED 9 | 10 | #include 11 | 12 | /** 13 | * Assert macro 14 | * 15 | * This macro works similarly to the Standard C assert macro, except for the following differences: 16 | * 17 | *
    18 | *
  • The macro always checks the specified expression regardless of the NDEBUG macro.
  • 19 | *
  • The macro aborts the execution of the current test, but not necessarily the execution of the 20 | * whole testsuite.
  • 21 | *
22 | */ 23 | #define ASSERT(expr)\ 24 | (!(expr) ? (tlib__assert(__func__, __FILE__, __LINE__, #expr), abort()) : (void)0) 25 | 26 | /** 27 | * Register a test suite for execution. 28 | * 29 | * Test suites are simple functions with prototype void testsuite(). When executed 30 | * they register individual test cases for execution. 31 | */ 32 | #define TESTSUITE(fn)\ 33 | do\ 34 | {\ 35 | void fn(void);\ 36 | tlib_add_test_suite(#fn, fn);\ 37 | } while (0) 38 | 39 | /** 40 | * Register a test case for execution. 41 | * 42 | * Test cases are simple functions with prototype void testcase(). 43 | */ 44 | #define TEST(fn)\ 45 | do\ 46 | {\ 47 | void fn(void);\ 48 | tlib_add_test(#fn, fn);\ 49 | } while (0) 50 | 51 | /** 52 | * Register an optional test case for execution. 53 | * 54 | * Test cases are simple functions with prototype void testcase(). 55 | * Optional tests are not executed by default. 56 | */ 57 | #define TEST_OPT(fn)\ 58 | do\ 59 | {\ 60 | void fn(void);\ 61 | tlib_add_test_opt(#fn, fn);\ 62 | } while (0) 63 | 64 | void tlib_add_test_suite(const char *name, void (*fn)(void)); 65 | void tlib_add_test(const char *name, void (*fn)(void)); 66 | void tlib_add_test_opt(const char *name, void (*fn)(void)); 67 | 68 | /** 69 | * Printf function. 70 | * 71 | * Use this to produce output in the appropriate tlib file stream. This function uses the local 72 | * printf facilities and understands the same format strings. 73 | */ 74 | #if defined(__GNUC__) 75 | void tlib_printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); 76 | #else 77 | void tlib_printf(const char *fmt, ...); 78 | #endif 79 | /** 80 | * Run tests. 81 | * 82 | * This function will first execute all registered test suites, thus giving them the chance to 83 | * register any test cases. It will then execute all registered test cases according to the 84 | * command line arguments passed in argv. The command line syntax is a follows: 85 | * 86 | * Usage: testprog [--list][[--tap][--no-abort][--repeat-forever] [[+-]TESTNAME...] 87 | * 88 | *
    89 | *
  • --list - list tests only
  • 90 | *
  • --tap - produce output in TAP format
  • 91 | *
  • --no-abort - do not abort all tests when an ASSERT fails 92 | * (only the current test is aborted)
  • 93 | *
  • --repeat-forever - repeat tests forever
  • 94 | *
95 | * 96 | * By default all test cases are executed unless specific test cases are named. By default optional 97 | * test cases are not executed. To execute a specific test case specify its TESTNAME; if it is an 98 | * optional test case specify +TESTNAME. To excluse a test case specify -TESTNAME. 99 | * 100 | * TESTNAME may also contain a single asterisk at the end; for example, mytest* will match all test 101 | * cases that have names starting with "mytest". 102 | * 103 | * @param argc 104 | * Argument count. 105 | * @param argv 106 | * Argument vector. 107 | */ 108 | void tlib_run_tests(int argc, char *argv[]); 109 | 110 | void tlib__assert(const char *func, const char *file, int line, const char *expr); 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /tst/memfs-fuse3/memfs-fuse3.out.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {38ad1d24-7745-4b6e-9928-bd9731fed72a} 15 | Linux 16 | memfs_fuse3_out 17 | 15.0 18 | Linux 19 | 1.0 20 | Generic 21 | {2238F9CD-F817-4ECC-BD14-2524D2669B35} 22 | 23 | 24 | 25 | true 26 | WSL_1_0 27 | 28 | 29 | false 30 | WSL_1_0 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | $(SolutionDir)build\$(Configuration)\ 39 | $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ 40 | $(SolutionName) 41 | 42 | 43 | $(SolutionDir)build\$(Configuration)\ 44 | $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ 45 | $(SolutionName) 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | /usr/include/fuse3;%(AdditionalIncludeDirectories) 56 | FUSE_USE_VERSION=32; 57 | c++17 58 | 59 | 60 | fuse3 61 | 62 | 63 | 64 | 65 | /usr/include/fuse3;%(AdditionalIncludeDirectories) 66 | FUSE_USE_VERSION=32;NDEBUG;%(PreprocessorDefinitions) 67 | c++17 68 | 69 | 70 | fuse3 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /tools/deploy.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | setlocal 4 | setlocal EnableDelayedExpansion 5 | 6 | set Config=Debug 7 | set Suffix=x64 8 | set Deploy=C:\Deploy\winfuse 9 | set Target=Win10DBG 10 | set Chkpnt=winfuse 11 | if not X%1==X set Target=%1 12 | if not X%2==X set Chkpnt=%2 13 | 14 | ( 15 | echo sc create WinFsp type=filesys binPath=%%~dp0winfsp-%SUFFIX%.sys 16 | echo sc create LxLdr type=kernel binPath=%%~dp0lxldr.sys 17 | echo sc create WinFuse type=kernel binPath=%%~dp0winfuse-%SUFFIX%.sys 18 | echo sc create WslFuse type=kernel binPath=%%~dp0wslfuse-%SUFFIX%.sys 19 | echo reg add HKLM\Software\WinFsp\Fsext /v 00093118 /d "winfuse" /f /reg:32 20 | echo reg add HKLM\Software\LxDK\Services\wslfuse /v Depends /d "winfsp" /f 21 | echo sc start winfsp 22 | echo sc start lxldr 23 | ) >%~dp0..\build\VStudio\build\%Config%\deploy-setup.bat 24 | 25 | (set LF=^ 26 | %=this line is empty=% 27 | ) 28 | ( 29 | set /p =sudo mknod /dev/fuse c 10 229!LF! 30 | set /p =sudo chmod a+w /dev/fuse!LF! 31 | set /p =sudo cp fusermount.out /usr/bin/fusermount!LF! 32 | set /p =sudo cp fusermount.out /usr/bin/fusermount3!LF! 33 | set /p =sudo cp fusermount-helper.exe /usr/bin/fusermount-helper.exe!LF! 34 | set /p =sudo chmod u+s /usr/bin/fusermount!LF! 35 | set /p =sudo chmod u+s /usr/bin/fusermount3!LF! 36 | ) %~dp0..\build\VStudio\build\%Config%\deploy-setup.sh 37 | 38 | if exist %~dp0..\ext\winfsp\build\VStudio\build\%Config% ( 39 | set WINFSP=%~dp0..\ext\winfsp\build\VStudio\build\%Config%\ 40 | ) else ( 41 | set RegKey="HKLM\SOFTWARE\WinFsp" 42 | set RegVal="InstallDir" 43 | reg query !RegKey! /v !RegVal! /reg:32 >nul 2>&1 44 | if !ERRORLEVEL! equ 0 ( 45 | for /f "tokens=2,*" %%i in ('reg query !RegKey! /v !RegVal! /reg:32 ^| findstr !RegVal!') do ( 46 | set WINFSP=%%jbin\ 47 | ) 48 | ) 49 | if not exist "!WINFSP!" (echo cannot find WinFsp installation >&2 & goto fail) 50 | ) 51 | 52 | if exist %~dp0..\ext\lxdk\build\VStudio\build\%Config% ( 53 | set LXDK=%~dp0..\ext\lxdk\build\VStudio\build\%Config%\ 54 | ) else ( 55 | set RegKey="HKLM\SOFTWARE\LxDK" 56 | set RegVal="InstallDir" 57 | reg query !RegKey! /v !RegVal! >nul 2>&1 58 | if !ERRORLEVEL! equ 0 ( 59 | for /f "tokens=2,*" %%i in ('reg query !RegKey! /v !RegVal! ^| findstr !RegVal!') do ( 60 | set LXDK=%%jbin\ 61 | ) 62 | ) 63 | if not exist "!LXDK!" (echo cannot find LxDK installation >&2 & goto fail) 64 | ) 65 | 66 | set MEMFS_FUSE3_EXE= 67 | if exist %~dp0..\tst\memfs-fuse3\build\%Config%\memfs-fuse3-x64.exe ( 68 | set MEMFS_FUSE3_EXE=memfs-fuse3-x64.exe fuse3-x64.dll 69 | ) 70 | set MEMFS_FUSE3_OUT= 71 | if exist %~dp0..\tst\memfs-fuse3\build\%Config%\memfs-fuse3.out ( 72 | set MEMFS_FUSE3_OUT=memfs-fuse3.out 73 | ) 74 | 75 | set Files= 76 | for %%f in ( 77 | %~dp0..\build\VStudio\build\%Config%\ 78 | winfuse-%Suffix%.sys 79 | wslfuse-%Suffix%.sys 80 | winfuse-tests-%Suffix%.exe 81 | wslfuse-tests.out 82 | fusermount.out 83 | fusermount-helper.exe 84 | deploy-setup.bat 85 | deploy-setup.sh 86 | %~dp0..\tst\memfs-fuse3\build\%Config%\ 87 | !MEMFS_FUSE3_EXE! 88 | !MEMFS_FUSE3_OUT! 89 | "!WINFSP!" 90 | winfsp-%Suffix%.sys 91 | winfsp-%Suffix%.dll 92 | "!LXDK!" 93 | lxldr.sys 94 | ) do ( 95 | set File=%%~f 96 | if [!File:~-1!] == [\] ( 97 | set Dir=!File! 98 | ) else ( 99 | if not [!Files!] == [] set Files=!Files!, 100 | set Files=!Files!'!Dir!!File!' 101 | ) 102 | ) 103 | 104 | powershell -NoProfile -ExecutionPolicy Bypass -Command "& '%~dp0deploy.ps1' -Name '%Target%' -CheckpointName '%Chkpnt%' -Files !Files! -Destination '%Deploy%'" 105 | -------------------------------------------------------------------------------- /tst/wslfuse-tests/mount-test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file mount-test.c 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | static void mount_createvol_dotest(wchar_t *Prefix) 31 | { 32 | WSLFUSE_IOCTL_CREATEVOLUME_ARG CreateArg = { .VolumeParams.Version = sizeof(FSP_FSCTL_VOLUME_PARAMS)}; 33 | int fusefd, res; 34 | 35 | if (0 != Prefix && L'\\' == Prefix[0] && L'\\' == Prefix[1]) 36 | { 37 | /* note that on Linux: sizeof(wchar_t) != sizeof(WCHAR) */ 38 | wchar_t *P = Prefix + 1; 39 | WCHAR *Q = CreateArg.VolumeParams.Prefix; 40 | while (0 != (*Q++ = (WCHAR)*P++)) 41 | ; 42 | } 43 | 44 | fusefd = open("/dev/fuse", O_RDWR); 45 | ASSERT(-1 != fusefd); 46 | 47 | res = ioctl(fusefd, WSLFUSE_IOCTL_CREATEVOLUME, &CreateArg); 48 | ASSERT(0 == res); 49 | ASSERT(0 == strncmp(CreateArg.VolumeName, "\\Device\\Volume{", 15)); 50 | 51 | res = close(fusefd); 52 | ASSERT(0 == res); 53 | } 54 | 55 | static void mount_createvol_test(void) 56 | { 57 | mount_createvol_dotest(0); 58 | mount_createvol_dotest(L"\\\\wslfuse-tests\\share"); 59 | } 60 | 61 | #if 0 62 | static void mount_unmount_test(void) 63 | { 64 | WSLFUSE_IOCTL_MOUNTID_ARG MountArg; 65 | int fusefd, res; 66 | 67 | fusefd = open("/dev/fuse", O_RDWR); 68 | ASSERT(-1 != fusefd); 69 | 70 | MountArg.Operation = '?'; 71 | MountArg.MountId = 0x42424242; 72 | res = ioctl(fusefd, WSLFUSE_IOCTL_MOUNTID, &MountArg); 73 | ASSERT(-1 == res); 74 | ASSERT(ENOENT == errno); 75 | 76 | MountArg.Operation = '-'; 77 | MountArg.MountId = 0x42424242; 78 | res = ioctl(fusefd, WSLFUSE_IOCTL_MOUNTID, &MountArg); 79 | ASSERT(-1 == res); 80 | ASSERT(ENOENT == errno); 81 | 82 | MountArg.Operation = '+'; 83 | MountArg.MountId = 0x42424242; 84 | res = ioctl(fusefd, WSLFUSE_IOCTL_MOUNTID, &MountArg); 85 | ASSERT(0 == res); 86 | 87 | MountArg.Operation = '+'; 88 | MountArg.MountId = 0x42424242; 89 | res = ioctl(fusefd, WSLFUSE_IOCTL_MOUNTID, &MountArg); 90 | ASSERT(-1 == res); 91 | ASSERT(EEXIST == errno); 92 | 93 | MountArg.Operation = '?'; 94 | MountArg.MountId = 0x42424242; 95 | res = ioctl(fusefd, WSLFUSE_IOCTL_MOUNTID, &MountArg); 96 | ASSERT(0 == res); 97 | 98 | MountArg.Operation = '?'; 99 | MountArg.MountId = 0x43434343; 100 | res = ioctl(fusefd, WSLFUSE_IOCTL_MOUNTID, &MountArg); 101 | ASSERT(-1 == res); 102 | ASSERT(ENOENT == errno); 103 | 104 | MountArg.Operation = '-'; 105 | MountArg.MountId = 0x42424242; 106 | res = ioctl(fusefd, WSLFUSE_IOCTL_MOUNTID, &MountArg); 107 | ASSERT(0 == res); 108 | 109 | MountArg.Operation = '-'; 110 | MountArg.MountId = 0x42424242; 111 | res = ioctl(fusefd, WSLFUSE_IOCTL_MOUNTID, &MountArg); 112 | ASSERT(-1 == res); 113 | ASSERT(ENOENT == errno); 114 | 115 | MountArg.Operation = '?'; 116 | MountArg.MountId = 0x42424242; 117 | res = ioctl(fusefd, WSLFUSE_IOCTL_MOUNTID, &MountArg); 118 | ASSERT(-1 == res); 119 | ASSERT(ENOENT == errno); 120 | 121 | res = close(fusefd); 122 | ASSERT(0 == res); 123 | } 124 | #endif 125 | 126 | void mount_tests(void) 127 | { 128 | TEST(mount_createvol_test); 129 | #if 0 130 | TEST(mount_unmount_test); 131 | #endif 132 | } 133 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | 3 | image: Visual Studio 2019 4 | 5 | environment: 6 | matrix: 7 | - CONFIGURATION: Debug 8 | WINFSP: rel 9 | LXDK: rel 10 | - CONFIGURATION: Release 11 | WINFSP: rel 12 | LXDK: rel 13 | 14 | init: 15 | #- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) 16 | 17 | install: 18 | - git submodule update --init --recursive 19 | # install WinFsp 20 | - appveyor AddMessage "Install WinFsp" -Category Information 21 | - if %WINFSP%==rel appveyor DownloadFile https://github.com/billziss-gh/winfsp/releases/download/v1.8B2/winfsp-1.8.20221.msi 22 | - if %WINFSP%==rel appveyor DownloadFile https://github.com/billziss-gh/winfsp/releases/download/v1.8B2/winfsp-tests-1.8.20221.zip 23 | - if %WINFSP%==src tools\build-winfsp.bat %CONFIGURATION% 24 | - if %WINFSP%==src copy ext\winfsp\build\VStudio\build\%CONFIGURATION%\winfsp-*.msi . 25 | - if %WINFSP%==src copy ext\winfsp\build\VStudio\build\%CONFIGURATION%\winfsp-tests-*.zip . 26 | - for %%f in ("winfsp-*.msi") do start /wait msiexec /i %%f /qn INSTALLLEVEL=1000 27 | - for %%f in ("winfsp-tests-*.zip") do powershell -command "Expand-Archive -Path %%f -DestinationPath 'C:\Program Files (x86)\WinFsp\bin'" 28 | # install LxDK 29 | - appveyor AddMessage "Install LxDK" -Category Information 30 | - if %LXDK%==rel appveyor DownloadFile https://github.com/billziss-gh/lxdk/releases/download/v0.3/lxdk-0.3.20228.msi 31 | - if %LXDK%==src tools\build-lxdk.bat %CONFIGURATION% 32 | - if %LXDK%==src copy ext\lxdk\build\VStudio\build\%CONFIGURATION%\lxdk-*.msi . 33 | - for %%f in ("lxdk-*.msi") do start /wait msiexec /i %%f /qn INSTALLLEVEL=1000 34 | # install WSL Ubuntu 20.04 - adapted from appveyor/build-images:scripts/Windows/install_wsl.ps1 35 | - appveyor AddMessage "Install WSL Ubuntu 20.04" -Category Information 36 | - ps: (New-Object Net.WebClient).DownloadFile('https://aka.ms/wslubuntu2004', "$env:TEMP\wsl-ubuntu-2004.zip") 37 | - ps: Expand-Archive -Path "$env:TEMP\wsl-ubuntu-2004.zip" -DestinationPath "C:\WSL\WinFuse-Ubuntu2004" -Force 38 | - ps: Remove-Item "$env:TEMP\wsl-ubuntu-2004.zip" 39 | - ps: . "C:\WSL\WinFuse-Ubuntu2004\ubuntu2004.exe" install --root 40 | - ps: . "C:\WSL\WinFuse-Ubuntu2004\ubuntu2004.exe" run adduser appveyor --gecos `"First,Last,RoomNumber,WorkPhone,HomePhone`" --disabled-password 41 | - ps: . "C:\WSL\WinFuse-Ubuntu2004\ubuntu2004.exe" run "echo 'appveyor:Password12!' | sudo chpasswd" 42 | - ps: . "C:\WSL\WinFuse-Ubuntu2004\ubuntu2004.exe" run usermod -aG sudo appveyor 43 | - ps: | 44 | . "C:\WSL\WinFuse-Ubuntu2004\ubuntu2004.exe" run "echo -e `"`"appveyor\tALL=(ALL)\tNOPASSWD: ALL`"`" > /etc/sudoers.d/appveyor" 45 | - ps: . "C:\WSL\WinFuse-Ubuntu2004\ubuntu2004.exe" run chmod 0755 /etc/sudoers.d/appveyor 46 | - ps: . "C:\WSL\WinFuse-Ubuntu2004\ubuntu2004.exe" config --default-user appveyor 47 | - wslconfig /setdefault Ubuntu-20.04 48 | - wsl -- lsb_release -a 49 | # install WSL tools 50 | - appveyor AddMessage "Install WSL tools" -Category Information 51 | - wsl -- sudo apt-get update; sudo apt-get install -y g++ gdb make ninja-build rsync zip libfuse3-dev 52 | # enable test-signing and verifier and reboot 53 | - appveyor AddMessage "Change boot configuration and reboot" -Category Information 54 | - bcdedit /set testsigning on 55 | - verifier /standard /driver winfuse-x64.sys || ver>nul 56 | - if exist %SystemRoot%\memory.dmp del %SystemRoot%\memory.dmp 57 | - ps: Restart-Computer -Force 58 | - ps: Start-Sleep -s 60 59 | 60 | build_script: 61 | - appveyor AddMessage "Reboot complete" -Category Information 62 | - tools\build.bat %CONFIGURATION% 63 | - for %%f in ("build\VStudio\build\%CONFIGURATION%\winfuse-*-x64.msi") do start /wait msiexec /i %%f /qn INSTALLLEVEL=1000 64 | - copy ext\libfuse\build\%CONFIGURATION%\x86\lib\fuse3-x86.dll "C:\Program Files\WinFuse\opt\libfuse\bin" 65 | - copy ext\libfuse\build\%CONFIGURATION%\x86\lib\fuse3-x86.lib "C:\Program Files\WinFuse\opt\libfuse\lib" 66 | 67 | test_script: 68 | - tools\nmake-ext-test.bat 69 | - tools\run-tests.bat %CONFIGURATION% 70 | - tools\run-tests.bat %CONFIGURATION% sample 71 | - if exist %SystemRoot%\memory.dmp exit 1 72 | 73 | on_finish: 74 | - if exist %SystemRoot%\memory.dmp (7z a memory.dmp.zip %SystemRoot%\memory.dmp && appveyor PushArtifact memory.dmp.zip) 75 | - verifier /query 76 | #- ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) 77 | -------------------------------------------------------------------------------- /tst/winfuse-tests/path-test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file path-test.c 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #include 23 | #include 24 | 25 | #define SHARED_KM_SHARED_H_INCLUDED 26 | #define PAGED_CODE() 27 | #include 28 | 29 | void path_prefix_test(void) 30 | { 31 | PSTR ipaths[] = 32 | { 33 | "", 34 | "/", 35 | "//", 36 | "/a", 37 | "//a", 38 | "//a/", 39 | "//a//", 40 | "a/", 41 | "a//", 42 | "a/b", 43 | "a//b", 44 | "foo///bar//baz", 45 | "foo///bar//baz/", 46 | "foo///bar//baz//", 47 | "foo", 48 | "/foo/bar/baz", 49 | }; 50 | PSTR opaths[] = 51 | { 52 | "", "", 53 | "/", "", 54 | "/", "", 55 | "/", "a", 56 | "/", "a", 57 | "/", "a/", 58 | "/", "a//", 59 | "a", "", 60 | "a", "", 61 | "a", "b", 62 | "a", "b", 63 | "foo", "bar//baz", 64 | "foo", "bar//baz/", 65 | "foo", "bar//baz//", 66 | "foo", "", 67 | "/", "foo/bar/baz", 68 | }; 69 | 70 | for (size_t i = 0; sizeof ipaths / sizeof ipaths[0] > i; i++) 71 | { 72 | STRING Path, Prefix, Remain; 73 | 74 | Path.Length = Path.MaximumLength = (USHORT)strlen(ipaths[i]); 75 | Path.Buffer = ipaths[i]; 76 | 77 | FusePosixPathPrefix(&Path, &Prefix, &Remain); 78 | 79 | ASSERT(Prefix.Length == strlen(opaths[2 * i + 0])); 80 | ASSERT(Prefix.Length == Prefix.MaximumLength); 81 | ASSERT(0 == memcmp(opaths[2 * i + 0], Prefix.Buffer, Prefix.Length)); 82 | 83 | ASSERT(Remain.Length == strlen(opaths[2 * i + 1])); 84 | ASSERT(Remain.Length == Remain.MaximumLength); 85 | ASSERT(0 == memcmp(opaths[2 * i + 1], Remain.Buffer, Remain.Length)); 86 | } 87 | } 88 | 89 | void path_suffix_test(void) 90 | { 91 | PSTR ipaths[] = 92 | { 93 | "", 94 | "/", 95 | "//", 96 | "/a", 97 | "//a", 98 | "//a/", 99 | "//a//", 100 | "a/", 101 | "a//", 102 | "a/b", 103 | "a//b", 104 | "foo///bar//baz", 105 | "foo///bar//baz/", 106 | "foo///bar//baz//", 107 | "foo", 108 | "/foo/bar/baz", 109 | }; 110 | PSTR opaths[] = 111 | { 112 | "", "", 113 | "/", "", 114 | "/", "", 115 | "/", "a", 116 | "/", "a", 117 | "//a", "", 118 | "//a", "", 119 | "a", "", 120 | "a", "", 121 | "a", "b", 122 | "a", "b", 123 | "foo///bar", "baz", 124 | "foo///bar//baz", "", 125 | "foo///bar//baz", "", 126 | "foo", "", 127 | "/foo/bar", "baz", 128 | }; 129 | 130 | for (size_t i = 0; sizeof ipaths / sizeof ipaths[0] > i; i++) 131 | { 132 | STRING Path, Remain, Suffix; 133 | 134 | Path.Length = Path.MaximumLength = (USHORT)strlen(ipaths[i]); 135 | Path.Buffer = ipaths[i]; 136 | 137 | FusePosixPathSuffix(&Path, &Remain, &Suffix); 138 | 139 | ASSERT(Remain.Length == strlen(opaths[2 * i + 0])); 140 | ASSERT(Remain.Length == Remain.MaximumLength); 141 | ASSERT(0 == memcmp(opaths[2 * i + 0], Remain.Buffer, Remain.Length)); 142 | 143 | ASSERT(Suffix.Length == strlen(opaths[2 * i + 1])); 144 | ASSERT(Suffix.Length == Suffix.MaximumLength); 145 | ASSERT(0 == memcmp(opaths[2 * i + 1], Suffix.Buffer, Suffix.Length)); 146 | } 147 | } 148 | 149 | void path_tests(void) 150 | { 151 | TEST(path_prefix_test); 152 | TEST(path_suffix_test); 153 | } 154 | -------------------------------------------------------------------------------- /tools/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | setlocal 4 | setlocal EnableDelayedExpansion 5 | 6 | set MsiName="WinFuse - FUSE for the Windows kernel" 7 | set CrossCert="%~dp0DigiCert High Assurance EV Root CA.crt" 8 | set Issuer="DigiCert" 9 | set Subject="Navimatics LLC" 10 | 11 | set Configuration=Release 12 | set SignedPackage= 13 | 14 | if not X%1==X set Configuration=%1 15 | if not X%2==X set SignedPackage=%2 16 | 17 | set vswhere="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" 18 | for /f "usebackq tokens=*" %%i in (`%vswhere% -find VC\**\vcvarsall.bat`) do ( 19 | call "%%i" x64 20 | ) 21 | 22 | if not X%SignedPackage%==X ( 23 | if not exist "%~dp0..\build\VStudio\build\%Configuration%\winfuse-*.msi" (echo previous build not found >&2 & exit /b 1) 24 | if not exist "%SignedPackage%" (echo signed package not found >&2 & exit /b 1) 25 | del "%~dp0..\build\VStudio\build\%Configuration%\winfuse-*.msi" 26 | if exist "%~dp0..\build\VStudio\build\%Configuration%\winfuse.*.nupkg" del "%~dp0..\build\VStudio\build\%Configuration%\winfuse.*.nupkg" 27 | for /R "%SignedPackage%" %%f in (*.sys) do ( 28 | copy "%%f" "%~dp0..\build\VStudio\build\%Configuration%" >nul 29 | ) 30 | ) 31 | 32 | cd %~dp0..\build\VStudio 33 | set signfail=0 34 | 35 | if X%SignedPackage%==X ( 36 | if exist build\ for /R build\ %%d in (%Configuration%) do ( 37 | if exist "%%d" rmdir /s/q "%%d" 38 | ) 39 | 40 | devenv winfuse.sln /build "%Configuration%|x64" 41 | if errorlevel 1 goto fail 42 | call %~dp0build-libfuse.bat %Configuration% x64 build\%Configuration%\libfuse\x64 43 | if errorlevel 1 goto fail 44 | 45 | devenv winfuse.sln /build "%Configuration%|x86" 46 | if errorlevel 1 goto fail 47 | call %~dp0build-libfuse.bat %Configuration% x86 build\%Configuration%\libfuse\x86 48 | if errorlevel 1 goto fail 49 | 50 | for %%f in (build\%Configuration%\winfuse-x64.sys build\%Configuration%\wslfuse-x64.sys build\%Configuration%\winfuse-x86.sys) do ( 51 | signtool sign /ac %CrossCert% /i %Issuer% /n %Subject% /fd sha1 /t http://timestamp.digicert.com %%f 52 | if errorlevel 1 set /a signfail=signfail+1 53 | signtool sign /as /ac %CrossCert% /i %Issuer% /n %Subject% /fd sha256 /tr http://timestamp.digicert.com /td sha256 %%f 54 | if errorlevel 1 set /a signfail=signfail+1 55 | ) 56 | 57 | pushd build\%Configuration% 58 | echo .OPTION EXPLICIT >driver.ddf 59 | echo .Set CabinetFileCountThreshold=0 >>driver.ddf 60 | echo .Set FolderFileCountThreshold=0 >>driver.ddf 61 | echo .Set FolderSizeThreshold=0 >>driver.ddf 62 | echo .Set MaxCabinetSize=0 >>driver.ddf 63 | echo .Set MaxDiskFileCount=0 >>driver.ddf 64 | echo .Set MaxDiskSize=0 >>driver.ddf 65 | echo .Set CompressionType=MSZIP >>driver.ddf 66 | echo .Set Cabinet=on >>driver.ddf 67 | echo .Set Compress=on >>driver.ddf 68 | echo .Set CabinetNameTemplate=driver.cab >>driver.ddf 69 | echo .Set DiskDirectory1=. >>driver.ddf 70 | echo .Set DestinationDir=x64 >>driver.ddf 71 | echo winfuse-x64.inf >>driver.ddf 72 | echo winfuse-x64.sys >>driver.ddf 73 | echo wslfuse-x64.inf >>driver.ddf 74 | echo wslfuse-x64.sys >>driver.ddf 75 | echo .Set DestinationDir=x86 >>driver.ddf 76 | echo winfuse-x86.inf >>driver.ddf 77 | echo winfuse-x86.sys >>driver.ddf 78 | makecab /F driver.ddf 79 | signtool sign /ac %CrossCert% /i %Issuer% /n %Subject% /t http://timestamp.digicert.com driver.cab 80 | if errorlevel 1 set /a signfail=signfail+1 81 | popd 82 | ) 83 | 84 | devenv winfuse.sln /build "Installer.%Configuration%|x64" 85 | if errorlevel 1 goto fail 86 | 87 | devenv winfuse.sln /build "Installer.%Configuration%|x86" 88 | if errorlevel 1 goto fail 89 | 90 | for %%f in (build\%Configuration%\winfuse-*.msi) do ( 91 | signtool sign /ac %CrossCert% /i %Issuer% /n %Subject% /fd sha1 /t http://timestamp.digicert.com /d %MsiName% %%f 92 | if errorlevel 1 set /a signfail=signfail+1 93 | REM signtool sign /ac %CrossCert% /i %Issuer% /n %Subject% /fd sha256 /tr http://timestamp.digicert.com /td sha256 /d %MsiName% %%f 94 | REM if errorlevel 1 set /a signfail=signfail+1 95 | ) 96 | 97 | if not %signfail%==0 echo SIGNING FAILED! The product has been successfully built, but not signed. 98 | 99 | exit /b 0 100 | 101 | :fail 102 | exit /b 1 103 | -------------------------------------------------------------------------------- /src/winfuse/device.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file winfuse/device.c 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #include 23 | 24 | static NTSTATUS FuseDeviceInit(PDEVICE_OBJECT DeviceObject, FSP_FSCTL_VOLUME_PARAMS *VolumeParams); 25 | static VOID FuseDeviceFini(PDEVICE_OBJECT DeviceObject); 26 | static VOID FuseDeviceExpirationRoutine(PDEVICE_OBJECT DeviceObject, UINT64 ExpirationTime); 27 | static NTSTATUS FuseDeviceTransact(PDEVICE_OBJECT DeviceObject, PIRP Irp); 28 | 29 | #ifdef ALLOC_PRAGMA 30 | #pragma alloc_text(PAGE, FuseDeviceInit) 31 | #pragma alloc_text(PAGE, FuseDeviceFini) 32 | #pragma alloc_text(PAGE, FuseDeviceExpirationRoutine) 33 | #pragma alloc_text(PAGE, FuseDeviceTransact) 34 | #endif 35 | 36 | static NTSTATUS FuseDeviceInit(PDEVICE_OBJECT DeviceObject, FSP_FSCTL_VOLUME_PARAMS *VolumeParams) 37 | { 38 | PAGED_CODE(); 39 | 40 | FUSE_INSTANCE *Instance = FuseInstanceFromDeviceObject(DeviceObject); 41 | NTSTATUS Result; 42 | 43 | KeEnterCriticalRegion(); 44 | 45 | Result = FuseInstanceInit(Instance, VolumeParams, FuseInstanceWindows); 46 | 47 | KeLeaveCriticalRegion(); 48 | 49 | return Result; 50 | } 51 | 52 | static VOID FuseDeviceFini(PDEVICE_OBJECT DeviceObject) 53 | { 54 | PAGED_CODE(); 55 | 56 | FUSE_INSTANCE *Instance = FuseInstanceFromDeviceObject(DeviceObject); 57 | 58 | KeEnterCriticalRegion(); 59 | 60 | FuseInstanceFini(Instance); 61 | 62 | KeLeaveCriticalRegion(); 63 | } 64 | 65 | static VOID FuseDeviceExpirationRoutine(PDEVICE_OBJECT DeviceObject, UINT64 ExpirationTime) 66 | { 67 | PAGED_CODE(); 68 | 69 | FUSE_INSTANCE *Instance = FuseInstanceFromDeviceObject(DeviceObject); 70 | 71 | KeEnterCriticalRegion(); 72 | 73 | FuseInstanceExpirationRoutine(Instance, ExpirationTime); 74 | 75 | KeLeaveCriticalRegion(); 76 | } 77 | 78 | static NTSTATUS FuseDeviceTransact(PDEVICE_OBJECT DeviceObject, PIRP Irp) 79 | { 80 | PAGED_CODE(); 81 | 82 | ASSERT(KeAreApcsDisabled()); 83 | 84 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 85 | ASSERT(IRP_MJ_FILE_SYSTEM_CONTROL == IrpSp->MajorFunction); 86 | ASSERT(IRP_MN_USER_FS_REQUEST == IrpSp->MinorFunction); 87 | ASSERT(FUSE_FSCTL_TRANSACT == IrpSp->Parameters.FileSystemControl.FsControlCode); 88 | ASSERT(METHOD_BUFFERED == (IrpSp->Parameters.FileSystemControl.FsControlCode & 3)); 89 | ASSERT(IrpSp->FileObject->FsContext2 == DeviceObject); 90 | 91 | FUSE_INSTANCE *Instance = FuseInstanceFromDeviceObject(DeviceObject); 92 | ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; 93 | ULONG OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; 94 | FUSE_PROTO_RSP *FuseResponse = 0 != InputBufferLength ? Irp->AssociatedIrp.SystemBuffer : 0; 95 | FUSE_PROTO_REQ *FuseRequest = 0 != OutputBufferLength ? Irp->AssociatedIrp.SystemBuffer : 0; 96 | NTSTATUS Result; 97 | 98 | Result = FuseInstanceTransact(Instance, 99 | FuseResponse, InputBufferLength, 100 | FuseRequest, &OutputBufferLength, 101 | IrpSp->DeviceObject, IrpSp->FileObject, 102 | Irp); 103 | 104 | Irp->IoStatus.Information = OutputBufferLength; 105 | 106 | return Result; 107 | } 108 | 109 | FSP_FSEXT_PROVIDER FuseProvider = 110 | { 111 | /* Version */ 112 | sizeof FuseProvider, 113 | 114 | /* DeviceTransactCode */ 115 | FUSE_FSCTL_TRANSACT, 116 | 117 | /* DeviceExtensionSize */ 118 | sizeof(FUSE_INSTANCE), 119 | 120 | /* DeviceInit */ 121 | FuseDeviceInit, 122 | 123 | /* DeviceFini */ 124 | FuseDeviceFini, 125 | 126 | /* DeviceExpirationRoutine */ 127 | FuseDeviceExpirationRoutine, 128 | 129 | /* DeviceTransact */ 130 | FuseDeviceTransact, 131 | }; 132 | -------------------------------------------------------------------------------- /ext/tlib/injection.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tlib/injection.c 3 | * 4 | * @copyright 2014-2019 Bill Zissimopoulos 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #undef NDEBUG 14 | #include 15 | 16 | #define NBUCKETS 256 17 | struct injection_cond_s 18 | { 19 | struct injection_cond_s *cnext; 20 | char *sym; 21 | unsigned trigger, count; 22 | }; 23 | struct injection_entry_s 24 | { 25 | struct injection_entry_s *hnext; 26 | char *name; 27 | struct injection_cond_s *clist; 28 | }; 29 | struct injection_htab_s 30 | { 31 | struct injection_entry_s **buckets; 32 | }; 33 | 34 | static inline size_t hash_chars(const char *s, size_t length) 35 | { 36 | /* djb2: see http://www.cse.yorku.ca/~oz/hash.html */ 37 | size_t h = 5381; 38 | for (const char *t = s + length; t > s; ++s) 39 | h = 33 * h + *s; 40 | return h; 41 | } 42 | static struct injection_htab_s *injection_htab() 43 | { 44 | static struct injection_htab_s *htab; 45 | if (0 == htab) 46 | { 47 | htab = calloc(1, sizeof *htab); 48 | assert(0 != htab); 49 | htab->buckets = calloc(NBUCKETS, sizeof(struct injection_entry_s *)); 50 | } 51 | return htab; 52 | } 53 | static struct injection_entry_s *injection_lookup(const char *name) 54 | { 55 | struct injection_htab_s *htab = injection_htab(); 56 | size_t i = hash_chars(name, strlen(name)) & (NBUCKETS - 1); 57 | for (struct injection_entry_s *entry = htab->buckets[i]; entry; entry = entry->hnext) 58 | if (0 == strcmp(entry->name, name)) 59 | return entry; 60 | return 0; 61 | } 62 | static struct injection_entry_s *injection_insert(const char *name) 63 | { 64 | struct injection_htab_s *htab = injection_htab(); 65 | size_t i = hash_chars(name, strlen(name)) & (NBUCKETS - 1); 66 | struct injection_entry_s *entry = calloc(1, sizeof *entry); 67 | assert(0 != entry); 68 | entry->name = strdup(name); 69 | entry->hnext = htab->buckets[i]; 70 | htab->buckets[i] = entry; 71 | return entry; 72 | } 73 | static struct injection_cond_s *injection_cond_get(struct injection_entry_s *entry, const char **syms) 74 | { 75 | struct injection_cond_s *deinjection_centry = 0; 76 | for (struct injection_cond_s *centry = entry->clist; centry; centry = centry->cnext) 77 | if ('*' == centry->sym[0] && '\0' == centry->sym[1]) 78 | deinjection_centry = centry; 79 | else 80 | { 81 | for (const char *sym; 0 != (sym = *syms); syms++) 82 | if (0 == strcmp(centry->sym, sym)) 83 | return centry; 84 | } 85 | return deinjection_centry; 86 | } 87 | static void injection_cond_set(struct injection_entry_s *entry, const char *sym, unsigned trigger) 88 | { 89 | for (struct injection_cond_s *centry = entry->clist; centry; centry = centry->cnext) 90 | if (0 == strcmp(centry->sym, sym)) 91 | { 92 | centry->trigger = trigger; 93 | return; 94 | } 95 | struct injection_cond_s *centry = calloc(1, sizeof *centry); 96 | assert(0 != centry); 97 | centry->sym = strdup(sym); 98 | centry->trigger = trigger; 99 | centry->cnext = entry->clist; 100 | entry->clist = centry; 101 | } 102 | static void injection_cond_remove(struct injection_entry_s *entry, const char *sym) 103 | { 104 | struct injection_cond_s **p = &entry->clist; 105 | for (; *p; p = &(*p)->cnext) 106 | if (0 == strcmp((*p)->sym, sym)) 107 | break; 108 | if (*p) /* did we find the condition? */ 109 | { 110 | struct injection_cond_s *q = *p; 111 | *p = q->cnext; 112 | free(q->sym); 113 | free(q); 114 | } 115 | } 116 | 117 | void *tlib_injection(const char *name) 118 | { 119 | struct injection_entry_s *entry = injection_lookup(name); 120 | if (0 == entry) 121 | entry = injection_insert(name); 122 | return entry; 123 | } 124 | int tlib_injection_trace(void *injection) 125 | { 126 | if (0 == ((struct injection_entry_s *)injection)->clist) 127 | return 0; 128 | struct tlib_callstack_s stack; 129 | tlib_callstack(2, TLIB_MAX_SYMRET, &stack); 130 | struct injection_cond_s *centry = injection_cond_get(injection, stack.syms); 131 | if (0 == centry) 132 | return 0; 133 | return centry->count++ == centry->trigger || ~0 == centry->trigger; 134 | } 135 | void tlib_injection_enable(const char *name, const char *sym, unsigned trigger) 136 | { 137 | struct injection_entry_s *entry = tlib_injection(name); 138 | injection_cond_set(entry, sym, trigger); 139 | } 140 | void tlib_injection_disable(const char *name, const char *sym) 141 | { 142 | struct injection_entry_s *entry = tlib_injection(name); 143 | injection_cond_remove(entry, sym); 144 | } 145 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | WinFuse · FUSE for the Windows kernel 3 |

4 | 5 |

6 | Download
7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 |

16 | 17 |

18 | WinFuse is a suite of software components that allows FUSE file systems to run on Windows. Both Windows and Linux (WSL1) FUSE file systems are supported. Windows FUSE file systems are supported via a kernel driver that exposes the FUSE protocol via DeviceIoControl and a port of the libfuse library compiled as a Windows DLL. Linux FUSE file systems are expected to run in the WSL1 environment and are supported via a kernel driver that exposes /dev/fuse. 19 |
20 |
21 | 22 |

23 | 24 | ## ARCHITECTURE 25 | 26 | ### Structural Diagram 27 | 28 | The following component diagram shows the WinFuse project components and their relations to the Windows OS (NTOS), WSL (LXCORE) and components that are provided by other projects. 29 | 30 | ![Component Diagram](doc/component.svg) 31 | 32 | The components and their interfaces are: 33 | 34 | - The **winfsp** component which is provided by the [WinFsp](https://github.com/billziss-gh/winfsp) project. 35 | - The component is an NTOS File System Driver (FSD). 36 | - The component provides an **FSP_FSCTL_TRANSACT** `DeviceIoControl` interface. 37 | - The component is responsible for loading the **winfuse** component. 38 | - The **winfuse** component which is provided by the WinFuse project (this project). 39 | - The component is an NTOS driver. 40 | - The component provides an **FUSE_FSCTL_TRANSACT** `DeviceIoControl` interface. 41 | - The component connects to the **FSP_FSCTL_TRANSACT** interface. WinFsp file system requests retrieved via this interface are translated to FUSE file system requests and become available via **FUSE_FSCTL_TRANSACT**. 42 | - The **wslfuse** component which is provided by the WinFuse project (this project). 43 | - The component is an LXCORE driver. 44 | - The component provides a **/dev/fuse** Linux interface. 45 | - The component connects to the **FSP_FSCTL_TRANSACT** interface. WinFsp file system requests retrieved via this interface are translated to FUSE file system requests and become available via **/dev/fuse**. 46 | - The **lxldr** component which is provided by the [LxDK](https://github.com/billziss-gh/lxdk) project. 47 | - The component is an LXCORE driver. 48 | - The component is responsible for loading the **wslfuse** component. 49 | - The file system (**FS**) components which are user-mode programs provided by third parties. 50 | - A Windows WinFsp file system connects to the **FSP_FSCTL_TRANSACT** interface. 51 | - A Windows FUSE file system connects to the **FUSE_FSCTL_TRANSACT** interface. 52 | - A Linux FUSE file system connects to the **/dev/fuse** interface. 53 | 54 | ### Behavioral Diagrams 55 | 56 | The following sequence diagrams show how the WinFuse project components interact with other components in two scenarios: (1) I/O handled by a Windows FUSE file system, and (2) I/O handled by a Linux FUSE file system. 57 | 58 | #### I/O handled by a Windows FUSE file system: 59 | 60 | ![Windows FS Sequence Diagram](doc/winseq.svg) 61 | 62 | - The I/O originates with one of: 63 | - A Windows process which uses a familiar API like `ReadFile` or `WriteFile`. 64 | - A WSL (Linux) process which uses an API like `read(2)` or `write(2)` that LXCORE translates into the equivalent `NtReadFile` or `NtWriteFile`. 65 | - The Windows OS (NTOS) packages this I/O into an IRP (I/O Request Packet) and routes it to the **winfsp** FSD. 66 | - The **winfsp** FSD posts the request into an internal I/O queue. This request is retrieved at a later time and in a different process context via an `FSP_FSCTL_TRANSACT` (issued via `DeviceIoControl`) by the **winfuse** driver. 67 | - The **winfuse** driver translates the request to equivalent FUSE requests, which are then retrieved via an `FUSE_FSCTL_TRANSACT` (issued via `DeviceIoControl`) by the Windows FUSE file system. 68 | - The Windows FUSE file system processes the request and prepares a response, which it sends to the **winfuse** driver via another `FUSE_FSCTL_TRANSACT`. 69 | - The **winfuse** driver processes the FUSE response and it may issue additional FUSE requests or it may translate it to a final WinFsp response, which it sends to the **winfsp** FSD via another `FSP_FSCTL_TRANSACT`. 70 | - The **winfsp** FSD completes the corresponding I/O, which allows the Originating Process to resume and retrieve the response. 71 | 72 | #### I/O handled by a Linux FUSE file system: 73 | 74 | ![Linux FS Sequence Diagram](doc/wslseq.svg) 75 | 76 | This case is similar to the previous case except that the **winfuse** driver is replaced by the **wslfuse** driver that exposes the FUSE protocol via the **/dev/fuse** interface that the Linux FUSE file system understands. 77 | -------------------------------------------------------------------------------- /src/shared/km/errnosym.i: -------------------------------------------------------------------------------- 1 | #if FUSE_ERRNO == 87 /* Windows */ 2 | 3 | case 0: return "0"; 4 | case 1: return "EPERM"; 5 | case 2: return "ENOENT"; 6 | case 3: return "ESRCH"; 7 | case 4: return "EINTR"; 8 | case 5: return "EIO"; 9 | case 6: return "ENXIO"; 10 | case 7: return "E2BIG"; 11 | case 8: return "ENOEXEC"; 12 | case 9: return "EBADF"; 13 | case 12: return "ENOMEM"; 14 | case 13: return "EACCES"; 15 | case 14: return "EFAULT"; 16 | case 16: return "EBUSY"; 17 | case 17: return "EEXIST"; 18 | case 18: return "EXDEV"; 19 | case 19: return "ENODEV"; 20 | case 20: return "ENOTDIR"; 21 | case 21: return "EISDIR"; 22 | case 22: return "EINVAL"; 23 | case 23: return "ENFILE"; 24 | case 24: return "EMFILE"; 25 | case 27: return "EFBIG"; 26 | case 28: return "ENOSPC"; 27 | case 29: return "ESPIPE"; 28 | case 30: return "EROFS"; 29 | case 31: return "EMLINK"; 30 | case 32: return "EPIPE"; 31 | case 33: return "EDOM"; 32 | case 34: return "ERANGE"; 33 | case 36: return "EDEADLK"; 34 | case 38: return "ENAMETOOLONG"; 35 | case 39: return "ENOLCK"; 36 | case 40: return "ENOSYS"; 37 | case 41: return "ENOTEMPTY"; 38 | case 42: return "EILSEQ"; 39 | case 100: return "EADDRINUSE"; 40 | case 103: return "EALREADY"; 41 | case 105: return "ECANCELED"; 42 | case 106: return "ECONNABORTED"; 43 | case 107: return "ECONNREFUSED"; 44 | case 108: return "ECONNRESET"; 45 | case 110: return "EHOSTUNREACH"; 46 | case 113: return "EISCONN"; 47 | case 114: return "ELOOP"; 48 | case 116: return "ENETDOWN"; 49 | case 117: return "ENETRESET"; 50 | case 118: return "ENETUNREACH"; 51 | case 119: return "ENOBUFS"; 52 | case 120: return "ENODATA"; 53 | case 121: return "ENOLINK"; 54 | case 126: return "ENOTCONN"; 55 | case 128: return "ENOTSOCK"; 56 | case 138: return "ETIMEDOUT"; 57 | 58 | #elif FUSE_ERRNO == 67 /* Cygwin */ 59 | 60 | case 0: return "0"; 61 | case 1: return "EPERM"; 62 | case 2: return "ENOENT"; 63 | case 3: return "ESRCH"; 64 | case 4: return "EINTR"; 65 | case 5: return "EIO"; 66 | case 6: return "ENXIO"; 67 | case 7: return "E2BIG"; 68 | case 8: return "ENOEXEC"; 69 | case 9: return "EBADF"; 70 | case 12: return "ENOMEM"; 71 | case 13: return "EACCES"; 72 | case 14: return "EFAULT"; 73 | case 16: return "EBUSY"; 74 | case 17: return "EEXIST"; 75 | case 18: return "EXDEV"; 76 | case 19: return "ENODEV"; 77 | case 20: return "ENOTDIR"; 78 | case 21: return "EISDIR"; 79 | case 22: return "EINVAL"; 80 | case 23: return "ENFILE"; 81 | case 24: return "EMFILE"; 82 | case 27: return "EFBIG"; 83 | case 28: return "ENOSPC"; 84 | case 29: return "ESPIPE"; 85 | case 30: return "EROFS"; 86 | case 31: return "EMLINK"; 87 | case 32: return "EPIPE"; 88 | case 33: return "EDOM"; 89 | case 34: return "ERANGE"; 90 | case 45: return "EDEADLK"; 91 | case 91: return "ENAMETOOLONG"; 92 | case 46: return "ENOLCK"; 93 | case 88: return "ENOSYS"; 94 | case 90: return "ENOTEMPTY"; 95 | case 138: return "EILSEQ"; 96 | case 112: return "EADDRINUSE"; 97 | case 120: return "EALREADY"; 98 | case 140: return "ECANCELED"; 99 | case 113: return "ECONNABORTED"; 100 | case 111: return "ECONNREFUSED"; 101 | case 104: return "ECONNRESET"; 102 | case 118: return "EHOSTUNREACH"; 103 | case 127: return "EISCONN"; 104 | case 92: return "ELOOP"; 105 | case 115: return "ENETDOWN"; 106 | case 126: return "ENETRESET"; 107 | case 114: return "ENETUNREACH"; 108 | case 105: return "ENOBUFS"; 109 | case 61: return "ENODATA"; 110 | case 67: return "ENOLINK"; 111 | case 128: return "ENOTCONN"; 112 | case 108: return "ENOTSOCK"; 113 | case 116: return "ETIMEDOUT"; 114 | 115 | #elif FUSE_ERRNO == 76 /* Linux */ 116 | 117 | case 0: return "0"; 118 | case 1: return "EPERM"; 119 | case 2: return "ENOENT"; 120 | case 3: return "ESRCH"; 121 | case 4: return "EINTR"; 122 | case 5: return "EIO"; 123 | case 6: return "ENXIO"; 124 | case 7: return "E2BIG"; 125 | case 8: return "ENOEXEC"; 126 | case 9: return "EBADF"; 127 | case 12: return "ENOMEM"; 128 | case 13: return "EACCES"; 129 | case 14: return "EFAULT"; 130 | case 16: return "EBUSY"; 131 | case 17: return "EEXIST"; 132 | case 18: return "EXDEV"; 133 | case 19: return "ENODEV"; 134 | case 20: return "ENOTDIR"; 135 | case 21: return "EISDIR"; 136 | case 22: return "EINVAL"; 137 | case 23: return "ENFILE"; 138 | case 24: return "EMFILE"; 139 | case 27: return "EFBIG"; 140 | case 28: return "ENOSPC"; 141 | case 29: return "ESPIPE"; 142 | case 30: return "EROFS"; 143 | case 31: return "EMLINK"; 144 | case 32: return "EPIPE"; 145 | case 33: return "EDOM"; 146 | case 34: return "ERANGE"; 147 | case 35: return "EDEADLK"; 148 | case 36: return "ENAMETOOLONG"; 149 | case 37: return "ENOLCK"; 150 | case 38: return "ENOSYS"; 151 | case 39: return "ENOTEMPTY"; 152 | case 84: return "EILSEQ"; 153 | case 98: return "EADDRINUSE"; 154 | case 114: return "EALREADY"; 155 | case 125: return "ECANCELED"; 156 | case 103: return "ECONNABORTED"; 157 | case 111: return "ECONNREFUSED"; 158 | case 104: return "ECONNRESET"; 159 | case 113: return "EHOSTUNREACH"; 160 | case 106: return "EISCONN"; 161 | case 40: return "ELOOP"; 162 | case 100: return "ENETDOWN"; 163 | case 102: return "ENETRESET"; 164 | case 101: return "ENETUNREACH"; 165 | case 105: return "ENOBUFS"; 166 | case 61: return "ENODATA"; 167 | case 67: return "ENOLINK"; 168 | case 107: return "ENOTCONN"; 169 | case 88: return "ENOTSOCK"; 170 | case 110: return "ETIMEDOUT"; 171 | 172 | #endif 173 | -------------------------------------------------------------------------------- /src/shared/km/coro.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file shared/km/coro.h 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #ifndef SHARED_KM_CORO_H_INCLUDED 23 | #define SHARED_KM_CORO_H_INCLUDED 24 | 25 | /* 26 | * Nested coroutines 27 | * 28 | * This is a simple implementation of nested coroutines for C using macros. It introduces 29 | * the macros coro_block, coro_await, coro_yield and coro_break that are used to create coroutines, 30 | * suspend/resume them and exit them. This implementation supports nested coroutines in that 31 | * a coroutine may invoke another coroutine and the whole stack of coroutines may be suspended 32 | * and later resumed. 33 | * 34 | * The implementation is able to do this by maintaining a stack of program "points" where 35 | * coroutine execution may be suspended and later resumed. The implementation achieves this by 36 | * (ab)using a peculiarity of the C switch statement in that it allows case: labels to appear 37 | * anywhere within the body of a switch statement including nested compound statements. 38 | * 39 | * While this implementation is able to maintain the stack of program "points" where execution 40 | * may be resumed, it is not able to automatically maintain important state such as activation 41 | * records (local variables). It is the responsibility of the programmer using these macros 42 | * to maintain this state. Usually this is done by moving all local variables together with the 43 | * space for the program "points" stack into a heap allocation that is then passed as an argument 44 | * to the coroutines. 45 | * 46 | * The original coroutine implementation was done some time around 2010 and did not support 47 | * nesting. It was used to facilitate the implementation of complex iterators in a C++ library. 48 | * It was inspired by Simon Tatham's "Coroutines in C": 49 | * http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html 50 | * 51 | * Reference 52 | * 53 | * The coroutine state is maintained in an integer array (S) whose address is passed to the 54 | * "block" statement. The array's number of elements must be 2 plus the number of expected 55 | * coroutines (e.g. if you expect to have up to 3 nested coroutines, the array must have at 56 | * least 5 elements). The array must be initialized to 0 on initial entry to a coroutine block. 57 | * 58 | * coro_block(S) 59 | * This macro introduces a coroutine statement block { ... } where the "await", "yield", 60 | * and "break" statements can be used. There can only be one such block within a function. 61 | * coro_await(E) 62 | * This macro executes the expression E, which should be an invocation of a coroutine. 63 | * The nested coroutine may suspend itself, in which case the "await" statement exits 64 | * the current coroutine statement block. The coroutine block can be reentered in which 65 | * case execution will continue within the coroutine invoked by E, unless the coroutine 66 | * is complete. It is an error to use "await" outside of a coroutine block. 67 | * coro_yield 68 | * This macro exits the current coroutine statement block. The coroutine block can be 69 | * reentered in which case execution will continue after the "yield" statement. It is 70 | * an error to use "yield" outside of a coroutine block. 71 | * coro_break 72 | * This macro exits the current coroutine statement block and marks it as "complete". 73 | * If the coroutine block is reentered it exits immediately. It is an error to use "break" 74 | * outside of a coroutine block. 75 | */ 76 | #define coro_block(S) short *coro_S__ = (S); if (!coro_active()) coro_X__:; else \ 77 | switch (coro_enter__()) for (; 0,0; coro_leave__(-1)) case 0: 78 | #define coro_await__(N, E) do { E; coro_cond_leave__(N); goto coro_X__; case N:; } while (1,1) 79 | #define coro_yield__(N) do { coro_leave__(N); goto coro_X__; case N:; } while (0,0) 80 | #define coro_break do { coro_leave__(-1); goto coro_X__; } while (0,0) 81 | #define coro_active() (-1 != coro_below__) 82 | #define coro_below__ (coro_S__[coro_S__[0] + 1]) 83 | #define coro_enter__() (coro_S__[++coro_S__[0]]) 84 | #define coro_leave__(N) (coro_below__ = 0, coro_S__[coro_S__[0]--] = N) 85 | #define coro_cond_leave__(N)if (!coro_active()) { coro_below__ = 0; break; } (coro_S__[coro_S__[0]--] = N) 86 | #if defined(__COUNTER__) 87 | #define coro_await(...) coro_await__((__COUNTER__ + 1), (__VA_ARGS__)) 88 | #define coro_yield coro_yield__((__COUNTER__ + 1)) 89 | #else 90 | #define coro_await(...) coro_await__(__LINE__, (__VA_ARGS__)) 91 | #define coro_yield coro_yield__(__LINE__) 92 | #endif 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /Contributors.asciidoc: -------------------------------------------------------------------------------- 1 | CONTRIBUTORS 2 | ============ 3 | 4 | This document contains a list of all contributors to this project. Contributors that submit changes to this project MUST also change this document to identify themselves by adding their name and email address to the CONTRIBUTOR LIST. The CONTRIBUTOR LIST is maintained in alphabetical order. 5 | 6 | A contributor who adds themselves to this list indicates their acceptance of the terms of the CONTRIBUTOR AGREEMENT below. There are NO EXCEPTIONS to this rule. If you do not wish to accept the CONTRIBUTOR AGREEMENT, please do not submit any changes. 7 | 8 | This CONTRIBUTOR AGREEMENT is based on the Oracle Contributor Agreement and is used under the Creative Commons Attribution-Share Alike 3.0 Unported License. The original agreement and an FAQ can be found at this location: http://www.oracle.com/technetwork/community/oca-486395.html 9 | 10 | This document uses the asciidoc format: http://asciidoc.org. 11 | 12 | 13 | CONTRIBUTOR AGREEMENT 14 | --------------------- 15 | 16 | This CONTRIBUTOR AGREEMENT applies to any contribution that you make to the WinFuse project (the "project"), and sets out the intellectual property rights you grant to us in the contributed materials. The term “us” shall mean the original author of this project: Bill Zissimopoulos . The term “you” shall mean the persons or entities identified below. If you agree to be bound by these terms, add your name and email address to the CONTRIBUTOR LIST below; this action will constitute signing this CONTRIBUTOR AGREEMENT. These terms and conditions constitute a binding legal agreement. 17 | 18 | 1. The term 'contribution' or ‘contributed materials’ means any source code, object code, patch, tool, sample, graphic, specification, manual, documentation, or any other material posted or submitted by you to the project. 19 | 20 | 2. With respect to any worldwide copyrights, or copyright applications and registrations, in your contribution: 21 | * you hereby assign to us joint ownership, and to the extent that such assignment is or becomes invalid, ineffective or unenforceable, you hereby grant to us a perpetual, irrevocable, non-exclusive, worldwide, no-charge, royalty-free, unrestricted license to exercise all rights under those copyrights. This includes, at our option, the right to sublicense these same rights to third parties through multiple levels of sublicensees or other licensing arrangements; 22 | * you agree that each of us can do all things in relation to your contribution as if each of us were the sole owners, and if one of us makes a derivative work of your contribution, the one who makes the derivative work (or has it made) will be the sole owner of that derivative work; 23 | * you agree that you will not assert any moral rights in your contribution against us, our licensees or transferees; 24 | * you agree that we may register a copyright in your contribution and exercise all ownership rights associated with it; and 25 | * you agree that neither of us has any duty to consult with, obtain the consent of, pay or render an accounting to the other for any use or distribution of your contribution. 26 | 27 | 3. With respect to any patents you own, or that you can license without payment to any third party, you hereby grant to us a perpetual, irrevocable, non-exclusive, worldwide, no-charge, royalty-free license to: 28 | * make, have made, use, sell, offer to sell, import, and otherwise transfer your contribution in whole or in part, alone or in combination with or included in any product, work or materials arising out of the project to which your contribution was submitted, and 29 | * at our option, to sublicense these same rights to third parties through multiple levels of sublicensees or other licensing arrangements. 30 | 31 | 4. Except as set out above, you keep all right, title, and interest in your contribution. The rights that you grant to us under these terms are effective on the date you first submitted a contribution to us, even if your submission took place before the date you sign these terms. Any contribution we make available under any license will also be made available under a suitable FSF (Free Software Foundation) or OSI (Open Source Initiative) approved license. 32 | 33 | 5. You covenant, represent, warrant and agree that: 34 | * each contribution that you submit is and shall be an original work of authorship and you can legally grant the rights set out in this CONTRIBUTOR AGREEMENT; 35 | * to the best of your knowledge, each contribution will not violate any third party's copyrights, trademarks, patents, or other intellectual property rights; and 36 | * each contribution shall be in compliance with U.S. export control laws and other applicable export and import laws. 37 | * You agree to notify us if you become aware of any circumstance which would make any of the foregoing representations inaccurate in any respect. 38 | 39 | 6. This CONTRIBUTOR AGREEMENT is governed by the laws of the State of Washington and applicable U.S. Federal law. Any choice of law rules will not apply. 40 | 41 | 7. Please add your name and email address in the CONTRIBUTOR LIST below: 42 | * Either "I am signing on behalf of myself as an individual and no other person or entity, including my employer, has or will have rights with respect my contributions". In this case add your name using the following format: 43 | + 44 | ---- 45 | |First Last |name at example.com 46 | ---- 47 | * Or "I am signing on behalf of my employer or a legal entity and I have the actual authority to contractually bind that entity". In this case add your name using the following format: 48 | + 49 | ---- 50 | |First Last (Company and/or URL) |name at example.com 51 | ---- 52 | 53 | 54 | CONTRIBUTOR LIST 55 | ---------------- 56 | |=== 57 | |Bill Zissimopoulos |billziss at navimatics.com 58 | |=== 59 | -------------------------------------------------------------------------------- /src/fusermount/fusermount-helper.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fusermount-helper.c 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #include 23 | #include 24 | 25 | #define PROGNAME "fusermount-helper" 26 | 27 | #undef RtlFillMemory 28 | #undef RtlMoveMemory 29 | NTSYSAPI VOID NTAPI RtlFillMemory(VOID *Destination, DWORD Length, BYTE Fill); 30 | NTSYSAPI VOID NTAPI RtlMoveMemory(VOID *Destination, CONST VOID *Source, DWORD Length); 31 | 32 | #pragma function(memset) 33 | #pragma function(memcpy) 34 | static inline 35 | void *memset(void *dst, int val, size_t siz) 36 | { 37 | RtlFillMemory(dst, (DWORD)siz, val); 38 | return dst; 39 | } 40 | static inline 41 | void *memcpy(void *dst, const void *src, size_t siz) 42 | { 43 | RtlMoveMemory(dst, src, (DWORD)siz); 44 | return dst; 45 | } 46 | 47 | #define warn(format, ...) \ 48 | printlog(GetStdHandle(STD_ERROR_HANDLE), PROGNAME ": " format, __VA_ARGS__) 49 | #define fatal(ExitCode, format, ...) (warn(format, __VA_ARGS__), ExitProcess(ExitCode)) 50 | 51 | static void vprintlog(HANDLE h, const char *format, va_list ap) 52 | { 53 | char buf[1024]; 54 | /* wvsprintf is only safe with a 1024 byte buffer */ 55 | size_t len; 56 | DWORD BytesTransferred; 57 | 58 | wvsprintfA(buf, format, ap); 59 | buf[sizeof buf - 1] = '\0'; 60 | 61 | len = lstrlenA(buf); 62 | buf[len++] = '\n'; 63 | 64 | WriteFile(h, buf, (DWORD)len, &BytesTransferred, 0); 65 | } 66 | 67 | static void printlog(HANDLE h, const char *format, ...) 68 | { 69 | va_list ap; 70 | 71 | va_start(ap, format); 72 | vprintlog(h, format, ap); 73 | va_end(ap); 74 | } 75 | 76 | NTSTATUS NTAPI NtOpenSymbolicLinkObject(PHANDLE LinkHandle, 77 | ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes); 78 | NTSTATUS NTAPI NtMakeTemporaryObject(HANDLE Handle); 79 | NTSTATUS NTAPI NtClose(HANDLE Handle); 80 | 81 | static BOOL MountSet_Drive(PWSTR VolumeName, PWSTR MountPoint, PHANDLE PMountHandle) 82 | { 83 | WCHAR SymlinkBuf[6]; 84 | UNICODE_STRING Symlink; 85 | OBJECT_ATTRIBUTES Obja; 86 | NTSTATUS Result; 87 | 88 | *PMountHandle = 0; 89 | 90 | if (!DefineDosDeviceW(DDD_RAW_TARGET_PATH, MountPoint, VolumeName)) 91 | return FALSE; 92 | 93 | memcpy(SymlinkBuf, L"\\??\\X:", sizeof SymlinkBuf); 94 | SymlinkBuf[4] = MountPoint[0]; 95 | Symlink.Length = Symlink.MaximumLength = sizeof SymlinkBuf; 96 | Symlink.Buffer = SymlinkBuf; 97 | 98 | memset(&Obja, 0, sizeof Obja); 99 | Obja.Length = sizeof Obja; 100 | Obja.ObjectName = &Symlink; 101 | Obja.Attributes = OBJ_CASE_INSENSITIVE; 102 | 103 | Result = NtOpenSymbolicLinkObject(PMountHandle, DELETE, &Obja); 104 | if (NT_SUCCESS(Result)) 105 | { 106 | Result = NtMakeTemporaryObject(*PMountHandle); 107 | if (!NT_SUCCESS(Result)) 108 | { 109 | NtClose(*PMountHandle); 110 | *PMountHandle = 0; 111 | } 112 | } 113 | 114 | return TRUE; 115 | } 116 | 117 | static BOOL MountSet(PWSTR VolumeName, PWSTR MountPoint, PHANDLE PMountHandle) 118 | { 119 | *PMountHandle = 0; 120 | 121 | if (L'*' == MountPoint[0] && ':' == MountPoint[1] && L'\0' == MountPoint[2]) 122 | { 123 | DWORD Drives; 124 | WCHAR Drive; 125 | 126 | Drives = GetLogicalDrives(); 127 | if (0 == Drives) 128 | return FALSE; 129 | 130 | for (Drive = 'Z'; 'D' <= Drive; Drive--) 131 | if (0 == (Drives & (1 << (Drive - 'A')))) 132 | { 133 | MountPoint[0] = Drive; 134 | if (MountSet_Drive(VolumeName, MountPoint, PMountHandle)) 135 | return TRUE; 136 | } 137 | MountPoint[0] = L'*'; 138 | SetLastError(ERROR_NO_SUCH_DEVICE); 139 | /* error code chosen for WinFsp compatibility (FspMountSet) */ 140 | return FALSE; 141 | } 142 | else if ( 143 | ( 144 | (L'A' <= MountPoint[0] && MountPoint[0] <= L'Z') || 145 | (L'a' <= MountPoint[0] && MountPoint[0] <= L'z') 146 | ) && L':' == MountPoint[1] && L'\0' == MountPoint[2]) 147 | return MountSet_Drive(VolumeName, MountPoint, PMountHandle); 148 | else 149 | { 150 | SetLastError(ERROR_INVALID_PARAMETER); 151 | return FALSE; 152 | } 153 | } 154 | 155 | int wmain(int argc, wchar_t **argv) 156 | { 157 | PWSTR VolumeName; 158 | PWSTR MountPoint; 159 | HANDLE MountHandle; 160 | WCHAR WildMountPoint[] = L"*:"; 161 | UINT8 MountPointU8[MAX_PATH]; 162 | UINT8 Buffer[1]; 163 | DWORD BytesTransferred; 164 | 165 | if (2 > argc || 3 < argc) 166 | fatal(2, "usage: VolumeName [WinMountPoint]"); 167 | 168 | VolumeName = argv[1]; 169 | MountPoint = 2 < argc ? argv[2] : WildMountPoint; 170 | 171 | if (!MountSet(VolumeName, MountPoint, &MountHandle)) 172 | fatal(1, "cannot set Windows mount point %S (LastError=%lu)", MountPoint, GetLastError()); 173 | 174 | if (0 == WideCharToMultiByte(CP_UTF8, 0, MountPoint, -1, MountPointU8, MAX_PATH, 0, 0)) 175 | fatal(1, "invalid Windows mount point (LastError=%lu)", GetLastError()); 176 | 177 | BytesTransferred = lstrlenA(MountPointU8) + 1; 178 | if (!WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), MountPointU8, BytesTransferred, &BytesTransferred, 0)) 179 | fatal(1, "cannot write to stdout (LastError=%lu)", GetLastError()); 180 | 181 | if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), Buffer, sizeof Buffer, &BytesTransferred, 0)) 182 | fatal(1, "cannot read from stdin (LastError=%lu)", GetLastError()); 183 | 184 | return 0; 185 | } 186 | 187 | void wmainCRTStartup(void) 188 | { 189 | DWORD Argc; 190 | PWSTR *Argv; 191 | 192 | Argv = CommandLineToArgvW(GetCommandLineW(), &Argc); 193 | if (0 == Argv) 194 | ExitProcess(GetLastError()); 195 | 196 | ExitProcess(wmain(Argc, Argv)); 197 | } 198 | -------------------------------------------------------------------------------- /ext/tlib/testsuite.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file tlib/testsuite.c 3 | * 4 | * @copyright 2014-2019 Bill Zissimopoulos 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | struct test 17 | { 18 | char name[64]; 19 | void (*fn)(void); 20 | int optional; 21 | struct test *next; 22 | }; 23 | static struct test test_suite_sentinel = { .next = &test_suite_sentinel }; 24 | static struct test *test_suite_tail = &test_suite_sentinel; 25 | static struct test test_sentinel = { .next = &test_sentinel }; 26 | static struct test *test_tail = &test_sentinel; 27 | static void add_test_to_list(const char *name, void (*fn)(void), int optional, struct test **tail) 28 | { 29 | struct test *test = calloc(1, sizeof *test); 30 | strncpy(test->name, name, sizeof test->name - 1); 31 | test->name[sizeof test->name - 1] = '\0'; 32 | test->fn = fn; 33 | test->optional = optional; 34 | test->next = (*tail)->next; 35 | (*tail)->next = test; 36 | (*tail) = test; 37 | } 38 | void tlib_add_test_suite(const char *name, void (*fn)(void)) 39 | { 40 | add_test_to_list(name, fn, 0, &test_suite_tail); 41 | } 42 | void tlib_add_test(const char *name, void (*fn)(void)) 43 | { 44 | add_test_to_list(name, fn, 0, &test_tail); 45 | } 46 | void tlib_add_test_opt(const char *name, void (*fn)(void)) 47 | { 48 | add_test_to_list(name, fn, 1, &test_tail); 49 | } 50 | 51 | static FILE *tlib_out, *tlib_err; 52 | static jmp_buf test_jmp_buf, *test_jmp; 53 | static char assert_buf[256]; 54 | static void test_printf(const char *fmt, ...); 55 | static double run_test(struct test *test) 56 | { 57 | #if defined(_WIN64) || defined(_WIN32) 58 | #pragma comment(lib, "winmm.lib") 59 | unsigned long __stdcall timeGetTime(void); 60 | unsigned long t0 = timeGetTime(); 61 | test->fn(); 62 | unsigned long t1 = timeGetTime(); 63 | return (t1 - t0) / 1000.0; 64 | #else 65 | time_t t0 = time(0); 66 | test->fn(); 67 | time_t t1 = time(0); 68 | return difftime(t1, t0); 69 | #endif 70 | } 71 | static void do_test_default(struct test *test, int testno) 72 | { 73 | if (0 != test) 74 | { 75 | snprintf(assert_buf, sizeof assert_buf, "KO\n "); 76 | char dispname[39 + 1]; 77 | size_t displen = strlen(test->name); 78 | if (displen > sizeof dispname - 1) 79 | displen = sizeof dispname - 1; 80 | memcpy(dispname, test->name, displen); 81 | memset(dispname + displen, '.', sizeof dispname - 1 - displen); 82 | dispname[sizeof dispname - 1] = '\0'; 83 | test_printf("%s ", dispname); 84 | double d = run_test(test); 85 | test_printf("OK %.2fs\n", d); 86 | } 87 | else 88 | test_printf("--- COMPLETE ---\n"); 89 | } 90 | static void do_test_list(struct test *test, int testno) 91 | { 92 | if (0 != test) 93 | test_printf("%s\n", test->name); 94 | } 95 | static void do_test_tap(struct test *test, int testno) 96 | { 97 | if (0 != test) 98 | { 99 | snprintf(assert_buf, sizeof assert_buf, "not ok %d %s\n# ", testno + 1, test->name); 100 | run_test(test); 101 | test_printf("ok %d %s\n", testno + 1, test->name); 102 | } 103 | else 104 | test_printf("1..%d\n", testno); 105 | } 106 | static void test_printf(const char *fmt, ...) 107 | { 108 | va_list ap; 109 | va_start(ap, fmt); 110 | FILE *f = tlib_out ? tlib_out : stdout; 111 | vfprintf(f, fmt, ap); 112 | fflush(f); 113 | va_end(ap); 114 | } 115 | void tlib_printf(const char *fmt, ...) 116 | { 117 | va_list ap; 118 | va_start(ap, fmt); 119 | FILE *f = tlib_err ? tlib_err : stderr; 120 | vfprintf(f, fmt, ap); 121 | fflush(f); 122 | va_end(ap); 123 | } 124 | void tlib_run_tests(int argc, char *argv[]) 125 | { 126 | argc--; argv++; 127 | void (*do_test)(struct test *, int) = do_test_default; 128 | int match_any = 1, no_abort = 0; 129 | unsigned long repeat = 1; 130 | for (char **ap = argv, **aendp = ap + argc; aendp > ap; ap++) 131 | { 132 | const char *a = *ap; 133 | if ('-' == a[0]) 134 | { 135 | if (0 == strcmp("--list", a)) 136 | do_test = do_test_list; 137 | else if (0 == strcmp("--tap", a)) 138 | do_test = do_test_tap; 139 | else if (0 == strcmp("--no-abort", a)) 140 | no_abort = 1; 141 | else if (0 == strcmp("--repeat-forever", a)) 142 | repeat = ULONG_MAX; 143 | else if ('-' == a[1]) 144 | { 145 | fprintf(stderr, "tlib_run_tests: unknown option %s\n", a); 146 | exit(2); 147 | } 148 | } 149 | else 150 | match_any = 0; 151 | } 152 | for (struct test *test = test_suite_tail->next->next; 0 != test->fn; test = test->next) 153 | test->fn(); 154 | while (repeat--) 155 | { 156 | int testno = 0; 157 | for (struct test *test = test_tail->next->next; 0 != test->fn; test = test->next) 158 | { 159 | int match_arg = match_any && !test->optional; 160 | for (char **ap = argv, **aendp = ap + argc; aendp > ap; aendp--) 161 | { 162 | const char *a = aendp[-1]; 163 | int sign = a[0]; 164 | if ('+' == sign) 165 | a++; 166 | else if ('-' == sign) 167 | { 168 | if ('-' == a[1]) 169 | continue; 170 | a++; 171 | } 172 | size_t l = strlen(a); 173 | if (0 == (0 < l && '*' == a[l - 1] ? 174 | strncmp(test->name, a, l - 1) : strcmp(test->name, a))) 175 | { 176 | if ('+' == sign) 177 | match_arg = 1; 178 | else if ('-' == sign) 179 | match_arg = 0; 180 | else 181 | match_arg = !test->optional; 182 | break; 183 | } 184 | } 185 | if (!match_arg) 186 | continue; 187 | assert_buf[0] = '\0'; 188 | if (no_abort) 189 | { 190 | test_jmp = &test_jmp_buf; 191 | if (0 == setjmp(*test_jmp)) 192 | do_test(test, testno); 193 | test_jmp = 0; 194 | } 195 | else 196 | do_test(test, testno); 197 | testno++; 198 | } 199 | do_test(0, testno); 200 | } 201 | } 202 | void tlib__assert(const char *func, const char *file, int line, const char *expr) 203 | { 204 | #if defined(_WIN64) || defined(_WIN32) 205 | const char *p = strrchr(file, '\\'); 206 | #else 207 | const char *p = strrchr(file, '/'); 208 | #endif 209 | file = 0 != p ? p + 1 : file; 210 | if (0 == func) 211 | test_printf("%sASSERT(%s) failed at: %s:%d\n", assert_buf, expr, file, line); 212 | else 213 | test_printf("%sASSERT(%s) failed at %s:%d:%s\n", assert_buf, expr, file, line, func); 214 | if (0 != test_jmp) 215 | longjmp(*test_jmp, 1); 216 | } 217 | -------------------------------------------------------------------------------- /src/shared/km/errno.i: -------------------------------------------------------------------------------- 1 | #if FUSE_ERRNO == 87 /* Windows */ 2 | 3 | case 0: return STATUS_SUCCESS; 4 | case 1: return STATUS_ACCESS_DENIED; 5 | case 2: return STATUS_OBJECT_NAME_NOT_FOUND; 6 | case 3: return STATUS_PROCEDURE_NOT_FOUND; 7 | case 4: return STATUS_CANCELLED; 8 | case 5: return STATUS_IO_DEVICE_ERROR; 9 | case 6: return STATUS_FILE_INVALID; 10 | case 7: return STATUS_INSUFFICIENT_RESOURCES; 11 | case 8: return STATUS_INVALID_IMAGE_FORMAT; 12 | case 9: return STATUS_INVALID_HANDLE; 13 | case 12: return STATUS_INSUFFICIENT_RESOURCES; 14 | case 13: return STATUS_ACCESS_DENIED; 15 | case 14: return STATUS_ACCESS_VIOLATION; 16 | case 16: return STATUS_DEVICE_BUSY; 17 | case 17: return STATUS_OBJECT_NAME_COLLISION; 18 | case 18: return STATUS_NOT_SAME_DEVICE; 19 | case 19: return STATUS_NO_SUCH_DEVICE; 20 | case 20: return STATUS_NOT_A_DIRECTORY; 21 | case 21: return STATUS_FILE_IS_A_DIRECTORY; 22 | case 22: return STATUS_INVALID_PARAMETER; 23 | case 23: return STATUS_TOO_MANY_OPENED_FILES; 24 | case 24: return STATUS_TOO_MANY_OPENED_FILES; 25 | case 27: return STATUS_DISK_FULL; 26 | case 28: return STATUS_DISK_FULL; 27 | case 29: return STATUS_INVALID_PARAMETER; 28 | case 30: return STATUS_MEDIA_WRITE_PROTECTED; 29 | case 31: return STATUS_TOO_MANY_LINKS; 30 | case 32: return STATUS_PIPE_BROKEN; 31 | case 33: return STATUS_INVALID_PARAMETER; 32 | case 34: return STATUS_INVALID_PARAMETER; 33 | case 36: return STATUS_POSSIBLE_DEADLOCK; 34 | case 38: return STATUS_NAME_TOO_LONG; 35 | case 39: return STATUS_LOCK_NOT_GRANTED; 36 | case 40: return STATUS_INVALID_DEVICE_REQUEST; 37 | case 41: return STATUS_DIRECTORY_NOT_EMPTY; 38 | case 42: return STATUS_INVALID_PARAMETER; 39 | case 100: return STATUS_ADDRESS_ALREADY_ASSOCIATED; 40 | case 103: return STATUS_CONNECTION_ACTIVE; 41 | case 105: return STATUS_CANCELLED; 42 | case 106: return STATUS_CONNECTION_ABORTED; 43 | case 107: return STATUS_CONNECTION_REFUSED; 44 | case 108: return STATUS_CONNECTION_RESET; 45 | case 110: return STATUS_HOST_UNREACHABLE; 46 | case 113: return STATUS_CONNECTION_ACTIVE; 47 | case 114: return STATUS_REPARSE_POINT_NOT_RESOLVED; 48 | case 116: return STATUS_HOST_DOWN; 49 | case 117: return STATUS_CONNECTION_RESET; 50 | case 118: return STATUS_NETWORK_UNREACHABLE; 51 | case 119: return STATUS_INSUFFICIENT_RESOURCES; 52 | case 120: return STATUS_END_OF_FILE; 53 | case 121: return STATUS_CONNECTION_INVALID; 54 | case 126: return STATUS_CONNECTION_INVALID; 55 | case 128: return STATUS_INVALID_HANDLE; 56 | case 138: return STATUS_TRANSACTION_TIMED_OUT; 57 | 58 | #elif FUSE_ERRNO == 67 /* Cygwin */ 59 | 60 | case 0: return STATUS_SUCCESS; 61 | case 1: return STATUS_ACCESS_DENIED; 62 | case 2: return STATUS_OBJECT_NAME_NOT_FOUND; 63 | case 3: return STATUS_PROCEDURE_NOT_FOUND; 64 | case 4: return STATUS_CANCELLED; 65 | case 5: return STATUS_IO_DEVICE_ERROR; 66 | case 6: return STATUS_FILE_INVALID; 67 | case 7: return STATUS_INSUFFICIENT_RESOURCES; 68 | case 8: return STATUS_INVALID_IMAGE_FORMAT; 69 | case 9: return STATUS_INVALID_HANDLE; 70 | case 12: return STATUS_INSUFFICIENT_RESOURCES; 71 | case 13: return STATUS_ACCESS_DENIED; 72 | case 14: return STATUS_ACCESS_VIOLATION; 73 | case 16: return STATUS_DEVICE_BUSY; 74 | case 17: return STATUS_OBJECT_NAME_COLLISION; 75 | case 18: return STATUS_NOT_SAME_DEVICE; 76 | case 19: return STATUS_NO_SUCH_DEVICE; 77 | case 20: return STATUS_NOT_A_DIRECTORY; 78 | case 21: return STATUS_FILE_IS_A_DIRECTORY; 79 | case 22: return STATUS_INVALID_PARAMETER; 80 | case 23: return STATUS_TOO_MANY_OPENED_FILES; 81 | case 24: return STATUS_TOO_MANY_OPENED_FILES; 82 | case 27: return STATUS_DISK_FULL; 83 | case 28: return STATUS_DISK_FULL; 84 | case 29: return STATUS_INVALID_PARAMETER; 85 | case 30: return STATUS_MEDIA_WRITE_PROTECTED; 86 | case 31: return STATUS_TOO_MANY_LINKS; 87 | case 32: return STATUS_PIPE_BROKEN; 88 | case 33: return STATUS_INVALID_PARAMETER; 89 | case 34: return STATUS_INVALID_PARAMETER; 90 | case 45: return STATUS_POSSIBLE_DEADLOCK; 91 | case 91: return STATUS_NAME_TOO_LONG; 92 | case 46: return STATUS_LOCK_NOT_GRANTED; 93 | case 88: return STATUS_INVALID_DEVICE_REQUEST; 94 | case 90: return STATUS_DIRECTORY_NOT_EMPTY; 95 | case 138: return STATUS_INVALID_PARAMETER; 96 | case 112: return STATUS_ADDRESS_ALREADY_ASSOCIATED; 97 | case 120: return STATUS_CONNECTION_ACTIVE; 98 | case 140: return STATUS_CANCELLED; 99 | case 113: return STATUS_CONNECTION_ABORTED; 100 | case 111: return STATUS_CONNECTION_REFUSED; 101 | case 104: return STATUS_CONNECTION_RESET; 102 | case 118: return STATUS_HOST_UNREACHABLE; 103 | case 127: return STATUS_CONNECTION_ACTIVE; 104 | case 92: return STATUS_REPARSE_POINT_NOT_RESOLVED; 105 | case 115: return STATUS_HOST_DOWN; 106 | case 126: return STATUS_CONNECTION_RESET; 107 | case 114: return STATUS_NETWORK_UNREACHABLE; 108 | case 105: return STATUS_INSUFFICIENT_RESOURCES; 109 | case 61: return STATUS_END_OF_FILE; 110 | case 67: return STATUS_CONNECTION_INVALID; 111 | case 128: return STATUS_CONNECTION_INVALID; 112 | case 108: return STATUS_INVALID_HANDLE; 113 | case 116: return STATUS_TRANSACTION_TIMED_OUT; 114 | 115 | #elif FUSE_ERRNO == 76 /* Linux */ 116 | 117 | case 0: return STATUS_SUCCESS; 118 | case 1: return STATUS_ACCESS_DENIED; 119 | case 2: return STATUS_OBJECT_NAME_NOT_FOUND; 120 | case 3: return STATUS_PROCEDURE_NOT_FOUND; 121 | case 4: return STATUS_CANCELLED; 122 | case 5: return STATUS_IO_DEVICE_ERROR; 123 | case 6: return STATUS_FILE_INVALID; 124 | case 7: return STATUS_INSUFFICIENT_RESOURCES; 125 | case 8: return STATUS_INVALID_IMAGE_FORMAT; 126 | case 9: return STATUS_INVALID_HANDLE; 127 | case 12: return STATUS_INSUFFICIENT_RESOURCES; 128 | case 13: return STATUS_ACCESS_DENIED; 129 | case 14: return STATUS_ACCESS_VIOLATION; 130 | case 16: return STATUS_DEVICE_BUSY; 131 | case 17: return STATUS_OBJECT_NAME_COLLISION; 132 | case 18: return STATUS_NOT_SAME_DEVICE; 133 | case 19: return STATUS_NO_SUCH_DEVICE; 134 | case 20: return STATUS_NOT_A_DIRECTORY; 135 | case 21: return STATUS_FILE_IS_A_DIRECTORY; 136 | case 22: return STATUS_INVALID_PARAMETER; 137 | case 23: return STATUS_TOO_MANY_OPENED_FILES; 138 | case 24: return STATUS_TOO_MANY_OPENED_FILES; 139 | case 27: return STATUS_DISK_FULL; 140 | case 28: return STATUS_DISK_FULL; 141 | case 29: return STATUS_INVALID_PARAMETER; 142 | case 30: return STATUS_MEDIA_WRITE_PROTECTED; 143 | case 31: return STATUS_TOO_MANY_LINKS; 144 | case 32: return STATUS_PIPE_BROKEN; 145 | case 33: return STATUS_INVALID_PARAMETER; 146 | case 34: return STATUS_INVALID_PARAMETER; 147 | case 35: return STATUS_POSSIBLE_DEADLOCK; 148 | case 36: return STATUS_NAME_TOO_LONG; 149 | case 37: return STATUS_LOCK_NOT_GRANTED; 150 | case 38: return STATUS_INVALID_DEVICE_REQUEST; 151 | case 39: return STATUS_DIRECTORY_NOT_EMPTY; 152 | case 84: return STATUS_INVALID_PARAMETER; 153 | case 98: return STATUS_ADDRESS_ALREADY_ASSOCIATED; 154 | case 114: return STATUS_CONNECTION_ACTIVE; 155 | case 125: return STATUS_CANCELLED; 156 | case 103: return STATUS_CONNECTION_ABORTED; 157 | case 111: return STATUS_CONNECTION_REFUSED; 158 | case 104: return STATUS_CONNECTION_RESET; 159 | case 113: return STATUS_HOST_UNREACHABLE; 160 | case 106: return STATUS_CONNECTION_ACTIVE; 161 | case 40: return STATUS_REPARSE_POINT_NOT_RESOLVED; 162 | case 100: return STATUS_HOST_DOWN; 163 | case 102: return STATUS_CONNECTION_RESET; 164 | case 101: return STATUS_NETWORK_UNREACHABLE; 165 | case 105: return STATUS_INSUFFICIENT_RESOURCES; 166 | case 61: return STATUS_END_OF_FILE; 167 | case 67: return STATUS_CONNECTION_INVALID; 168 | case 107: return STATUS_CONNECTION_INVALID; 169 | case 88: return STATUS_INVALID_HANDLE; 170 | case 110: return STATUS_TRANSACTION_TIMED_OUT; 171 | 172 | #endif 173 | -------------------------------------------------------------------------------- /src/shared/km/ioq.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file shared/km/ioq.c 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #include 23 | 24 | NTSTATUS FuseIoqCreate(FUSE_IOQ **PIoq); 25 | VOID FuseIoqDelete(FUSE_IOQ *Ioq); 26 | VOID FuseIoqStartProcessing(FUSE_IOQ *Ioq, FUSE_CONTEXT *Context); 27 | FUSE_CONTEXT *FuseIoqEndProcessing(FUSE_IOQ *Ioq, UINT64 Unique); 28 | VOID FuseIoqPostPending(FUSE_IOQ *Ioq, FUSE_CONTEXT *Context); 29 | VOID FuseIoqPostPendingAndStop(FUSE_IOQ *Ioq, FUSE_CONTEXT *Context); 30 | FUSE_CONTEXT *FuseIoqNextPending(FUSE_IOQ *Ioq); 31 | 32 | #ifdef ALLOC_PRAGMA 33 | #pragma alloc_text(PAGE, FuseIoqCreate) 34 | #pragma alloc_text(PAGE, FuseIoqDelete) 35 | #pragma alloc_text(PAGE, FuseIoqStartProcessing) 36 | #pragma alloc_text(PAGE, FuseIoqEndProcessing) 37 | #pragma alloc_text(PAGE, FuseIoqPostPending) 38 | #pragma alloc_text(PAGE, FuseIoqPostPendingAndStop) 39 | #pragma alloc_text(PAGE, FuseIoqNextPending) 40 | #endif 41 | 42 | #define FUSE_IOQ_SIZE 1024 43 | 44 | struct _FUSE_IOQ 45 | { 46 | FAST_MUTEX Mutex; 47 | LIST_ENTRY PendingList, ProcessList; 48 | FUSE_CONTEXT *LastContext; 49 | ULONG ProcessBucketCount; 50 | FUSE_CONTEXT *ProcessBuckets[]; 51 | }; 52 | 53 | NTSTATUS FuseIoqCreate(FUSE_IOQ **PIoq) 54 | { 55 | PAGED_CODE(); 56 | 57 | *PIoq = 0; 58 | 59 | FUSE_IOQ *Ioq; 60 | ULONG BucketCount = (FUSE_IOQ_SIZE - sizeof *Ioq) / sizeof Ioq->ProcessBuckets[0]; 61 | Ioq = FuseAllocNonPaged(FUSE_IOQ_SIZE); 62 | if (0 == Ioq) 63 | return STATUS_INSUFFICIENT_RESOURCES; 64 | RtlZeroMemory(Ioq, FUSE_IOQ_SIZE); 65 | 66 | ExInitializeFastMutex(&Ioq->Mutex); 67 | InitializeListHead(&Ioq->PendingList); 68 | InitializeListHead(&Ioq->ProcessList); 69 | Ioq->ProcessBucketCount = BucketCount; 70 | 71 | *PIoq = Ioq; 72 | 73 | return STATUS_SUCCESS; 74 | } 75 | 76 | VOID FuseIoqDelete(FUSE_IOQ *Ioq) 77 | { 78 | PAGED_CODE(); 79 | 80 | for (PLIST_ENTRY Entry = Ioq->PendingList.Flink; &Ioq->PendingList != Entry;) 81 | { 82 | FUSE_CONTEXT *Context = CONTAINING_RECORD(Entry, FUSE_CONTEXT, ListEntry); 83 | Entry = Entry->Flink; 84 | FuseContextDelete(Context); 85 | } 86 | for (PLIST_ENTRY Entry = Ioq->ProcessList.Flink; &Ioq->ProcessList != Entry;) 87 | { 88 | FUSE_CONTEXT *Context = CONTAINING_RECORD(Entry, FUSE_CONTEXT, ListEntry); 89 | Entry = Entry->Flink; 90 | FuseContextDelete(Context); 91 | } 92 | FuseFree(Ioq); 93 | } 94 | 95 | VOID FuseIoqStartProcessing(FUSE_IOQ *Ioq, FUSE_CONTEXT *Context) 96 | { 97 | PAGED_CODE(); 98 | 99 | ExAcquireFastMutex(&Ioq->Mutex); 100 | 101 | if (0 != Ioq->LastContext) 102 | { 103 | if (Context != Ioq->LastContext) 104 | { 105 | ExReleaseFastMutex(&Ioq->Mutex); 106 | ASSERT(0 != Context->FuseRequest); 107 | if (0 != Context->FuseRequest) 108 | Context->FuseRequest->len = 0; 109 | FuseContextDelete(Context); 110 | return; 111 | } 112 | else 113 | Ioq->LastContext = (PVOID)(UINT_PTR)1; 114 | } 115 | 116 | InsertTailList(&Ioq->ProcessList, &Context->ListEntry); 117 | 118 | ULONG Index = FuseHashMixPointer(Context) % Ioq->ProcessBucketCount; 119 | #if DBG 120 | for (FUSE_CONTEXT *ContextX = Ioq->ProcessBuckets[Index]; ContextX; ContextX = ContextX->DictNext) 121 | ASSERT(ContextX != Context); 122 | #endif 123 | ASSERT(0 == Context->DictNext); 124 | Context->DictNext = Ioq->ProcessBuckets[Index]; 125 | Ioq->ProcessBuckets[Index] = Context; 126 | 127 | ExReleaseFastMutex(&Ioq->Mutex); 128 | } 129 | 130 | FUSE_CONTEXT *FuseIoqEndProcessing(FUSE_IOQ *Ioq, UINT64 Unique) 131 | { 132 | PAGED_CODE(); 133 | 134 | FUSE_CONTEXT *ContextHint = (PVOID)(UINT_PTR)Unique; 135 | FUSE_CONTEXT *Context = 0; 136 | 137 | ExAcquireFastMutex(&Ioq->Mutex); 138 | 139 | ULONG Index = FuseHashMixPointer(ContextHint) % Ioq->ProcessBucketCount; 140 | for (FUSE_CONTEXT **PContext = &Ioq->ProcessBuckets[Index]; *PContext; PContext = &(*PContext)->DictNext) 141 | { 142 | if (*PContext == ContextHint) 143 | { 144 | *PContext = ContextHint->DictNext; 145 | ContextHint->DictNext = 0; 146 | 147 | Context = ContextHint; 148 | RemoveEntryList(&Context->ListEntry); 149 | 150 | break; 151 | } 152 | } 153 | 154 | ExReleaseFastMutex(&Ioq->Mutex); 155 | 156 | return Context; 157 | } 158 | 159 | VOID FuseIoqPostPending(FUSE_IOQ *Ioq, FUSE_CONTEXT *Context) 160 | { 161 | PAGED_CODE(); 162 | 163 | ExAcquireFastMutex(&Ioq->Mutex); 164 | 165 | if (0 != Ioq->LastContext) 166 | { 167 | ExReleaseFastMutex(&Ioq->Mutex); 168 | FuseContextDelete(Context); 169 | return; 170 | } 171 | 172 | InsertTailList(&Ioq->PendingList, &Context->ListEntry); 173 | 174 | ExReleaseFastMutex(&Ioq->Mutex); 175 | } 176 | 177 | VOID FuseIoqPostPendingAndStop(FUSE_IOQ *Ioq, FUSE_CONTEXT *Context) 178 | /* 179 | * This function is used to post the last Context for processing (usually DESTROY). 180 | * It clears the Pending list, but does not touch the Process list. The reason is 181 | * that the last Context must be retrieved only after all in-flight Context's are 182 | * done and FuseIoqNextPending checks for this condition when the last Context has 183 | * been posted. 184 | */ 185 | { 186 | PAGED_CODE(); 187 | 188 | ExAcquireFastMutex(&Ioq->Mutex); 189 | 190 | if (0 != Ioq->LastContext) 191 | { 192 | ExReleaseFastMutex(&Ioq->Mutex); 193 | FuseContextDelete(Context); 194 | return; 195 | } 196 | 197 | for (PLIST_ENTRY Entry = Ioq->PendingList.Flink; &Ioq->PendingList != Entry;) 198 | { 199 | FUSE_CONTEXT *Temp = CONTAINING_RECORD(Entry, FUSE_CONTEXT, ListEntry); 200 | Entry = Entry->Flink; 201 | FuseContextDelete(Temp); 202 | } 203 | InitializeListHead(&Ioq->PendingList); 204 | 205 | InsertTailList(&Ioq->PendingList, &Context->ListEntry); 206 | Ioq->LastContext = Context; 207 | 208 | ExReleaseFastMutex(&Ioq->Mutex); 209 | } 210 | 211 | FUSE_CONTEXT *FuseIoqNextPending(FUSE_IOQ *Ioq) 212 | { 213 | PAGED_CODE(); 214 | 215 | ExAcquireFastMutex(&Ioq->Mutex); 216 | 217 | if (0 != Ioq->LastContext && !IsListEmpty(&Ioq->ProcessList)) 218 | { 219 | ExReleaseFastMutex(&Ioq->Mutex); 220 | return 0; 221 | } 222 | 223 | PLIST_ENTRY Entry = Ioq->PendingList.Flink; 224 | FUSE_CONTEXT *Context = &Ioq->PendingList != Entry ? 225 | CONTAINING_RECORD(Entry, FUSE_CONTEXT, ListEntry) : 0; 226 | 227 | if (0 != Context) 228 | RemoveEntryList(&Context->ListEntry); 229 | 230 | ExReleaseFastMutex(&Ioq->Mutex); 231 | 232 | return Context; 233 | } 234 | -------------------------------------------------------------------------------- /src/shared/ku/wslfuse.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file shared/ku/wslfuse.h 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #ifndef SHARED_KU_WSLFUSE_H_INCLUDED 23 | #define SHARED_KU_WSLFUSE_H_INCLUDED 24 | 25 | #if defined(__linux__) 26 | /* 27 | * We duplicate some of the definitions from WinFsp and WinFuse 28 | * to avoid having either of them as a build dependency when 29 | * building on Linux. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | typedef char CHAR; 36 | typedef uint16_t WCHAR; 37 | typedef uint8_t UINT8; 38 | typedef uint16_t UINT16; 39 | typedef uint32_t UINT32; 40 | typedef uint64_t UINT64; 41 | 42 | #define FSP_FSCTL_DISK_DEVICE_NAME "WinFsp.Disk" 43 | #define FSP_FSCTL_NET_DEVICE_NAME "WinFsp.Net" 44 | #define FSP_FSCTL_VOLUME_NAME_SIZE (64 * sizeof(WCHAR)) 45 | #define FSP_FSCTL_VOLUME_PREFIX_SIZE (192 * sizeof(WCHAR)) 46 | #define FSP_FSCTL_VOLUME_FSNAME_SIZE (16 * sizeof(WCHAR)) 47 | #define FSP_FSCTL_VOLUME_NAME_SIZEMAX (FSP_FSCTL_VOLUME_NAME_SIZE + FSP_FSCTL_VOLUME_PREFIX_SIZE) 48 | #define FSP_FSCTL_VOLUME_PARAMS_V0_FIELD_DEFN\ 49 | UINT16 Version; /* set to 0 or sizeof(FSP_FSCTL_VOLUME_PARAMS) */\ 50 | /* volume information */\ 51 | UINT16 SectorSize;\ 52 | UINT16 SectorsPerAllocationUnit;\ 53 | UINT16 MaxComponentLength; /* maximum file name component length (bytes) */\ 54 | UINT64 VolumeCreationTime;\ 55 | UINT32 VolumeSerialNumber;\ 56 | /* I/O timeouts, capacity, etc. */\ 57 | UINT32 TransactTimeout; /* DEPRECATED: (millis; 1 sec - 10 sec) */\ 58 | UINT32 IrpTimeout; /* pending IRP timeout (millis; 1 min - 10 min) */\ 59 | UINT32 IrpCapacity; /* maximum number of pending IRP's (100 - 1000)*/\ 60 | UINT32 FileInfoTimeout; /* FileInfo/Security/VolumeInfo timeout (millis) */\ 61 | /* FILE_FS_ATTRIBUTE_INFORMATION::FileSystemAttributes */\ 62 | UINT32 CaseSensitiveSearch:1; /* file system supports case-sensitive file names */\ 63 | UINT32 CasePreservedNames:1; /* file system preserves the case of file names */\ 64 | UINT32 UnicodeOnDisk:1; /* file system supports Unicode in file names */\ 65 | UINT32 PersistentAcls:1; /* file system preserves and enforces access control lists */\ 66 | UINT32 ReparsePoints:1; /* file system supports reparse points */\ 67 | UINT32 ReparsePointsAccessCheck:1; /* file system performs reparse point access checks */\ 68 | UINT32 NamedStreams:1; /* file system supports named streams */\ 69 | UINT32 HardLinks:1; /* unimplemented; set to 0 */\ 70 | UINT32 ExtendedAttributes:1; /* file system supports extended attributes */\ 71 | UINT32 ReadOnlyVolume:1;\ 72 | /* kernel-mode flags */\ 73 | UINT32 PostCleanupWhenModifiedOnly:1; /* post Cleanup when a file was modified/deleted */\ 74 | UINT32 PassQueryDirectoryPattern:1; /* pass Pattern during QueryDirectory operations */\ 75 | UINT32 AlwaysUseDoubleBuffering:1;\ 76 | UINT32 PassQueryDirectoryFileName:1; /* pass FileName during QueryDirectory (GetDirInfoByName) */\ 77 | UINT32 FlushAndPurgeOnCleanup:1; /* keeps file off "standby" list */\ 78 | UINT32 DeviceControl:1; /* support user-mode ioctl handling */\ 79 | /* user-mode flags */\ 80 | UINT32 UmFileContextIsUserContext2:1; /* user mode: FileContext parameter is UserContext2 */\ 81 | UINT32 UmFileContextIsFullContext:1; /* user mode: FileContext parameter is FullContext */\ 82 | UINT32 UmReservedFlags:6;\ 83 | /* additional kernel-mode flags */\ 84 | UINT32 AllowOpenInKernelMode:1; /* allow kernel mode to open files when possible */\ 85 | UINT32 CasePreservedExtendedAttributes:1; /* preserve case of EA (default is UPPERCASE) */\ 86 | UINT32 WslFeatures:1; /* support features required for WSLinux */\ 87 | UINT32 KmReservedFlags:5;\ 88 | WCHAR Prefix[FSP_FSCTL_VOLUME_PREFIX_SIZE / sizeof(WCHAR)]; /* UNC prefix (\Server\Share) */\ 89 | WCHAR FileSystemName[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR)]; 90 | #define FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN\ 91 | /* additional fields; specify .Version == sizeof(FSP_FSCTL_VOLUME_PARAMS) */\ 92 | UINT32 VolumeInfoTimeoutValid:1; /* VolumeInfoTimeout field is valid */\ 93 | UINT32 DirInfoTimeoutValid:1; /* DirInfoTimeout field is valid */\ 94 | UINT32 SecurityTimeoutValid:1; /* SecurityTimeout field is valid*/\ 95 | UINT32 StreamInfoTimeoutValid:1; /* StreamInfoTimeout field is valid */\ 96 | UINT32 EaTimeoutValid:1; /* EaTimeout field is valid */\ 97 | UINT32 KmAdditionalReservedFlags:27;\ 98 | UINT32 VolumeInfoTimeout; /* volume info timeout (millis); overrides FileInfoTimeout */\ 99 | UINT32 DirInfoTimeout; /* dir info timeout (millis); overrides FileInfoTimeout */\ 100 | UINT32 SecurityTimeout; /* security info timeout (millis); overrides FileInfoTimeout */\ 101 | UINT32 StreamInfoTimeout; /* stream info timeout (millis); overrides FileInfoTimeout */\ 102 | UINT32 EaTimeout; /* EA timeout (millis); overrides FileInfoTimeout */\ 103 | UINT32 FsextControlCode;\ 104 | UINT32 Reserved32[1];\ 105 | UINT64 Reserved64[2]; 106 | typedef struct 107 | { 108 | FSP_FSCTL_VOLUME_PARAMS_V0_FIELD_DEFN 109 | FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN 110 | } FSP_FSCTL_VOLUME_PARAMS; 111 | _Static_assert(504 == sizeof(FSP_FSCTL_VOLUME_PARAMS), 112 | "sizeof(FSP_FSCTL_VOLUME_PARAMS) must be 504."); 113 | #endif 114 | 115 | typedef union 116 | { 117 | FSP_FSCTL_VOLUME_PARAMS VolumeParams; 118 | struct 119 | { 120 | CHAR VolumeName[(FSP_FSCTL_VOLUME_NAME_SIZEMAX / sizeof(WCHAR)) * 3 / 2]; 121 | UINT64 VolumeId; 122 | }; 123 | } WSLFUSE_IOCTL_CREATEVOLUME_ARG; 124 | #if defined(__linux__) 125 | _Static_assert(504 == sizeof(WSLFUSE_IOCTL_CREATEVOLUME_ARG), 126 | "sizeof(WSLFUSE_IOCTL_CREATEVOLUME_ARG) must be 504."); 127 | #endif 128 | 129 | typedef struct 130 | { 131 | CHAR WinMountPoint[260]; 132 | } WSLFUSE_IOCTL_WINMOUNT_ARG; 133 | #if defined(__linux__) 134 | _Static_assert(260 == sizeof(WSLFUSE_IOCTL_WINMOUNT_ARG), 135 | "sizeof(WSLFUSE_IOCTL_WINMOUNT_ARG) must be 260."); 136 | #endif 137 | 138 | typedef struct 139 | { 140 | UINT8 Operation; 141 | UINT64 VolumeId; 142 | UINT64 LxMountId; 143 | } WSLFUSE_IOCTL_LXMOUNT_ARG; 144 | #if defined(__linux__) 145 | _Static_assert(24 == sizeof(WSLFUSE_IOCTL_LXMOUNT_ARG), 146 | "sizeof(WSLFUSE_IOCTL_LXMOUNT_ARG) must be 16."); 147 | #endif 148 | 149 | /* 150 | * _IOWR('F', 'C', WSLFUSE_IOCTL_CREATEVOLUME_ARG) 151 | * sh tools/ioc.c 3 70 67 504 152 | */ 153 | #define WSLFUSE_IOCTL_CREATEVOLUME 0xc1f84643 154 | #if defined(__linux__) 155 | _Static_assert(WSLFUSE_IOCTL_CREATEVOLUME == _IOWR('F', 'C', WSLFUSE_IOCTL_CREATEVOLUME_ARG), 156 | "WSLFUSE_IOCTL_CREATEVOLUME"); 157 | #endif 158 | 159 | /* 160 | * _IOW('F', 'M', WSLFUSE_IOCTL_WINMOUNT_ARG) 161 | * sh tools/ioc.c 1 70 77 260 162 | */ 163 | #define WSLFUSE_IOCTL_WINMOUNT 0x4104464d 164 | #if defined(__linux__) 165 | _Static_assert(WSLFUSE_IOCTL_WINMOUNT == _IOW('F', 'M', WSLFUSE_IOCTL_WINMOUNT_ARG), 166 | "WSLFUSE_IOCTL_WINMOUNT"); 167 | #endif 168 | 169 | /* 170 | * _IOW('F', 'm', WSLFUSE_IOCTL_LXMOUNT_ARG) 171 | * sh tools/ioc.c 1 70 109 24 172 | */ 173 | #define WSLFUSE_IOCTL_LXMOUNT 0x4018466d 174 | #if defined(__linux__) 175 | _Static_assert(WSLFUSE_IOCTL_LXMOUNT == _IOW('F', 'm', WSLFUSE_IOCTL_LXMOUNT_ARG), 176 | "WSLFUSE_IOCTL_LXMOUNT"); 177 | #endif 178 | 179 | #endif 180 | -------------------------------------------------------------------------------- /doc/wslseq.svg: -------------------------------------------------------------------------------- 1 | Originating Process ContextFile System Process ContextOriginatingProcesswinfspwinfspwslfuseLinuxFUSE FSI/OContext SwitchFSP_FSCTL_TRANSACT Req/dev/fuse readProcess/dev/fuse writeFSP_FSCTL_TRANSACT RspContext Switch and Return -------------------------------------------------------------------------------- /doc/winseq.svg: -------------------------------------------------------------------------------- 1 | Originating Process ContextFile System Process ContextOriginatingProcesswinfspwinfspwinfuseWindowsFUSE FSI/OContext SwitchFSP_FSCTL_TRANSACT ReqFUSE_FSCTL_TRANSACT ReqProcessFUSE_FSCTL_TRANSACT RspFSP_FSCTL_TRANSACT RspContext Switch and Return -------------------------------------------------------------------------------- /tools/run-tests.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | setlocal 4 | setlocal EnableDelayedExpansion 5 | 6 | set Configuration=Release 7 | if not X%1==X set Configuration=%1 8 | 9 | cd %~dp0.. 10 | set ProjRoot=%cd% 11 | 12 | cd build\VStudio 13 | if not exist build\%Configuration% echo === No tests found >&2 & goto fail 14 | cd build\%Configuration% 15 | 16 | set dfl_tests=^ 17 | winfuse-tests-x64 ^ 18 | winfuse-tests-x86 ^ 19 | wslfuse-tests 20 | set opt_tests=^ 21 | sample-build-memfs-fuse3-x64 ^ 22 | sample-test0-memfs-fuse3-x64 ^ 23 | sample-test1-memfs-fuse3-x64 ^ 24 | sample-testinf-memfs-fuse3-x64 ^ 25 | sample-fsx0-memfs-fuse3-x64 ^ 26 | sample-fsx1-memfs-fuse3-x64 ^ 27 | sample-fsxinf-memfs-fuse3-x64 ^ 28 | sample-build-memfs-fuse3-x86 ^ 29 | sample-test0-memfs-fuse3-x86 ^ 30 | sample-test1-memfs-fuse3-x86 ^ 31 | sample-testinf-memfs-fuse3-x86 ^ 32 | sample-fsx0-memfs-fuse3-x86 ^ 33 | sample-fsx1-memfs-fuse3-x86 ^ 34 | sample-fsxinf-memfs-fuse3-x86 ^ 35 | sample-build-memfs-fuse3-wsl 36 | 37 | REM disable WSL tests that do not pass on AppVeyor (because it uses old Windows/WSL build) 38 | REM sample-test0-memfs-fuse3-wsl ^ 39 | REM sample-test1-memfs-fuse3-wsl ^ 40 | REM sample-testinf-memfs-fuse3-wsl ^ 41 | REM sample-fsx0-memfs-fuse3-wsl ^ 42 | REM sample-fsx1-memfs-fuse3-wsl ^ 43 | REM sample-fsxinf-memfs-fuse3-wsl 44 | 45 | set tests= 46 | for %%f in (%dfl_tests%) do ( 47 | if X%2==X ( 48 | set tests=!tests! %%f 49 | ) else ( 50 | set test=%%f 51 | if not "X!test:%2=!"=="X!test!" set tests=!tests! %%f 52 | ) 53 | ) 54 | for %%f in (%opt_tests%) do ( 55 | if X%2==X ( 56 | rem 57 | ) else ( 58 | set test=%%f 59 | if not "X!test:%2=!"=="X!test!" set tests=!tests! %%f 60 | ) 61 | ) 62 | 63 | set testpass=0 64 | set testfail=0 65 | for %%f in (%tests%) do ( 66 | echo === Running %%f 67 | 68 | if defined APPVEYOR ( 69 | appveyor AddTest "%%f" -FileName None -Framework None -Outcome Running 70 | ) 71 | 72 | pushd %cd% 73 | call :%%f 74 | popd 75 | 76 | if !ERRORLEVEL! neq 0 ( 77 | set /a testfail=testfail+1 78 | 79 | echo === Failed %%f 80 | 81 | if defined APPVEYOR ( 82 | appveyor UpdateTest "%%f" -FileName None -Framework None -Outcome Failed -Duration 0 83 | ) 84 | ) else ( 85 | set /a testpass=testpass+1 86 | 87 | echo === Passed %%f 88 | 89 | if defined APPVEYOR ( 90 | appveyor UpdateTest "%%f" -FileName None -Framework None -Outcome Passed -Duration 0 91 | ) 92 | ) 93 | echo: 94 | ) 95 | 96 | set /a total=testpass+testfail 97 | echo === Total: %testpass%/%total% 98 | call :leak-test 99 | if !ERRORLEVEL! neq 0 goto fail 100 | if not %testfail%==0 goto fail 101 | 102 | exit /b 0 103 | 104 | :fail 105 | exit /b 1 106 | 107 | :winfuse-tests-x64 108 | winfuse-tests-x64 +* 109 | if !ERRORLEVEL! neq 0 goto fail 110 | exit /b 0 111 | 112 | :winfuse-tests-x86 113 | winfuse-tests-x86 +* 114 | if !ERRORLEVEL! neq 0 goto fail 115 | exit /b 0 116 | 117 | :wslfuse-tests 118 | wsl -- sudo mknod /dev/fuse c 10 229; sudo ./wslfuse-tests.out 119 | if !ERRORLEVEL! neq 0 goto fail 120 | exit /b 0 121 | 122 | :sample-build-memfs-fuse3-x64 123 | call :__run_sample_build memfs-fuse3 memfs-fuse3.exe x64 124 | if !ERRORLEVEL! neq 0 goto fail 125 | exit /b 0 126 | 127 | :sample-build-memfs-fuse3-x86 128 | call :__run_sample_build memfs-fuse3 memfs-fuse3.exe x86 129 | if !ERRORLEVEL! neq 0 goto fail 130 | exit /b 0 131 | 132 | :sample-build-memfs-fuse3-wsl 133 | call :__run_sample_build memfs-fuse3 memfs-fuse3.out x64 134 | if !ERRORLEVEL! neq 0 goto fail 135 | exit /b 0 136 | 137 | :__run_sample_build 138 | call %ProjRoot%\tools\build-sample.bat %Configuration% %3 %1*%2 "%TMP%\%1" 139 | if !ERRORLEVEL! neq 0 goto fail 140 | exit /b 0 141 | 142 | :sample-test0-memfs-fuse3-x64 143 | call :__run_sample_test memfs-fuse3 x64 0 144 | if !ERRORLEVEL! neq 0 goto fail 145 | exit /b 0 146 | 147 | :sample-test1-memfs-fuse3-x64 148 | call :__run_sample_test memfs-fuse3 x64 1 149 | if !ERRORLEVEL! neq 0 goto fail 150 | exit /b 0 151 | 152 | :sample-testinf-memfs-fuse3-x64 153 | call :__run_sample_test memfs-fuse3 x64 -1 154 | if !ERRORLEVEL! neq 0 goto fail 155 | exit /b 0 156 | 157 | :sample-test0-memfs-fuse3-x86 158 | call :__run_sample_test memfs-fuse3 x86 0 159 | if !ERRORLEVEL! neq 0 goto fail 160 | exit /b 0 161 | 162 | :sample-test1-memfs-fuse3-x86 163 | call :__run_sample_test memfs-fuse3 x86 1 164 | if !ERRORLEVEL! neq 0 goto fail 165 | exit /b 0 166 | 167 | :sample-testinf-memfs-fuse3-x86 168 | call :__run_sample_test memfs-fuse3 x86 -1 169 | if !ERRORLEVEL! neq 0 goto fail 170 | exit /b 0 171 | 172 | :__run_sample_test 173 | set TestExit=0 174 | start "" /b "%TMP%\%1\build\%Configuration%\%1-%2.exe" -oFileInfoTimeout=%3 L: 175 | waitfor 7BF47D72F6664550B03248ECFE77C7DD /t 3 2>nul 176 | pushd >nul 177 | cd L: >nul 2>nul || (echo Unable to find drive L: >&2 & goto fail) 178 | L: 179 | "C:\Program Files (x86)\WinFsp\bin\winfsp-tests-x64.exe" ^ 180 | --external --resilient ^ 181 | create_test create_related_test create_sd_test create_notraverse_test create_backup_test ^ 182 | create_restore_test create_share_test create_curdir_test create_namelen_test ^ 183 | getfileinfo_test delete_test delete_pending_test delete_mmap_test delete_standby_test ^ 184 | rename_* ^ 185 | getvolinfo_test ^ 186 | getsecurity_test ^ 187 | rdwr_* ^ 188 | flush_* ^ 189 | lock_* ^ 190 | querydir_* ^ 191 | dirnotify_test ^ 192 | exec_* 193 | if !ERRORLEVEL! neq 0 set TestExit=1 194 | popd 195 | taskkill /f /im %1-%2.exe 196 | exit /b !TestExit! 197 | 198 | :sample-fsx0-memfs-fuse3-x64 199 | call :__run_sample_fsx_test memfs-fuse3 x64 0 200 | if !ERRORLEVEL! neq 0 goto fail 201 | exit /b 0 202 | 203 | :sample-fsx1-memfs-fuse3-x64 204 | call :__run_sample_fsx_test memfs-fuse3 x64 1 205 | if !ERRORLEVEL! neq 0 goto fail 206 | exit /b 0 207 | 208 | :sample-fsxinf-memfs-fuse3-x64 209 | call :__run_sample_fsx_test memfs-fuse3 x64 -1 210 | if !ERRORLEVEL! neq 0 goto fail 211 | exit /b 0 212 | 213 | :sample-fsx0-memfs-fuse3-x86 214 | call :__run_sample_fsx_test memfs-fuse3 x86 0 215 | if !ERRORLEVEL! neq 0 goto fail 216 | exit /b 0 217 | 218 | :sample-fsx1-memfs-fuse3-x86 219 | call :__run_sample_fsx_test memfs-fuse3 x86 1 220 | if !ERRORLEVEL! neq 0 goto fail 221 | exit /b 0 222 | 223 | :sample-fsxinf-memfs-fuse3-x86 224 | call :__run_sample_fsx_test memfs-fuse3 x86 -1 225 | if !ERRORLEVEL! neq 0 goto fail 226 | exit /b 0 227 | 228 | :__run_sample_fsx_test 229 | set TestExit=0 230 | start "" /b "%TMP%\%1\build\%Configuration%\%1-%2.exe" -oFileInfoTimeout=%3 L: 231 | waitfor 7BF47D72F6664550B03248ECFE77C7DD /t 3 2>nul 232 | pushd >nul 233 | cd L: >nul 2>nul || (echo Unable to find drive L: >&2 & goto fail) 234 | L: 235 | "%ProjRoot%\ext\winfsp\ext\test\fstools\src\fsx\fsx.exe" -N 5000 test xxxxxx 236 | if !ERRORLEVEL! neq 0 set TestExit=1 237 | popd 238 | taskkill /f /im %1-%2.exe 239 | exit /b !TestExit! 240 | 241 | :sample-test0-memfs-fuse3-wsl 242 | call :__run_sample_wsl_test memfs-fuse3 0 243 | if !ERRORLEVEL! neq 0 goto fail 244 | exit /b 0 245 | 246 | :sample-test1-memfs-fuse3-wsl 247 | call :__run_sample_wsl_test memfs-fuse3 1 248 | if !ERRORLEVEL! neq 0 goto fail 249 | exit /b 0 250 | 251 | :sample-testinf-memfs-fuse3-wsl 252 | call :__run_sample_wsl_test memfs-fuse3 -1 253 | if !ERRORLEVEL! neq 0 goto fail 254 | exit /b 0 255 | 256 | :__run_sample_wsl_test 257 | set TestExit=0 258 | start "" /b /d "%TMP%\%1\build\%Configuration%" wsl -- sudo sh "/mnt/c/Program Files/WinFuse/opt/wslfuse/install.sh"; mkdir -p mnt; ./%1.out -f -ocontext=FileInfoTimeout=%2,context=Volume=L: mnt 259 | waitfor 7BF47D72F6664550B03248ECFE77C7DD /t 10 2>nul 260 | pushd >nul 261 | cd L: >nul 2>nul || (echo Unable to find drive L: >&2 & goto fail) 262 | L: 263 | "C:\Program Files (x86)\WinFsp\bin\winfsp-tests-x64.exe" ^ 264 | --external --resilient ^ 265 | create_test create_related_test create_sd_test create_notraverse_test create_backup_test ^ 266 | create_restore_test create_share_test create_curdir_test create_namelen_test ^ 267 | getfileinfo_test delete_test delete_pending_test delete_mmap_test delete_standby_test ^ 268 | rename_* ^ 269 | getvolinfo_test ^ 270 | getsecurity_test ^ 271 | rdwr_* ^ 272 | flush_* ^ 273 | lock_* ^ 274 | querydir_* ^ 275 | dirnotify_test ^ 276 | exec_* 277 | if !ERRORLEVEL! neq 0 set TestExit=1 278 | popd 279 | start "" /b /d "%TMP%\%1\build\%Configuration%" wsl -- fusermount3 -u mnt 280 | exit /b !TestExit! 281 | 282 | :sample-fsx0-memfs-fuse3-wsl 283 | call :__run_sample_fsx_wsl_test memfs-fuse3 0 284 | if !ERRORLEVEL! neq 0 goto fail 285 | exit /b 0 286 | 287 | :sample-fsx1-memfs-fuse3-wsl 288 | call :__run_sample_fsx_wsl_test memfs-fuse3 1 289 | if !ERRORLEVEL! neq 0 goto fail 290 | exit /b 0 291 | 292 | :sample-fsxinf-memfs-fuse3-wsl 293 | call :__run_sample_fsx_wsl_test memfs-fuse3 -1 294 | if !ERRORLEVEL! neq 0 goto fail 295 | exit /b 0 296 | 297 | :__run_sample_fsx_wsl_test 298 | set TestExit=0 299 | start "" /b /d "%TMP%\%1\build\%Configuration%" wsl -- sudo sh "/mnt/c/Program Files/WinFuse/opt/wslfuse/install.sh"; mkdir -p mnt; ./%1.out -f -ocontext=FileInfoTimeout=%2,context=Volume=L: mnt 300 | waitfor 7BF47D72F6664550B03248ECFE77C7DD /t 10 2>nul 301 | pushd >nul 302 | cd L: >nul 2>nul || (echo Unable to find drive L: >&2 & goto fail) 303 | L: 304 | "%ProjRoot%\ext\winfsp\ext\test\fstools\src\fsx\fsx.exe" -N 5000 test xxxxxx 305 | if !ERRORLEVEL! neq 0 set TestExit=1 306 | popd 307 | start "" /b /d "%TMP%\%1\build\%Configuration%" wsl -- fusermount3 -u mnt 308 | exit /b !TestExit! 309 | 310 | :leak-test 311 | rem wait a bit to avoid reporting lingering allocations 312 | waitfor 7BF47D72F6664550B03248ECFE77C7DD /t 3 2>nul 313 | for /F "tokens=1,2 delims=:" %%i in ('verifier /query ^| findstr ^ 314 | /c:"Current Pool Allocations:" ^ 315 | /c:"CurrentPagedPoolAllocations:" ^ 316 | /c:"CurrentNonPagedPoolAllocations:"' 317 | ) do ( 318 | 319 | set FieldName=%%i 320 | set FieldName=!FieldName: =! 321 | 322 | set FieldValue=%%j 323 | set FieldValue=!FieldValue: =! 324 | set FieldValue=!FieldValue:^(=! 325 | set FieldValue=!FieldValue:^)=! 326 | 327 | if X!FieldName!==XCurrentPoolAllocations ( 328 | for /F "tokens=1,2 delims=/" %%k in ("!FieldValue!") do ( 329 | set NonPagedAlloc=%%k 330 | set PagedAlloc=%%l 331 | ) 332 | ) else if X!FieldName!==XCurrentPagedPoolAllocations ( 333 | set PagedAlloc=!FieldValue! 334 | ) else if X!FieldName!==XCurrentNonPagedPoolAllocations ( 335 | set NonPagedAlloc=!FieldValue! 336 | ) 337 | ) 338 | set /A TotalAlloc=PagedAlloc+NonPagedAlloc 339 | if !TotalAlloc! equ 0 ( 340 | echo === Leaks: None 341 | ) else ( 342 | echo === Leaks: !NonPagedAlloc! NP / !PagedAlloc! P 343 | goto fail 344 | ) 345 | exit /b 0 346 | -------------------------------------------------------------------------------- /src/shared/km/instance.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file shared/km/instance.c 3 | * 4 | * @copyright 2019-2020 Bill Zissimopoulos 5 | */ 6 | /* 7 | * This file is part of WinFuse. 8 | * 9 | * You can redistribute it and/or modify it under the terms of the GNU 10 | * Affero General Public License version 3 as published by the Free 11 | * Software Foundation. 12 | * 13 | * Licensees holding a valid commercial license may use this software 14 | * in accordance with the commercial license agreement provided in 15 | * conjunction with the software. The terms and conditions of any such 16 | * commercial license agreement shall govern, supersede, and render 17 | * ineffective any application of the AGPLv3 license to this software, 18 | * notwithstanding of any reference thereto in the software or 19 | * associated repository. 20 | */ 21 | 22 | #include 23 | 24 | NTSTATUS FuseInstanceInit(FUSE_INSTANCE *Instance, 25 | FSP_FSCTL_VOLUME_PARAMS *VolumeParams, 26 | FUSE_INSTANCE_TYPE InstanceType); 27 | VOID FuseInstanceFini(FUSE_INSTANCE *Instance); 28 | VOID FuseInstanceExpirationRoutine(FUSE_INSTANCE *Instance, UINT64 ExpirationTime); 29 | NTSTATUS FuseInstanceTransact(FUSE_INSTANCE *Instance, 30 | FUSE_PROTO_RSP *FuseResponse, ULONG InputBufferLength, 31 | FUSE_PROTO_REQ *FuseRequest, PULONG POutputBufferLength, 32 | PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, 33 | PIRP CancellableIrp); 34 | 35 | #ifdef ALLOC_PRAGMA 36 | #pragma alloc_text(PAGE, FuseInstanceInit) 37 | #pragma alloc_text(PAGE, FuseInstanceFini) 38 | #pragma alloc_text(PAGE, FuseInstanceExpirationRoutine) 39 | #pragma alloc_text(PAGE, FuseInstanceTransact) 40 | #endif 41 | 42 | NTSTATUS FuseInstanceInit(FUSE_INSTANCE *Instance, 43 | FSP_FSCTL_VOLUME_PARAMS *VolumeParams, 44 | FUSE_INSTANCE_TYPE InstanceType) 45 | { 46 | PAGED_CODE(); 47 | 48 | NTSTATUS Result; 49 | 50 | RtlZeroMemory(Instance, sizeof *Instance); 51 | Instance->VolumeParams = VolumeParams; 52 | Instance->InstanceType = InstanceType; 53 | 54 | FuseRwlockInitialize(&Instance->OpGuardLock); 55 | 56 | Result = FuseIoqCreate(&Instance->Ioq); 57 | if (!NT_SUCCESS(Result)) 58 | goto exit; 59 | 60 | Result = FuseCacheCreate(0, 0/*!VolumeParams->CaseSensitiveSearch*/, &Instance->Cache); 61 | if (!NT_SUCCESS(Result)) 62 | goto exit; 63 | 64 | FuseFileInstanceInit(Instance); 65 | 66 | KeInitializeEvent(&Instance->InitEvent, NotificationEvent, FALSE); 67 | Result = FuseProtoPostInit(Instance); 68 | if (!NT_SUCCESS(Result)) 69 | goto exit; 70 | 71 | /* ensure that VolumeParams can be used for FUSE operations */ 72 | VolumeParams->CaseSensitiveSearch = 1; /* revisit FuseCacheCreate above if this changes */ 73 | VolumeParams->CasePreservedNames = 1; 74 | VolumeParams->PersistentAcls = 1; 75 | VolumeParams->ReparsePoints = 1; 76 | VolumeParams->ReparsePointsAccessCheck = 0; 77 | VolumeParams->NamedStreams = 0; 78 | VolumeParams->ReadOnlyVolume = 0; 79 | VolumeParams->PostCleanupWhenModifiedOnly = 1; 80 | VolumeParams->PassQueryDirectoryFileName = 1; 81 | VolumeParams->DeviceControl = 1; 82 | VolumeParams->DirectoryMarkerAsNextOffset = 1; 83 | 84 | Result = STATUS_SUCCESS; 85 | 86 | exit: 87 | if (!NT_SUCCESS(Result)) 88 | { 89 | if (0 != Instance->Cache) 90 | FuseCacheDelete(Instance->Cache); 91 | 92 | if (0 != Instance->Ioq) 93 | FuseIoqDelete(Instance->Ioq); 94 | 95 | FuseRwlockFinalize(&Instance->OpGuardLock); 96 | 97 | RtlZeroMemory(Instance, sizeof *Instance); 98 | } 99 | 100 | return Result; 101 | } 102 | 103 | VOID FuseInstanceFini(FUSE_INSTANCE *Instance) 104 | { 105 | PAGED_CODE(); 106 | 107 | /* 108 | * The order of finalization is IMPORTANT: 109 | * 110 | * FuseIoqDelete must precede FuseFileInstanceFini, because the Ioq may contain Contexts 111 | * that hold File's. 112 | * 113 | * FuseIoqDelete must precede FuseCacheDelete, because the Ioq may contain Contexts 114 | * that hold CacheGen references. 115 | * 116 | * FuseFileInstanceFini must precede FuseCacheDelete, because some Files may hold 117 | * CacheItem references. 118 | */ 119 | 120 | FuseIoqDelete(Instance->Ioq); 121 | 122 | FuseFileInstanceFini(Instance); 123 | 124 | FuseCacheDelete(Instance->Cache); 125 | 126 | FuseRwlockFinalize(&Instance->OpGuardLock); 127 | } 128 | 129 | VOID FuseInstanceExpirationRoutine(FUSE_INSTANCE *Instance, UINT64 ExpirationTime) 130 | { 131 | PAGED_CODE(); 132 | 133 | FuseCacheExpirationRoutine(Instance->Cache, Instance, ExpirationTime); 134 | } 135 | 136 | NTSTATUS FuseInstanceTransact(FUSE_INSTANCE *Instance, 137 | FUSE_PROTO_RSP *FuseResponse, ULONG InputBufferLength, 138 | FUSE_PROTO_REQ *FuseRequest, PULONG POutputBufferLength, 139 | PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, 140 | PIRP CancellableIrp) 141 | { 142 | PAGED_CODE(); 143 | 144 | ULONG OutputBufferLength = *POutputBufferLength; 145 | FSP_FSCTL_TRANSACT_REQ *InternalRequest = 0; 146 | FSP_FSCTL_TRANSACT_RSP InternalResponse; 147 | FUSE_CONTEXT *Context; 148 | BOOLEAN Continue; 149 | NTSTATUS Result; 150 | 151 | *POutputBufferLength = 0; 152 | 153 | /* check parameters */ 154 | if (0 != FuseResponse) 155 | { 156 | if (FUSE_PROTO_RSP_HEADER_SIZE > InputBufferLength || 157 | FUSE_PROTO_RSP_HEADER_SIZE > FuseResponse->len || 158 | FuseResponse->len > InputBufferLength) 159 | return STATUS_INVALID_PARAMETER; 160 | } 161 | if (0 != FuseRequest) 162 | { 163 | if (FUSE_PROTO_REQ_SIZEMIN > OutputBufferLength) 164 | return STATUS_BUFFER_TOO_SMALL; 165 | } 166 | 167 | if (0 != FuseResponse) 168 | { 169 | Context = FuseIoqEndProcessing(Instance->Ioq, FuseResponse->unique); 170 | if (0 == Context) 171 | goto request; 172 | 173 | #if DBG 174 | if (fuse_debug & fuse_debug_dp) 175 | FuseDebugLogResponse(FuseResponse); 176 | #endif 177 | 178 | Continue = FuseContextProcess(Context, FuseResponse, 0, 0); 179 | 180 | if (Continue) 181 | FuseIoqPostPending(Instance->Ioq, Context); 182 | else if (0 == Context->InternalRequest) 183 | FuseContextDelete(Context); 184 | else 185 | { 186 | ASSERT(FspFsctlTransactReservedKind != Context->InternalResponse->Kind); 187 | 188 | Result = FspFsextProviderTransact( 189 | DeviceObject, FileObject, Context->InternalResponse, 0); 190 | FuseContextDelete(Context); 191 | if (!NT_SUCCESS(Result)) 192 | goto exit; 193 | } 194 | } 195 | 196 | request: 197 | if (0 != FuseRequest) 198 | { 199 | RtlZeroMemory(FuseRequest, sizeof(FUSE_PROTO_REQ)); 200 | 201 | Context = FuseIoqNextPending(Instance->Ioq); 202 | if (0 == Context) 203 | { 204 | UINT32 VersionMajor = Instance->VersionMajor; 205 | _ReadWriteBarrier(); 206 | /* 207 | * Compiler barrier only. 208 | * 209 | * A full memory barrier is not needed here, because: 210 | * 211 | * - WaitForSingleObject acts on a NotificationEvent that stays signaled. 212 | * - WaitForSingleObject is a memory barrier. 213 | */ 214 | if (0 == VersionMajor) 215 | { 216 | Result = FsRtlCancellableWaitForSingleObject(&Instance->InitEvent, 217 | 0, CancellableIrp); 218 | if (STATUS_TIMEOUT == Result || STATUS_THREAD_IS_TERMINATING == Result) 219 | Result = STATUS_CANCELLED; 220 | if (!NT_SUCCESS(Result)) 221 | goto exit; 222 | ASSERT(STATUS_SUCCESS == Result); 223 | 224 | VersionMajor = Instance->VersionMajor; 225 | } 226 | if ((UINT32)-1 == VersionMajor) 227 | { 228 | Result = STATUS_ACCESS_DENIED; 229 | goto exit; 230 | } 231 | 232 | Result = FspFsextProviderTransact( 233 | DeviceObject, FileObject, 0, &InternalRequest); 234 | if (!NT_SUCCESS(Result)) 235 | goto exit; 236 | if (0 == InternalRequest) 237 | { 238 | Result = STATUS_SUCCESS; 239 | goto exit; 240 | } 241 | 242 | ASSERT(FspFsctlTransactReservedKind != InternalRequest->Kind); 243 | 244 | FuseContextCreate(&Context, Instance, InternalRequest); 245 | ASSERT(0 != Context); 246 | 247 | Continue = FALSE; 248 | if (!FuseContextIsStatus(Context)) 249 | { 250 | InternalRequest = 0; 251 | Continue = FuseContextProcess(Context, 0, FuseRequest, OutputBufferLength); 252 | } 253 | } 254 | else 255 | { 256 | ASSERT(!FuseContextIsStatus(Context)); 257 | Continue = FuseContextProcess(Context, 0, FuseRequest, OutputBufferLength); 258 | } 259 | 260 | if (Continue) 261 | { 262 | ASSERT(!FuseContextIsStatus(Context)); 263 | FuseIoqStartProcessing(Instance->Ioq, Context); 264 | } 265 | else if (FuseContextIsStatus(Context)) 266 | { 267 | ASSERT(0 != InternalRequest); 268 | RtlZeroMemory(&InternalResponse, sizeof InternalResponse); 269 | InternalResponse.Size = sizeof InternalResponse; 270 | InternalResponse.Kind = InternalRequest->Kind; 271 | InternalResponse.Hint = InternalRequest->Hint; 272 | InternalResponse.IoStatus.Status = FuseContextToStatus(Context); 273 | Result = FspFsextProviderTransact( 274 | DeviceObject, FileObject, &InternalResponse, 0); 275 | if (!NT_SUCCESS(Result)) 276 | goto exit; 277 | } 278 | else if (0 == Context->InternalRequest) 279 | { 280 | switch (Context->InternalResponse->Hint) 281 | { 282 | case FUSE_PROTO_OPCODE_FORGET: 283 | case FUSE_PROTO_OPCODE_BATCH_FORGET: 284 | if (!IsListEmpty(&Context->Forget.ForgetList)) 285 | FuseIoqPostPending(Instance->Ioq, Context); 286 | else 287 | FuseContextDelete(Context); 288 | break; 289 | } 290 | } 291 | else 292 | { 293 | Result = FspFsextProviderTransact( 294 | DeviceObject, FileObject, Context->InternalResponse, 0); 295 | FuseContextDelete(Context); 296 | if (!NT_SUCCESS(Result)) 297 | goto exit; 298 | } 299 | 300 | *POutputBufferLength = FuseRequest->len; 301 | 302 | #if DBG 303 | if (fuse_debug & fuse_debug_dp) 304 | FuseDebugLogRequest(FuseRequest); 305 | #endif 306 | } 307 | 308 | Result = STATUS_SUCCESS; 309 | 310 | exit: 311 | if (0 != InternalRequest) 312 | FuseFreeExternal(InternalRequest); 313 | 314 | return Result; 315 | } 316 | -------------------------------------------------------------------------------- /tst/memfs-fuse3/memfs-fuse3.exe.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {CF538F42-C714-4653-B351-E72FD7B0B217} 23 | Win32Proj 24 | memfsfuse3 25 | $(LatestTargetPlatformVersion) 26 | 27 | 28 | 29 | Application 30 | true 31 | $(DefaultPlatformToolset) 32 | Unicode 33 | 34 | 35 | Application 36 | false 37 | $(DefaultPlatformToolset) 38 | true 39 | Unicode 40 | 41 | 42 | Application 43 | true 44 | $(DefaultPlatformToolset) 45 | Unicode 46 | 47 | 48 | Application 49 | false 50 | $(DefaultPlatformToolset) 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | $(SolutionDir)build\$(Configuration)\ 75 | $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ 76 | $(SolutionName)-$(PlatformTarget) 77 | 78 | 79 | true 80 | $(SolutionDir)build\$(Configuration)\ 81 | $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ 82 | $(SolutionName)-$(PlatformTarget) 83 | 84 | 85 | false 86 | $(SolutionDir)build\$(Configuration)\ 87 | $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ 88 | $(SolutionName)-$(PlatformTarget) 89 | 90 | 91 | false 92 | $(SolutionDir)build\$(Configuration)\ 93 | $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ 94 | $(SolutionName)-$(PlatformTarget) 95 | 96 | 97 | 98 | 99 | 100 | Level3 101 | Disabled 102 | FUSE_USE_VERSION=32;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 103 | true 104 | C:\Program Files\WinFuse\opt\libfuse\include\fuse3 105 | MultiThreadedDebug 106 | 4018 107 | 108 | 109 | Console 110 | true 111 | C:\Program Files\WinFuse\opt\libfuse\lib\fuse3-$(PlatformTarget).lib 112 | 113 | 114 | 115 | 116 | copy "C:\Program Files\WinFuse\opt\libfuse\bin\fuse3-$(PlatformTarget).dll" $(OutDir) 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | Level3 125 | Disabled 126 | FUSE_USE_VERSION=32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 127 | true 128 | C:\Program Files\WinFuse\opt\libfuse\include\fuse3 129 | MultiThreadedDebug 130 | 4018 131 | 132 | 133 | Console 134 | true 135 | C:\Program Files\WinFuse\opt\libfuse\lib\fuse3-$(PlatformTarget).lib 136 | 137 | 138 | 139 | 140 | copy "C:\Program Files\WinFuse\opt\libfuse\bin\fuse3-$(PlatformTarget).dll" $(OutDir) 141 | 142 | 143 | 144 | 145 | 146 | Level3 147 | 148 | 149 | MaxSpeed 150 | true 151 | true 152 | FUSE_USE_VERSION=32;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 153 | true 154 | C:\Program Files\WinFuse\opt\libfuse\include\fuse3 155 | MultiThreaded 156 | 4018 157 | 158 | 159 | Console 160 | true 161 | true 162 | true 163 | C:\Program Files\WinFuse\opt\libfuse\lib\fuse3-$(PlatformTarget).lib 164 | 165 | 166 | 167 | 168 | copy "C:\Program Files\WinFuse\opt\libfuse\bin\fuse3-$(PlatformTarget).dll" $(OutDir) 169 | 170 | 171 | 172 | 173 | 174 | Level3 175 | 176 | 177 | MaxSpeed 178 | true 179 | true 180 | FUSE_USE_VERSION=32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 181 | true 182 | C:\Program Files\WinFuse\opt\libfuse\include\fuse3 183 | MultiThreaded 184 | 4018 185 | 186 | 187 | Console 188 | true 189 | true 190 | true 191 | C:\Program Files\WinFuse\opt\libfuse\lib\fuse3-$(PlatformTarget).lib 192 | 193 | 194 | 195 | 196 | copy "C:\Program Files\WinFuse\opt\libfuse\bin\fuse3-$(PlatformTarget).dll" $(OutDir) 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | -------------------------------------------------------------------------------- /doc/component.svg: -------------------------------------------------------------------------------- 1 | NTOSLXCOREwinfuseFUSE_FSCTLTRANSACTwinfspFSP_FSCTLTRANSACTwslfuse/dev/fuselxldrWindowsWinFsp FSWindowsFUSE FSLinuxFUSE FSloadsloads --------------------------------------------------------------------------------