├── .github └── workflows │ ├── build.yml │ └── ntptfs.yml ├── .gitignore ├── .idea ├── .gitignore ├── modules.xml ├── vcs.xml └── winfsp-rs.iml ├── Cargo.lock ├── Cargo.toml ├── LICENSE.md ├── README.md ├── deprecated ├── README.md └── ptfs-winfsp-rs │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── build.rs │ └── src │ ├── fs │ ├── mod.rs │ └── passthrough.rs │ ├── main.rs │ └── service.rs ├── filesystems └── ntptfs-winfsp-rs │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── build.rs │ └── src │ ├── fs │ ├── context.rs │ ├── file.rs │ ├── mod.rs │ └── ntptfs.rs │ ├── lib.rs │ ├── main.rs │ ├── native │ ├── lfs │ │ ├── async_io.rs │ │ └── mod.rs │ ├── mod.rs │ └── volume.rs │ └── service.rs ├── test └── winfsp-test │ ├── winfsp-tests-x64.exe │ └── winfsp-x64.dll ├── winfsp-sys ├── .gitattributes ├── Cargo.toml ├── README.md ├── build.rs ├── src │ ├── bindings.rs │ └── lib.rs └── winfsp │ ├── .gitrev │ ├── bin │ ├── winfsp-a64.dll │ ├── winfsp-msil.dll │ ├── winfsp-x64.dll │ └── winfsp-x86.dll │ ├── inc │ ├── fuse │ │ ├── fuse.h │ │ ├── fuse_common.h │ │ ├── fuse_opt.h │ │ └── winfsp_fuse.h │ ├── fuse3 │ │ ├── fuse.h │ │ ├── fuse_common.h │ │ ├── fuse_opt.h │ │ └── winfsp_fuse.h │ └── winfsp │ │ ├── fsctl.h │ │ ├── launch.h │ │ └── winfsp.h │ └── lib │ ├── fuse.pc │ ├── fuse3.pc │ ├── winfsp-a64.lib │ ├── winfsp-x64.lib │ └── winfsp-x86.lib └── winfsp ├── Cargo.toml └── src ├── constants.rs ├── error.rs ├── filesystem ├── context.rs ├── directory.rs ├── internals │ ├── fileinfo.rs │ ├── mod.rs │ ├── volumeinfo.rs │ └── widenameinfo.rs ├── mod.rs └── stream.rs ├── host ├── debug.rs ├── fshost.rs ├── interface.rs ├── mod.rs └── volumeparams.rs ├── init.rs ├── lib.rs ├── notify ├── context.rs ├── mod.rs ├── notifier.rs ├── notifyinfo.rs └── timer.rs ├── service.rs ├── util ├── handle.rs └── mod.rs └── vsb.rs /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build winfsp-rs 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | schedule: 9 | - cron: "0 0 * * 6" 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | build: 15 | strategy: 16 | matrix: 17 | profile: ['dev', 'release'] 18 | runs-on: windows-latest 19 | steps: 20 | - uses: actions/checkout@v4 21 | - name: Install nightly Rust 22 | uses: dtolnay/rust-toolchain@nightly 23 | - name: Install WinFSP 24 | run: choco install winfsp 25 | - name: Build winfsp-rs 26 | run: cargo build --profile ${{ matrix.profile }} -p winfsp --verbose --features=full 27 | -------------------------------------------------------------------------------- /.github/workflows/ntptfs.yml: -------------------------------------------------------------------------------- 1 | name: Test ntptfs-rs 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | schedule: 9 | - cron: "0 0 * * 6" 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | test: 15 | strategy: 16 | matrix: 17 | profile: [ 'dev', 'release' ] 18 | runs-on: windows-latest 19 | steps: 20 | - uses: actions/checkout@v4 21 | - name: Install nightly Rust 22 | uses: dtolnay/rust-toolchain@nightly 23 | - name: Install WinFSP 24 | run: choco install winfsp 25 | - name: Build ntptfs 26 | run: cargo build --profile ${{ matrix.profile }} -p ntptfs-winfsp-rs --verbose 27 | - name: Test ntptfs 28 | shell: cmd 29 | run: | 30 | mkdir D:\a\test 31 | start /b "" cargo run --profile ${{ matrix.profile }} --bin ntptfs-winfsp-rs -- -p D:\a\test -m R: 32 | echo "Waiting 45 seconds for ntptfs to start" 33 | waitfor /T 45 pause 2>NUL 34 | R: 35 | ${{ github.workspace }}\test\winfsp-test\winfsp-tests-x64 --external --resilient +* --case-insensitive-cmp -delete_access_test -getfileattr_test -exec_rename_dir_test -rename_flipflop_test -stream_rename_flipflop_test -stream_getstreaminfo_test -create_backup_test -create_restore_test -reparse* -ea* 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/winfsp-rs.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "filesystems/ntptfs-winfsp-rs", 5 | "winfsp", 6 | "winfsp-sys" 7 | ] 8 | 9 | resolver = "2" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # winfsp-rs 2 | 3 | [![Latest Version](https://img.shields.io/crates/v/winfsp.svg)](https://crates.io/crates/winfsp) [![Docs](https://docs.rs/winfsp/badge.svg)](https://docs.rs/winfsp) ![License](https://img.shields.io/crates/l/winfsp) 4 | 5 | Safe Rust bindings to [WinFSP](https://github.com/winfsp/winfsp) with examples. 6 | 7 | > **Warning** 8 | > 9 | > A best effort has been made to keep Rust's aliasing rules in mind, and provide a safe and sound wrapper over 10 | > WinFSP. However, FFI with WinFSP involves a lot of pointers that end up as references and the nature of FFI makes 11 | > it difficult to test with miri. While ntptfs is used to test the correctness of the bindings, 12 | > there is still a chance these bindings are unsound. 13 | > 14 | > Please file a bug report if you encounter unsoundness when using the safe APIs of these bindings. 15 | 16 | ## Usage 17 | By default, winfsp-rs builds against an included import library. To build against the installed WinFSP libraries, enable the `system` 18 | feature. The path will automatically be determined via the Registry. 19 | 20 | ```toml 21 | [dependencies.winfsp] 22 | version = "0.11" 23 | features = ["system"] 24 | ``` 25 | ### Delay-loading 26 | To enable delay-loading of WinFSP, add `winfsp` to `build-dependencies` and call `winfsp::build::winfsp_link_delayload()` in 27 | the build script. This is required for winfsp-rs. 28 | 29 | #### Cargo.toml 30 | ```toml 31 | [build-dependencies] 32 | winfsp = "0.11" 33 | ``` 34 | 35 | #### build.rs 36 | ```rust 37 | fn main() { 38 | winfsp::build::winfsp_link_delayload(); 39 | } 40 | ``` 41 | 42 | ### Debugging 43 | Debug output can be enabled with the `debug` feature. Debug output will be written to standard output, 44 | and redirection of output is not configurable at this time. 45 | ```toml 46 | [dependencies.winfsp] 47 | features = ["debug"] 48 | ``` 49 | ### Building on Stable Rust 50 | It is recommended you build winfsp-rs on nightly Rust as it relies on [`io_error_more`](https://github.com/rust-lang/rust/issues/86442) 51 | and [`strict_provenance`](https://github.com/rust-lang/rust/issues/95228). However, it is possible to build winfsp-rs 52 | on stable Rust without support for these features. 53 | 54 | ```toml 55 | [dependencies.winfsp] 56 | default-features = false 57 | features = ["stable"] 58 | ``` 59 | 60 | ### Using with `windows-sys` 61 | With default features, the `winfsp` crate includes helper utilities and `From` implementations for error types in the 62 | version of the `windows` crate at the time of publishing. If you wish to use `winfsp` without `windows` types, 63 | disable the `windows-rs` crate feature. 64 | 65 | ```toml 66 | [dependencies.winfsp] 67 | default-features = false 68 | features = ["stable", "nightly"] 69 | ``` 70 | 71 | ## Testing 72 | `ntptfs-winfsp-rs`, a port of `ntptfs` to `winfsp-rs` is used to test the bindings. It passes all tests that `ntptfs` 73 | passes at the same elevation level. 74 | 75 |
76 | Test results 77 | 78 | ``` 79 | ❯ F:\winfsp-tests-x64 --external --resilient +* --case-insensitive-cmp -delete_access_test -getfileattr_test -exec_rename_dir_test -rename_flipflop_test -stream_rename_flipflop_test -stream_getstreaminfo_test -ea* 80 | create_test............................ OK 0.02s 81 | create_fileattr_test................... OK 0.01s 82 | create_readonlydir_test................ OK 0.01s 83 | create_related_test.................... OK 0.00s 84 | create_allocation_test................. OK 0.01s 85 | create_sd_test......................... OK 0.01s 86 | create_notraverse_test................. OK 0.00s 87 | create_backup_test..................... OK 0.00s 88 | create_restore_test.................... OK 0.00s 89 | create_share_test...................... OK 0.01s 90 | create_curdir_test..................... OK 0.00s 91 | create_namelen_test.................... OK 0.01s 92 | getfileinfo_test....................... OK 0.00s 93 | getfileinfo_name_test.................. OK 0.00s 94 | setfileinfo_test....................... OK 0.00s 95 | delete_test............................ OK 0.00s 96 | delete_pending_test.................... OK 0.00s 97 | delete_mmap_test....................... OK 0.00s 98 | delete_standby_test.................... OK 0.07s 99 | delete_ex_test......................... OK 0.01s 100 | rename_test............................ OK 0.02s 101 | rename_backslash_test.................. OK 0.01s 102 | rename_open_test....................... OK 0.00s 103 | rename_caseins_test.................... OK 0.01s 104 | rename_mmap_test....................... OK 0.01s 105 | rename_standby_test.................... OK 0.15s 106 | rename_ex_test......................... OK 0.01s 107 | getvolinfo_test........................ OK 0.00s 108 | setvolinfo_test........................ OK 0.00s 109 | getsecurity_test....................... OK 0.00s 110 | setsecurity_test....................... OK 0.00s 111 | security_stress_meta_test.............. OK 0.24s 112 | rdwr_noncached_test.................... OK 0.02s 113 | rdwr_noncached_overlapped_test......... OK 0.02s 114 | rdwr_cached_test....................... OK 0.02s 115 | rdwr_cached_append_test................ OK 0.01s 116 | rdwr_cached_overlapped_test............ OK 0.02s 117 | rdwr_writethru_test.................... OK 0.01s 118 | rdwr_writethru_append_test............. OK 0.01s 119 | rdwr_writethru_overlapped_test......... OK 0.01s 120 | rdwr_mmap_test......................... OK 0.15s 121 | rdwr_mixed_test........................ OK 0.01s 122 | flush_test............................. OK 0.05s 123 | flush_volume_test...................... OK 0.00s 124 | lock_noncached_test.................... OK 0.02s 125 | lock_noncached_overlapped_test......... OK 0.01s 126 | lock_cached_test....................... OK 0.01s 127 | lock_cached_overlapped_test............ OK 0.01s 128 | querydir_test.......................... OK 0.64s 129 | querydir_nodup_test.................... OK 4.43s 130 | querydir_single_test................... OK 1.78s 131 | querydir_expire_cache_test............. OK 0.00s 132 | querydir_buffer_overflow_test.......... OK 0.00s 133 | querydir_namelen_test.................. OK 0.01s 134 | dirnotify_test......................... OK 1.01s 135 | exec_test.............................. OK 0.02s 136 | exec_delete_test....................... OK 1.03s 137 | exec_rename_test....................... OK 1.03s 138 | reparse_guid_test...................... OK 4.83s 139 | reparse_nfs_test....................... OK 0.00s 140 | reparse_symlink_test................... OK 0.01s 141 | reparse_symlink_relative_test.......... OK 0.04s 142 | stream_create_test..................... OK 0.02s 143 | stream_create_overwrite_test........... OK 0.01s 144 | stream_create_related_test............. OK 0.00s 145 | stream_create_sd_test.................. OK 0.00s 146 | stream_create_share_test............... OK 0.03s 147 | stream_getfileinfo_test................ OK 0.00s 148 | stream_setfileinfo_test................ OK 0.01s 149 | stream_delete_test..................... OK 0.01s 150 | stream_delete_pending_test............. OK 0.01s 151 | stream_getsecurity_test................ OK 0.00s 152 | stream_setsecurity_test................ OK 0.00s 153 | stream_getstreaminfo_expire_cache_test. OK 0.00s 154 | stream_dirnotify_test.................. OK 1.01s 155 | oplock_level1_test..................... OK 1.31s 156 | oplock_level2_test..................... OK 2.48s 157 | oplock_batch_test...................... OK 1.25s 158 | oplock_filter_test..................... OK 1.24s 159 | oplock_rwh_test........................ OK 1.24s 160 | oplock_rw_test......................... OK 1.24s 161 | oplock_rh_test......................... OK 2.48s 162 | oplock_r_test.......................... OK 2.48s 163 | oplock_not_granted_test................ OK 0.00s 164 | wsl_stat_test.......................... OK 0.00s 165 | --- COMPLETE --- 166 | ``` 167 | 168 |
169 | 170 | ## Legal 171 | winfsp-rs is licensed under the terms of the GNU General Public License version 3 as published by the 172 | Free Software Foundation. 173 | 174 | ### Attribution 175 | 176 | > WinFsp - Windows File System Proxy, 177 | > 178 | > Copyright (C) Bill Zissimopoulos \ 179 | > https://github.com/winfsp/winfsp 180 | -------------------------------------------------------------------------------- /deprecated/README.md: -------------------------------------------------------------------------------- 1 | # ptfs 2 | 3 | This was originally a port of [passthrough.c](https://github.com/winfsp/winfsp/tree/master/tst/passthrough) in WinFSP. 4 | 5 | Using the Win32 API isn't a very compatible approach to a usable passthrough file system, and working around the deficiencies 6 | in Win32 with Rust makes this more complicated than ntptfs as an example. 7 | 8 | It is highly advised you don't use this. -------------------------------------------------------------------------------- /deprecated/ptfs-winfsp-rs/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /deprecated/ptfs-winfsp-rs/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "snowflake-fsp" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "windows", 10 | ] 11 | 12 | [[package]] 13 | name = "windows" 14 | version = "0.39.0" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" 17 | dependencies = [ 18 | "windows_aarch64_msvc", 19 | "windows_i686_gnu", 20 | "windows_i686_msvc", 21 | "windows_x86_64_gnu", 22 | "windows_x86_64_msvc", 23 | ] 24 | 25 | [[package]] 26 | name = "windows_aarch64_msvc" 27 | version = "0.39.0" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" 30 | 31 | [[package]] 32 | name = "windows_i686_gnu" 33 | version = "0.39.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" 36 | 37 | [[package]] 38 | name = "windows_i686_msvc" 39 | version = "0.39.0" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" 42 | 43 | [[package]] 44 | name = "windows_x86_64_gnu" 45 | version = "0.39.0" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" 48 | 49 | [[package]] 50 | name = "windows_x86_64_msvc" 51 | version = "0.39.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" 54 | -------------------------------------------------------------------------------- /deprecated/ptfs-winfsp-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ptfs-winfsp-rs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | clap = { version = "3.2", features = ["derive"] } 10 | windows = { version = "0.51.0", features = ["Win32_Foundation", "Win32_System_LibraryLoader", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_WindowsProgramming", "Win32_System_Console", "Win32_System_IO"] } 11 | windows-sys = { version = "0.48.0", features = ["Win32_Storage_FileSystem"] } 12 | 13 | widestring = "1" 14 | winfsp = { path = "../winfsp", features = ["system"] } 15 | anyhow = "1" 16 | 17 | [build-dependencies] 18 | winfsp = { path = "../winfsp", features = ["delayload"] } 19 | 20 | -------------------------------------------------------------------------------- /deprecated/ptfs-winfsp-rs/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | winfsp::build::winfsp_link_delayload(); 3 | } 4 | -------------------------------------------------------------------------------- /deprecated/ptfs-winfsp-rs/src/fs/mod.rs: -------------------------------------------------------------------------------- 1 | mod passthrough; 2 | 3 | pub use passthrough::Ptfs; 4 | -------------------------------------------------------------------------------- /deprecated/ptfs-winfsp-rs/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(io_error_more)] 2 | #![feature(let_chains)] 3 | #![deny(unsafe_op_in_unsafe_fn)] 4 | 5 | mod fs; 6 | mod service; 7 | 8 | use clap::Parser; 9 | use std::path::PathBuf; 10 | use std::time::Duration; 11 | use windows::Win32::Foundation::STATUS_NONCONTINUABLE_EXCEPTION; 12 | use winfsp::service::FileSystemServiceBuilder; 13 | use winfsp::winfsp_init_or_die; 14 | 15 | /// MainArgs 16 | #[derive(Parser, Debug)] 17 | #[clap(author, version, about, long_about = None,)] 18 | pub struct Args { 19 | /// -1: enable all debug logs 20 | #[clap(short = 'd', default_value = "0")] 21 | flags: u32, 22 | 23 | /// file path 24 | #[clap(short = 'D', long)] 25 | logfile: Option, 26 | 27 | #[clap(short = 'u', long)] 28 | volume_prefix: Option, 29 | 30 | #[clap(short = 'p', long)] 31 | directory: PathBuf, 32 | 33 | #[clap(short = 'm', long)] 34 | mountpoint: PathBuf, 35 | } 36 | 37 | fn main() { 38 | let init = winfsp_init_or_die(); 39 | let fsp = FileSystemServiceBuilder::new() 40 | .with_start(|| { 41 | let args = Args::parse(); 42 | service::svc_start(args).map_err(|_e| STATUS_NONCONTINUABLE_EXCEPTION) 43 | }) 44 | .with_stop(|f| { 45 | service::svc_stop(f); 46 | Ok(()) 47 | }) 48 | .build("ptfs-winfsp-rs", init) 49 | .expect("failed to build fsp"); 50 | 51 | fsp.start(); 52 | std::thread::sleep(Duration::MAX); 53 | } 54 | -------------------------------------------------------------------------------- /deprecated/ptfs-winfsp-rs/src/service.rs: -------------------------------------------------------------------------------- 1 | use crate::fs::Ptfs; 2 | use crate::Args; 3 | 4 | #[inline] 5 | pub fn svc_start(args: Args) -> anyhow::Result { 6 | let mut ptfs = Ptfs::create( 7 | &args.directory, 8 | &args.volume_prefix.unwrap_or_else(|| String::from("")), 9 | )?; 10 | 11 | ptfs.fs.mount(args.mountpoint.as_os_str())?; 12 | ptfs.fs.start()?; 13 | Ok(ptfs) 14 | } 15 | 16 | #[inline] 17 | pub fn svc_stop(fs: Option<&mut Ptfs>) { 18 | if let Some(f) = fs { 19 | f.fs.stop(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "snowflake-fsp" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "windows", 10 | ] 11 | 12 | [[package]] 13 | name = "windows" 14 | version = "0.39.0" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" 17 | dependencies = [ 18 | "windows_aarch64_msvc", 19 | "windows_i686_gnu", 20 | "windows_i686_msvc", 21 | "windows_x86_64_gnu", 22 | "windows_x86_64_msvc", 23 | ] 24 | 25 | [[package]] 26 | name = "windows_aarch64_msvc" 27 | version = "0.39.0" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" 30 | 31 | [[package]] 32 | name = "windows_i686_gnu" 33 | version = "0.39.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" 36 | 37 | [[package]] 38 | name = "windows_i686_msvc" 39 | version = "0.39.0" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" 42 | 43 | [[package]] 44 | name = "windows_x86_64_gnu" 45 | version = "0.39.0" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" 48 | 49 | [[package]] 50 | name = "windows_x86_64_msvc" 51 | version = "0.39.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" 54 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ntptfs-winfsp-rs" 3 | version = "0.1.0-alpha" 4 | edition = "2021" 5 | license = "GPL-3.0" 6 | description = "NTFS Passthrough File System via winfsp-rs" 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | clap = { version = "4.5.4", features = ["derive"] } 11 | windows = { version = "0.56.0", features = ["Win32_Foundation", "Win32_System_LibraryLoader", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_WindowsProgramming", "Win32_System_Console", "Win32_System_IO", 12 | "Wdk_Foundation", "Wdk_Storage_FileSystem", "Wdk_System_IO", "Wdk_Storage_FileSystem_Minifilters", "Wdk_System_SystemServices", "Win32_System_Ioctl", "Win32_System_SystemServices"] } 13 | 14 | bytemuck = "1.12.1" 15 | widestring = "1" 16 | winfsp = { path = "../../winfsp", features = ["debug", "system", "full"] } 17 | anyhow = "1" 18 | tokio = { version = "1.32.0", features = ["rt", "rt-multi-thread"] } 19 | 20 | [build-dependencies] 21 | winfsp = { path = "../../winfsp", features = ["delayload"] } 22 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | winfsp::build::winfsp_link_delayload(); 3 | // println!("cargo:rustc-link-lib=ntdll"); 4 | } 5 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/src/fs/file.rs: -------------------------------------------------------------------------------- 1 | use windows::Win32::Foundation::HANDLE; 2 | use winfsp::filesystem::DirBuffer; 3 | use winfsp::util::{AtomicHandle, NtHandleDrop, NtSafeHandle}; 4 | 5 | /// A file context in the passthrough file system. 6 | #[derive(Debug)] 7 | pub struct NtPassthroughFile { 8 | handle: AtomicHandle, 9 | is_directory: bool, 10 | dir_buffer: DirBuffer, 11 | file_size_hint: u64, 12 | } 13 | 14 | impl NtPassthroughFile { 15 | /// Create a new entry from an NT handle. 16 | pub fn new(handle: NtSafeHandle, file_size_hint: u64, is_directory: bool) -> Self { 17 | Self { 18 | handle: handle.into(), 19 | file_size_hint, 20 | is_directory, 21 | dir_buffer: DirBuffer::new(), 22 | } 23 | } 24 | 25 | /// Get a HANDLE to this file entry. 26 | pub fn handle(&self) -> HANDLE { 27 | self.handle.handle() 28 | } 29 | 30 | /// Invalidate the underlying handle for this file entry. 31 | pub fn invalidate(&self) { 32 | self.handle.invalidate() 33 | } 34 | 35 | /// Whether or not this entry is a directory. 36 | pub fn is_directory(&self) -> bool { 37 | self.is_directory 38 | } 39 | 40 | /// The size of the file in bytes. 41 | pub fn size(&self) -> u32 { 42 | self.file_size_hint as u32 43 | } 44 | 45 | /// Get a reference to the directory buffer for this entry. 46 | pub fn dir_buffer(&self) -> &DirBuffer { 47 | &self.dir_buffer 48 | } 49 | 50 | /// Explicitly invalidate the handle before drop. 51 | pub fn close(self) { 52 | self.invalidate(); 53 | drop(self) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/src/fs/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod context; 2 | pub mod file; 3 | pub mod ntptfs; 4 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/src/fs/ntptfs.rs: -------------------------------------------------------------------------------- 1 | use crate::fs::context::NtPassthroughContext; 2 | use std::io::ErrorKind; 3 | 4 | use std::path::Path; 5 | 6 | use winfsp::host::{DebugMode, FileSystemHost, FileSystemParams, VolumeParams}; 7 | 8 | /// An passthrough filesystem using the NT API. 9 | pub struct NtPassthroughFilesystem { 10 | /// The host for this filesystem. 11 | pub fs: FileSystemHost<'static>, 12 | } 13 | 14 | impl NtPassthroughFilesystem { 15 | pub fn create>(path: P, volume_prefix: &str) -> anyhow::Result { 16 | let metadata = std::fs::metadata(&path)?; 17 | if !metadata.is_dir() { 18 | return Err(std::io::Error::new(ErrorKind::NotADirectory, "not a directory").into()); 19 | } 20 | let canonical_path = std::fs::canonicalize(&path)?; 21 | 22 | let mut volume_params = VolumeParams::new(); 23 | volume_params 24 | .prefix(volume_prefix) 25 | .filesystem_name("ntptfs"); 26 | 27 | let context = 28 | NtPassthroughContext::new_with_volume_params(canonical_path, &mut volume_params)?; 29 | 30 | volume_params.file_info_timeout(1000); 31 | Ok(NtPassthroughFilesystem { 32 | fs: FileSystemHost::new_with_options_async( 33 | FileSystemParams::default_params_debug(volume_params, DebugMode::all()), 34 | context, 35 | )?, 36 | }) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(strict_provenance)] 2 | #![feature(io_error_more)] 3 | #![feature(let_chains)] 4 | #![feature(offset_of_nested)] 5 | #![deny(unsafe_op_in_unsafe_fn)] 6 | mod fs; 7 | mod native; 8 | 9 | pub use fs::context::NtPassthroughContext; 10 | pub use fs::file::NtPassthroughFile; 11 | pub use fs::ntptfs::NtPassthroughFilesystem; 12 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(io_error_more)] 2 | #![feature(let_chains)] 3 | #![feature(strict_provenance)] 4 | #![feature(offset_of_nested)] 5 | #![deny(unsafe_op_in_unsafe_fn)] 6 | 7 | pub mod fs; 8 | mod native; 9 | mod service; 10 | 11 | use clap::Parser; 12 | use std::path::PathBuf; 13 | use std::time::Duration; 14 | use windows::Win32::Foundation::STATUS_NONCONTINUABLE_EXCEPTION; 15 | use winfsp::service::FileSystemServiceBuilder; 16 | use winfsp::winfsp_init_or_die; 17 | 18 | /// MainArgs 19 | #[derive(Parser, Debug)] 20 | #[clap(author, version, about, long_about = None,)] 21 | pub struct Args { 22 | /// -1: enable all debug logs 23 | #[clap(short = 'd', default_value = "0")] 24 | flags: i32, 25 | 26 | /// file path 27 | #[clap(short = 'D', long)] 28 | logfile: Option, 29 | 30 | #[clap(short = 'u', long)] 31 | volume_prefix: Option, 32 | 33 | #[clap(short = 'p', long)] 34 | directory: PathBuf, 35 | 36 | #[clap(short = 'm', long)] 37 | mountpoint: PathBuf, 38 | } 39 | 40 | fn main() { 41 | let init = winfsp_init_or_die(); 42 | let fsp = FileSystemServiceBuilder::new() 43 | .with_start(|| { 44 | let args = Args::parse(); 45 | service::svc_start(args).map_err(|_e| STATUS_NONCONTINUABLE_EXCEPTION) 46 | }) 47 | .with_stop(|f| { 48 | service::svc_stop(f); 49 | Ok(()) 50 | }) 51 | .build("ntptfs-winfsp-rs", init) 52 | .expect("failed to build fsp"); 53 | 54 | fsp.start(); 55 | std::thread::sleep(Duration::MAX); 56 | } 57 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/src/native/lfs/async_io.rs: -------------------------------------------------------------------------------- 1 | use crate::native::lfs; 2 | use crate::native::lfs::LFS_EVENT; 3 | use std::cell::UnsafeCell; 4 | use std::future::Future; 5 | use std::mem::MaybeUninit; 6 | use std::pin::Pin; 7 | use std::ptr::addr_of; 8 | use std::task::{Context, Poll}; 9 | use widestring::U16CStr; 10 | use windows::core::imp::CloseHandle; 11 | use windows::core::PCWSTR; 12 | use windows::Wdk::Storage::FileSystem::{ 13 | NtQueryDirectoryFile, NtReadFile, NtWriteFile, FILE_INFORMATION_CLASS, 14 | }; 15 | use windows::Win32::Foundation::{ 16 | BOOLEAN, HANDLE, NTSTATUS, STATUS_ABANDONED, STATUS_PENDING, STATUS_SUCCESS, UNICODE_STRING, 17 | WAIT_ABANDONED, WAIT_ABANDONED_0, WAIT_FAILED, WAIT_OBJECT_0, 18 | }; 19 | use windows::Win32::System::Threading::WaitForSingleObject; 20 | use windows::Win32::System::WindowsProgramming::RtlInitUnicodeString; 21 | use windows::Win32::System::IO::IO_STATUS_BLOCK; 22 | use winfsp::FspError; 23 | 24 | struct LfsReadFuture<'a> { 25 | event: HANDLE, 26 | file: HANDLE, 27 | iosb: UnsafeCell>, 28 | result: Option, 29 | buffer: &'a mut [u8], 30 | offset: i64, 31 | } 32 | 33 | #[derive(Debug)] 34 | #[repr(transparent)] 35 | pub(crate) struct AssertThreadSafe(pub T); 36 | 37 | unsafe impl Send for AssertThreadSafe {} 38 | 39 | impl<'a> LfsReadFuture<'a> { 40 | fn new(file: HANDLE, buffer: &'a mut [u8], offset: i64) -> winfsp::Result { 41 | let event = lfs::new_event()?; 42 | Ok(Self { 43 | event, 44 | file, 45 | iosb: UnsafeCell::new(AssertThreadSafe(IO_STATUS_BLOCK::default())), 46 | result: None, 47 | buffer, 48 | offset, 49 | }) 50 | } 51 | } 52 | 53 | impl<'a> Drop for LfsReadFuture<'a> { 54 | fn drop(&mut self) { 55 | unsafe { 56 | CloseHandle(self.event.0); 57 | } 58 | } 59 | } 60 | 61 | impl<'a> Future for LfsReadFuture<'a> { 62 | type Output = Result; 63 | 64 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 65 | let Some(result) = self.result else { 66 | let initial_result = unsafe { 67 | NtReadFile( 68 | self.file, 69 | self.event, 70 | None, 71 | None, 72 | self.iosb.get() as *mut _, 73 | self.buffer.as_mut_ptr() as *mut _, 74 | self.buffer.len() as u32, 75 | Some(&self.offset), 76 | None, 77 | ) 78 | }; 79 | self.result = Some(initial_result); 80 | cx.waker().wake_by_ref(); 81 | return Poll::Pending; 82 | }; 83 | 84 | if result != STATUS_PENDING { 85 | return if result != STATUS_SUCCESS { 86 | Poll::Ready(Err(FspError::from(result))) 87 | } else { 88 | Poll::Ready(Ok(unsafe { self.iosb.get().read().0 })) 89 | }; 90 | } 91 | 92 | let wait_result = unsafe { WaitForSingleObject(self.event, 0) }; 93 | 94 | if wait_result == WAIT_OBJECT_0 { 95 | let code = unsafe { addr_of!((*self.iosb.get()).0.Anonymous.Status).read() }; 96 | self.result = Some(code); 97 | } else if wait_result == WAIT_FAILED 98 | || wait_result == WAIT_ABANDONED 99 | || wait_result == WAIT_ABANDONED_0 100 | { 101 | self.result = Some(STATUS_ABANDONED); 102 | } 103 | 104 | // if timed out, io isn't ready 105 | cx.waker().wake_by_ref(); 106 | Poll::Pending 107 | } 108 | } 109 | 110 | pub async fn lfs_read_file_async( 111 | handle: HANDLE, 112 | buffer: &mut [u8], 113 | offset: u64, 114 | bytes_transferred: &mut u32, 115 | ) -> winfsp::Result<()> { 116 | let lfs = LfsReadFuture::new(handle, buffer, offset as i64)?; 117 | 118 | let iosb = lfs.await?; 119 | *bytes_transferred = iosb.Information as u32; 120 | 121 | Ok(()) 122 | } 123 | 124 | struct LfsWriteFuture<'a> { 125 | event: HANDLE, 126 | file: HANDLE, 127 | iosb: UnsafeCell>, 128 | result: Option, 129 | buffer: &'a [u8], 130 | offset: i64, 131 | } 132 | 133 | impl<'a> LfsWriteFuture<'a> { 134 | fn new(file: HANDLE, buffer: &'a [u8], offset: i64) -> winfsp::Result { 135 | let event = lfs::new_event()?; 136 | 137 | Ok(Self { 138 | event, 139 | file, 140 | iosb: UnsafeCell::new(AssertThreadSafe(IO_STATUS_BLOCK::default())), 141 | result: None, 142 | buffer, 143 | offset, 144 | }) 145 | } 146 | } 147 | 148 | impl<'a> Drop for LfsWriteFuture<'a> { 149 | fn drop(&mut self) { 150 | unsafe { 151 | CloseHandle(self.event.0); 152 | } 153 | } 154 | } 155 | 156 | impl<'a> Future for LfsWriteFuture<'a> { 157 | type Output = Result; 158 | 159 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 160 | let Some(result) = self.result else { 161 | let initial_result = unsafe { 162 | NtWriteFile( 163 | self.file, 164 | self.event, 165 | None, 166 | None, 167 | self.iosb.get() as *mut _, 168 | self.buffer.as_ptr() as *const _, 169 | self.buffer.len() as u32, 170 | Some(&self.offset), 171 | None, 172 | ) 173 | }; 174 | self.result = Some(initial_result); 175 | cx.waker().wake_by_ref(); 176 | return Poll::Pending; 177 | }; 178 | 179 | if result != STATUS_PENDING { 180 | return if result != STATUS_SUCCESS { 181 | Poll::Ready(Err(FspError::from(result))) 182 | } else { 183 | Poll::Ready(Ok(unsafe { self.iosb.get().read().0 })) 184 | }; 185 | } 186 | 187 | let wait_result = unsafe { WaitForSingleObject(self.event, 0) }; 188 | 189 | if wait_result == WAIT_OBJECT_0 { 190 | let code = unsafe { addr_of!((*self.iosb.get()).0.Anonymous.Status).read() }; 191 | self.result = Some(code); 192 | } else if wait_result == WAIT_FAILED 193 | || wait_result == WAIT_ABANDONED 194 | || wait_result == WAIT_ABANDONED_0 195 | { 196 | self.result = Some(STATUS_ABANDONED); 197 | } 198 | 199 | // if timed out, io isn't ready 200 | cx.waker().wake_by_ref(); 201 | Poll::Pending 202 | } 203 | } 204 | 205 | pub async fn lfs_write_file_async( 206 | handle: HANDLE, 207 | buffer: &[u8], 208 | offset: u64, 209 | bytes_transferred: &mut u32, 210 | ) -> winfsp::Result<()> { 211 | let lfs = LfsWriteFuture::new(handle, buffer, offset as i64)?; 212 | 213 | let iosb = lfs.await?; 214 | *bytes_transferred = iosb.Information as u32; 215 | 216 | Ok(()) 217 | } 218 | 219 | struct LfsQueryDirectoryFileFuture<'a> { 220 | file: HANDLE, 221 | event: HANDLE, 222 | file_name: Option<&'a U16CStr>, 223 | iosb: UnsafeCell>, 224 | result: Option, 225 | buffer: &'a mut [u8], 226 | return_single_entry: bool, 227 | restart_scan: bool, 228 | class: FILE_INFORMATION_CLASS, 229 | } 230 | 231 | impl<'a> LfsQueryDirectoryFileFuture<'a> { 232 | fn new( 233 | file: HANDLE, 234 | file_name: Option<&'a U16CStr>, 235 | buffer: &'a mut [u8], 236 | return_single_entry: bool, 237 | restart_scan: bool, 238 | class: FILE_INFORMATION_CLASS, 239 | ) -> winfsp::Result { 240 | let event = lfs::new_event()?; 241 | 242 | Ok(Self { 243 | file, 244 | event, 245 | file_name, 246 | iosb: UnsafeCell::new(AssertThreadSafe(IO_STATUS_BLOCK::default())), 247 | result: None, 248 | buffer, 249 | return_single_entry, 250 | restart_scan, 251 | class, 252 | }) 253 | } 254 | } 255 | 256 | impl<'a> Drop for LfsQueryDirectoryFileFuture<'a> { 257 | fn drop(&mut self) { 258 | unsafe { 259 | CloseHandle(self.event.0); 260 | } 261 | } 262 | } 263 | 264 | impl<'a> Future for LfsQueryDirectoryFileFuture<'a> { 265 | type Output = Result; 266 | 267 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 268 | let Some(result) = self.result else { 269 | let unicode_filename = self.file_name.map(|f| unsafe { 270 | let mut unicode_filename: MaybeUninit = MaybeUninit::zeroed(); 271 | RtlInitUnicodeString(unicode_filename.as_mut_ptr(), PCWSTR(f.as_ptr())); 272 | unicode_filename.assume_init() 273 | }); 274 | 275 | let initial_result = unsafe { 276 | NtQueryDirectoryFile( 277 | self.file, 278 | self.event, 279 | None, 280 | None, 281 | self.iosb.get() as *mut _, 282 | self.buffer.as_mut_ptr() as *mut _, 283 | self.buffer.len() as u32, 284 | self.class, 285 | BOOLEAN::from(self.return_single_entry), 286 | unicode_filename 287 | .as_ref() 288 | .map(|p| p as *const UNICODE_STRING as *const _), 289 | BOOLEAN::from(self.restart_scan), 290 | ) 291 | }; 292 | self.result = Some(initial_result); 293 | cx.waker().wake_by_ref(); 294 | return Poll::Pending; 295 | }; 296 | 297 | if result != STATUS_PENDING { 298 | return if result != STATUS_SUCCESS { 299 | Poll::Ready(Err(FspError::from(result))) 300 | } else { 301 | Poll::Ready(Ok(unsafe { self.iosb.get().read().0 })) 302 | }; 303 | } 304 | 305 | let wait_result = unsafe { WaitForSingleObject(self.event, 0) }; 306 | 307 | if wait_result == WAIT_OBJECT_0 { 308 | let code = unsafe { addr_of!((*self.iosb.get()).0.Anonymous.Status).read() }; 309 | self.result = Some(code); 310 | } else if wait_result == WAIT_FAILED 311 | || wait_result == WAIT_ABANDONED 312 | || wait_result == WAIT_ABANDONED_0 313 | { 314 | self.result = Some(STATUS_ABANDONED); 315 | } 316 | 317 | // if timed out, io isn't ready 318 | cx.waker().wake_by_ref(); 319 | Poll::Pending 320 | } 321 | } 322 | 323 | pub async fn lfs_query_directory_file_async( 324 | handle: HANDLE, 325 | buffer: &mut [u8], 326 | class: FILE_INFORMATION_CLASS, 327 | return_single_entry: bool, 328 | file_name: Option<&U16CStr>, 329 | restart_scan: bool, 330 | ) -> winfsp::Result { 331 | let query_ft = LfsQueryDirectoryFileFuture::new( 332 | handle, 333 | file_name, 334 | buffer, 335 | return_single_entry, 336 | restart_scan, 337 | class, 338 | )?; 339 | let iosb = query_ft.await?; 340 | 341 | Ok(iosb.Information) 342 | } 343 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/src/native/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod lfs; 2 | pub mod volume; 3 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/src/native/volume.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_void; 2 | use std::mem::MaybeUninit; 3 | use windows::Wdk::Storage::FileSystem::{ 4 | FileFsAttributeInformation, FileFsSizeInformation, NtQueryVolumeInformationFile, 5 | FILE_FS_ATTRIBUTE_INFORMATION, 6 | }; 7 | use windows::Wdk::System::SystemServices::FILE_FS_SIZE_INFORMATION; 8 | use windows::Win32::Foundation::HANDLE; 9 | use windows::Win32::System::IO::IO_STATUS_BLOCK; 10 | use winfsp::constants::MAX_PATH; 11 | use winfsp::util::VariableSizedBox; 12 | 13 | pub fn get_attr(handle: HANDLE) -> winfsp::Result> { 14 | let mut iosb: MaybeUninit = MaybeUninit::uninit(); 15 | let mut info = VariableSizedBox::::new( 16 | MAX_PATH * std::mem::size_of::(), 17 | ); 18 | 19 | unsafe { 20 | NtQueryVolumeInformationFile( 21 | handle, 22 | iosb.as_mut_ptr(), 23 | info.as_mut_ptr() as *mut _, 24 | info.len() as u32, 25 | FileFsAttributeInformation, 26 | ) 27 | .ok()?; 28 | } 29 | Ok(info) 30 | } 31 | 32 | pub fn get_size(handle: HANDLE) -> winfsp::Result { 33 | let mut iosb: MaybeUninit = MaybeUninit::uninit(); 34 | let mut info: FILE_FS_SIZE_INFORMATION = unsafe { std::mem::zeroed() }; 35 | 36 | unsafe { 37 | NtQueryVolumeInformationFile( 38 | handle, 39 | iosb.as_mut_ptr(), 40 | (&mut info) as *mut _ as *mut c_void, 41 | std::mem::size_of::() as u32, 42 | FileFsSizeInformation, 43 | ) 44 | .ok()?; 45 | }; 46 | 47 | Ok(info) 48 | } 49 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/src/service.rs: -------------------------------------------------------------------------------- 1 | use crate::fs::ntptfs::NtPassthroughFilesystem; 2 | use crate::Args; 3 | 4 | #[inline] 5 | pub fn svc_start(args: Args) -> anyhow::Result { 6 | let mut ntptfs = NtPassthroughFilesystem::create( 7 | &args.directory, 8 | &args.volume_prefix.unwrap_or_else(|| String::from("")), 9 | )?; 10 | ntptfs.fs.mount(args.mountpoint.as_os_str())?; 11 | ntptfs.fs.start()?; 12 | Ok(ntptfs) 13 | } 14 | 15 | #[inline] 16 | pub fn svc_stop(fs: Option<&mut NtPassthroughFilesystem>) { 17 | if let Some(f) = fs { 18 | f.fs.stop(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/winfsp-test/winfsp-tests-x64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/3fb1d63f562085d376d0d5ba5884959a96e7b68d/test/winfsp-test/winfsp-tests-x64.exe -------------------------------------------------------------------------------- /test/winfsp-test/winfsp-x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/3fb1d63f562085d376d0d5ba5884959a96e7b68d/test/winfsp-test/winfsp-x64.dll -------------------------------------------------------------------------------- /winfsp-sys/.gitattributes: -------------------------------------------------------------------------------- 1 | winfsp/** linguist-vendored 2 | -------------------------------------------------------------------------------- /winfsp-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winfsp-sys" 3 | version = "0.2.2+winfsp-2.0" 4 | edition = "2021" 5 | license = "GPL-3.0" 6 | description = "Raw bindings to WinFSP" 7 | keywords = ["filesystem", "winfsp", "fuse"] 8 | categories = ["filesystem", "os::windows-apis", "external-ffi-bindings"] 9 | readme = "./README.md" 10 | repository = "https://github.com/SnowflakePowered/winfsp-rs" 11 | 12 | [build-dependencies] 13 | bindgen = "0.68.1" 14 | windows-registry = { version = "0.1.1", optional = true } 15 | 16 | [features] 17 | system = ["dep:windows-registry"] 18 | docsrs = [] 19 | 20 | [package.metadata.docs.rs] 21 | default-target = "x86_64-pc-windows-msvc" 22 | targets = [] 23 | features = ["docsrs"] 24 | -------------------------------------------------------------------------------- /winfsp-sys/README.md: -------------------------------------------------------------------------------- 1 | # winfsp-sys 2 | 3 | [![Latest Version](https://img.shields.io/crates/v/winfsp-sys.svg)](https://crates.io/crates/winfsp-sys) [![Docs](https://docs.rs/winfsp-sys/badge.svg)](https://docs.rs/winfsp-sys) ![License](https://img.shields.io/crates/l/winfsp-sys) 4 | 5 | 6 | Raw FFI bindings to [WinFSP](https://github.com/winfsp/winfsp). 7 | 8 | ## Usage 9 | The [winfsp](https://crates.io/crates/winfsp) crate provides idiomatic wrappers around the raw WinFSP APIs. 10 | 11 | By default, winfsp-sys builds against an included import library. To build against the installed WinFSP libraries, enable the `system` 12 | feature. The path will automatically be determined via the Registry. 13 | 14 | ```toml 15 | [dependencies.winfsp-sys] 16 | version = "0.2" 17 | features = ["system"] 18 | ``` -------------------------------------------------------------------------------- /winfsp-sys/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs::File; 3 | use std::io::Write; 4 | use std::path::{Path, PathBuf}; 5 | #[cfg(feature = "system")] 6 | use windows_registry::{LOCAL_MACHINE, Value}; 7 | 8 | static HEADER: &str = r#" 9 | #include 10 | #include 11 | #include 12 | "#; 13 | 14 | #[cfg(not(feature = "system"))] 15 | fn local() -> String { 16 | let project_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); 17 | 18 | println!( 19 | "cargo:rustc-link-search={}", 20 | project_dir.join("winfsp/lib").to_string_lossy() 21 | ); 22 | 23 | "--include-directory=winfsp/inc".into() 24 | } 25 | 26 | #[cfg(feature = "system")] 27 | fn system() -> String { 28 | let winfsp_install = LOCAL_MACHINE 29 | .open("SOFTWARE\\WOW6432Node\\WinFsp") 30 | .ok() 31 | .and_then(|u| u.get_value("InstallDir").ok()) 32 | .expect("WinFsp installation directory not found."); 33 | let directory = match winfsp_install { 34 | Value::String(string) => string, 35 | _ => panic!("unexpected install directory"), 36 | }; 37 | 38 | println!("cargo:rustc-link-search={}/lib", directory); 39 | 40 | format!("--include-directory={}/inc", directory) 41 | } 42 | 43 | fn main() { 44 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 45 | 46 | // host needs to be windows 47 | if cfg!(feature = "docsrs") { 48 | println!("cargo:warning=WinFSP does not build on any operating system but Windows. This feature is meant for docs.rs only. It will not link when compiled into a binary."); 49 | File::create(out_dir.join("bindings.rs")).unwrap(); 50 | return; 51 | } 52 | 53 | if !cfg!(windows) { 54 | panic!("WinFSP is only supported on Windows."); 55 | } 56 | 57 | #[cfg(feature = "system")] 58 | let link_include = system(); 59 | #[cfg(not(feature = "system"))] 60 | let link_include = local(); 61 | 62 | println!("cargo:rustc-link-lib=dylib=delayimp"); 63 | 64 | if cfg!(target_os = "windows") && cfg!(target_arch = "x86_64") && cfg!(target_env = "msvc") { 65 | println!("cargo:rustc-link-lib=dylib=winfsp-x64"); 66 | println!("cargo:rustc-link-arg=/DELAYLOAD:winfsp-x64.dll"); 67 | } else if cfg!(target_os = "windows") && cfg!(target_arch = "x86") && cfg!(target_env = "msvc") 68 | { 69 | println!("cargo:rustc-link-lib=dylib=winfsp-x86"); 70 | println!("cargo:rustc-link-arg=/DELAYLOAD:winfsp-x86.dll"); 71 | } else if cfg!(target_arch = "aarch64") && cfg!(target_env = "msvc") { 72 | println!("cargo:rustc-link-lib=dylib=winfsp-a64"); 73 | println!("cargo:rustc-link-arg=/DELAYLOAD:winfsp-a64.dll"); 74 | } else { 75 | panic!("unsupported triple {}", env::var("TARGET").unwrap()) 76 | } 77 | 78 | let bindings_path_str = out_dir.join("bindings.rs"); 79 | 80 | if !Path::new(&bindings_path_str).exists() { 81 | let gen_h_path = out_dir.join("gen.h"); 82 | let mut gen_h = File::create(&gen_h_path).expect("could not create file"); 83 | gen_h 84 | .write_all(HEADER.as_bytes()) 85 | .expect("could not write header file"); 86 | 87 | let bindings = bindgen::Builder::default() 88 | .header(gen_h_path.to_str().unwrap()) 89 | .derive_default(true) 90 | .blocklist_type("_?P?IMAGE_TLS_DIRECTORY.*") 91 | .allowlist_function("Fsp.*") 92 | .allowlist_type("FSP.*") 93 | .allowlist_type("Fsp.*") 94 | .allowlist_var("FSP_.*") 95 | .allowlist_var("Fsp.*") 96 | .allowlist_var("CTL_CODE") 97 | .clang_arg("-DUNICODE") 98 | .clang_arg(link_include); 99 | 100 | let bindings = if cfg!(target_os = "windows") 101 | && cfg!(target_arch = "x86_64") 102 | && cfg!(target_env = "msvc") 103 | { 104 | bindings.clang_arg("--target=x86_64-pc-windows-msvc") 105 | } else if cfg!(target_os = "windows") 106 | && cfg!(target_arch = "x86") 107 | && cfg!(target_env = "msvc") 108 | { 109 | bindings.clang_arg("--target=x86-pc-windows-msvc") 110 | } else if cfg!(target_os = "windows") 111 | && cfg!(target_arch = "aarch64") 112 | && cfg!(target_env = "msvc") 113 | { 114 | bindings.clang_arg("--target=aarch64-pc-windows-msvc") 115 | } else { 116 | panic!("unsupported triple {}", env::var("TARGET").unwrap()) 117 | }; 118 | 119 | let bindings = bindings 120 | .parse_callbacks(Box::new(bindgen::CargoCallbacks)) 121 | .generate() 122 | .expect("Unable to generate bindings"); 123 | 124 | bindings 125 | .write_to_file(out_dir.join("bindings.rs")) 126 | .expect("Couldn't write bindings!"); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /winfsp-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_snake_case)] 4 | #![allow(clippy::useless_transmute)] 5 | #![allow(clippy::type_complexity)] 6 | #![allow(clippy::too_many_arguments)] 7 | #![allow(clippy::missing_safety_doc)] 8 | 9 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 10 | 11 | #[cfg(feature = "docsrs")] 12 | mod bindings; 13 | 14 | #[cfg(feature = "docsrs")] 15 | pub use bindings::*; 16 | 17 | #[allow(non_camel_case_types)] 18 | pub type FILE_ACCESS_RIGHTS = u32; 19 | #[allow(non_camel_case_types)] 20 | pub type FILE_FLAGS_AND_ATTRIBUTES = u32; 21 | -------------------------------------------------------------------------------- /winfsp-sys/winfsp/.gitrev: -------------------------------------------------------------------------------- 1 | 5251dcbce93a334cc2b69565eb43de42eeaa2a64 -------------------------------------------------------------------------------- /winfsp-sys/winfsp/bin/winfsp-a64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/3fb1d63f562085d376d0d5ba5884959a96e7b68d/winfsp-sys/winfsp/bin/winfsp-a64.dll -------------------------------------------------------------------------------- /winfsp-sys/winfsp/bin/winfsp-msil.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/3fb1d63f562085d376d0d5ba5884959a96e7b68d/winfsp-sys/winfsp/bin/winfsp-msil.dll -------------------------------------------------------------------------------- /winfsp-sys/winfsp/bin/winfsp-x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/3fb1d63f562085d376d0d5ba5884959a96e7b68d/winfsp-sys/winfsp/bin/winfsp-x64.dll -------------------------------------------------------------------------------- /winfsp-sys/winfsp/bin/winfsp-x86.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/3fb1d63f562085d376d0d5ba5884959a96e7b68d/winfsp-sys/winfsp/bin/winfsp-x86.dll -------------------------------------------------------------------------------- /winfsp-sys/winfsp/inc/fuse/fuse.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fuse/fuse.h 3 | * WinFsp FUSE compatible API. 4 | * 5 | * This file is derived from libfuse/include/fuse.h: 6 | * FUSE: Filesystem in Userspace 7 | * Copyright (C) 2001-2007 Miklos Szeredi 8 | * 9 | * @copyright 2015-2022 Bill Zissimopoulos 10 | */ 11 | /* 12 | * This file is part of WinFsp. 13 | * 14 | * You can redistribute it and/or modify it under the terms of the GNU 15 | * General Public License version 3 as published by the Free Software 16 | * Foundation. 17 | * 18 | * Licensees holding a valid commercial license may use this software 19 | * in accordance with the commercial license agreement provided in 20 | * conjunction with the software. The terms and conditions of any such 21 | * commercial license agreement shall govern, supersede, and render 22 | * ineffective any application of the GPLv3 license to this software, 23 | * notwithstanding of any reference thereto in the software or 24 | * associated repository. 25 | */ 26 | 27 | #ifndef FUSE_H_ 28 | #define FUSE_H_ 29 | 30 | #include "fuse_common.h" 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | struct fuse; 37 | 38 | typedef int (*fuse_fill_dir_t)(void *buf, const char *name, 39 | const struct fuse_stat *stbuf, fuse_off_t off); 40 | typedef struct fuse_dirhandle *fuse_dirh_t; 41 | typedef int (*fuse_dirfil_t)(fuse_dirh_t h, const char *name, 42 | int type, fuse_ino_t ino); 43 | 44 | struct fuse_operations 45 | { 46 | /* S - supported by WinFsp */ 47 | /* S */ int (*getattr)(const char *path, struct fuse_stat *stbuf); 48 | /* S */ int (*getdir)(const char *path, fuse_dirh_t h, fuse_dirfil_t filler); 49 | /* S */ int (*readlink)(const char *path, char *buf, size_t size); 50 | /* S */ int (*mknod)(const char *path, fuse_mode_t mode, fuse_dev_t dev); 51 | /* S */ int (*mkdir)(const char *path, fuse_mode_t mode); 52 | /* S */ int (*unlink)(const char *path); 53 | /* S */ int (*rmdir)(const char *path); 54 | /* S */ int (*symlink)(const char *dstpath, const char *srcpath); 55 | /* S */ int (*rename)(const char *oldpath, const char *newpath); 56 | /* _ */ int (*link)(const char *srcpath, const char *dstpath); 57 | /* S */ int (*chmod)(const char *path, fuse_mode_t mode); 58 | /* S */ int (*chown)(const char *path, fuse_uid_t uid, fuse_gid_t gid); 59 | /* S */ int (*truncate)(const char *path, fuse_off_t size); 60 | /* S */ int (*utime)(const char *path, struct fuse_utimbuf *timbuf); 61 | /* S */ int (*open)(const char *path, struct fuse_file_info *fi); 62 | /* S */ int (*read)(const char *path, char *buf, size_t size, fuse_off_t off, 63 | struct fuse_file_info *fi); 64 | /* S */ int (*write)(const char *path, const char *buf, size_t size, fuse_off_t off, 65 | struct fuse_file_info *fi); 66 | /* S */ int (*statfs)(const char *path, struct fuse_statvfs *stbuf); 67 | /* S */ int (*flush)(const char *path, struct fuse_file_info *fi); 68 | /* S */ int (*release)(const char *path, struct fuse_file_info *fi); 69 | /* S */ int (*fsync)(const char *path, int datasync, struct fuse_file_info *fi); 70 | /* S */ int (*setxattr)(const char *path, const char *name, const char *value, size_t size, 71 | int flags); 72 | /* S */ int (*getxattr)(const char *path, const char *name, char *value, size_t size); 73 | /* S */ int (*listxattr)(const char *path, char *namebuf, size_t size); 74 | /* S */ int (*removexattr)(const char *path, const char *name); 75 | /* S */ int (*opendir)(const char *path, struct fuse_file_info *fi); 76 | /* S */ int (*readdir)(const char *path, void *buf, fuse_fill_dir_t filler, fuse_off_t off, 77 | struct fuse_file_info *fi); 78 | /* S */ int (*releasedir)(const char *path, struct fuse_file_info *fi); 79 | /* S */ int (*fsyncdir)(const char *path, int datasync, struct fuse_file_info *fi); 80 | /* S */ void *(*init)(struct fuse_conn_info *conn); 81 | /* S */ void (*destroy)(void *data); 82 | /* S */ int (*access)(const char *path, int mask); 83 | /* S */ int (*create)(const char *path, fuse_mode_t mode, struct fuse_file_info *fi); 84 | /* S */ int (*ftruncate)(const char *path, fuse_off_t off, struct fuse_file_info *fi); 85 | /* S */ int (*fgetattr)(const char *path, struct fuse_stat *stbuf, struct fuse_file_info *fi); 86 | /* _ */ int (*lock)(const char *path, 87 | struct fuse_file_info *fi, int cmd, struct fuse_flock *lock); 88 | /* S */ int (*utimens)(const char *path, const struct fuse_timespec tv[2]); 89 | /* _ */ int (*bmap)(const char *path, size_t blocksize, uint64_t *idx); 90 | /* _ */ unsigned int flag_nullpath_ok:1; 91 | /* _ */ unsigned int flag_nopath:1; 92 | /* _ */ unsigned int flag_utime_omit_ok:1; 93 | /* _ */ unsigned int flag_reserved:29; 94 | /* S */ int (*ioctl)(const char *path, int cmd, void *arg, struct fuse_file_info *fi, 95 | unsigned int flags, void *data); 96 | /* _ */ int (*poll)(const char *path, struct fuse_file_info *fi, 97 | struct fuse_pollhandle *ph, unsigned *reventsp); 98 | /* FUSE 2.9 */ 99 | /* _ */ int (*write_buf)(const char *path, 100 | struct fuse_bufvec *buf, fuse_off_t off, struct fuse_file_info *fi); 101 | /* _ */ int (*read_buf)(const char *path, 102 | struct fuse_bufvec **bufp, size_t size, fuse_off_t off, struct fuse_file_info *fi); 103 | /* _ */ int (*flock)(const char *path, struct fuse_file_info *, int op); 104 | /* _ */ int (*fallocate)(const char *path, int mode, fuse_off_t off, fuse_off_t len, 105 | struct fuse_file_info *fi); 106 | /* WinFsp */ 107 | /* S */ int (*getpath)(const char *path, char *buf, size_t size, 108 | struct fuse_file_info *fi); 109 | /* OSXFUSE */ 110 | /* _ */ int (*reserved01)(); 111 | /* _ */ int (*reserved02)(); 112 | /* _ */ int (*statfs_x)(const char *path, struct fuse_statfs *stbuf); 113 | /* _ */ int (*setvolname)(const char *volname); 114 | /* _ */ int (*exchange)(const char *oldpath, const char *newpath, unsigned long flags); 115 | /* _ */ int (*getxtimes)(const char *path, 116 | struct fuse_timespec *bkuptime, struct fuse_timespec *crtime); 117 | /* _ */ int (*setbkuptime)(const char *path, const struct fuse_timespec *tv); 118 | /* S */ int (*setchgtime)(const char *path, const struct fuse_timespec *tv); 119 | /* S */ int (*setcrtime)(const char *path, const struct fuse_timespec *tv); 120 | /* S */ int (*chflags)(const char *path, uint32_t flags); 121 | /* _ */ int (*setattr_x)(const char *path, struct fuse_setattr_x *attr); 122 | /* _ */ int (*fsetattr_x)(const char *path, struct fuse_setattr_x *attr, 123 | struct fuse_file_info *fi); 124 | }; 125 | 126 | struct fuse_context 127 | { 128 | struct fuse *fuse; 129 | fuse_uid_t uid; 130 | fuse_gid_t gid; 131 | fuse_pid_t pid; 132 | void *private_data; 133 | fuse_mode_t umask; 134 | }; 135 | 136 | #define fuse_main(argc, argv, ops, data)\ 137 | fuse_main_real(argc, argv, ops, sizeof *(ops), data) 138 | 139 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_main_real)(struct fsp_fuse_env *env, 140 | int argc, char *argv[], 141 | const struct fuse_operations *ops, size_t opsize, void *data); 142 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_is_lib_option)(struct fsp_fuse_env *env, 143 | const char *opt); 144 | FSP_FUSE_API struct fuse *FSP_FUSE_API_NAME(fsp_fuse_new)(struct fsp_fuse_env *env, 145 | struct fuse_chan *ch, struct fuse_args *args, 146 | const struct fuse_operations *ops, size_t opsize, void *data); 147 | FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_destroy)(struct fsp_fuse_env *env, 148 | struct fuse *f); 149 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_loop)(struct fsp_fuse_env *env, 150 | struct fuse *f); 151 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_loop_mt)(struct fsp_fuse_env *env, 152 | struct fuse *f); 153 | FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_exit)(struct fsp_fuse_env *env, 154 | struct fuse *f); 155 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_exited)(struct fsp_fuse_env *env, 156 | struct fuse *f); 157 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_notify)(struct fsp_fuse_env *env, 158 | struct fuse *f, const char *path, uint32_t action); 159 | FSP_FUSE_API struct fuse_context *FSP_FUSE_API_NAME(fsp_fuse_get_context)(struct fsp_fuse_env *env); 160 | 161 | FSP_FUSE_SYM( 162 | int fuse_main_real(int argc, char *argv[], 163 | const struct fuse_operations *ops, size_t opsize, void *data), 164 | { 165 | return FSP_FUSE_API_CALL(fsp_fuse_main_real) 166 | (fsp_fuse_env(), argc, argv, ops, opsize, data); 167 | }) 168 | 169 | FSP_FUSE_SYM( 170 | int fuse_is_lib_option(const char *opt), 171 | { 172 | return FSP_FUSE_API_CALL(fsp_fuse_is_lib_option) 173 | (fsp_fuse_env(), opt); 174 | }) 175 | 176 | FSP_FUSE_SYM( 177 | struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args, 178 | const struct fuse_operations *ops, size_t opsize, void *data), 179 | { 180 | return FSP_FUSE_API_CALL(fsp_fuse_new) 181 | (fsp_fuse_env(), ch, args, ops, opsize, data); 182 | }) 183 | 184 | FSP_FUSE_SYM( 185 | void fuse_destroy(struct fuse *f), 186 | { 187 | FSP_FUSE_API_CALL(fsp_fuse_destroy) 188 | (fsp_fuse_env(), f); 189 | }) 190 | 191 | FSP_FUSE_SYM( 192 | int fuse_loop(struct fuse *f), 193 | { 194 | return FSP_FUSE_API_CALL(fsp_fuse_loop) 195 | (fsp_fuse_env(), f); 196 | }) 197 | 198 | FSP_FUSE_SYM( 199 | int fuse_loop_mt(struct fuse *f), 200 | { 201 | return FSP_FUSE_API_CALL(fsp_fuse_loop_mt) 202 | (fsp_fuse_env(), f); 203 | }) 204 | 205 | FSP_FUSE_SYM( 206 | void fuse_exit(struct fuse *f), 207 | { 208 | FSP_FUSE_API_CALL(fsp_fuse_exit) 209 | (fsp_fuse_env(), f); 210 | }) 211 | 212 | FSP_FUSE_SYM( 213 | int fuse_exited(struct fuse *f), 214 | { 215 | return FSP_FUSE_API_CALL(fsp_fuse_exited) 216 | (fsp_fuse_env(), f); 217 | }) 218 | 219 | FSP_FUSE_SYM( 220 | int fuse_notify(struct fuse *f, const char *path, uint32_t action), 221 | { 222 | return FSP_FUSE_API_CALL(fsp_fuse_notify) 223 | (fsp_fuse_env(), f, path, action); 224 | }) 225 | 226 | FSP_FUSE_SYM( 227 | struct fuse_context *fuse_get_context(void), 228 | { 229 | return FSP_FUSE_API_CALL(fsp_fuse_get_context) 230 | (fsp_fuse_env()); 231 | }) 232 | 233 | FSP_FUSE_SYM( 234 | int fuse_getgroups(int size, fuse_gid_t list[]), 235 | { 236 | (void)size; 237 | (void)list; 238 | return -ENOSYS; 239 | }) 240 | 241 | FSP_FUSE_SYM( 242 | int fuse_interrupted(void), 243 | { 244 | return 0; 245 | }) 246 | 247 | FSP_FUSE_SYM( 248 | int fuse_invalidate(struct fuse *f, const char *path), 249 | { 250 | return FSP_FUSE_API_CALL(fsp_fuse_notify) 251 | (fsp_fuse_env(), f, path, 0); 252 | }) 253 | 254 | FSP_FUSE_SYM( 255 | int fuse_notify_poll(struct fuse_pollhandle *ph), 256 | { 257 | (void)ph; 258 | return 0; 259 | }) 260 | 261 | FSP_FUSE_SYM( 262 | struct fuse_session *fuse_get_session(struct fuse *f), 263 | { 264 | return (struct fuse_session *)f; 265 | }) 266 | 267 | #ifdef __cplusplus 268 | } 269 | #endif 270 | 271 | #endif 272 | -------------------------------------------------------------------------------- /winfsp-sys/winfsp/inc/fuse/fuse_common.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fuse/fuse_common.h 3 | * WinFsp FUSE compatible API. 4 | * 5 | * This file is derived from libfuse/include/fuse_common.h: 6 | * FUSE: Filesystem in Userspace 7 | * Copyright (C) 2001-2007 Miklos Szeredi 8 | * 9 | * @copyright 2015-2022 Bill Zissimopoulos 10 | */ 11 | /* 12 | * This file is part of WinFsp. 13 | * 14 | * You can redistribute it and/or modify it under the terms of the GNU 15 | * General Public License version 3 as published by the Free Software 16 | * Foundation. 17 | * 18 | * Licensees holding a valid commercial license may use this software 19 | * in accordance with the commercial license agreement provided in 20 | * conjunction with the software. The terms and conditions of any such 21 | * commercial license agreement shall govern, supersede, and render 22 | * ineffective any application of the GPLv3 license to this software, 23 | * notwithstanding of any reference thereto in the software or 24 | * associated repository. 25 | */ 26 | 27 | #ifndef FUSE_COMMON_H_ 28 | #define FUSE_COMMON_H_ 29 | 30 | #include "winfsp_fuse.h" 31 | #include "fuse_opt.h" 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | #define FUSE_MAJOR_VERSION 2 38 | #define FUSE_MINOR_VERSION 8 39 | #define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) 40 | #define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) 41 | 42 | #define FUSE_CAP_ASYNC_READ (1 << 0) 43 | #define FUSE_CAP_POSIX_LOCKS (1 << 1) 44 | #define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3) 45 | #define FUSE_CAP_EXPORT_SUPPORT (1 << 4) 46 | #define FUSE_CAP_BIG_WRITES (1 << 5) 47 | #define FUSE_CAP_DONT_MASK (1 << 6) 48 | #define FUSE_CAP_ALLOCATE (1 << 27) /* reserved (OSXFUSE) */ 49 | #define FUSE_CAP_EXCHANGE_DATA (1 << 28) /* reserved (OSXFUSE) */ 50 | #define FUSE_CAP_CASE_INSENSITIVE (1 << 29) /* file system is case insensitive */ 51 | #define FUSE_CAP_VOL_RENAME (1 << 30) /* reserved (OSXFUSE) */ 52 | #define FUSE_CAP_XTIMES (1 << 31) /* reserved (OSXFUSE) */ 53 | 54 | #define FSP_FUSE_CAP_READDIR_PLUS (1 << 21) /* file system supports enhanced readdir */ 55 | #define FSP_FUSE_CAP_READ_ONLY (1 << 22) /* file system is marked read-only */ 56 | #define FSP_FUSE_CAP_STAT_EX (1 << 23) /* file system supports fuse_stat_ex */ 57 | #define FSP_FUSE_CAP_DELETE_ACCESS (1 << 24) /* file system supports access with DELETE_OK */ 58 | #define FSP_FUSE_CAP_CASE_INSENSITIVE FUSE_CAP_CASE_INSENSITIVE 59 | 60 | #define FUSE_IOCTL_COMPAT (1 << 0) 61 | #define FUSE_IOCTL_UNRESTRICTED (1 << 1) 62 | #define FUSE_IOCTL_RETRY (1 << 2) 63 | #define FUSE_IOCTL_MAX_IOV 256 64 | 65 | /* from FreeBSD */ 66 | #define FSP_FUSE_UF_HIDDEN 0x00008000 67 | #define FSP_FUSE_UF_READONLY 0x00001000 68 | #define FSP_FUSE_UF_SYSTEM 0x00000080 69 | #define FSP_FUSE_UF_ARCHIVE 0x00000800 70 | #if !defined(UF_HIDDEN) 71 | #define UF_HIDDEN FSP_FUSE_UF_HIDDEN 72 | #endif 73 | #if !defined(UF_READONLY) 74 | #define UF_READONLY FSP_FUSE_UF_READONLY 75 | #endif 76 | #if !defined(UF_SYSTEM) 77 | #define UF_SYSTEM FSP_FUSE_UF_SYSTEM 78 | #endif 79 | #if !defined(UF_ARCHIVE) 80 | #define UF_ARCHIVE FSP_FUSE_UF_ARCHIVE 81 | #endif 82 | 83 | /* delete access */ 84 | #define FSP_FUSE_DELETE_OK 0x40000000 85 | 86 | /* notify extension */ 87 | #define FSP_FUSE_NOTIFY_MKDIR 0x0001 88 | #define FSP_FUSE_NOTIFY_RMDIR 0x0002 89 | #define FSP_FUSE_NOTIFY_CREATE 0x0004 90 | #define FSP_FUSE_NOTIFY_UNLINK 0x0008 91 | #define FSP_FUSE_NOTIFY_CHMOD 0x0010 92 | #define FSP_FUSE_NOTIFY_CHOWN 0x0020 93 | #define FSP_FUSE_NOTIFY_UTIME 0x0040 94 | #define FSP_FUSE_NOTIFY_CHFLAGS 0x0080 95 | #define FSP_FUSE_NOTIFY_TRUNCATE 0x0100 96 | 97 | /* getpath extension */ 98 | #define FSP_FUSE_HAS_GETPATH 1 99 | 100 | struct fuse_file_info 101 | { 102 | int flags; 103 | unsigned int fh_old; 104 | int writepage; 105 | unsigned int direct_io:1; 106 | unsigned int keep_cache:1; 107 | unsigned int flush:1; 108 | unsigned int nonseekable:1; 109 | unsigned int padding:28; 110 | uint64_t fh; 111 | uint64_t lock_owner; 112 | }; 113 | 114 | struct fuse_conn_info 115 | { 116 | unsigned proto_major; 117 | unsigned proto_minor; 118 | unsigned async_read; 119 | unsigned max_write; 120 | unsigned max_readahead; 121 | unsigned capable; 122 | unsigned want; 123 | unsigned reserved[25]; 124 | }; 125 | 126 | struct fuse_session; 127 | struct fuse_chan; 128 | struct fuse_pollhandle; 129 | struct fuse_bufvec; 130 | struct fuse_statfs; 131 | struct fuse_setattr_x; 132 | 133 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_version)(struct fsp_fuse_env *env); 134 | FSP_FUSE_API struct fuse_chan *FSP_FUSE_API_NAME(fsp_fuse_mount)(struct fsp_fuse_env *env, 135 | const char *mountpoint, struct fuse_args *args); 136 | FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_unmount)(struct fsp_fuse_env *env, 137 | const char *mountpoint, struct fuse_chan *ch); 138 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_parse_cmdline)(struct fsp_fuse_env *env, 139 | struct fuse_args *args, 140 | char **mountpoint, int *multithreaded, int *foreground); 141 | FSP_FUSE_API int32_t FSP_FUSE_API_NAME(fsp_fuse_ntstatus_from_errno)(struct fsp_fuse_env *env, 142 | int err); 143 | 144 | FSP_FUSE_SYM( 145 | int fuse_version(void), 146 | { 147 | return FSP_FUSE_API_CALL(fsp_fuse_version) 148 | (fsp_fuse_env()); 149 | }) 150 | 151 | FSP_FUSE_SYM( 152 | struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args), 153 | { 154 | return FSP_FUSE_API_CALL(fsp_fuse_mount) 155 | (fsp_fuse_env(), mountpoint, args); 156 | }) 157 | 158 | FSP_FUSE_SYM( 159 | void fuse_unmount(const char *mountpoint, struct fuse_chan *ch), 160 | { 161 | FSP_FUSE_API_CALL(fsp_fuse_unmount) 162 | (fsp_fuse_env(), mountpoint, ch); 163 | }) 164 | 165 | FSP_FUSE_SYM( 166 | int fuse_parse_cmdline(struct fuse_args *args, 167 | char **mountpoint, int *multithreaded, int *foreground), 168 | { 169 | return FSP_FUSE_API_CALL(fsp_fuse_parse_cmdline) 170 | (fsp_fuse_env(), args, mountpoint, multithreaded, foreground); 171 | }) 172 | 173 | FSP_FUSE_SYM( 174 | void fuse_pollhandle_destroy(struct fuse_pollhandle *ph), 175 | { 176 | (void)ph; 177 | }) 178 | 179 | FSP_FUSE_SYM( 180 | int fuse_daemonize(int foreground), 181 | { 182 | return fsp_fuse_daemonize(foreground); 183 | }) 184 | 185 | FSP_FUSE_SYM( 186 | int fuse_set_signal_handlers(struct fuse_session *se), 187 | { 188 | return fsp_fuse_set_signal_handlers(se); 189 | }) 190 | 191 | FSP_FUSE_SYM( 192 | void fuse_remove_signal_handlers(struct fuse_session *se), 193 | { 194 | (void)se; 195 | fsp_fuse_set_signal_handlers(0); 196 | }) 197 | 198 | #ifdef __cplusplus 199 | } 200 | #endif 201 | 202 | #endif 203 | -------------------------------------------------------------------------------- /winfsp-sys/winfsp/inc/fuse/fuse_opt.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fuse/fuse_opt.h 3 | * WinFsp FUSE compatible API. 4 | * 5 | * This file is derived from libfuse/include/fuse_opt.h: 6 | * FUSE: Filesystem in Userspace 7 | * Copyright (C) 2001-2007 Miklos Szeredi 8 | * 9 | * @copyright 2015-2022 Bill Zissimopoulos 10 | */ 11 | /* 12 | * This file is part of WinFsp. 13 | * 14 | * You can redistribute it and/or modify it under the terms of the GNU 15 | * General Public License version 3 as published by the Free Software 16 | * Foundation. 17 | * 18 | * Licensees holding a valid commercial license may use this software 19 | * in accordance with the commercial license agreement provided in 20 | * conjunction with the software. The terms and conditions of any such 21 | * commercial license agreement shall govern, supersede, and render 22 | * ineffective any application of the GPLv3 license to this software, 23 | * notwithstanding of any reference thereto in the software or 24 | * associated repository. 25 | */ 26 | 27 | #ifndef FUSE_OPT_H_ 28 | #define FUSE_OPT_H_ 29 | 30 | #include "winfsp_fuse.h" 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | #define FUSE_OPT_KEY(templ, key) { templ, -1, key } 37 | #define FUSE_OPT_END { NULL, 0, 0 } 38 | 39 | #define FUSE_OPT_KEY_OPT -1 40 | #define FUSE_OPT_KEY_NONOPT -2 41 | #define FUSE_OPT_KEY_KEEP -3 42 | #define FUSE_OPT_KEY_DISCARD -4 43 | 44 | #define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 } 45 | 46 | struct fuse_opt 47 | { 48 | const char *templ; 49 | unsigned int offset; 50 | int value; 51 | }; 52 | 53 | struct fuse_args 54 | { 55 | int argc; 56 | char **argv; 57 | int allocated; 58 | }; 59 | 60 | typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key, 61 | struct fuse_args *outargs); 62 | 63 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_parse)(struct fsp_fuse_env *env, 64 | struct fuse_args *args, void *data, 65 | const struct fuse_opt opts[], fuse_opt_proc_t proc); 66 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_add_arg)(struct fsp_fuse_env *env, 67 | struct fuse_args *args, const char *arg); 68 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_insert_arg)(struct fsp_fuse_env *env, 69 | struct fuse_args *args, int pos, const char *arg); 70 | FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_opt_free_args)(struct fsp_fuse_env *env, 71 | struct fuse_args *args); 72 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_add_opt)(struct fsp_fuse_env *env, 73 | char **opts, const char *opt); 74 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_add_opt_escaped)(struct fsp_fuse_env *env, 75 | char **opts, const char *opt); 76 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_match)(struct fsp_fuse_env *env, 77 | const struct fuse_opt opts[], const char *opt); 78 | 79 | FSP_FUSE_SYM( 80 | int fuse_opt_parse(struct fuse_args *args, void *data, 81 | const struct fuse_opt opts[], fuse_opt_proc_t proc), 82 | { 83 | return FSP_FUSE_API_CALL(fsp_fuse_opt_parse) 84 | (fsp_fuse_env(), args, data, opts, proc); 85 | }) 86 | 87 | FSP_FUSE_SYM( 88 | int fuse_opt_add_arg(struct fuse_args *args, const char *arg), 89 | { 90 | return FSP_FUSE_API_CALL(fsp_fuse_opt_add_arg) 91 | (fsp_fuse_env(), args, arg); 92 | }) 93 | 94 | FSP_FUSE_SYM( 95 | int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg), 96 | { 97 | return FSP_FUSE_API_CALL(fsp_fuse_opt_insert_arg) 98 | (fsp_fuse_env(), args, pos, arg); 99 | }) 100 | 101 | FSP_FUSE_SYM( 102 | void fuse_opt_free_args(struct fuse_args *args), 103 | { 104 | FSP_FUSE_API_CALL(fsp_fuse_opt_free_args) 105 | (fsp_fuse_env(), args); 106 | }) 107 | 108 | FSP_FUSE_SYM( 109 | int fuse_opt_add_opt(char **opts, const char *opt), 110 | { 111 | return FSP_FUSE_API_CALL(fsp_fuse_opt_add_opt) 112 | (fsp_fuse_env(), opts, opt); 113 | }) 114 | 115 | FSP_FUSE_SYM( 116 | int fuse_opt_add_opt_escaped(char **opts, const char *opt), 117 | { 118 | return FSP_FUSE_API_CALL(fsp_fuse_opt_add_opt_escaped) 119 | (fsp_fuse_env(), opts, opt); 120 | }) 121 | 122 | FSP_FUSE_SYM( 123 | int fuse_opt_match(const struct fuse_opt opts[], const char *opt), 124 | { 125 | return FSP_FUSE_API_CALL(fsp_fuse_opt_match) 126 | (fsp_fuse_env(), opts, opt); 127 | }) 128 | 129 | #ifdef __cplusplus 130 | } 131 | #endif 132 | 133 | #endif 134 | -------------------------------------------------------------------------------- /winfsp-sys/winfsp/inc/fuse/winfsp_fuse.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fuse/winfsp_fuse.h 3 | * WinFsp FUSE compatible API. 4 | * 5 | * @copyright 2015-2022 Bill Zissimopoulos 6 | */ 7 | /* 8 | * This file is part of WinFsp. 9 | * 10 | * You can redistribute it and/or modify it under the terms of the GNU 11 | * General Public License version 3 as published by the Free Software 12 | * Foundation. 13 | * 14 | * Licensees holding a valid commercial license may use this software 15 | * in accordance with the commercial license agreement provided in 16 | * conjunction with the software. The terms and conditions of any such 17 | * commercial license agreement shall govern, supersede, and render 18 | * ineffective any application of the GPLv3 license to this software, 19 | * notwithstanding of any reference thereto in the software or 20 | * associated repository. 21 | */ 22 | 23 | #ifndef FUSE_WINFSP_FUSE_H_INCLUDED 24 | #define FUSE_WINFSP_FUSE_H_INCLUDED 25 | 26 | #include 27 | #include 28 | #if !defined(WINFSP_DLL_INTERNAL) 29 | #include 30 | #endif 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | #if !defined(FSP_FUSE_API) 37 | #if defined(WINFSP_DLL_INTERNAL) 38 | #define FSP_FUSE_API __declspec(dllexport) 39 | #else 40 | #define FSP_FUSE_API __declspec(dllimport) 41 | #endif 42 | #endif 43 | 44 | #if !defined(FSP_FUSE_API_NAME) 45 | #define FSP_FUSE_API_NAME(n) (n) 46 | #endif 47 | 48 | #if !defined(FSP_FUSE_API_CALL) 49 | #define FSP_FUSE_API_CALL(n) (n) 50 | #endif 51 | 52 | #if !defined(FSP_FUSE_SYM) 53 | #if !defined(CYGFUSE) 54 | #define FSP_FUSE_SYM(proto, ...) static inline proto { __VA_ARGS__ } 55 | #else 56 | #define FSP_FUSE_SYM(proto, ...) proto; 57 | #endif 58 | #endif 59 | 60 | #define FSP_FUSE_DEVICE_TYPE (0x8000 | 'W' | 'F' * 0x100) /* DeviceIoControl -> ioctl */ 61 | #define FSP_FUSE_CTLCODE_FROM_IOCTL(cmd)\ 62 | (FSP_FUSE_DEVICE_TYPE << 16) | (((cmd) & 0x0fff) << 2) 63 | #define FSP_FUSE_IOCTL(cmd, isiz, osiz) \ 64 | ( \ 65 | (((osiz) != 0) << 31) | \ 66 | (((isiz) != 0) << 30) | \ 67 | (((isiz) | (osiz)) << 16) | \ 68 | (cmd) \ 69 | ) 70 | 71 | /* 72 | * FUSE uses a number of types (notably: struct stat) that are OS specific. 73 | * Furthermore there are sometimes multiple definitions of the same type even 74 | * within the same OS. This is certainly true on Windows, where these types 75 | * are not even native. 76 | * 77 | * For this reason we will define our own fuse_* types which represent the 78 | * types as the WinFsp DLL expects to see them. We will define these types 79 | * to be compatible with the equivalent Cygwin types as we want WinFsp-FUSE 80 | * to be usable from Cygwin. 81 | */ 82 | 83 | #define FSP_FUSE_STAT_FIELD_DEFN \ 84 | fuse_dev_t st_dev; \ 85 | fuse_ino_t st_ino; \ 86 | fuse_mode_t st_mode; \ 87 | fuse_nlink_t st_nlink; \ 88 | fuse_uid_t st_uid; \ 89 | fuse_gid_t st_gid; \ 90 | fuse_dev_t st_rdev; \ 91 | fuse_off_t st_size; \ 92 | struct fuse_timespec st_atim; \ 93 | struct fuse_timespec st_mtim; \ 94 | struct fuse_timespec st_ctim; \ 95 | fuse_blksize_t st_blksize; \ 96 | fuse_blkcnt_t st_blocks; \ 97 | struct fuse_timespec st_birthtim; 98 | #define FSP_FUSE_STAT_EX_FIELD_DEFN \ 99 | FSP_FUSE_STAT_FIELD_DEFN \ 100 | uint32_t st_flags; \ 101 | uint32_t st_reserved32[3]; \ 102 | uint64_t st_reserved64[2]; 103 | 104 | #if defined(_WIN64) || defined(_WIN32) 105 | 106 | typedef uint32_t fuse_uid_t; 107 | typedef uint32_t fuse_gid_t; 108 | typedef int32_t fuse_pid_t; 109 | 110 | typedef uint32_t fuse_dev_t; 111 | typedef uint64_t fuse_ino_t; 112 | typedef uint32_t fuse_mode_t; 113 | typedef uint16_t fuse_nlink_t; 114 | typedef int64_t fuse_off_t; 115 | 116 | #if defined(_WIN64) 117 | typedef uint64_t fuse_fsblkcnt_t; 118 | typedef uint64_t fuse_fsfilcnt_t; 119 | #else 120 | typedef uint32_t fuse_fsblkcnt_t; 121 | typedef uint32_t fuse_fsfilcnt_t; 122 | #endif 123 | typedef int32_t fuse_blksize_t; 124 | typedef int64_t fuse_blkcnt_t; 125 | 126 | #if defined(_WIN64) 127 | struct fuse_utimbuf 128 | { 129 | int64_t actime; 130 | int64_t modtime; 131 | }; 132 | struct fuse_timespec 133 | { 134 | int64_t tv_sec; 135 | int64_t tv_nsec; 136 | }; 137 | #else 138 | struct fuse_utimbuf 139 | { 140 | int32_t actime; 141 | int32_t modtime; 142 | }; 143 | struct fuse_timespec 144 | { 145 | int32_t tv_sec; 146 | int32_t tv_nsec; 147 | }; 148 | #endif 149 | 150 | #if !defined(FSP_FUSE_USE_STAT_EX) 151 | struct fuse_stat 152 | { 153 | FSP_FUSE_STAT_FIELD_DEFN 154 | }; 155 | #else 156 | struct fuse_stat 157 | { 158 | FSP_FUSE_STAT_EX_FIELD_DEFN 159 | }; 160 | #endif 161 | 162 | #if defined(_WIN64) 163 | struct fuse_statvfs 164 | { 165 | uint64_t f_bsize; 166 | uint64_t f_frsize; 167 | fuse_fsblkcnt_t f_blocks; 168 | fuse_fsblkcnt_t f_bfree; 169 | fuse_fsblkcnt_t f_bavail; 170 | fuse_fsfilcnt_t f_files; 171 | fuse_fsfilcnt_t f_ffree; 172 | fuse_fsfilcnt_t f_favail; 173 | uint64_t f_fsid; 174 | uint64_t f_flag; 175 | uint64_t f_namemax; 176 | }; 177 | #else 178 | struct fuse_statvfs 179 | { 180 | uint32_t f_bsize; 181 | uint32_t f_frsize; 182 | fuse_fsblkcnt_t f_blocks; 183 | fuse_fsblkcnt_t f_bfree; 184 | fuse_fsblkcnt_t f_bavail; 185 | fuse_fsfilcnt_t f_files; 186 | fuse_fsfilcnt_t f_ffree; 187 | fuse_fsfilcnt_t f_favail; 188 | uint32_t f_fsid; 189 | uint32_t f_flag; 190 | uint32_t f_namemax; 191 | }; 192 | #endif 193 | 194 | struct fuse_flock 195 | { 196 | int16_t l_type; 197 | int16_t l_whence; 198 | fuse_off_t l_start; 199 | fuse_off_t l_len; 200 | fuse_pid_t l_pid; 201 | }; 202 | 203 | #if defined(WINFSP_DLL_INTERNAL) 204 | #define FSP_FUSE_ENV_INIT \ 205 | { \ 206 | 'W', \ 207 | MemAlloc, MemFree, \ 208 | fsp_fuse_daemonize, \ 209 | fsp_fuse_set_signal_handlers, \ 210 | 0/*conv_to_win_path*/, \ 211 | 0/*winpid_to_pid*/, \ 212 | { 0 }, \ 213 | } 214 | #else 215 | #define FSP_FUSE_ENV_INIT \ 216 | { \ 217 | 'W', \ 218 | malloc, free, \ 219 | fsp_fuse_daemonize, \ 220 | fsp_fuse_set_signal_handlers, \ 221 | 0/*conv_to_win_path*/, \ 222 | 0/*winpid_to_pid*/, \ 223 | { 0 }, \ 224 | } 225 | #endif 226 | 227 | #elif defined(__CYGWIN__) 228 | 229 | #include 230 | #include 231 | #include 232 | #include 233 | #include 234 | #include 235 | #include 236 | 237 | #define fuse_uid_t uid_t 238 | #define fuse_gid_t gid_t 239 | #define fuse_pid_t pid_t 240 | 241 | #define fuse_dev_t dev_t 242 | #define fuse_ino_t ino_t 243 | #define fuse_mode_t mode_t 244 | #define fuse_nlink_t nlink_t 245 | #define fuse_off_t off_t 246 | 247 | #define fuse_fsblkcnt_t fsblkcnt_t 248 | #define fuse_fsfilcnt_t fsfilcnt_t 249 | #define fuse_blksize_t blksize_t 250 | #define fuse_blkcnt_t blkcnt_t 251 | 252 | #define fuse_utimbuf utimbuf 253 | #define fuse_timespec timespec 254 | 255 | #if !defined(FSP_FUSE_USE_STAT_EX) 256 | #define fuse_stat stat 257 | #else 258 | struct fuse_stat 259 | { 260 | FSP_FUSE_STAT_EX_FIELD_DEFN 261 | }; 262 | #endif 263 | #define fuse_statvfs statvfs 264 | #define fuse_flock flock 265 | 266 | #define FSP_FUSE_ENV_INIT \ 267 | { \ 268 | 'C', \ 269 | malloc, free, \ 270 | fsp_fuse_daemonize, \ 271 | fsp_fuse_set_signal_handlers, \ 272 | fsp_fuse_conv_to_win_path, \ 273 | fsp_fuse_winpid_to_pid, \ 274 | { 0 }, \ 275 | } 276 | 277 | /* 278 | * Note that long is 8 bytes long in Cygwin64 and 4 bytes long in Win64. 279 | * For this reason we avoid using long anywhere in these headers. 280 | */ 281 | 282 | #else 283 | #error unsupported environment 284 | #endif 285 | 286 | struct fuse_stat_ex 287 | { 288 | FSP_FUSE_STAT_EX_FIELD_DEFN 289 | }; 290 | 291 | struct fsp_fuse_env 292 | { 293 | unsigned environment; 294 | void *(*memalloc)(size_t); 295 | void (*memfree)(void *); 296 | int (*daemonize)(int); 297 | int (*set_signal_handlers)(void *); 298 | char *(*conv_to_win_path)(const char *); 299 | fuse_pid_t (*winpid_to_pid)(uint32_t); 300 | void (*reserved[2])(); 301 | }; 302 | 303 | FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_signal_handler)(int sig); 304 | 305 | #if defined(_WIN64) || defined(_WIN32) 306 | 307 | static inline int fsp_fuse_daemonize(int foreground) 308 | { 309 | (void)foreground; 310 | return 0; 311 | } 312 | 313 | static inline int fsp_fuse_set_signal_handlers(void *se) 314 | { 315 | (void)se; 316 | return 0; 317 | } 318 | 319 | #elif defined(__CYGWIN__) 320 | 321 | static inline int fsp_fuse_daemonize(int foreground) 322 | { 323 | int daemon(int nochdir, int noclose); 324 | int chdir(const char *path); 325 | 326 | if (!foreground) 327 | { 328 | if (-1 == daemon(0, 0)) 329 | return -1; 330 | } 331 | else 332 | chdir("/"); 333 | 334 | return 0; 335 | } 336 | 337 | static inline void *fsp_fuse_signal_thread(void *psigmask) 338 | { 339 | int sig; 340 | 341 | if (0 == sigwait((sigset_t *)psigmask, &sig)) 342 | FSP_FUSE_API_CALL(fsp_fuse_signal_handler)(sig); 343 | 344 | return 0; 345 | } 346 | 347 | static inline int fsp_fuse_set_signal_handlers(void *se) 348 | { 349 | #define FSP_FUSE_SET_SIGNAL_HANDLER(sig, newha)\ 350 | if (-1 != sigaction((sig), 0, &oldsa) &&\ 351 | oldsa.sa_handler == (se ? SIG_DFL : (newha)))\ 352 | {\ 353 | newsa.sa_handler = se ? (newha) : SIG_DFL;\ 354 | sigaction((sig), &newsa, 0);\ 355 | } 356 | #define FSP_FUSE_SIGADDSET(sig)\ 357 | if (-1 != sigaction((sig), 0, &oldsa) &&\ 358 | oldsa.sa_handler == SIG_DFL)\ 359 | sigaddset(&sigmask, (sig)); 360 | 361 | static sigset_t sigmask; 362 | static pthread_t sigthr; 363 | struct sigaction oldsa, newsa; 364 | 365 | // memset instead of initializer to avoid GCC -Wmissing-field-initializers warning 366 | memset(&newsa, 0, sizeof newsa); 367 | 368 | if (0 != se) 369 | { 370 | if (0 == sigthr) 371 | { 372 | FSP_FUSE_SET_SIGNAL_HANDLER(SIGPIPE, SIG_IGN); 373 | 374 | sigemptyset(&sigmask); 375 | FSP_FUSE_SIGADDSET(SIGHUP); 376 | FSP_FUSE_SIGADDSET(SIGINT); 377 | FSP_FUSE_SIGADDSET(SIGTERM); 378 | if (0 != pthread_sigmask(SIG_BLOCK, &sigmask, 0)) 379 | return -1; 380 | 381 | if (0 != pthread_create(&sigthr, 0, fsp_fuse_signal_thread, &sigmask)) 382 | return -1; 383 | } 384 | } 385 | else 386 | { 387 | if (0 != sigthr) 388 | { 389 | pthread_cancel(sigthr); 390 | pthread_join(sigthr, 0); 391 | sigthr = 0; 392 | 393 | if (0 != pthread_sigmask(SIG_UNBLOCK, &sigmask, 0)) 394 | return -1; 395 | sigemptyset(&sigmask); 396 | 397 | FSP_FUSE_SET_SIGNAL_HANDLER(SIGPIPE, SIG_IGN); 398 | } 399 | } 400 | 401 | return 0; 402 | 403 | #undef FSP_FUSE_SIGADDSET 404 | #undef FSP_FUSE_SET_SIGNAL_HANDLER 405 | } 406 | 407 | static inline char *fsp_fuse_conv_to_win_path(const char *path) 408 | { 409 | void *cygwin_create_path(unsigned, const void *); 410 | return (char *)cygwin_create_path( 411 | 0/*CCP_POSIX_TO_WIN_A*/ | 0x100/*CCP_RELATIVE*/, 412 | path); 413 | } 414 | 415 | static inline fuse_pid_t fsp_fuse_winpid_to_pid(uint32_t winpid) 416 | { 417 | pid_t cygwin_winpid_to_pid(int winpid); 418 | pid_t pid = cygwin_winpid_to_pid(winpid); 419 | return -1 != pid ? pid : (fuse_pid_t)winpid; 420 | } 421 | #endif 422 | 423 | 424 | static inline struct fsp_fuse_env *fsp_fuse_env(void) 425 | { 426 | static struct fsp_fuse_env env = FSP_FUSE_ENV_INIT; 427 | return &env; 428 | } 429 | 430 | #ifdef __cplusplus 431 | } 432 | #endif 433 | 434 | #endif 435 | -------------------------------------------------------------------------------- /winfsp-sys/winfsp/inc/fuse3/fuse.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fuse3/fuse.h 3 | * WinFsp FUSE3 compatible API. 4 | * 5 | * This file is derived from libfuse/include/fuse.h: 6 | * FUSE: Filesystem in Userspace 7 | * Copyright (C) 2001-2007 Miklos Szeredi 8 | * 9 | * @copyright 2015-2022 Bill Zissimopoulos 10 | */ 11 | /* 12 | * This file is part of WinFsp. 13 | * 14 | * You can redistribute it and/or modify it under the terms of the GNU 15 | * General Public License version 3 as published by the Free Software 16 | * Foundation. 17 | * 18 | * Licensees holding a valid commercial license may use this software 19 | * in accordance with the commercial license agreement provided in 20 | * conjunction with the software. The terms and conditions of any such 21 | * commercial license agreement shall govern, supersede, and render 22 | * ineffective any application of the GPLv3 license to this software, 23 | * notwithstanding of any reference thereto in the software or 24 | * associated repository. 25 | */ 26 | 27 | #ifndef FUSE_H_ 28 | #define FUSE_H_ 29 | 30 | #include "fuse_common.h" 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | struct fuse3; 37 | 38 | enum fuse3_readdir_flags 39 | { 40 | FUSE_READDIR_PLUS = (1 << 0), 41 | }; 42 | 43 | enum fuse3_fill_dir_flags 44 | { 45 | FUSE_FILL_DIR_PLUS = (1 << 1), 46 | }; 47 | 48 | typedef int (*fuse3_fill_dir_t)(void *buf, const char *name, 49 | const struct fuse_stat *stbuf, fuse_off_t off, 50 | enum fuse3_fill_dir_flags flags); 51 | 52 | struct fuse3_config 53 | { 54 | int set_gid; 55 | unsigned int gid; 56 | int set_uid; 57 | unsigned int uid; 58 | int set_mode; 59 | unsigned int umask; 60 | double entry_timeout; 61 | double negative_timeout; 62 | double attr_timeout; 63 | int intr; 64 | int intr_signal; 65 | int remember; 66 | int hard_remove; 67 | int use_ino; 68 | int readdir_ino; 69 | int direct_io; 70 | int kernel_cache; 71 | int auto_cache; 72 | int ac_attr_timeout_set; 73 | double ac_attr_timeout; 74 | int nullpath_ok; 75 | /* private */ 76 | int show_help; 77 | char *modules; 78 | int debug; 79 | }; 80 | 81 | struct fuse3_operations 82 | { 83 | /* S - supported by WinFsp */ 84 | /* S */ int (*getattr)(const char *path, struct fuse_stat *stbuf, 85 | struct fuse3_file_info *fi); 86 | /* S */ int (*readlink)(const char *path, char *buf, size_t size); 87 | /* S */ int (*mknod)(const char *path, fuse_mode_t mode, fuse_dev_t dev); 88 | /* S */ int (*mkdir)(const char *path, fuse_mode_t mode); 89 | /* S */ int (*unlink)(const char *path); 90 | /* S */ int (*rmdir)(const char *path); 91 | /* S */ int (*symlink)(const char *dstpath, const char *srcpath); 92 | /* S */ int (*rename)(const char *oldpath, const char *newpath, unsigned int flags); 93 | /* _ */ int (*link)(const char *srcpath, const char *dstpath); 94 | /* S */ int (*chmod)(const char *path, fuse_mode_t mode, 95 | struct fuse3_file_info *fi); 96 | /* S */ int (*chown)(const char *path, fuse_uid_t uid, fuse_gid_t gid, 97 | struct fuse3_file_info *fi); 98 | /* S */ int (*truncate)(const char *path, fuse_off_t size, 99 | struct fuse3_file_info *fi); 100 | /* S */ int (*open)(const char *path, struct fuse3_file_info *fi); 101 | /* S */ int (*read)(const char *path, char *buf, size_t size, fuse_off_t off, 102 | struct fuse3_file_info *fi); 103 | /* S */ int (*write)(const char *path, const char *buf, size_t size, fuse_off_t off, 104 | struct fuse3_file_info *fi); 105 | /* S */ int (*statfs)(const char *path, struct fuse_statvfs *stbuf); 106 | /* S */ int (*flush)(const char *path, struct fuse3_file_info *fi); 107 | /* S */ int (*release)(const char *path, struct fuse3_file_info *fi); 108 | /* S */ int (*fsync)(const char *path, int datasync, struct fuse3_file_info *fi); 109 | /* S */ int (*setxattr)(const char *path, const char *name, const char *value, size_t size, 110 | int flags); 111 | /* S */ int (*getxattr)(const char *path, const char *name, char *value, size_t size); 112 | /* S */ int (*listxattr)(const char *path, char *namebuf, size_t size); 113 | /* S */ int (*removexattr)(const char *path, const char *name); 114 | /* S */ int (*opendir)(const char *path, struct fuse3_file_info *fi); 115 | /* S */ int (*readdir)(const char *path, void *buf, fuse3_fill_dir_t filler, fuse_off_t off, 116 | struct fuse3_file_info *fi, enum fuse3_readdir_flags); 117 | /* S */ int (*releasedir)(const char *path, struct fuse3_file_info *fi); 118 | /* S */ int (*fsyncdir)(const char *path, int datasync, struct fuse3_file_info *fi); 119 | /* S */ void *(*init)(struct fuse3_conn_info *conn, 120 | struct fuse3_config *conf); 121 | /* S */ void (*destroy)(void *data); 122 | /* _ */ int (*access)(const char *path, int mask); 123 | /* S */ int (*create)(const char *path, fuse_mode_t mode, struct fuse3_file_info *fi); 124 | /* _ */ int (*lock)(const char *path, 125 | struct fuse3_file_info *fi, int cmd, struct fuse_flock *lock); 126 | /* S */ int (*utimens)(const char *path, const struct fuse_timespec tv[2], 127 | struct fuse3_file_info *fi); 128 | /* _ */ int (*bmap)(const char *path, size_t blocksize, uint64_t *idx); 129 | /* S */ int (*ioctl)(const char *path, int cmd, void *arg, struct fuse3_file_info *fi, 130 | unsigned int flags, void *data); 131 | /* _ */ int (*poll)(const char *path, struct fuse3_file_info *fi, 132 | struct fuse3_pollhandle *ph, unsigned *reventsp); 133 | /* _ */ int (*write_buf)(const char *path, 134 | struct fuse3_bufvec *buf, fuse_off_t off, struct fuse3_file_info *fi); 135 | /* _ */ int (*read_buf)(const char *path, 136 | struct fuse3_bufvec **bufp, size_t size, fuse_off_t off, struct fuse3_file_info *fi); 137 | /* _ */ int (*flock)(const char *path, struct fuse3_file_info *, int op); 138 | /* _ */ int (*fallocate)(const char *path, int mode, fuse_off_t off, fuse_off_t len, 139 | struct fuse3_file_info *fi); 140 | }; 141 | 142 | struct fuse3_context 143 | { 144 | struct fuse3 *fuse; 145 | fuse_uid_t uid; 146 | fuse_gid_t gid; 147 | fuse_pid_t pid; 148 | void *private_data; 149 | fuse_mode_t umask; 150 | }; 151 | 152 | #define fuse_main(argc, argv, ops, data)\ 153 | fuse3_main_real(argc, argv, ops, sizeof *(ops), data) 154 | 155 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse3_main_real)(struct fsp_fuse_env *env, 156 | int argc, char *argv[], 157 | const struct fuse3_operations *ops, size_t opsize, void *data); 158 | FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse3_lib_help)(struct fsp_fuse_env *env, 159 | struct fuse_args *args); 160 | FSP_FUSE_API struct fuse3 *FSP_FUSE_API_NAME(fsp_fuse3_new_30)(struct fsp_fuse_env *env, 161 | struct fuse_args *args, 162 | const struct fuse3_operations *ops, size_t opsize, void *data); 163 | FSP_FUSE_API struct fuse3 *FSP_FUSE_API_NAME(fsp_fuse3_new)(struct fsp_fuse_env *env, 164 | struct fuse_args *args, 165 | const struct fuse3_operations *ops, size_t opsize, void *data); 166 | FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse3_destroy)(struct fsp_fuse_env *env, 167 | struct fuse3 *f); 168 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse3_mount)(struct fsp_fuse_env *env, 169 | struct fuse3 *f, const char *mountpoint); 170 | FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse3_unmount)(struct fsp_fuse_env *env, 171 | struct fuse3 *f); 172 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse3_loop)(struct fsp_fuse_env *env, 173 | struct fuse3 *f); 174 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse3_loop_mt_31)(struct fsp_fuse_env *env, 175 | struct fuse3 *f, int clone_fd); 176 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse3_loop_mt)(struct fsp_fuse_env *env, 177 | struct fuse3 *f, struct fuse3_loop_config *config); 178 | FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse3_exit)(struct fsp_fuse_env *env, 179 | struct fuse3 *f); 180 | FSP_FUSE_API struct fuse3_context *FSP_FUSE_API_NAME(fsp_fuse3_get_context)(struct fsp_fuse_env *env); 181 | 182 | FSP_FUSE_SYM( 183 | int fuse3_main_real(int argc, char *argv[], 184 | const struct fuse3_operations *ops, size_t opsize, void *data), 185 | { 186 | return FSP_FUSE_API_CALL(fsp_fuse3_main_real) 187 | (fsp_fuse_env(), argc, argv, ops, opsize, data); 188 | }) 189 | 190 | FSP_FUSE_SYM( 191 | void fuse3_lib_help(struct fuse_args *args), 192 | { 193 | FSP_FUSE_API_CALL(fsp_fuse3_lib_help) 194 | (fsp_fuse_env(), args); 195 | }) 196 | 197 | #if FUSE_USE_VERSION == 30 198 | FSP_FUSE_SYM( 199 | struct fuse3 *fuse3_new_30(struct fuse_args *args, 200 | const struct fuse3_operations *ops, size_t opsize, void *data), 201 | { 202 | return FSP_FUSE_API_CALL(fsp_fuse3_new_30) 203 | (fsp_fuse_env(), args, ops, opsize, data); 204 | }) 205 | #define fuse_new(args, op, size, data)\ 206 | fuse3_new_30(args, op, size, data) 207 | 208 | #else 209 | FSP_FUSE_SYM( 210 | struct fuse3 *fuse3_new(struct fuse_args *args, 211 | const struct fuse3_operations *ops, size_t opsize, void *data), 212 | { 213 | return FSP_FUSE_API_CALL(fsp_fuse3_new) 214 | (fsp_fuse_env(), args, ops, opsize, data); 215 | }) 216 | #endif 217 | 218 | FSP_FUSE_SYM( 219 | void fuse3_destroy(struct fuse3 *f), 220 | { 221 | FSP_FUSE_API_CALL(fsp_fuse3_destroy) 222 | (fsp_fuse_env(), f); 223 | }) 224 | 225 | FSP_FUSE_SYM( 226 | int fuse3_mount(struct fuse3 *f, const char *mountpoint), 227 | { 228 | return FSP_FUSE_API_CALL(fsp_fuse3_mount) 229 | (fsp_fuse_env(), f, mountpoint); 230 | }) 231 | 232 | FSP_FUSE_SYM( 233 | void fuse3_unmount(struct fuse3 *f), 234 | { 235 | FSP_FUSE_API_CALL(fsp_fuse3_unmount) 236 | (fsp_fuse_env(), f); 237 | }) 238 | 239 | FSP_FUSE_SYM( 240 | int fuse3_loop(struct fuse3 *f), 241 | { 242 | return FSP_FUSE_API_CALL(fsp_fuse3_loop) 243 | (fsp_fuse_env(), f); 244 | }) 245 | 246 | #if FUSE_USE_VERSION < 32 247 | FSP_FUSE_SYM( 248 | int fuse3_loop_mt_31(struct fuse3 *f, int clone_fd), 249 | { 250 | return FSP_FUSE_API_CALL(fsp_fuse3_loop_mt_31) 251 | (fsp_fuse_env(), f, clone_fd); 252 | }) 253 | #define fuse_loop_mt(f, clone_fd)\ 254 | fuse3_loop_mt_31(f, clone_fd) 255 | 256 | #else 257 | FSP_FUSE_SYM( 258 | int fuse3_loop_mt(struct fuse3 *f, struct fuse3_loop_config *config), 259 | { 260 | return FSP_FUSE_API_CALL(fsp_fuse3_loop_mt) 261 | (fsp_fuse_env(), f, config); 262 | }) 263 | #endif 264 | 265 | FSP_FUSE_SYM( 266 | void fuse3_exit(struct fuse3 *f), 267 | { 268 | FSP_FUSE_API_CALL(fsp_fuse3_exit) 269 | (fsp_fuse_env(), f); 270 | }) 271 | 272 | FSP_FUSE_SYM( 273 | struct fuse3_context *fuse3_get_context(void), 274 | { 275 | return FSP_FUSE_API_CALL(fsp_fuse3_get_context) 276 | (fsp_fuse_env()); 277 | }) 278 | 279 | FSP_FUSE_SYM( 280 | int fuse3_getgroups(int size, fuse_gid_t list[]), 281 | { 282 | (void)size; 283 | (void)list; 284 | return -ENOSYS; 285 | }) 286 | 287 | FSP_FUSE_SYM( 288 | int fuse3_interrupted(void), 289 | { 290 | return 0; 291 | }) 292 | 293 | FSP_FUSE_SYM( 294 | int fuse3_invalidate_path(struct fuse3 *f, const char *path), 295 | { 296 | (void)f; 297 | (void)path; 298 | return -ENOENT; 299 | }) 300 | 301 | FSP_FUSE_SYM( 302 | int fuse3_notify_poll(struct fuse3_pollhandle *ph), 303 | { 304 | (void)ph; 305 | return 0; 306 | }) 307 | 308 | FSP_FUSE_SYM( 309 | int fuse3_start_cleanup_thread(struct fuse3 *f), 310 | { 311 | (void)f; 312 | return 0; 313 | }) 314 | 315 | FSP_FUSE_SYM( 316 | void fuse3_stop_cleanup_thread(struct fuse3 *f), 317 | { 318 | (void)f; 319 | }) 320 | 321 | FSP_FUSE_SYM( 322 | int fuse3_clean_cache(struct fuse3 *f), 323 | { 324 | (void)f; 325 | return 600; 326 | }) 327 | 328 | FSP_FUSE_SYM( 329 | struct fuse3_session *fuse3_get_session(struct fuse3 *f), 330 | { 331 | return (struct fuse3_session *)f; 332 | }) 333 | 334 | #ifdef __cplusplus 335 | } 336 | #endif 337 | 338 | #endif 339 | -------------------------------------------------------------------------------- /winfsp-sys/winfsp/inc/fuse3/fuse_common.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fuse3/fuse_common.h 3 | * WinFsp FUSE3 compatible API. 4 | * 5 | * This file is derived from libfuse/include/fuse_common.h: 6 | * FUSE: Filesystem in Userspace 7 | * Copyright (C) 2001-2007 Miklos Szeredi 8 | * 9 | * @copyright 2015-2022 Bill Zissimopoulos 10 | */ 11 | /* 12 | * This file is part of WinFsp. 13 | * 14 | * You can redistribute it and/or modify it under the terms of the GNU 15 | * General Public License version 3 as published by the Free Software 16 | * Foundation. 17 | * 18 | * Licensees holding a valid commercial license may use this software 19 | * in accordance with the commercial license agreement provided in 20 | * conjunction with the software. The terms and conditions of any such 21 | * commercial license agreement shall govern, supersede, and render 22 | * ineffective any application of the GPLv3 license to this software, 23 | * notwithstanding of any reference thereto in the software or 24 | * associated repository. 25 | */ 26 | 27 | #ifndef FUSE_COMMON_H_ 28 | #define FUSE_COMMON_H_ 29 | 30 | #include "winfsp_fuse.h" 31 | #if !defined(WINFSP_DLL_INTERNAL) 32 | #include "fuse_opt.h" 33 | #endif 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | #define FUSE_MAJOR_VERSION 3 40 | #define FUSE_MINOR_VERSION 2 41 | #define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) 42 | #define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) 43 | 44 | #define FUSE_CAP_ASYNC_READ (1 << 0) 45 | #define FUSE_CAP_POSIX_LOCKS (1 << 1) 46 | #define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3) 47 | #define FUSE_CAP_EXPORT_SUPPORT (1 << 4) 48 | #define FUSE_CAP_DONT_MASK (1 << 6) 49 | #define FUSE_CAP_SPLICE_WRITE (1 << 7) 50 | #define FUSE_CAP_SPLICE_MOVE (1 << 8) 51 | #define FUSE_CAP_SPLICE_READ (1 << 9) 52 | #define FUSE_CAP_FLOCK_LOCKS (1 << 10) 53 | #define FUSE_CAP_IOCTL_DIR (1 << 11) 54 | #define FUSE_CAP_AUTO_INVAL_DATA (1 << 12) 55 | #define FUSE_CAP_READDIRPLUS (1 << 13) 56 | #define FUSE_CAP_READDIRPLUS_AUTO (1 << 14) 57 | #define FUSE_CAP_ASYNC_DIO (1 << 15) 58 | #define FUSE_CAP_WRITEBACK_CACHE (1 << 16) 59 | #define FUSE_CAP_NO_OPEN_SUPPORT (1 << 17) 60 | #define FUSE_CAP_PARALLEL_DIROPS (1 << 18) 61 | #define FUSE_CAP_POSIX_ACL (1 << 19) 62 | #define FUSE_CAP_HANDLE_KILLPRIV (1 << 20) 63 | #define FUSE_CAP_ALLOCATE (1 << 27) /* reserved (OSXFUSE) */ 64 | #define FUSE_CAP_EXCHANGE_DATA (1 << 28) /* reserved (OSXFUSE) */ 65 | #define FUSE_CAP_CASE_INSENSITIVE (1 << 29) /* file system is case insensitive */ 66 | #define FUSE_CAP_VOL_RENAME (1 << 30) /* reserved (OSXFUSE) */ 67 | #define FUSE_CAP_XTIMES (1 << 31) /* reserved (OSXFUSE) */ 68 | 69 | #define FSP_FUSE_CAP_CASE_INSENSITIVE FUSE_CAP_CASE_INSENSITIVE 70 | 71 | #define FUSE_IOCTL_COMPAT (1 << 0) 72 | #define FUSE_IOCTL_UNRESTRICTED (1 << 1) 73 | #define FUSE_IOCTL_RETRY (1 << 2) 74 | #define FUSE_IOCTL_DIR (1 << 4) 75 | #define FUSE_IOCTL_MAX_IOV 256 76 | 77 | #define FUSE_BUFVEC_INIT(s) \ 78 | ((struct fuse3_bufvec){ 1, 0, 0, { {s, (enum fuse3_buf_flags)0, 0, -1, 0} } }) 79 | 80 | struct fuse3_file_info 81 | { 82 | int flags; 83 | unsigned int writepage:1; 84 | unsigned int direct_io:1; 85 | unsigned int keep_cache:1; 86 | unsigned int flush:1; 87 | unsigned int nonseekable:1; 88 | unsigned int flock_release:1; 89 | unsigned int padding:27; 90 | uint64_t fh; 91 | uint64_t lock_owner; 92 | uint32_t poll_events; 93 | }; 94 | 95 | struct fuse3_loop_config 96 | { 97 | int clone_fd; 98 | unsigned int max_idle_threads; 99 | }; 100 | 101 | struct fuse3_conn_info 102 | { 103 | unsigned proto_major; 104 | unsigned proto_minor; 105 | unsigned max_write; 106 | unsigned max_read; 107 | unsigned max_readahead; 108 | unsigned capable; 109 | unsigned want; 110 | unsigned max_background; 111 | unsigned congestion_threshold; 112 | unsigned time_gran; 113 | unsigned reserved[22]; 114 | }; 115 | 116 | enum fuse3_buf_flags 117 | { 118 | FUSE_BUF_IS_FD = (1 << 1), 119 | FUSE_BUF_FD_SEEK = (1 << 2), 120 | FUSE_BUF_FD_RETRY = (1 << 3), 121 | }; 122 | 123 | enum fuse3_buf_copy_flags 124 | { 125 | FUSE_BUF_NO_SPLICE = (1 << 1), 126 | FUSE_BUF_FORCE_SPLICE = (1 << 2), 127 | FUSE_BUF_SPLICE_MOVE = (1 << 3), 128 | FUSE_BUF_SPLICE_NONBLOCK = (1 << 4), 129 | }; 130 | 131 | struct fuse3_buf 132 | { 133 | size_t size; 134 | enum fuse3_buf_flags flags; 135 | void *mem; 136 | int fd; 137 | fuse_off_t pos; 138 | }; 139 | 140 | struct fuse3_bufvec 141 | { 142 | size_t count; 143 | size_t idx; 144 | size_t off; 145 | struct fuse3_buf buf[1]; 146 | }; 147 | 148 | struct fuse3_session; 149 | struct fuse3_pollhandle; 150 | struct fuse3_conn_info_opts; 151 | 152 | FSP_FUSE_API struct fuse3_conn_info_opts *FSP_FUSE_API_NAME(fsp_fuse3_parse_conn_info_opts)( 153 | struct fsp_fuse_env *env, 154 | struct fuse_args *args); 155 | FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse3_apply_conn_info_opts)(struct fsp_fuse_env *env, 156 | struct fuse3_conn_info_opts *opts, struct fuse3_conn_info *conn); 157 | FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse3_version)(struct fsp_fuse_env *env); 158 | FSP_FUSE_API const char *FSP_FUSE_API_NAME(fsp_fuse3_pkgversion)(struct fsp_fuse_env *env); 159 | FSP_FUSE_API int32_t FSP_FUSE_API_NAME(fsp_fuse_ntstatus_from_errno)(struct fsp_fuse_env *env, 160 | int err); 161 | 162 | FSP_FUSE_SYM( 163 | struct fuse3_conn_info_opts* fuse3_parse_conn_info_opts( 164 | struct fuse_args *args), 165 | { 166 | return FSP_FUSE_API_CALL(fsp_fuse3_parse_conn_info_opts) 167 | (fsp_fuse_env(), args); 168 | }) 169 | 170 | FSP_FUSE_SYM( 171 | void fuse3_apply_conn_info_opts( 172 | struct fuse3_conn_info_opts *opts, struct fuse3_conn_info *conn), 173 | { 174 | FSP_FUSE_API_CALL(fsp_fuse3_apply_conn_info_opts) 175 | (fsp_fuse_env(), opts, conn); 176 | }) 177 | 178 | FSP_FUSE_SYM( 179 | int fuse3_version(void), 180 | { 181 | return FSP_FUSE_API_CALL(fsp_fuse3_version) 182 | (fsp_fuse_env()); 183 | }) 184 | 185 | FSP_FUSE_SYM( 186 | const char *fuse3_pkgversion(void), 187 | { 188 | return FSP_FUSE_API_CALL(fsp_fuse3_pkgversion) 189 | (fsp_fuse_env()); 190 | }) 191 | 192 | FSP_FUSE_SYM( 193 | void fuse3_pollhandle_destroy(struct fuse3_pollhandle *ph), 194 | { 195 | (void)ph; 196 | }) 197 | 198 | FSP_FUSE_SYM( 199 | size_t fuse3_buf_size(const struct fuse3_bufvec *bufv), 200 | { 201 | (void)bufv; 202 | return 0; 203 | }) 204 | 205 | FSP_FUSE_SYM( 206 | ssize_t fuse3_buf_copy(struct fuse3_bufvec *dst, struct fuse3_bufvec *src, 207 | enum fuse3_buf_copy_flags flags), 208 | { 209 | (void)dst; 210 | (void)src; 211 | (void)flags; 212 | return 0; 213 | }) 214 | 215 | FSP_FUSE_SYM( 216 | int fuse3_daemonize(int foreground), 217 | { 218 | return fsp_fuse_daemonize(foreground); 219 | }) 220 | 221 | FSP_FUSE_SYM( 222 | int fuse3_set_signal_handlers(struct fuse3_session *se), 223 | { 224 | return fsp_fuse_set_signal_handlers(se); 225 | }) 226 | 227 | FSP_FUSE_SYM( 228 | void fuse3_remove_signal_handlers(struct fuse3_session *se), 229 | { 230 | (void)se; 231 | fsp_fuse_set_signal_handlers(0); 232 | }) 233 | 234 | #ifdef __cplusplus 235 | } 236 | #endif 237 | 238 | #endif 239 | -------------------------------------------------------------------------------- /winfsp-sys/winfsp/inc/fuse3/fuse_opt.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fuse3/fuse_opt.h 3 | * WinFsp FUSE3 compatible API. 4 | * 5 | * @copyright 2015-2022 Bill Zissimopoulos 6 | */ 7 | /* 8 | * This file is part of WinFsp. 9 | * 10 | * You can redistribute it and/or modify it under the terms of the GNU 11 | * General Public License version 3 as published by the Free Software 12 | * Foundation. 13 | * 14 | * Licensees holding a valid commercial license may use this software 15 | * in accordance with the commercial license agreement provided in 16 | * conjunction with the software. The terms and conditions of any such 17 | * commercial license agreement shall govern, supersede, and render 18 | * ineffective any application of the GPLv3 license to this software, 19 | * notwithstanding of any reference thereto in the software or 20 | * associated repository. 21 | */ 22 | 23 | #include "../fuse/fuse_opt.h" 24 | -------------------------------------------------------------------------------- /winfsp-sys/winfsp/inc/fuse3/winfsp_fuse.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fuse3/winfsp_fuse.h 3 | * WinFsp FUSE3 compatible API. 4 | * 5 | * @copyright 2015-2022 Bill Zissimopoulos 6 | */ 7 | /* 8 | * This file is part of WinFsp. 9 | * 10 | * You can redistribute it and/or modify it under the terms of the GNU 11 | * General Public License version 3 as published by the Free Software 12 | * Foundation. 13 | * 14 | * Licensees holding a valid commercial license may use this software 15 | * in accordance with the commercial license agreement provided in 16 | * conjunction with the software. The terms and conditions of any such 17 | * commercial license agreement shall govern, supersede, and render 18 | * ineffective any application of the GPLv3 license to this software, 19 | * notwithstanding of any reference thereto in the software or 20 | * associated repository. 21 | */ 22 | 23 | #ifndef FUSE3_WINFSP_FUSE_H_INCLUDED 24 | #define FUSE3_WINFSP_FUSE_H_INCLUDED 25 | 26 | #include "../fuse/winfsp_fuse.h" 27 | 28 | #if defined(_WIN64) || defined(_WIN32) 29 | typedef intptr_t ssize_t; 30 | #endif 31 | 32 | #if !defined(WINFSP_DLL_INTERNAL) 33 | #define fuse3 fuse 34 | #define fuse3_apply_conn_info_opts fuse_apply_conn_info_opts 35 | #define fuse3_buf fuse_buf 36 | #define fuse3_buf_copy fuse_buf_copy 37 | #define fuse3_buf_copy_flags fuse_buf_copy_flags 38 | #define fuse3_buf_flags fuse_buf_flags 39 | #define fuse3_buf_size fuse_buf_size 40 | #define fuse3_bufvec fuse_bufvec 41 | #define fuse3_clean_cache fuse_clean_cache 42 | #define fuse3_config fuse_config 43 | #define fuse3_conn_info fuse_conn_info 44 | #define fuse3_conn_info_opts fuse_conn_info_opts 45 | #define fuse3_context fuse_context 46 | #define fuse3_daemonize fuse_daemonize 47 | #define fuse3_destroy fuse_destroy 48 | #define fuse3_exit fuse_exit 49 | #define fuse3_file_info fuse_file_info 50 | #define fuse3_fill_dir_flags fuse_fill_dir_flags 51 | #define fuse3_fill_dir_t fuse_fill_dir_t 52 | #define fuse3_get_context fuse_get_context 53 | #define fuse3_get_session fuse_get_session 54 | #define fuse3_getgroups fuse_getgroups 55 | #define fuse3_interrupted fuse_interrupted 56 | #define fuse3_invalidate_path fuse_invalidate_path 57 | #define fuse3_lib_help fuse_lib_help 58 | #define fuse3_loop fuse_loop 59 | #define fuse3_loop_config fuse_loop_config 60 | #define fuse3_loop_mt fuse_loop_mt 61 | #define fuse3_loop_mt_31 fuse_loop_mt_31 62 | #define fuse3_main_real fuse_main_real 63 | #define fuse3_mount fuse_mount 64 | #define fuse3_new fuse_new 65 | #define fuse3_new_30 fuse_new_30 66 | #define fuse3_notify_poll fuse_notify_poll 67 | #define fuse3_operations fuse_operations 68 | #define fuse3_parse_conn_info_opts fuse_parse_conn_info_opts 69 | #define fuse3_pkgversion fuse_pkgversion 70 | #define fuse3_pollhandle fuse_pollhandle 71 | #define fuse3_pollhandle_destroy fuse_pollhandle_destroy 72 | #define fuse3_readdir_flags fuse_readdir_flags 73 | #define fuse3_remove_signal_handlers fuse_remove_signal_handlers 74 | #define fuse3_session fuse_session 75 | #define fuse3_set_signal_handlers fuse_set_signal_handlers 76 | #define fuse3_start_cleanup_thread fuse_start_cleanup_thread 77 | #define fuse3_stop_cleanup_thread fuse_stop_cleanup_thread 78 | #define fuse3_unmount fuse_unmount 79 | #define fuse3_version fuse_version 80 | #endif 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /winfsp-sys/winfsp/inc/winfsp/launch.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file winfsp/launch.h 3 | * WinFsp Launch API. 4 | * 5 | * In order to use the WinFsp Launch API a program must include <winfsp/launch.h> 6 | * and link with the winfsp_x64.dll (or winfsp_x86.dll) library. 7 | * 8 | * @copyright 2015-2022 Bill Zissimopoulos 9 | */ 10 | /* 11 | * This file is part of WinFsp. 12 | * 13 | * You can redistribute it and/or modify it under the terms of the GNU 14 | * General Public License version 3 as published by the Free Software 15 | * Foundation. 16 | * 17 | * Licensees holding a valid commercial license may use this software 18 | * in accordance with the commercial license agreement provided in 19 | * conjunction with the software. The terms and conditions of any such 20 | * commercial license agreement shall govern, supersede, and render 21 | * ineffective any application of the GPLv3 license to this software, 22 | * notwithstanding of any reference thereto in the software or 23 | * associated repository. 24 | */ 25 | 26 | #ifndef WINFSP_LAUNCH_H_INCLUDED 27 | #define WINFSP_LAUNCH_H_INCLUDED 28 | 29 | #include 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | #define FSP_LAUNCH_REGKEY FSP_FSCTL_PRODUCT_REGKEY "\\Services" 36 | #define FSP_LAUNCH_REGKEY_WOW64 FSP_FSCTL_PRODUCT_REGKEY_WOW64 37 | #define FSP_LAUNCH_FULL_REGKEY FSP_FSCTL_PRODUCT_FULL_REGKEY "\\Services" 38 | 39 | #define FSP_LAUNCH_PIPE_NAME "\\\\.\\pipe\\" FSP_FSCTL_PRODUCT_NAME ".{14E7137D-22B4-437A-B0C1-D21D1BDF3767}" 40 | #define FSP_LAUNCH_PIPE_BUFFER_SIZE 4096 41 | #define FSP_LAUNCH_PIPE_OWNER ((PSID)WinLocalSystemSid) 42 | 43 | /* 44 | * The launcher named pipe SDDL gives full access to LocalSystem and Administrators and 45 | * GENERIC_READ and FILE_WRITE_DATA access to Everyone. We are careful not to give the 46 | * FILE_CREATE_PIPE_INSTANCE right to Everyone to disallow the creation of additional 47 | * pipe instances. 48 | */ 49 | #define FSP_LAUNCH_PIPE_SDDL "O:SYG:SYD:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRDCCR;;;WD)" 50 | 51 | /* 52 | * The default service instance SDDL gives full access to LocalSystem and Administrators. 53 | * The only possible service instance rights are as follows: 54 | * RP SERVICE_START 55 | * WP SERVICE_STOP 56 | * LC SERVICE_QUERY_STATUS 57 | * 58 | * To create a service that can be started, stopped or queried by Everyone, you can set 59 | * the following SDDL: 60 | * D:P(A;;RPWPLC;;;WD) 61 | */ 62 | #define FSP_LAUNCH_SERVICE_DEFAULT_SDDL "D:P(A;;RPWPLC;;;SY)(A;;RPWPLC;;;BA)" 63 | #define FSP_LAUNCH_SERVICE_WORLD_SDDL "D:P(A;;RPWPLC;;;WD)" 64 | 65 | enum 66 | { 67 | FspLaunchCmdStart = 'S', /* requires: SERVICE_START */ 68 | FspLaunchCmdStartWithSecret = 'X', /* requires: SERVICE_START */ 69 | FspLaunchCmdStop = 'T', /* requires: SERVICE_STOP */ 70 | FspLaunchCmdGetInfo = 'I', /* requires: SERVICE_QUERY_STATUS */ 71 | FspLaunchCmdGetNameList = 'L', /* requires: none*/ 72 | FspLaunchCmdDefineDosDevice = 'D', /* internal: do not use! */ 73 | FspLaunchCmdQuit = 'Q', /* DEBUG version only */ 74 | }; 75 | 76 | enum 77 | { 78 | FspLaunchCmdSuccess = '$', 79 | FspLaunchCmdFailure = '!', 80 | }; 81 | 82 | /** 83 | * @group Launch Control 84 | */ 85 | /** 86 | * Call launcher pipe. 87 | * 88 | * This function is used to send a command to the launcher and receive a response. 89 | * 90 | * @param Command 91 | * Launcher command to send. For example, the 'L' launcher command instructs 92 | * the launcher to list all running service instances. 93 | * @param Argc 94 | * Command argument count. May be 0. 95 | * @param Argv 96 | * Command argument array. May be NULL. 97 | * @param Argl 98 | * Command argument length array. May be NULL. If this is NULL all command arguments 99 | * are assumed to be NULL-terminated strings. It is also possible for specific arguments 100 | * to be NULL-terminated; in this case pass -1 in the corresponding Argl position. 101 | * @param Buffer 102 | * Buffer that receives the command response. May be NULL. 103 | * @param PSize 104 | * Pointer to a ULONG. On input it contains the size of the Buffer. On output it 105 | * contains the number of bytes transferred. May be NULL. 106 | * @param PLauncherError 107 | * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. 108 | * @return 109 | * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher 110 | * returns an error. Other status codes indicate a communication error. Launcher errors are 111 | * reported through PLauncherError. 112 | */ 113 | FSP_API NTSTATUS FspLaunchCallLauncherPipe( 114 | WCHAR Command, ULONG Argc, PWSTR *Argv, ULONG *Argl, 115 | PWSTR Buffer, PULONG PSize, 116 | PULONG PLauncherError); 117 | /** 118 | * Call launcher pipe. 119 | * 120 | * This function is used to send a command to the launcher and receive a response. 121 | * 122 | * @param Command 123 | * Launcher command to send. For example, the 'L' launcher command instructs 124 | * the launcher to list all running service instances. 125 | * @param Argc 126 | * Command argument count. May be 0. 127 | * @param Argv 128 | * Command argument array. May be NULL. 129 | * @param Argl 130 | * Command argument length array. May be NULL. If this is NULL all command arguments 131 | * are assumed to be NULL-terminated strings. It is also possible for specific arguments 132 | * to be NULL-terminated; in this case pass -1 in the corresponding Argl position. 133 | * @param Buffer 134 | * Buffer that receives the command response. May be NULL. 135 | * @param PSize 136 | * Pointer to a ULONG. On input it contains the size of the Buffer. On output it 137 | * contains the number of bytes transferred. May be NULL. 138 | * @param AllowImpersonation 139 | * Allow caller to be impersonated by launcher. 140 | * @param PLauncherError 141 | * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. 142 | * @return 143 | * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher 144 | * returns an error. Other status codes indicate a communication error. Launcher errors are 145 | * reported through PLauncherError. 146 | */ 147 | FSP_API NTSTATUS FspLaunchCallLauncherPipeEx( 148 | WCHAR Command, ULONG Argc, PWSTR *Argv, ULONG *Argl, 149 | PWSTR Buffer, PULONG PSize, 150 | BOOLEAN AllowImpersonation, 151 | PULONG PLauncherError); 152 | /** 153 | * Start a service instance. 154 | * 155 | * @param ClassName 156 | * Class name of the service instance to start. 157 | * @param InstanceName 158 | * Instance name of the service instance to start. 159 | * @param Argc 160 | * Service instance argument count. May be 0. 161 | * @param Argv 162 | * Service instance argument array. May be NULL. 163 | * @param HasSecret 164 | * Whether the last argument in Argv is assumed to be a secret (e.g. password) or not. 165 | * Secrets are passed to service instances through standard input rather than the command 166 | * line. 167 | * @param PLauncherError 168 | * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. 169 | * @return 170 | * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher 171 | * returns an error. Other status codes indicate a communication error. Launcher errors are 172 | * reported through PLauncherError. 173 | */ 174 | FSP_API NTSTATUS FspLaunchStart( 175 | PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv, 176 | BOOLEAN HasSecret, 177 | PULONG PLauncherError); 178 | /** 179 | * Start a service instance. 180 | * 181 | * @param ClassName 182 | * Class name of the service instance to start. 183 | * @param InstanceName 184 | * Instance name of the service instance to start. 185 | * @param Argc 186 | * Service instance argument count. May be 0. 187 | * @param Argv 188 | * Service instance argument array. May be NULL. 189 | * @param HasSecret 190 | * Whether the last argument in Argv is assumed to be a secret (e.g. password) or not. 191 | * Secrets are passed to service instances through standard input rather than the command 192 | * line. 193 | * @param AllowImpersonation 194 | * Allow caller to be impersonated by launcher. 195 | * @param PLauncherError 196 | * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. 197 | * @return 198 | * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher 199 | * returns an error. Other status codes indicate a communication error. Launcher errors are 200 | * reported through PLauncherError. 201 | */ 202 | FSP_API NTSTATUS FspLaunchStartEx( 203 | PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv, 204 | BOOLEAN HasSecret, 205 | BOOLEAN AllowImpersonation, 206 | PULONG PLauncherError); 207 | /** 208 | * Stop a service instance. 209 | * 210 | * @param ClassName 211 | * Class name of the service instance to stop. 212 | * @param InstanceName 213 | * Instance name of the service instance to stop. 214 | * @param PLauncherError 215 | * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. 216 | * @return 217 | * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher 218 | * returns an error. Other status codes indicate a communication error. Launcher errors are 219 | * reported through PLauncherError. 220 | */ 221 | FSP_API NTSTATUS FspLaunchStop( 222 | PWSTR ClassName, PWSTR InstanceName, 223 | PULONG PLauncherError); 224 | /** 225 | * Get information about a service instance. 226 | * 227 | * The information is a list of NULL-terminated strings: the class name of the service instance, 228 | * the instance name of the service instance and the full command line used to start the service 229 | * instance. 230 | * 231 | * @param ClassName 232 | * Class name of the service instance to stop. 233 | * @param InstanceName 234 | * Instance name of the service instance to stop. 235 | * @param Buffer 236 | * Buffer that receives the command response. May be NULL. 237 | * @param PSize 238 | * Pointer to a ULONG. On input it contains the size of the Buffer. On output it 239 | * contains the number of bytes transferred. May be NULL. 240 | * @param PLauncherError 241 | * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. 242 | * @return 243 | * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher 244 | * returns an error. Other status codes indicate a communication error. Launcher errors are 245 | * reported through PLauncherError. 246 | */ 247 | FSP_API NTSTATUS FspLaunchGetInfo( 248 | PWSTR ClassName, PWSTR InstanceName, 249 | PWSTR Buffer, PULONG PSize, 250 | PULONG PLauncherError); 251 | /** 252 | * List service instances. 253 | * 254 | * The information is a list of pairs of NULL-terminated strings. Each pair contains the class 255 | * name and instance name of a service instance. All currently running service instances are 256 | * listed. 257 | * 258 | * @param Buffer 259 | * Buffer that receives the command response. May be NULL. 260 | * @param PSize 261 | * Pointer to a ULONG. On input it contains the size of the Buffer. On output it 262 | * contains the number of bytes transferred. May be NULL. 263 | * @param PLauncherError 264 | * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. 265 | * @return 266 | * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher 267 | * returns an error. Other status codes indicate a communication error. Launcher errors are 268 | * reported through PLauncherError. 269 | */ 270 | FSP_API NTSTATUS FspLaunchGetNameList( 271 | PWSTR Buffer, PULONG PSize, 272 | PULONG PLauncherError); 273 | 274 | /** 275 | * @group Service Registry 276 | */ 277 | #pragma warning(push) 278 | #pragma warning(disable:4200) /* zero-sized array in struct/union */ 279 | /** 280 | * Service registry record. 281 | */ 282 | typedef struct _FSP_LAUNCH_REG_RECORD 283 | { 284 | PWSTR Agent; 285 | PWSTR Executable; 286 | PWSTR CommandLine; 287 | PWSTR WorkDirectory; 288 | PWSTR RunAs; 289 | PWSTR Security; 290 | PWSTR AuthPackage; 291 | PWSTR Stderr; 292 | PVOID Reserved0[4]; 293 | ULONG JobControl; 294 | ULONG Credentials; 295 | ULONG AuthPackageId; 296 | ULONG Recovery; 297 | ULONG Reserved1[4]; 298 | UINT8 Buffer[]; 299 | } FSP_LAUNCH_REG_RECORD; 300 | #pragma warning(pop) 301 | /** 302 | * Add/change/delete a service registry record. 303 | * 304 | * @param ClassName 305 | * The service class name. 306 | * @param Record 307 | * The record to set in the registry. If NULL, the registry record is deleted. 308 | * @return 309 | * STATUS_SUCCESS or error code. 310 | */ 311 | FSP_API NTSTATUS FspLaunchRegSetRecord( 312 | PWSTR ClassName, 313 | const FSP_LAUNCH_REG_RECORD *Record); 314 | /** 315 | * Get a service registry record. 316 | * 317 | * @param ClassName 318 | * The service class name. 319 | * @param Agent 320 | * The name of the agent that is retrieving the service record. This API matches 321 | * the supplied Agent against the Agent in the service record and it only returns 322 | * the record if they match. Pass NULL to match any Agent. 323 | * @param PRecord 324 | * Pointer to a record pointer. Memory for the service record will be allocated 325 | * and a pointer to it will be stored at this address. This memory must be later 326 | * freed using FspLaunchRegFreeRecord. 327 | * @return 328 | * STATUS_SUCCESS or error code. 329 | * @see 330 | * FspLaunchRegFreeRecord 331 | */ 332 | FSP_API NTSTATUS FspLaunchRegGetRecord( 333 | PWSTR ClassName, PWSTR Agent, 334 | FSP_LAUNCH_REG_RECORD **PRecord); 335 | /** 336 | * Free a service registry record. 337 | * 338 | * @param Record 339 | * The service record to free. 340 | * @see 341 | * FspLaunchRegGetRecord 342 | */ 343 | FSP_API VOID FspLaunchRegFreeRecord( 344 | FSP_LAUNCH_REG_RECORD *Record); 345 | 346 | #ifdef __cplusplus 347 | } 348 | #endif 349 | 350 | #endif 351 | -------------------------------------------------------------------------------- /winfsp-sys/winfsp/lib/fuse.pc: -------------------------------------------------------------------------------- 1 | arch=x64 2 | prefix=${pcfiledir}/.. 3 | incdir=${prefix}/inc/fuse 4 | implib=${prefix}/bin/winfsp-${arch}.dll 5 | 6 | Name: fuse 7 | Description: WinFsp FUSE compatible API 8 | Version: 2.8 9 | URL: https://winfsp.dev 10 | Libs: "${implib}" 11 | Cflags: -I"${incdir}" 12 | -------------------------------------------------------------------------------- /winfsp-sys/winfsp/lib/fuse3.pc: -------------------------------------------------------------------------------- 1 | arch=x64 2 | prefix=${pcfiledir}/.. 3 | incdir=${prefix}/inc/fuse3 4 | implib=${prefix}/bin/winfsp-${arch}.dll 5 | 6 | Name: fuse3 7 | Description: WinFsp FUSE3 compatible API 8 | Version: 3.2 9 | URL: https://winfsp.dev 10 | Libs: "${implib}" 11 | Cflags: -I"${incdir}" 12 | -------------------------------------------------------------------------------- /winfsp-sys/winfsp/lib/winfsp-a64.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/3fb1d63f562085d376d0d5ba5884959a96e7b68d/winfsp-sys/winfsp/lib/winfsp-a64.lib -------------------------------------------------------------------------------- /winfsp-sys/winfsp/lib/winfsp-x64.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/3fb1d63f562085d376d0d5ba5884959a96e7b68d/winfsp-sys/winfsp/lib/winfsp-x64.lib -------------------------------------------------------------------------------- /winfsp-sys/winfsp/lib/winfsp-x86.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/3fb1d63f562085d376d0d5ba5884959a96e7b68d/winfsp-sys/winfsp/lib/winfsp-x86.lib -------------------------------------------------------------------------------- /winfsp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winfsp" 3 | version = "0.11.3+winfsp-2.0" 4 | edition = "2021" 5 | readme = "../README.md" 6 | license = "GPL-3.0" 7 | description = "Rust bindings to WinFSP" 8 | keywords = ["filesystem", "winfsp", "fuse"] 9 | categories = ["filesystem", "os::windows-apis", "api-bindings"] 10 | repository = "https://github.com/SnowflakePowered/winfsp-rs" 11 | 12 | [dependencies] 13 | winfsp-sys = { path = "../winfsp-sys", version = "0.2" } 14 | windows = { version = "0.56.0", features = ["Win32_Foundation", "Win32_System_LibraryLoader", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_WindowsProgramming", "Win32_System_Console", "Win32_System_Threading", "Win32_System_ProcessStatus", "Wdk_Foundation"] } 15 | 16 | widestring = "1" 17 | thiserror = "1" 18 | paste = "1" 19 | static_assertions = "1.1" 20 | bytemuck = "1.13" 21 | 22 | [features] 23 | default = ["stable", "nightly", "windows-rs"] 24 | debug = [] 25 | system = ["windows/Win32_System_Registry", "winfsp-sys/system"] 26 | notify = [] 27 | build = [] 28 | delayload = ["build"] 29 | docsrs = ["winfsp-sys/docsrs"] 30 | stable = ["notify", "delayload"] 31 | windows-rs = ["handle-util", "windows-rs-error"] 32 | windows-rs-error = [] 33 | nightly = ["io-error", "strict-provenance"] 34 | strict-provenance = [] 35 | io-error = [] 36 | handle-util = [] 37 | async-io = [ ] 38 | full = ["stable", "nightly", "async-io"] 39 | [package.metadata.docs.rs] 40 | default-target = "x86_64-pc-windows-msvc" 41 | targets = [] 42 | features = ["full", "docsrs"] 43 | -------------------------------------------------------------------------------- /winfsp/src/constants.rs: -------------------------------------------------------------------------------- 1 | //! Useful constants re-exported from [`winfsp-sys`](https://docs.rs/winfsp-sys/). 2 | use winfsp_sys::{FSP_FSCTL_TRANSACT_REQ, FSP_FSCTL_TRANSACT_RSP, WCHAR}; 3 | 4 | /// Flags passed to [FileSystemContext::cleanup](crate::filesystem::FileSystemContext::cleanup) 5 | #[repr(u32)] 6 | #[derive(Copy, Clone)] 7 | pub enum FspCleanupFlags { 8 | /// Delete the file. 9 | FspCleanupDelete = 0x01, 10 | /// Set the allocation size of the file. 11 | FspCleanupSetAllocationSize = 0x02, 12 | /// Set the archive bit of the file. 13 | FspCleanupSetArchiveBit = 0x10, 14 | /// Set the last access time for the file. 15 | FspCleanupSetLastAccessTime = 0x20, 16 | /// Set the last write time for the file. 17 | FspCleanupSetLastWriteTime = 0x40, 18 | /// Set the change time for the file. 19 | FspCleanupSetChangeTime = 0x80, 20 | } 21 | 22 | impl FspCleanupFlags { 23 | /// Check if the flag is set in the provided bitfield. 24 | pub fn is_flagged(&self, flag: u32) -> bool { 25 | (*self as u32) & flag != 0 26 | } 27 | } 28 | 29 | #[repr(u32)] 30 | #[derive(Copy, Clone)] 31 | /// An enumeration of possible transaction kinds by the WinFSP file system driver. 32 | pub enum FspTransactKind { 33 | /// Reserved. 34 | FspFsctlTransactReservedKind = 0, 35 | /// Create 36 | FspFsctlTransactCreateKind, 37 | /// Overwrite 38 | FspFsctlTransactOverwriteKind, 39 | /// Cleanup 40 | FspFsctlTransactCleanupKind, 41 | /// Close 42 | FspFsctlTransactCloseKind, 43 | /// Read 44 | FspFsctlTransactReadKind, 45 | /// Write 46 | FspFsctlTransactWriteKind, 47 | /// QueryInformation 48 | FspFsctlTransactQueryInformationKind, 49 | /// SetInformation 50 | FspFsctlTransactSetInformationKind, 51 | /// QueryEa 52 | FspFsctlTransactQueryEaKind, 53 | /// SetEa 54 | FspFsctlTransactSetEaKind, 55 | /// FlushBuffers 56 | FspFsctlTransactFlushBuffersKind, 57 | /// QueryVolumeInformation 58 | FspFsctlTransactQueryVolumeInformationKind, 59 | /// SetVolumeInformation 60 | FspFsctlTransactSetVolumeInformationKind, 61 | /// QueryDirectory 62 | FspFsctlTransactQueryDirectoryKind, 63 | /// FileSystemControl 64 | FspFsctlTransactFileSystemControlKind, 65 | /// DeviceControl 66 | FspFsctlTransactDeviceControlKind, 67 | /// Shutdown 68 | FspFsctlTransactShutdownKind, 69 | /// LockControl 70 | FspFsctlTransactLockControlKind, 71 | /// QuerySecurity 72 | FspFsctlTransactQuerySecurityKind, 73 | /// SetSecurity 74 | FspFsctlTransactSetSecurityKind, 75 | /// QueryStreamInformation 76 | FspFsctlTransactQueryStreamInformationKind, 77 | /// Maximum valid number of transaction kinds 78 | FspFsctlTransactKindCount, 79 | } 80 | 81 | /// The maximum size of a file path in a WinFSP transaction. 82 | pub const FSP_FSCTL_TRANSACT_PATH_SIZEMAX: usize = 1024 * std::mem::size_of::(); 83 | 84 | /// The maximum size of a request buffer in a WinFSP transaction. 85 | pub const FSP_FSCTL_TRANSACT_REQ_BUFFER_SIZEMAX: usize = 86 | FSP_FSCTL_TRANSACT_REQ_SIZEMAX - std::mem::size_of::(); 87 | 88 | /// The maximum size of a response buffer in a WinFSP transaction. 89 | pub const FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX: usize = 90 | FSP_FSCTL_TRANSACT_RSP_SIZEMAX - std::mem::size_of::(); 91 | 92 | /// The default alignment for WinFSP structs. 93 | pub const FSP_FSCTL_DEFAULT_ALIGNMENT: usize = winfsp_sys::FSP_FSCTL_DEFAULT_ALIGNMENT as usize; 94 | 95 | /// The maximum size of a device control buffer. 96 | pub const FSP_FSCTL_DEVICECONTROL_SIZEMAX: usize = 97 | winfsp_sys::FSP_FSCTL_DEVICECONTROL_SIZEMAX as usize; 98 | 99 | /// The minimum size of a transaction buffer in a WinFSP transaction. 100 | pub const FSP_FSCTL_TRANSACT_BUFFER_SIZEMIN: usize = 101 | winfsp_sys::FSP_FSCTL_TRANSACT_BUFFER_SIZEMIN as usize; 102 | 103 | /// The minimum size of a request buffer in a WinFSP transaction. 104 | pub const FSP_FSCTL_TRANSACT_REQ_SIZEMAX: usize = 105 | winfsp_sys::FSP_FSCTL_TRANSACT_REQ_SIZEMAX as usize; 106 | 107 | /// The minimum size of a response buffer in a WinFSP transaction. 108 | pub const FSP_FSCTL_TRANSACT_RSP_SIZEMAX: usize = 109 | winfsp_sys::FSP_FSCTL_TRANSACT_RSP_SIZEMAX as usize; 110 | 111 | /// The maximum length of a path. 112 | pub const MAX_PATH: usize = 260; 113 | -------------------------------------------------------------------------------- /winfsp/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Error, ErrorKind}; 2 | use thiserror::Error; 3 | use windows::core::HRESULT; 4 | use windows::Win32::Foundation::{ 5 | ERROR_ACCESS_DENIED, ERROR_ALREADY_EXISTS, ERROR_FILE_NOT_FOUND, ERROR_INVALID_PARAMETER, 6 | NTSTATUS, WIN32_ERROR, 7 | }; 8 | 9 | #[cfg(feature = "io-error")] 10 | use windows::Win32::Foundation::{ 11 | ERROR_DIRECTORY, ERROR_DIRECTORY_NOT_SUPPORTED, ERROR_FILENAME_EXCED_RANGE, 12 | }; 13 | 14 | use winfsp_sys::FspNtStatusFromWin32; 15 | 16 | /// Error type for WinFSP. 17 | /// 18 | /// WinFSP wraps errors from the [`windows`](https://github.com/microsoft/windows-rs) crate 19 | /// and can coerces errors into the proper NTSTATUS where necessary. 20 | #[non_exhaustive] 21 | #[derive(Error, Debug)] 22 | pub enum FspError { 23 | #[error("HRESULT")] 24 | /// Wraps a Windows HRESULT. 25 | HRESULT(i32), 26 | #[error("WIN32_ERROR")] 27 | /// Wraps a Windows error returned from `GetLastError`. 28 | WIN32(u32), 29 | #[error("NTRESULT")] 30 | /// Wraps a NTSTATUS error. 31 | NTSTATUS(i32), 32 | #[error("IO")] 33 | /// Wraps a Rust IO [`ErrorKind`](std::io::ErrorKind). 34 | /// Only a few, limited IO errors are supported. Unsupported IO 35 | /// errors will panic when transformed into an NTSTATUS value. 36 | IO(ErrorKind), 37 | } 38 | 39 | impl FspError { 40 | /// Get the corresponding NTSTATUS for this error. 41 | #[inline(always)] 42 | pub fn to_ntstatus(&self) -> winfsp_sys::NTSTATUS { 43 | match self { 44 | &FspError::HRESULT(h) => unsafe { FspNtStatusFromWin32(h as u32) }, 45 | &FspError::WIN32(e) => { 46 | unsafe { FspNtStatusFromWin32(e) } 47 | // e.0 as i32 48 | } 49 | FspError::IO(e) => { 50 | let win32_equiv = match e { 51 | ErrorKind::NotFound => ERROR_FILE_NOT_FOUND, 52 | ErrorKind::PermissionDenied => ERROR_ACCESS_DENIED, 53 | ErrorKind::AlreadyExists => ERROR_ALREADY_EXISTS, 54 | ErrorKind::InvalidInput => ERROR_INVALID_PARAMETER, 55 | #[cfg(feature = "io-error")] 56 | ErrorKind::InvalidFilename => ERROR_FILENAME_EXCED_RANGE, 57 | #[cfg(feature = "io-error")] 58 | ErrorKind::IsADirectory => ERROR_DIRECTORY_NOT_SUPPORTED, 59 | #[cfg(feature = "io-error")] 60 | ErrorKind::NotADirectory => ERROR_DIRECTORY, 61 | // todo: return something sensible. 62 | _ => panic!("Unsupported IO error {:?}", e), 63 | }; 64 | unsafe { FspNtStatusFromWin32(win32_equiv.0) } 65 | } 66 | &FspError::NTSTATUS(e) => e, 67 | } 68 | } 69 | } 70 | 71 | /// Result type for WinFSP. 72 | pub type Result = std::result::Result; 73 | impl From for FspError { 74 | fn from(e: Error) -> Self { 75 | // prefer raw error if available 76 | if let Some(e) = e.raw_os_error() { 77 | FspError::WIN32(e as u32) 78 | } else { 79 | FspError::IO(e.kind()) 80 | } 81 | } 82 | } 83 | 84 | #[cfg(feature = "windows-rs-error")] 85 | impl From for FspError { 86 | fn from(h: HRESULT) -> Self { 87 | FspError::HRESULT(h.0) 88 | } 89 | } 90 | 91 | #[cfg(feature = "windows-rs-error")] 92 | impl From for FspError { 93 | fn from(h: WIN32_ERROR) -> Self { 94 | FspError::WIN32(h.0) 95 | } 96 | } 97 | 98 | #[cfg(feature = "windows-rs-error")] 99 | impl From for FspError { 100 | fn from(h: NTSTATUS) -> Self { 101 | FspError::NTSTATUS(h.0) 102 | } 103 | } 104 | 105 | #[cfg(feature = "windows-rs-error")] 106 | impl From for FspError { 107 | fn from(e: windows::core::Error) -> Self { 108 | let code = e.code().0 as u32; 109 | // https://learn.microsoft.com/en-us/windows/win32/com/structure-of-com-error-codes 110 | // N bit indicates mapped NTSTATUS. 111 | if (code & 0x1000_0000) >> 28 == 1 { 112 | let nt_status = code & !(1 << 28); 113 | return FspError::NTSTATUS(nt_status as i32); 114 | } 115 | match WIN32_ERROR::from_error(&e) { 116 | None => FspError::HRESULT(e.code().0), 117 | Some(w) => FspError::WIN32(w.0), 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /winfsp/src/filesystem/directory.rs: -------------------------------------------------------------------------------- 1 | use std::cell::UnsafeCell; 2 | use widestring::{u16cstr, U16CStr}; 3 | use windows::Win32::Foundation::STATUS_SUCCESS; 4 | use winfsp_sys::{ 5 | FspFileSystemAcquireDirectoryBufferEx, FspFileSystemAddDirInfo, 6 | FspFileSystemDeleteDirectoryBuffer, FspFileSystemFillDirectoryBuffer, 7 | FspFileSystemReadDirectoryBuffer, FspFileSystemReleaseDirectoryBuffer, FSP_FSCTL_DIR_INFO, 8 | PVOID, 9 | }; 10 | 11 | use crate::error::Result; 12 | use crate::filesystem::sealed::WideNameInfoInternal; 13 | use crate::filesystem::{ensure_layout, FileInfo, WideNameInfo}; 14 | use crate::util::AssertThreadSafe; 15 | 16 | /// A buffer used to hold directory entries when enumerating directories 17 | /// with the [`read_directory`](crate::filesystem::FileSystemContext::read_directory) 18 | /// callback. 19 | /// 20 | /// DirBuffer provides interior mutability for the directory buffer, which is 21 | /// managed completely by the filesystem driver. This is because the filesystem 22 | /// driver may create multiple threads that could possibly run afoul of the 23 | /// aliasing rules of `&mut`. 24 | #[derive(Debug)] 25 | pub struct DirBuffer(AssertThreadSafe>); 26 | /// A lock into the directory read buffer that must be held while writing, and dropped 27 | /// as soon as writing of directory entries into the buffer is complete. 28 | #[derive(Debug)] 29 | pub struct DirBufferLock<'a>(&'a DirBuffer); 30 | /// A marker into the current position of the directory file when 31 | /// enumerating directories with [`read_directory`](crate::filesystem::FileSystemContext::read_directory) 32 | #[derive(Debug)] 33 | pub struct DirMarker<'a>(pub(crate) Option<&'a U16CStr>); 34 | 35 | impl DirMarker<'_> { 36 | /// Reset the marker. 37 | pub fn reset(&mut self) { 38 | self.0.take(); 39 | } 40 | 41 | /// Returns whether this marker exists. 42 | pub fn is_none(&self) -> bool { 43 | self.0.is_none() 44 | } 45 | 46 | /// Returns whether this marker is the parent directory '..'. 47 | pub fn is_parent(&self) -> bool { 48 | if let Some(marker) = self.0 { 49 | return marker == u16cstr!(".."); 50 | } 51 | false 52 | } 53 | 54 | /// Returns whether this marker is the current directory '.'. 55 | pub fn is_current(&self) -> bool { 56 | if let Some(marker) = self.0 { 57 | return marker == u16cstr!("."); 58 | } 59 | false 60 | } 61 | 62 | /// Returns the inner contents of the marker. 63 | /// If the inner contents were not validly null-terminated, returns None. 64 | pub fn inner(&self) -> Option<&[u16]> { 65 | self.0.map(U16CStr::as_slice) 66 | } 67 | 68 | /// Returns the inner contents of the marker. 69 | /// If the inner contents were not validly null-terminated, returns None. 70 | pub fn inner_as_cstr(&self) -> Option<&U16CStr> { 71 | self.0 72 | } 73 | } 74 | 75 | impl Default for DirBuffer { 76 | fn default() -> Self { 77 | Self::new() 78 | } 79 | } 80 | 81 | impl DirBuffer { 82 | /// Create a new unacquired directory buffer. 83 | pub fn new() -> Self { 84 | Self(AssertThreadSafe(UnsafeCell::new(std::ptr::null_mut()))) 85 | } 86 | 87 | /// Try to acquire a lock on the directory buffer to write entries into. 88 | pub fn acquire(&self, reset: bool, capacity_hint: Option) -> Result { 89 | let mut result = STATUS_SUCCESS; 90 | unsafe { 91 | if FspFileSystemAcquireDirectoryBufferEx( 92 | self.0 .0.get(), 93 | reset.into(), 94 | capacity_hint.unwrap_or(0), 95 | &mut result.0, 96 | ) != 0 97 | { 98 | Ok(DirBufferLock(self)) 99 | } else { 100 | Err(result.into()) 101 | } 102 | } 103 | } 104 | 105 | /// Read the contents of the directory buffer into the provided slice, 106 | /// returning the number of bytes written. 107 | /// 108 | /// If the directory buffer was never acquired, this is a no-op. 109 | pub fn read(&self, marker: DirMarker, buffer: &mut [u8]) -> u32 { 110 | let mut out = 0u32; 111 | unsafe { 112 | FspFileSystemReadDirectoryBuffer( 113 | self.0 .0.get(), 114 | marker 115 | .0 116 | .map_or(std::ptr::null_mut(), |v| v.as_ptr().cast_mut()), 117 | buffer.as_mut_ptr() as *mut _, 118 | buffer.len() as u32, 119 | &mut out, 120 | ); 121 | } 122 | out 123 | } 124 | } 125 | 126 | impl DirBufferLock<'_> { 127 | /// Write a directory entry into the directory buffer. 128 | /// 129 | /// A buffer can accept multiple DirInfos of varying sizes. 130 | pub fn write(&self, dir_info: &mut DirInfo) -> Result<()> { 131 | let mut status = STATUS_SUCCESS; 132 | unsafe { 133 | let buffer = self.0; 134 | // this is cursed. 135 | if FspFileSystemFillDirectoryBuffer( 136 | buffer.0 .0.get(), 137 | (dir_info as *mut DirInfo).cast(), 138 | &mut status.0, 139 | ) == 0 140 | { 141 | return Err(status.into()); 142 | } 143 | } 144 | Ok(()) 145 | } 146 | } 147 | 148 | impl Drop for DirBuffer { 149 | fn drop(&mut self) { 150 | unsafe { 151 | FspFileSystemDeleteDirectoryBuffer(self.0 .0.get()); 152 | } 153 | } 154 | } 155 | 156 | impl Drop for DirBufferLock<'_> { 157 | fn drop(&mut self) { 158 | let buffer = self.0; 159 | unsafe { FspFileSystemReleaseDirectoryBuffer(buffer.0 .0.get()) } 160 | } 161 | } 162 | 163 | #[repr(C)] 164 | union DirInfoPadding { 165 | next_offset: u64, 166 | padding: [u8; 24], 167 | } 168 | 169 | #[repr(C)] 170 | /// A directory information entry. 171 | /// 172 | /// ## Safety 173 | /// Note that `BUFFER_SIZE` is the size of the name buffer in characters, not bytes. 174 | /// In most cases, the default is sufficient. A buffer size that is too large 175 | /// may not be copyable to the request buffer. 176 | pub struct DirInfo { 177 | size: u16, 178 | file_info: FileInfo, 179 | padding: DirInfoPadding, 180 | file_name: [u16; BUFFER_SIZE], 181 | } 182 | 183 | ensure_layout!(FSP_FSCTL_DIR_INFO, DirInfo<0>); 184 | impl DirInfo { 185 | /// Create a new, empty directory entry info. 186 | pub fn new() -> Self { 187 | Self { 188 | // begin with initially no file_name 189 | size: std::mem::size_of::>() as u16, 190 | file_info: FileInfo::default(), 191 | padding: DirInfoPadding { padding: [0; 24] }, 192 | file_name: [0; BUFFER_SIZE], 193 | } 194 | } 195 | 196 | /// Get a mutable reference to the file information of this directory entry. 197 | pub fn file_info_mut(&mut self) -> &mut FileInfo { 198 | &mut self.file_info 199 | } 200 | } 201 | 202 | impl Default for DirInfo { 203 | fn default() -> Self { 204 | Self::new() 205 | } 206 | } 207 | 208 | impl WideNameInfoInternal for DirInfo { 209 | fn name_buffer(&mut self) -> &mut [u16; BUFFER_SIZE] { 210 | &mut self.file_name 211 | } 212 | 213 | fn set_size(&mut self, buffer_size: u16) { 214 | self.size = std::mem::size_of::>() as u16 + buffer_size 215 | } 216 | 217 | fn add_to_buffer_internal(entry: Option<&Self>, buffer: &mut [u8], cursor: &mut u32) -> bool { 218 | unsafe { 219 | // SAFETY: https://github.com/winfsp/winfsp/blob/0a91292e0502d6629f9a968a168c6e89eea69ea1/src/dll/fsop.c#L1500 220 | // does not mutate entry. 221 | if let Some(entry) = entry { 222 | FspFileSystemAddDirInfo( 223 | (entry as *const Self).cast_mut().cast(), 224 | buffer.as_mut_ptr().cast(), 225 | buffer.len() as u32, 226 | cursor, 227 | ) != 0 228 | } else { 229 | FspFileSystemAddDirInfo( 230 | std::ptr::null_mut(), 231 | buffer.as_mut_ptr().cast(), 232 | buffer.len() as u32, 233 | cursor, 234 | ) != 0 235 | } 236 | } 237 | } 238 | } 239 | 240 | impl WideNameInfo for DirInfo { 241 | /// Reset the directory entry. 242 | fn reset(&mut self) { 243 | self.size = std::mem::size_of::>() as u16; 244 | self.file_info = FileInfo::default(); 245 | self.padding.next_offset = 0; 246 | self.padding.padding = [0; 24]; 247 | self.file_name = [0; BUFFER_SIZE] 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /winfsp/src/filesystem/internals/fileinfo.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX; 2 | use crate::filesystem::ensure_layout; 3 | use winfsp_sys::{FSP_FSCTL_FILE_INFO, FSP_FSCTL_OPEN_FILE_INFO}; 4 | 5 | #[repr(C)] 6 | #[derive(Default, Clone, Debug)] 7 | /// A struct that holds information about a file. 8 | pub struct FileInfo { 9 | /// Specifies one or more FILE_ATTRIBUTE_XXX flags. For descriptions of these flags, 10 | /// see [File Attribute Constants](https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants) in the Microsoft Windows SDK. 11 | pub file_attributes: u32, 12 | /// Specifies the reparse point tag. If the `file_attributes` member includes the FILE_ATTRIBUTE_REPARSE_POINT attribute flag, 13 | /// this member specifies the reparse tag. Otherwise, this member is unused. 14 | pub reparse_tag: u32, 15 | /// The file allocation size in bytes. Usually, this value is a multiple of the sector or cluster size of the underlying physical device. 16 | pub allocation_size: u64, 17 | /// The end of file location as a byte offset. 18 | pub file_size: u64, 19 | /// Specifies the time that the file was created. 20 | pub creation_time: u64, 21 | /// Specifies the time that the file was last accessed. 22 | pub last_access_time: u64, 23 | /// Specifies the time that the file was last written to. 24 | pub last_write_time: u64, 25 | /// Specifies the last time the file was changed. 26 | pub change_time: u64, 27 | /// The 8-byte file reference number for the file. This number is assigned by the file system and is file-system-specific. 28 | /// (Note that this is not the same as the 16-byte "file object ID" that was added to NTFS for Microsoft Windows 2000.) 29 | pub index_number: u64, 30 | /// The number of hard links to the file. This is unimplemented in WinFSP and should always be 0. 31 | pub hard_links: u32, 32 | /// Specifies the combined length, in bytes, of the extended attributes for the file. 33 | pub ea_size: u32, 34 | } 35 | 36 | #[repr(C)] 37 | #[derive(Clone, Debug)] 38 | /// A struct that holds information about a file to be opened or created. 39 | /// 40 | /// `OpenFileInfo` implements [`AsRef`](core::convert::AsRef) and [`AsMut`](core::convert::AsMut) 41 | /// for [`FileInfo`](crate::filesystem::FileInfo), which should be used to access the fields 42 | /// that store the file information. 43 | pub struct OpenFileInfo { 44 | file_info: FileInfo, 45 | normalized_name: winfsp_sys::PWSTR, 46 | /// normalized name length in BYTES. 47 | normalized_name_len: u16, 48 | } 49 | 50 | impl OpenFileInfo { 51 | /// Sets the normalized name of the FileInfo. An optional prefix can be added to ensure that 52 | /// the prefix is written before the FileName. 53 | /// 54 | /// For case-sensitive filesystems, this functionality should be ignored. WinFSP will always assume 55 | /// that the normalized file name is the same as the file name used to open the file. 56 | /// 57 | /// ## Safety 58 | /// The size of the buffer **in bytes** can not exceed 59 | /// [`FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX`](crate::constants::FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX) - 1, 60 | pub fn set_normalized_name(&mut self, name: &[u16], prefix: Option) { 61 | let first_letter = name.first().cloned(); 62 | let file_name: &[u8] = bytemuck::cast_slice(name); 63 | if file_name.len() >= FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX { 64 | panic!("The file name buffer is too large to fit into the transaction!"); 65 | } 66 | 67 | if let (Some(prefix), false) = (prefix, first_letter == prefix) { 68 | unsafe { 69 | self.normalized_name.write(prefix); 70 | 71 | #[cfg(feature = "strict-provenance")] 72 | self.normalized_name 73 | .map_addr(|addr| addr.wrapping_add(1)) 74 | .cast::() 75 | .copy_from_nonoverlapping(file_name.as_ptr(), file_name.len()); 76 | 77 | #[cfg(not(feature = "strict-provenance"))] 78 | self.normalized_name 79 | .wrapping_offset(1) 80 | .cast::() 81 | .copy_from_nonoverlapping(file_name.as_ptr(), file_name.len()); 82 | } 83 | self.normalized_name_len = (std::mem::size_of::() + file_name.len()) as u16; 84 | } else { 85 | // either no prefix, or starts with prefix 86 | unsafe { 87 | self.normalized_name 88 | .cast::() 89 | .copy_from_nonoverlapping(file_name.as_ptr(), file_name.len()) 90 | } 91 | self.normalized_name_len = file_name.len() as u16; 92 | } 93 | } 94 | 95 | /// Get the size of the normalized name in bytes. 96 | /// This starts out as the size of the buffer. 97 | pub fn normalized_name_size(&self) -> u16 { 98 | self.normalized_name_len 99 | } 100 | } 101 | 102 | impl AsRef for OpenFileInfo { 103 | fn as_ref(&self) -> &FileInfo { 104 | &self.file_info 105 | } 106 | } 107 | 108 | impl AsMut for OpenFileInfo { 109 | fn as_mut(&mut self) -> &mut FileInfo { 110 | &mut self.file_info 111 | } 112 | } 113 | 114 | ensure_layout!(FSP_FSCTL_FILE_INFO, FileInfo); 115 | ensure_layout!(FSP_FSCTL_OPEN_FILE_INFO, OpenFileInfo); 116 | -------------------------------------------------------------------------------- /winfsp/src/filesystem/internals/mod.rs: -------------------------------------------------------------------------------- 1 | use std::alloc::Layout; 2 | 3 | mod fileinfo; 4 | mod volumeinfo; 5 | pub(crate) mod widenameinfo; 6 | 7 | pub use fileinfo::*; 8 | pub use volumeinfo::*; 9 | pub use widenameinfo::WideNameInfo; 10 | 11 | #[allow(dead_code)] 12 | pub(crate) const fn assert_layout() -> bool { 13 | let a = Layout::new::(); 14 | let b = Layout::new::(); 15 | a.size() == b.size() && b.align() == b.align() 16 | } 17 | 18 | macro_rules! ensure_layout { 19 | ($t:ty, $j:ty) => { 20 | static_assertions::assert_eq_align!($t, $j); 21 | static_assertions::assert_eq_size!($t, $j); 22 | static_assertions::const_assert!(crate::filesystem::assert_layout::<$t, $j>()); 23 | }; 24 | } 25 | 26 | pub(crate) use ensure_layout; 27 | -------------------------------------------------------------------------------- /winfsp/src/filesystem/internals/volumeinfo.rs: -------------------------------------------------------------------------------- 1 | use crate::filesystem::ensure_layout; 2 | use std::ffi::OsStr; 3 | use std::os::windows::ffi::OsStrExt; 4 | use winfsp_sys::FSP_FSCTL_VOLUME_INFO; 5 | 6 | /// A struct that holds information about the volume. 7 | #[repr(C)] 8 | pub struct VolumeInfo { 9 | /// The total size of the volume. 10 | pub total_size: u64, 11 | /// The free size remaining in the volume. 12 | pub free_size: u64, 13 | volume_label_length: u16, 14 | volume_label: [u16; 32], 15 | } 16 | 17 | ensure_layout!(FSP_FSCTL_VOLUME_INFO, VolumeInfo); 18 | impl VolumeInfo { 19 | /// Set the volume label for this `VolumeInfo`. 20 | /// 21 | /// A `VolumeInfo` can only hold up to 32 characters. A label longer than 32 characters 22 | /// will be truncated. 23 | pub fn set_volume_label>(&mut self, volume_label: P) -> &mut Self { 24 | let volume_label = volume_label.as_ref(); 25 | let volume_label: Vec = volume_label.encode_wide().collect(); 26 | 27 | let max_len = std::cmp::min(self.volume_label.len(), volume_label.len()); 28 | self.volume_label[0..max_len].copy_from_slice(&volume_label[0..max_len]); 29 | // safety: max_len is less than 32. 30 | self.volume_label_length = (max_len * std::mem::size_of::()) as u16; 31 | self 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /winfsp/src/filesystem/internals/widenameinfo.rs: -------------------------------------------------------------------------------- 1 | use crate::{Result, U16CStr}; 2 | use std::ffi::OsStr; 3 | use std::iter; 4 | use std::os::windows::ffi::OsStrExt; 5 | use windows::Win32::Foundation::STATUS_INSUFFICIENT_RESOURCES; 6 | 7 | pub trait WideNameInfoInternal: 8 | crate::filesystem::sealed::Sealed 9 | { 10 | #[doc(hidden)] 11 | /// Return a reference to the name buffer. 12 | fn name_buffer(&mut self) -> &mut [u16; BUFFER_SIZE]; 13 | 14 | #[doc(hidden)] 15 | /// Set the size of the entry. 16 | fn set_size(&mut self, buffer_size: u16); 17 | 18 | #[doc(hidden)] 19 | fn add_to_buffer_internal(entry: Option<&Self>, buffer: &mut [u8], cursor: &mut u32) -> bool; 20 | } 21 | 22 | /// A information entry that contains a wide name buffer. 23 | pub trait WideNameInfo: 24 | crate::filesystem::sealed::Sealed + WideNameInfoInternal 25 | { 26 | /// Finalize the buffer, indicating that no more entries are to be written. 27 | /// 28 | /// If successful, returns true, otherwise false indicates that no more entries 29 | /// can be accepted into the buffer. 30 | fn finalize_buffer(buffer: &mut [u8], cursor: &mut u32) -> bool { 31 | Self::add_to_buffer_internal(None, buffer, cursor) 32 | } 33 | 34 | /// Append the information entry into the provided buffer. 35 | /// 36 | /// If successful, returns true. If the buffer is too small to store any more entries, 37 | /// returns false. The provided cursor should be reused into the next call 38 | /// to `append_to_buffer`. 39 | fn append_to_buffer(&self, buffer: &mut [u8], cursor: &mut u32) -> bool { 40 | Self::add_to_buffer_internal(Some(self), buffer, cursor) 41 | } 42 | 43 | /// Reset the contents of the entry. 44 | fn reset(&mut self); 45 | 46 | /// Write the name of the entry as raw u16 bytes. 47 | /// 48 | /// If the input buffer is not null terminated, and the buffer 49 | /// was not reset prior to setting the name, the previous contents 50 | /// of the buffer will remain, however it is not memory unsafe to 51 | /// do so. 52 | /// 53 | /// If the input buffer is too large, this function will return 54 | /// `STATUS_INSUFFICIENT_RESOURCES`. 55 | fn set_name_raw<'a, P: Into<&'a [u16]>>(&mut self, file_name: P) -> Result<()> { 56 | let file_name = file_name.into(); 57 | if file_name.len() > BUFFER_SIZE { 58 | return Err(STATUS_INSUFFICIENT_RESOURCES.into()); 59 | } 60 | self.name_buffer()[0..std::cmp::min(file_name.len(), BUFFER_SIZE)] 61 | .copy_from_slice(&file_name[0..std::cmp::min(file_name.len(), BUFFER_SIZE)]); 62 | self.set_size(std::mem::size_of_val(file_name) as u16); 63 | Ok(()) 64 | } 65 | 66 | /// Write the name of the entry into the name buffer. 67 | fn set_name>(&mut self, file_name: P) -> Result<()> { 68 | let file_name = file_name.as_ref(); 69 | let file_name = file_name 70 | .encode_wide() 71 | .chain(iter::once(0)) 72 | .collect::>(); 73 | self.set_name_raw(file_name.as_slice()) 74 | } 75 | 76 | /// Write the name of the entry into the name buffer with an input wide CStr. 77 | fn set_name_cstr>(&mut self, file_name: P) -> Result<()> { 78 | let file_name = file_name.as_ref(); 79 | self.set_name_raw(file_name.as_slice_with_nul()) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /winfsp/src/filesystem/mod.rs: -------------------------------------------------------------------------------- 1 | //! The main filesystem interfaces and helpers used to implement a WinFSP filesystem. 2 | mod context; 3 | mod directory; 4 | mod internals; 5 | mod stream; 6 | 7 | mod sealed { 8 | use crate::filesystem::{directory, stream}; 9 | #[doc(hidden)] 10 | pub trait Sealed {} 11 | impl Sealed for directory::DirInfo {} 12 | impl Sealed for stream::StreamInfo {} 13 | impl Sealed for crate::notify::NotifyInfo {} 14 | 15 | pub use super::internals::widenameinfo::WideNameInfoInternal; 16 | } 17 | 18 | pub use context::*; 19 | pub use directory::*; 20 | pub use internals::*; 21 | pub use stream::*; 22 | -------------------------------------------------------------------------------- /winfsp/src/filesystem/stream.rs: -------------------------------------------------------------------------------- 1 | use crate::filesystem::sealed::WideNameInfoInternal; 2 | use crate::filesystem::{ensure_layout, WideNameInfo}; 3 | use winfsp_sys::{FspFileSystemAddStreamInfo, FSP_FSCTL_STREAM_INFO}; 4 | 5 | #[repr(C)] 6 | #[derive(Debug, Clone)] 7 | /// A named stream information entry. 8 | /// 9 | /// ## Safety 10 | /// Note that `BUFFER_SIZE` is the size of the name buffer in characters, not bytes. 11 | /// In most cases, the default is sufficient. A buffer size that is too large 12 | /// may not be copyable to the request buffer. 13 | pub struct StreamInfo { 14 | size: u16, 15 | /// The size of the named stream in bytes. 16 | pub stream_size: u64, 17 | /// The allocation size of the named stream. 18 | pub stream_alloc_size: u64, 19 | stream_name: [u16; BUFFER_SIZE], 20 | } 21 | 22 | ensure_layout!(FSP_FSCTL_STREAM_INFO, StreamInfo<0>); 23 | impl StreamInfo { 24 | /// Create a new, empty stream info. 25 | pub fn new() -> Self { 26 | Self { 27 | // begin with initially no file_name 28 | size: std::mem::size_of::>() as u16, 29 | stream_size: 0, 30 | stream_alloc_size: 0, 31 | stream_name: [0; BUFFER_SIZE], 32 | } 33 | } 34 | } 35 | 36 | impl Default for StreamInfo { 37 | fn default() -> Self { 38 | Self::new() 39 | } 40 | } 41 | 42 | impl WideNameInfoInternal for StreamInfo { 43 | fn name_buffer(&mut self) -> &mut [u16; BUFFER_SIZE] { 44 | &mut self.stream_name 45 | } 46 | 47 | fn set_size(&mut self, buffer_size: u16) { 48 | self.size = std::mem::size_of::>() as u16 + buffer_size; 49 | } 50 | 51 | fn add_to_buffer_internal(entry: Option<&Self>, buffer: &mut [u8], cursor: &mut u32) -> bool { 52 | unsafe { 53 | // SAFETY: https://github.com/winfsp/winfsp/blob/0a91292e0502d6629f9a968a168c6e89eea69ea1/src/dll/fsop.c#L1500 54 | // does not mutate entry. 55 | if let Some(entry) = entry { 56 | FspFileSystemAddStreamInfo( 57 | (entry as *const Self).cast_mut().cast(), 58 | buffer.as_mut_ptr().cast(), 59 | buffer.len() as u32, 60 | cursor, 61 | ) != 0 62 | } else { 63 | FspFileSystemAddStreamInfo( 64 | std::ptr::null_mut(), 65 | buffer.as_mut_ptr().cast(), 66 | buffer.len() as u32, 67 | cursor, 68 | ) != 0 69 | } 70 | } 71 | } 72 | } 73 | 74 | impl WideNameInfo for StreamInfo { 75 | fn reset(&mut self) { 76 | self.size = std::mem::size_of::>() as u16; 77 | self.stream_size = 0; 78 | self.stream_alloc_size = 0; 79 | self.stream_name = [0; BUFFER_SIZE]; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /winfsp/src/host/debug.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::FspTransactKind; 2 | 3 | /// Debug output mask on file system transaction kinds. 4 | #[derive(Debug, Copy, Clone)] 5 | pub struct DebugMode(u32); 6 | 7 | impl Default for DebugMode { 8 | fn default() -> Self { 9 | DebugMode::none() 10 | } 11 | } 12 | 13 | impl DebugMode { 14 | /// Disable all debug output. 15 | pub const fn none() -> Self { 16 | Self(0) 17 | } 18 | 19 | /// Enable debug output for all transaction kinds. 20 | pub const fn all() -> Self { 21 | Self(u32::MAX) 22 | } 23 | 24 | /// Enable debug output for the specific transaction kind. 25 | pub const fn enable_kind(self, kind: FspTransactKind) -> Self { 26 | Self(self.0 | (1 << kind as usize)) 27 | } 28 | 29 | /// Disable debug output for the specific transaction kind. 30 | pub const fn disable_kind(self, kind: FspTransactKind) -> Self { 31 | Self(self.0 & !(1 << kind as usize)) 32 | } 33 | } 34 | 35 | impl From for u32 { 36 | fn from(d: DebugMode) -> Self { 37 | d.0 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /winfsp/src/host/fshost.rs: -------------------------------------------------------------------------------- 1 | use std::cell::UnsafeCell; 2 | use std::ffi::OsStr; 3 | use std::marker::PhantomData; 4 | use std::ptr::null_mut; 5 | use std::ptr::NonNull; 6 | use windows::core::Result; 7 | use windows::core::HSTRING; 8 | use windows::Win32::Foundation::NTSTATUS; 9 | 10 | use winfsp_sys::{ 11 | FspFileSystemCreate, FspFileSystemRemoveMountPoint, FspFileSystemSetMountPoint, 12 | FspFileSystemSetOperationGuardStrategyF, FspFileSystemStartDispatcher, 13 | FspFileSystemStopDispatcher, FSP_FILE_SYSTEM, FSP_FILE_SYSTEM_INTERFACE, 14 | FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE, 15 | FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FINE, 16 | }; 17 | 18 | use crate::filesystem::FileSystemContext; 19 | use crate::host::interface::{FileSystemUserContext, Interface}; 20 | use crate::host::{DebugMode, VolumeParams}; 21 | 22 | use crate::notify::NotifyingFileSystemContext; 23 | use crate::notify::Timer; 24 | 25 | /// Mount point for a new file system. Used by [`FileSystemHost::mount`]. 26 | pub enum MountPoint<'a> { 27 | /// A drive letter such as `X:` or directory path such as `path/to/folder`. 28 | MountPoint(&'a OsStr), 29 | /// Use the next available drive letter counting downwards from `Z:` as the mount point. 30 | NextFreeDrive, 31 | } 32 | 33 | /// Create a mount point from anything that can be viewed as a file path. 34 | impl<'a, S> From<&'a S> for MountPoint<'a> 35 | where 36 | S: AsRef + ?Sized, 37 | { 38 | fn from(s: &'a S) -> Self { 39 | Self::MountPoint(s.as_ref()) 40 | } 41 | } 42 | impl<'short, 'long, 'middle> From<&'short MountPoint<'long>> for MountPoint<'middle> 43 | where 44 | 'long: 'middle, 45 | { 46 | fn from(s: &'short MountPoint<'long>) -> Self { 47 | match s { 48 | MountPoint::MountPoint(v) => MountPoint::MountPoint(v), 49 | MountPoint::NextFreeDrive => MountPoint::NextFreeDrive, 50 | } 51 | } 52 | } 53 | 54 | /// The usermode file system locking strategy. 55 | pub enum OperationGuardStrategy { 56 | /// A fine-grained concurrency model where file system NAMESPACE accesses are guarded using an exclusive-shared (read-write) lock. 57 | /// File I/O is not guarded and concurrent reads/writes/etc. are possible. 58 | /// Note that the FSD will still apply an exclusive-shared lock PER INDIVIDUAL FILE, but it will not limit I/O operations for different files. 59 | /// The fine-grained concurrency model applies the exclusive-shared lock as follows: 60 | /// * EXCL: `set_volume_label`, `flush(None)`, `create`, `cleanup` (delete), `rename` 61 | /// * SHRD: `get_volume_info`, `open`, `set_delete`, `read_directory` 62 | /// * NONE: all other operations 63 | Fine, 64 | /// A coarse-grained concurrency model where all file system accesses are guarded by a mutually exclusive lock. 65 | Coarse, 66 | } 67 | 68 | /// Options to create the filesystem with. 69 | pub struct FileSystemParams { 70 | /// Enable the file system driver to call [`FileSystemContext::get_dir_info_by_name`](crate::filesystem::FileSystemContext::get_dir_info_by_name). 71 | pub use_dir_info_by_name: bool, 72 | /// The parameters to mount the volume with. 73 | pub volume_params: VolumeParams, 74 | /// The usermode file system locking strategy to use. 75 | pub guard_strategy: OperationGuardStrategy, 76 | /// Set the debug output mask. Debug output is only displayed if the 77 | /// `debug` crate feature is enabled, regardless of the mask. 78 | /// 79 | /// See [`FspTransactKind`](crate::constants::FspTransactKind) for possible mask values. 80 | pub debug_mode: DebugMode, 81 | } 82 | 83 | impl FileSystemParams { 84 | /// Use the default options with the given volume parameters. 85 | pub fn default_params(volume_params: VolumeParams) -> Self { 86 | Self { 87 | use_dir_info_by_name: false, 88 | volume_params, 89 | guard_strategy: OperationGuardStrategy::Fine, 90 | debug_mode: Default::default(), 91 | } 92 | } 93 | 94 | /// Use the default options with the given volume parameters and debug mode. 95 | pub fn default_params_debug(volume_params: VolumeParams, debug_mode: DebugMode) -> Self { 96 | Self { 97 | use_dir_info_by_name: false, 98 | volume_params, 99 | guard_strategy: OperationGuardStrategy::Fine, 100 | debug_mode, 101 | } 102 | } 103 | } 104 | /// The user-mode filesystem host that manages the lifetime of the mounted filesystem. 105 | /// 106 | /// This is separate from the lifetime of the service which is managed by 107 | /// [`FileSystemService`](crate::service::FileSystemService). A `FileSystemHost` 108 | /// should start within the context of a service. 109 | pub struct FileSystemHost<'ctx>( 110 | NonNull, 111 | #[allow(dead_code)] Option, 112 | PhantomData<&'ctx FSP_FILE_SYSTEM>, 113 | ); 114 | 115 | #[cfg(feature = "async-io")] 116 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "async-io")))] 117 | impl FileSystemHost<'static> { 118 | fn new_filesystem_inner_async( 119 | options: FileSystemParams, 120 | context: T, 121 | ) -> Result> 122 | where 123 | ::FileContext: Sync, 124 | { 125 | #[allow(unused_variables)] 126 | let FileSystemParams { 127 | use_dir_info_by_name, 128 | volume_params, 129 | guard_strategy, 130 | debug_mode, 131 | } = options; 132 | 133 | let interface = if use_dir_info_by_name { 134 | Interface::create_with_dirinfo_by_name_async::() 135 | } else { 136 | Interface::create_with_read_directory_async::() 137 | }; 138 | 139 | Self::new_filesystem_inner_iface( 140 | interface, 141 | volume_params, 142 | guard_strategy, 143 | debug_mode, 144 | context, 145 | ) 146 | } 147 | 148 | /// Create a `FileSystemHost` with the default settings 149 | /// for the provided context implementation, using async implementations of `read`, `write`, and `read_directory`. 150 | pub fn new_async( 151 | volume_params: VolumeParams, 152 | context: T, 153 | ) -> Result 154 | where 155 | ::FileContext: Sync, 156 | { 157 | Self::new_with_options_async::( 158 | FileSystemParams { 159 | use_dir_info_by_name: false, 160 | volume_params, 161 | guard_strategy: OperationGuardStrategy::Fine, 162 | debug_mode: DebugMode::none(), 163 | }, 164 | context, 165 | ) 166 | } 167 | 168 | /// Create a `FileSystemHost` with the provided context implementation, and 169 | /// host options, using async implementations of `read`, `write`, and `read_directory`. 170 | pub fn new_with_options_async( 171 | options: FileSystemParams, 172 | context: T, 173 | ) -> Result 174 | where 175 | ::FileContext: Sync, 176 | { 177 | let fsp_struct = Self::new_filesystem_inner_async(options, context)?; 178 | Ok(FileSystemHost(fsp_struct, None, PhantomData)) 179 | } 180 | 181 | /// Create a `FileSystemHost` with the provided notifying context implementation, 182 | /// host options, and polling interval, using async implementations of `read`, `write`, and `read_directory`. 183 | #[cfg_attr( 184 | feature = "docsrs", 185 | doc(cfg(all(feature = "notify", feature = "async-io"))) 186 | )] 187 | #[cfg(all(feature = "notify", feature = "async-io"))] 188 | pub fn new_with_timer_async< 189 | T: crate::filesystem::AsyncFileSystemContext + NotifyingFileSystemContext, 190 | R, 191 | const INTERVAL: u32, 192 | >( 193 | options: FileSystemParams, 194 | context: T, 195 | ) -> Result 196 | where 197 | ::FileContext: Sync, 198 | { 199 | let fsp_struct = Self::new_filesystem_inner_async(options, context)?; 200 | let timer = Timer::create::(fsp_struct)?; 201 | Ok(FileSystemHost(fsp_struct, Some(timer), PhantomData)) 202 | } 203 | } 204 | 205 | impl<'ctx> FileSystemHost<'ctx> { 206 | #[allow(unused_variables)] 207 | fn new_filesystem_inner_iface( 208 | interface: Interface, 209 | volume_params: VolumeParams, 210 | guard_strategy: OperationGuardStrategy, 211 | debug_mode: DebugMode, 212 | context: T, 213 | ) -> Result> { 214 | let mut fsp_struct = std::ptr::null_mut(); 215 | 216 | let interface: FSP_FILE_SYSTEM_INTERFACE = interface.into(); 217 | let interface = Box::into_raw(Box::new(UnsafeCell::new(interface))); 218 | // SAFETY: WinFSP owns the allocation that fsp_struct points to. 219 | let result = unsafe { 220 | FspFileSystemCreate( 221 | volume_params.get_winfsp_device_name(), 222 | &volume_params.0, 223 | // SAFETY: UnsafeCell and T are transmutable. 224 | interface.cast(), 225 | &mut fsp_struct, 226 | ) 227 | }; 228 | 229 | let result = NTSTATUS(result); 230 | result.ok()?; 231 | 232 | #[cfg(feature = "debug")] 233 | unsafe { 234 | use windows::Win32::System::Console::{GetStdHandle, STD_ERROR_HANDLE}; 235 | // pointer crimes 236 | winfsp_sys::FspDebugLogSetHandle( 237 | GetStdHandle(STD_ERROR_HANDLE).unwrap().0 as *mut std::ffi::c_void, 238 | ); 239 | winfsp_sys::FspFileSystemSetDebugLogF(fsp_struct, debug_mode.into()); 240 | } 241 | 242 | unsafe { 243 | (*fsp_struct).UserContext = Box::into_raw(Box::new(UnsafeCell::new( 244 | FileSystemUserContext::new(context), 245 | ))) as *mut _; 246 | 247 | match guard_strategy { 248 | OperationGuardStrategy::Fine => FspFileSystemSetOperationGuardStrategyF(fsp_struct, FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FINE), 249 | OperationGuardStrategy::Coarse => FspFileSystemSetOperationGuardStrategyF(fsp_struct, FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE), 250 | } 251 | } 252 | 253 | assert!(!fsp_struct.is_null()); 254 | Ok(NonNull::new(fsp_struct).expect("FSP_FILE_SYSTEM pointer was created but was null!")) 255 | } 256 | 257 | fn new_filesystem_inner( 258 | options: FileSystemParams, 259 | context: T, 260 | ) -> Result> { 261 | #[allow(unused_variables)] 262 | let FileSystemParams { 263 | use_dir_info_by_name, 264 | volume_params, 265 | guard_strategy, 266 | debug_mode, 267 | } = options; 268 | 269 | let interface = if use_dir_info_by_name { 270 | Interface::create_with_dirinfo_by_name::() 271 | } else { 272 | Interface::create_with_read_directory::() 273 | }; 274 | 275 | Self::new_filesystem_inner_iface( 276 | interface, 277 | volume_params, 278 | guard_strategy, 279 | debug_mode, 280 | context, 281 | ) 282 | } 283 | 284 | /// Create a `FileSystemHost` with the default settings 285 | /// for the provided context implementation. 286 | pub fn new( 287 | volume_params: VolumeParams, 288 | context: T, 289 | ) -> Result { 290 | Self::new_with_options::( 291 | FileSystemParams { 292 | use_dir_info_by_name: false, 293 | volume_params, 294 | guard_strategy: OperationGuardStrategy::Fine, 295 | debug_mode: DebugMode::none(), 296 | }, 297 | context, 298 | ) 299 | } 300 | 301 | /// Create a `FileSystemHost` with the provided context implementation, and 302 | /// host options. 303 | pub fn new_with_options( 304 | options: FileSystemParams, 305 | context: T, 306 | ) -> Result { 307 | let fsp_struct = Self::new_filesystem_inner(options, context)?; 308 | Ok(FileSystemHost(fsp_struct, None, PhantomData)) 309 | } 310 | 311 | /// Create a `FileSystemHost` with the provided notifying context implementation, 312 | /// host options, and polling interval. 313 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "notify")))] 314 | #[cfg(feature = "notify")] 315 | pub fn new_with_timer< 316 | T: FileSystemContext + NotifyingFileSystemContext + 'ctx, 317 | R, 318 | const INTERVAL: u32, 319 | >( 320 | options: FileSystemParams, 321 | context: T, 322 | ) -> Result { 323 | let fsp_struct = Self::new_filesystem_inner(options, context)?; 324 | let timer = Timer::create::(fsp_struct)?; 325 | Ok(FileSystemHost(fsp_struct, Some(timer), PhantomData)) 326 | } 327 | 328 | /// Start the filesystem dispatcher for this filesystem. 329 | pub fn start(&mut self) -> Result<()> { 330 | self.start_with_threads(0) 331 | } 332 | 333 | /// Start the filesystem dispatcher for this filesystem with the specified number of threads. 334 | pub fn start_with_threads(&mut self, num_threads: u32) -> Result<()> { 335 | let result = unsafe { FspFileSystemStartDispatcher(self.0.as_ptr(), num_threads) }; 336 | let result = NTSTATUS(result); 337 | result.ok() 338 | } 339 | 340 | /// Stop the filesystem dispatcher for this filesystem. 341 | pub fn stop(&mut self) { 342 | unsafe { FspFileSystemStopDispatcher(self.0.as_ptr()) } 343 | } 344 | 345 | /// Mount the filesystem to the given mount point. 346 | /// 347 | /// # Examples 348 | /// 349 | /// ``` 350 | /// use winfsp::host::{FileSystemHost, MountPoint}; 351 | /// 352 | /// fn mount_file_system(host: &mut FileSystemHost<'static>) -> winfsp::Result<()> { 353 | /// // Can mount in one of the following ways: 354 | /// host.mount("X:")?; 355 | /// host.mount("../MyFileSystem".to_string())?; 356 | /// host.mount(&std::path::PathBuf::from("C:/WinFspFileSystem"))?; 357 | /// host.mount(MountPoint::NextFreeDrive)?; 358 | /// 359 | /// Ok(()) 360 | /// } 361 | /// ``` 362 | pub fn mount(&mut self, mount: S) -> Result<()> 363 | where 364 | // Convert a reference to the provided value in order to allow the 365 | // caller to provide owned values such as `String`: 366 | for<'a> &'a S: Into>, 367 | { 368 | let mount_str: HSTRING; 369 | let mount_ptr = match <&S as Into>>::into(&mount) { 370 | MountPoint::MountPoint(mount) => { 371 | mount_str = HSTRING::from(mount); 372 | // Pointer is valid until `mount_str` is dropped at the end of the function. 373 | mount_str.as_ptr().cast_mut() 374 | } 375 | MountPoint::NextFreeDrive => null_mut(), 376 | }; 377 | let result = unsafe { FspFileSystemSetMountPoint(self.0.as_ptr(), mount_ptr) }; 378 | 379 | let result = NTSTATUS(result); 380 | result.ok() 381 | } 382 | 383 | /// Unmount the filesystem. It is safe to call this function even if the 384 | /// file system is not mounted. 385 | pub fn unmount(&mut self) { 386 | unsafe { FspFileSystemRemoveMountPoint(self.0.as_ptr()) } 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /winfsp/src/host/mod.rs: -------------------------------------------------------------------------------- 1 | //! Interfaces and configuration relating to the filesystem runtime host that manages the lifetime 2 | //! of the filesystem context. 3 | mod debug; 4 | mod fshost; 5 | pub(crate) mod interface; 6 | mod volumeparams; 7 | 8 | pub use debug::*; 9 | pub use fshost::*; 10 | pub use volumeparams::*; 11 | -------------------------------------------------------------------------------- /winfsp/src/host/volumeparams.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::OsStr; 2 | use std::os::windows::ffi::OsStrExt; 3 | use windows::core::w; 4 | use winfsp_sys::FSP_FSCTL_VOLUME_PARAMS; 5 | 6 | #[repr(transparent)] 7 | #[derive(Debug, Clone)] 8 | /// Parameters that control how the WinFSP volume is mounted and processes requests. 9 | pub struct VolumeParams(pub(crate) FSP_FSCTL_VOLUME_PARAMS); 10 | 11 | macro_rules! make_setters { 12 | ( 13 | $( 14 | $(#[$outer:meta])* 15 | $name: ident = $ffi_name:ident: $ty:ty; 16 | )+ 17 | ) => { 18 | $( 19 | $(#[$outer])* 20 | pub fn $name(&mut self, n: $ty) -> &mut Self { 21 | self.0.$ffi_name = n; 22 | self 23 | } 24 | )+ 25 | }; 26 | ( 27 | $( 28 | $(#[$outer:meta])* 29 | $name: ident = $ffi_name:ident; 30 | )+ 31 | ) => { 32 | $( 33 | $(#[$outer])* 34 | pub fn $name(&mut self, n: bool) -> &mut Self { 35 | paste::paste! { 36 | self.0.[](if n { 1 } else { 0 }); 37 | } 38 | self 39 | } 40 | )+ 41 | }; 42 | } 43 | 44 | impl Default for VolumeParams { 45 | fn default() -> Self { 46 | VolumeParams::new() 47 | } 48 | } 49 | 50 | impl VolumeParams { 51 | /// Create a new `VolumeParams` 52 | pub fn new() -> Self { 53 | let mut params = FSP_FSCTL_VOLUME_PARAMS::default(); 54 | 55 | // descriptor mode 56 | params.set_UmFileContextIsFullContext(0); 57 | params.set_UmFileContextIsUserContext2(1); 58 | 59 | // hard links are unimplemented. 60 | params.set_HardLinks(0); 61 | 62 | // This is hardcoded, might as well ensure so. 63 | // See https://github.com/winfsp/winfsp/issues/55 64 | params.set_AlwaysUseDoubleBuffering(1); 65 | VolumeParams(params) 66 | } 67 | 68 | /// Safety: the returned pointer must not be modified. 69 | pub(crate) unsafe fn get_winfsp_device_name(&self) -> *mut u16 { 70 | if self.0.Prefix[0] != 0 { 71 | w!("WinFsp.Net").as_ptr().cast_mut() 72 | } else { 73 | w!("WinFsp.Disk").as_ptr().cast_mut() 74 | } 75 | } 76 | 77 | /// Set the prefix of the volume. 78 | pub fn prefix>(&mut self, prefix: P) -> &mut Self { 79 | let prefix = prefix.as_ref(); 80 | let prefix: Vec = prefix.encode_wide().collect(); 81 | self.0.Prefix[..std::cmp::min(prefix.len(), 192)] 82 | .copy_from_slice(&prefix[..std::cmp::min(prefix.len(), 192)]); 83 | self 84 | } 85 | 86 | /// Set the name of the filesystem. 87 | pub fn filesystem_name>(&mut self, filesystem_name: P) -> &mut Self { 88 | let filesystem_name = filesystem_name.as_ref(); 89 | let filesystem_name: Vec = filesystem_name.encode_wide().collect(); 90 | self.0.FileSystemName[..std::cmp::min(filesystem_name.len(), 192)] 91 | .copy_from_slice(&filesystem_name[..std::cmp::min(filesystem_name.len(), 192)]); 92 | self 93 | } 94 | 95 | make_setters! { 96 | /// Set the number of sectors in each allocation unit. 97 | sectors_per_allocation_unit = SectorsPerAllocationUnit: u16; 98 | /// Set the size of each sector. 99 | sector_size = SectorSize: u16; 100 | /// Set the maximum length of a file name component. A file name component is the portion of a file name between backslashes. 101 | max_component_length = MaxComponentLength: u16; 102 | /// Set the time of volume creation. 103 | volume_creation_time = VolumeCreationTime: u64; 104 | /// Set the volume serial number. 105 | volume_serial_number = VolumeSerialNumber: u32; 106 | /// Set the timeout of a pending IO request packet in milliseconds. 107 | /// 108 | /// Supports a a range of 1 minute (60000) to 10 minutes (600000) 109 | irp_timeout = IrpTimeout: u32; 110 | /// Set the maximum number of pending IO request packets. 111 | /// 112 | /// Supports from 100 packets to 1000 packets. 113 | irp_capacity = IrpCapacity: u32; 114 | /// Set the timeout in milliseconds before a FileInfo, or other info request times out. 115 | /// 116 | /// Setting this to `u32::MAX` enables the WinFSP cache manager 117 | file_info_timeout = FileInfoTimeout: u32; 118 | /// Set the timeout in milliseconds before a DirInfo request times out. Overrides `file_info_timeout`. 119 | dir_info_timeout = DirInfoTimeout: u32; 120 | /// Set the timeout in milliseconds before a VolumeInfo request times out. Overrides `file_info_timeout`. 121 | volume_info_timeout = VolumeInfoTimeout: u32; 122 | /// Set the timeout in milliseconds before a SecurityInfo request times out. Overrides `file_info_timeout`. 123 | security_timeout = SecurityTimeout: u32; 124 | /// Set the timeout in milliseconds before a StreamInfo request times out. Overrides `file_info_timeout`. 125 | stream_info_timeout = StreamInfoTimeout: u32; 126 | /// Set the timeout in milliseconds before a ExtendedAttributeInfo request times out. Overrides `file_info_timeout`. 127 | extended_attribute_timeout = EaTimeout: u32; 128 | /// Set the `FsextControlCode` for kernel mode drivers. This **must** be set to 0 for user-mode drivers, 129 | /// and non-zero for kernel-mode drivers. 130 | /// 131 | /// See the [WinFSP documentation](https://winfsp.dev/doc/WinFsp-Kernel-Mode-File-Systems/) for more details. 132 | /// winfsp-rs does not officially support kernel mode filesystems, and until this docstring is updated, 133 | /// kernel-mode support is exempt from semantic versioning requirements. 134 | fsext_control_code = FsextControlCode: u32; 135 | } 136 | 137 | make_setters! { 138 | /// Set if the file system supports case-sensitive file names. 139 | case_sensitive_search = CaseSensitiveSearch; 140 | /// Set if the file system preserves case of file names. 141 | case_preserved_names = CasePreservedNames; 142 | /// Set if the file system supports Unicode (i.e. UTF-16) in file names 143 | unicode_on_disk = UnicodeOnDisk; 144 | /// Set if the file system preserves and enforces access control lists. 145 | persistent_acls = PersistentAcls; 146 | /// Set if the file system supports reparse points. 147 | reparse_points = ReparsePoints; 148 | /// Set if the file system performs reparse point access checks. 149 | reparse_points_access_check = ReparsePointsAccessCheck; 150 | /// Set if the file system file system supports named streams. 151 | named_streams = NamedStreams; 152 | /// Set if the file system file system supports extended attributes. 153 | extended_attributes = ExtendedAttributes; 154 | /// Set if the file system is read only. 155 | read_only_volume = ReadOnlyVolume; 156 | /// Set whether or not to post Cleanup when a file was modified/deleted. 157 | post_cleanup_when_modified_only = PostCleanupWhenModifiedOnly; 158 | /// Set whether or not to ask the OS to close files as soon as possible. 159 | flush_and_purge_on_cleanup = FlushAndPurgeOnCleanup; 160 | /// Set whether to pass Pattern during when querying directories. 161 | pass_query_directory_pattern = PassQueryDirectoryPattern; 162 | /// Set whether to pass the filename when querying directory, as opposed to the 163 | /// search pattern. 164 | /// 165 | /// Enabling is required to use `get_dirinfo_by_name`. 166 | pass_query_directory_filename = PassQueryDirectoryFileName; 167 | /// Set whether or not to enable user-mode IOCTL handling. 168 | device_control = DeviceControl; 169 | /// Set whether to allow opening parse points regardless of the FILE_DIRECTORY_FILE / FILE_NON_DIRECTORY_FILE. 170 | /// This is needed for FUSE filesystems. 171 | no_reparse_points_dir_check = UmNoReparsePointsDirCheck; 172 | /// Set whether to allow the kernel mode driver to open files where possible. 173 | allow_open_in_kernel_mode = AllowOpenInKernelMode; 174 | /// Set whether to preserve the case of extended attributes. The default is UPPERCASE. 175 | case_preseve_extended_attributes = CasePreservedExtendedAttributes; 176 | /// Set whether or not to support features required for Windows Subsystem for Linux v1. 177 | wsl_features = WslFeatures; 178 | /// Set if the directory marker is the next offset instead of the last file name. 179 | directory_marker_as_next_offset = DirectoryMarkerAsNextOffset; 180 | /// Set if the file system supports POSIX-style unlink and rename. 181 | supports_posix_unlink_rename = SupportsPosixUnlinkRename; 182 | /// Set whether to check if the item to be disposed is a directory or has READONLY attribute 183 | /// before posting post Disposition. 184 | post_disposition_only_when_necessary = PostDispositionWhenNecessaryOnly; 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /winfsp/src/init.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "system")] 2 | use widestring::U16CStr; 3 | use windows::core::w; 4 | use windows::core::PCWSTR; 5 | #[allow(unused_imports)] 6 | use windows::Win32::Foundation::{ERROR_DELAY_LOAD_FAILED, ERROR_FILE_NOT_FOUND}; 7 | use windows::Win32::System::LibraryLoader::LoadLibraryW; 8 | 9 | use crate::Result; 10 | 11 | /// WinFSP initialization token. 12 | /// 13 | /// WinFSP must be initialized with [`winfsp_init`](crate::winfsp_init) or [`winfsp_init_or_die`](crate::winfsp_init_or_die) 14 | /// by the host process, which yields this token to be used with [`FileSystemServiceBuilder`](crate::service::FileSystemServiceBuilder). 15 | #[non_exhaustive] 16 | #[derive(Copy, Clone)] 17 | pub struct FspInit; 18 | 19 | #[cfg(feature = "system")] 20 | fn get_system_winfsp() -> Option { 21 | use crate::constants::MAX_PATH; 22 | use windows::Win32::System::Registry::{RegGetValueW, HKEY_LOCAL_MACHINE, RRF_RT_REG_SZ}; 23 | 24 | let mut path = [0u16; MAX_PATH]; 25 | let mut size = (path.len() * std::mem::size_of::()) as u32; 26 | let status = unsafe { 27 | RegGetValueW( 28 | HKEY_LOCAL_MACHINE, 29 | w!("SOFTWARE\\WOW6432Node\\WinFsp"), 30 | w!("InstallDir"), 31 | RRF_RT_REG_SZ, 32 | None, 33 | Some(path.as_mut_ptr().cast()), 34 | Some(&mut size), 35 | ) 36 | }; 37 | if status.is_err() { 38 | return None; 39 | }; 40 | 41 | let Ok(path) = U16CStr::from_slice(&path[0..(size as usize) / std::mem::size_of::()]) 42 | else { 43 | return None; 44 | }; 45 | 46 | let mut directory = path.to_os_string(); 47 | directory.push("\\bin\\"); 48 | 49 | if cfg!(target_arch = "x86_64") { 50 | directory.push("winfsp-x64.dll"); 51 | } else if cfg!(target_arch = "x86") { 52 | directory.push("winfsp-x86.dll"); 53 | } else if cfg!(target_arch = "aarch64") { 54 | directory.push("winfsp-a64.dll"); 55 | } else { 56 | panic!("unsupported arch") 57 | } 58 | 59 | Some(windows::core::HSTRING::from(directory)) 60 | } 61 | 62 | fn get_local_winfsp() -> PCWSTR { 63 | if cfg!(target_arch = "x86_64") { 64 | w!("winfsp-x64.dll") 65 | } else if cfg!(target_arch = "x86") { 66 | w!("winfsp-x86.dll") 67 | } else if cfg!(target_arch = "aarch64") { 68 | w!("winfsp-a64.dll") 69 | } else { 70 | panic!("unsupported arch") 71 | } 72 | } 73 | 74 | fn load_local_winfsp() -> Result<()> { 75 | unsafe { 76 | if LoadLibraryW(get_local_winfsp()).is_err() { 77 | Err(ERROR_DELAY_LOAD_FAILED.into()) 78 | } else { 79 | Ok(()) 80 | } 81 | } 82 | } 83 | 84 | fn load_system_winfsp() -> Result<()> { 85 | #[cfg(feature = "system")] 86 | unsafe { 87 | let system = get_system_winfsp().ok_or(ERROR_FILE_NOT_FOUND)?; 88 | if LoadLibraryW(&system).is_err() { 89 | Err(ERROR_DELAY_LOAD_FAILED.into()) 90 | } else { 91 | Ok(()) 92 | } 93 | } 94 | 95 | #[cfg(not(feature = "system"))] 96 | Err(ERROR_DELAY_LOAD_FAILED.into()) 97 | } 98 | 99 | /// Initialize WinFSP. 100 | pub fn winfsp_init() -> Result { 101 | if load_local_winfsp().is_err() && load_system_winfsp().is_err() { 102 | Err(ERROR_DELAY_LOAD_FAILED.into()) 103 | } else { 104 | Ok(FspInit) 105 | } 106 | } 107 | 108 | /// Initialize WinFSP, shutting down the executing process on failure. 109 | pub fn winfsp_init_or_die() -> FspInit { 110 | if winfsp_init().is_err() { 111 | std::process::exit(ERROR_DELAY_LOAD_FAILED.0 as i32) 112 | } 113 | FspInit 114 | } 115 | 116 | /// Build-time helper to enable `DELAYLOAD` linking to the system WinFSP. 117 | /// 118 | /// This function should be called from `build.rs`. 119 | pub fn winfsp_link_delayload() { 120 | if cfg!(all(target_os = "windows", target_env = "msvc")) { 121 | if cfg!(target_arch = "x86_64") { 122 | println!("cargo:rustc-link-lib=dylib=delayimp"); 123 | println!("cargo:rustc-link-arg=/DELAYLOAD:winfsp-x64.dll"); 124 | } else if cfg!(target_arch = "x86") { 125 | println!("cargo:rustc-link-lib=dylib=delayimp"); 126 | println!("cargo:rustc-link-arg=/DELAYLOAD:winfsp-x86.dll"); 127 | } else if cfg!(target_arch = "aarch64") { 128 | println!("cargo:rustc-link-lib=dylib=delayimp"); 129 | println!("cargo:rustc-link-arg=/DELAYLOAD:winfsp-a64.dll"); 130 | } else { 131 | panic!("unsupported architecture") 132 | } 133 | } else { 134 | panic!("unsupported triple") 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /winfsp/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(feature = "strict-provenance", feature(strict_provenance))] 2 | #![cfg_attr(feature = "io-error", feature(io_error_more))] 3 | #![cfg_attr(feature = "docsrs", feature(doc_cfg))] 4 | #![deny(unsafe_op_in_unsafe_fn)] 5 | #![forbid(missing_docs)] 6 | 7 | //! Safe Rust bindings to [WinFSP](https://github.com/winfsp/winfsp). 8 | //! 9 | //! ## Usage 10 | //! The `winfsp` crate wraps the WinFSP service architecture and user mode filesystem host. 11 | //! Implement the [`FileSystemContext`](crate::filesystem::FileSystemContext) trait, then 12 | //! create a [`FileSystemHost`](crate::host::FileSystemHost) instance. 13 | //! 14 | //! It is highly recommended to use the service architecture to manage the lifecycle of a `FileSystemHost`. 15 | //! 16 | //! Using [`FileSystemServiceBuilder`](crate::service::FileSystemServiceBuilder), create, start, and mount the `FileSystemHost` 17 | //! within the [`FileSystemServiceBuilder::with_start`](crate::service::FileSystemServiceBuilder::with_start) closure, 18 | //! and handle teardown in the [`FileSystemServiceBuilder::with_stop`](crate::service::FileSystemServiceBuilder::with_stop) 19 | //! closure. 20 | //! 21 | //! The resulting service can be built after initializing WinFSP for your application with [`winfsp_init`](crate::winfsp_init) or [`winfsp_init_or_die`](crate::winfsp_init_or_die). 22 | //! 23 | //! ## Build-time requirements 24 | //! WinFSP only supports delayloading of its library. You must emit the required 25 | //! compile flags in `build.rs` with [`winfsp_link_delayload`](crate::build::winfsp_link_delayload). 26 | //! 27 | //! ```rust 28 | //! fn main() { 29 | //! winfsp::build::winfsp_link_delayload(); 30 | //! } 31 | //! ``` 32 | //! 33 | pub mod constants; 34 | mod error; 35 | pub mod filesystem; 36 | pub mod host; 37 | mod init; 38 | pub mod service; 39 | pub mod util; 40 | mod vsb; 41 | 42 | #[cfg(feature = "notify")] 43 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "notify")))] 44 | pub mod notify; 45 | 46 | // only publicly export notify if feature is enabled. 47 | #[cfg(not(feature = "notify"))] 48 | mod notify; 49 | 50 | pub use error::FspError; 51 | pub use error::Result; 52 | 53 | pub use init::{winfsp_init, winfsp_init_or_die, FspInit}; 54 | 55 | pub use widestring::{U16CStr, U16CString}; 56 | 57 | #[cfg(feature = "delayload")] 58 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "build")))] 59 | pub mod build { 60 | //! Build-time helpers to be called from `build.rs`. 61 | pub use crate::init::winfsp_link_delayload; 62 | } 63 | -------------------------------------------------------------------------------- /winfsp/src/notify/context.rs: -------------------------------------------------------------------------------- 1 | use crate::filesystem::FileSystemContext; 2 | use crate::notify::Notifier; 3 | 4 | /// A filesystem that supports operating system notifications. 5 | pub trait NotifyingFileSystemContext: FileSystemContext { 6 | /// Calculate a sentinel or context value if the filesystem is ready to notify the 7 | /// operating system, or return None if the filesystem is not ready. 8 | fn should_notify(&self) -> Option; 9 | 10 | /// Publish the notification with the given sentinel or context value to the 11 | /// operating system. 12 | fn notify(&self, context: R, notifier: &Notifier); 13 | } 14 | -------------------------------------------------------------------------------- /winfsp/src/notify/mod.rs: -------------------------------------------------------------------------------- 1 | //! Helpers to implement filesystem notifications. 2 | mod context; 3 | mod notifier; 4 | mod notifyinfo; 5 | mod timer; 6 | 7 | pub use context::*; 8 | pub use notifier::*; 9 | pub use notifyinfo::*; 10 | pub(crate) use timer::*; 11 | -------------------------------------------------------------------------------- /winfsp/src/notify/notifier.rs: -------------------------------------------------------------------------------- 1 | use crate::notify::NotifyInfo; 2 | use winfsp_sys::{FspFileSystemNotify, FSP_FILE_SYSTEM}; 3 | 4 | /// A notifier used to notify the filesystem of changes. 5 | pub struct Notifier(pub(crate) *mut FSP_FILE_SYSTEM); 6 | impl Notifier { 7 | /// Notify the filesystem of the given change event. 8 | pub fn notify(&self, info: &NotifyInfo) { 9 | unsafe { 10 | FspFileSystemNotify( 11 | self.0, 12 | // SAFETY: FspFileSystemNotify calls DeviceIoControl with the buffer specified as [in]. 13 | (info as *const NotifyInfo).cast_mut().cast(), 14 | info.size as u64, 15 | ) 16 | }; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /winfsp/src/notify/notifyinfo.rs: -------------------------------------------------------------------------------- 1 | use crate::filesystem::{ensure_layout, WideNameInfo}; 2 | 3 | use crate::filesystem::widenameinfo::WideNameInfoInternal; 4 | use winfsp_sys::{FspFileSystemAddNotifyInfo, FSP_FSCTL_NOTIFY_INFO}; 5 | 6 | #[repr(C)] 7 | #[derive(Debug, Clone)] 8 | /// Information about a filesystem event. 9 | /// 10 | /// ## Safety 11 | /// Note that `BUFFER_SIZE` is the size of the name buffer in characters, not bytes. 12 | /// In most cases, the default is sufficient. A buffer size that is too large 13 | /// may not be copyable to the request buffer. 14 | pub struct NotifyInfo { 15 | pub(crate) size: u16, 16 | /// The filter for this event. 17 | pub filter: u32, 18 | /// The event action that occurred. 19 | pub action: u32, 20 | file_name: [u16; BUFFER_SIZE], 21 | } 22 | 23 | ensure_layout!(FSP_FSCTL_NOTIFY_INFO, NotifyInfo<0>); 24 | impl NotifyInfo { 25 | /// Create a new, empty `NotifyInfo`. 26 | pub fn new() -> Self { 27 | Self { 28 | // begin with initially no file_name 29 | size: std::mem::size_of::>() as u16, 30 | filter: 0, 31 | action: 0, 32 | file_name: [0; BUFFER_SIZE], 33 | } 34 | } 35 | } 36 | 37 | impl Default for NotifyInfo { 38 | fn default() -> Self { 39 | Self::new() 40 | } 41 | } 42 | 43 | impl WideNameInfoInternal for NotifyInfo { 44 | fn name_buffer(&mut self) -> &mut [u16; BUFFER_SIZE] { 45 | &mut self.file_name 46 | } 47 | 48 | fn set_size(&mut self, buffer_size: u16) { 49 | self.size = std::mem::size_of::>() as u16 + buffer_size; 50 | } 51 | fn add_to_buffer_internal(entry: Option<&Self>, buffer: &mut [u8], cursor: &mut u32) -> bool { 52 | unsafe { 53 | // SAFETY: https://github.com/winfsp/winfsp/blob/0a91292e0502d6629f9a968a168c6e89eea69ea1/src/dll/fsop.c#L1500 54 | // does not mutate entry. 55 | if let Some(entry) = entry { 56 | FspFileSystemAddNotifyInfo( 57 | (entry as *const Self).cast_mut().cast(), 58 | buffer.as_mut_ptr().cast(), 59 | buffer.len() as u32, 60 | cursor, 61 | ) != 0 62 | } else { 63 | FspFileSystemAddNotifyInfo( 64 | std::ptr::null_mut(), 65 | buffer.as_mut_ptr().cast(), 66 | buffer.len() as u32, 67 | cursor, 68 | ) != 0 69 | } 70 | } 71 | } 72 | } 73 | 74 | impl WideNameInfo for NotifyInfo { 75 | fn reset(&mut self) { 76 | self.size = std::mem::size_of::>() as u16; 77 | self.filter = 0; 78 | self.action = 0; 79 | self.file_name = [0; BUFFER_SIZE]; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /winfsp/src/notify/timer.rs: -------------------------------------------------------------------------------- 1 | use crate::host::interface::FileSystemUserContext; 2 | use crate::notify::{Notifier, NotifyingFileSystemContext}; 3 | use std::ptr::NonNull; 4 | use windows::core::Result; 5 | use windows::Win32::Foundation::{NTSTATUS, STATUS_SUCCESS}; 6 | use windows::Win32::System::Threading::{ 7 | CloseThreadpoolTimer, CreateThreadpoolTimer, SetThreadpoolTimer, PTP_CALLBACK_INSTANCE, 8 | PTP_TIMER, 9 | }; 10 | use winfsp_sys::{FspFileSystemNotifyBegin, FspFileSystemNotifyEnd, FSP_FILE_SYSTEM}; 11 | 12 | pub struct Timer(PTP_TIMER); 13 | 14 | impl Timer { 15 | pub fn create, const TIMEOUT: u32>( 16 | fs: NonNull, 17 | ) -> Result { 18 | let mut timer = Self(PTP_TIMER::default()); 19 | timer.0 = unsafe { 20 | CreateThreadpoolTimer( 21 | Some(timer_callback::), 22 | Some(fs.as_ptr().cast()), 23 | None, 24 | )? 25 | }; 26 | 27 | let timer_due = -(TIMEOUT as i64); 28 | unsafe { 29 | SetThreadpoolTimer( 30 | timer.0, 31 | Some(&timer_due as *const i64 as *const _), 32 | TIMEOUT, 33 | 0, 34 | ); 35 | } 36 | Ok(timer) 37 | } 38 | } 39 | 40 | impl Drop for Timer { 41 | fn drop(&mut self) { 42 | unsafe { CloseThreadpoolTimer(self.0) } 43 | } 44 | } 45 | 46 | unsafe extern "system" fn timer_callback< 47 | R, 48 | T: NotifyingFileSystemContext, 49 | const TIMEOUT: u32, 50 | >( 51 | _instance: PTP_CALLBACK_INSTANCE, 52 | context: *mut core::ffi::c_void, 53 | _timer: PTP_TIMER, 54 | ) { 55 | let fs = context.cast::(); 56 | if fs.is_null() { 57 | panic!("Timer callback was passed in a null pointer") 58 | } 59 | let context: &FileSystemUserContext = 60 | unsafe { &*(*fs).UserContext.cast::>() }; 61 | let notifier = Notifier(fs); 62 | if let Some(val) = context.should_notify() { 63 | unsafe { 64 | if NTSTATUS(FspFileSystemNotifyBegin(fs, TIMEOUT)) == STATUS_SUCCESS { 65 | context.notify(val, ¬ifier) 66 | }; 67 | FspFileSystemNotifyEnd(fs); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /winfsp/src/service.rs: -------------------------------------------------------------------------------- 1 | //! Interfaces to the WinFSP service API to run a filesystem. 2 | use crate::error::FspError; 3 | use crate::util::AssertThreadSafe; 4 | use crate::FspInit; 5 | use crate::Result; 6 | use std::cell::UnsafeCell; 7 | use std::ffi::{c_void, OsStr}; 8 | use std::marker::PhantomData; 9 | use std::ptr::{addr_of_mut, NonNull}; 10 | use std::thread::JoinHandle; 11 | use windows::core::HSTRING; 12 | use windows::Win32::Foundation::{NTSTATUS, STATUS_INVALID_PARAMETER, STATUS_SUCCESS}; 13 | use winfsp_sys::{ 14 | FspServiceAllowConsoleMode, FspServiceCreate, FspServiceLoop, FspServiceStop, FSP_SERVICE, 15 | }; 16 | 17 | // internal aliases for callback types 18 | type FileSystemStartCallback = Option std::result::Result>>; 19 | type FileSystemStopCallback = 20 | Option) -> std::result::Result<(), NTSTATUS>>>; 21 | type FileSystemControlCallback = 22 | Option, u32, u32, *mut c_void) -> i32>>; 23 | struct FileSystemServiceContext { 24 | start: FileSystemStartCallback, 25 | stop: FileSystemStopCallback, 26 | control: FileSystemControlCallback, 27 | context: Option>, 28 | } 29 | 30 | /// A service that runs a filesystem implemented by a [`FileSystemHost`](crate::host::FileSystemHost). 31 | pub struct FileSystemService(NonNull, PhantomData); 32 | impl FileSystemService { 33 | /// # Safety 34 | /// `raw` is valid and not null. 35 | unsafe fn from_raw_unchecked(raw: *mut FSP_SERVICE) -> Self { 36 | unsafe { FileSystemService(NonNull::new_unchecked(raw), Default::default()) } 37 | } 38 | 39 | /// Set the context. 40 | fn set_context(&mut self, context: T) { 41 | unsafe { 42 | let ptr: *mut UnsafeCell> = 43 | self.0.as_mut().UserContext.cast(); 44 | if let Some(ptr) = ptr.as_mut() { 45 | ptr.get_mut().context = Some(Box::new(context)) 46 | } 47 | } 48 | } 49 | 50 | fn get_context(&mut self) -> Option<&mut T> { 51 | unsafe { 52 | if let Some(p) = self 53 | .0 54 | .as_mut() 55 | .UserContext 56 | .cast::>>() 57 | .as_mut() 58 | { 59 | p.get_mut().context.as_deref_mut() 60 | } else { 61 | None 62 | } 63 | } 64 | } 65 | } 66 | 67 | impl FileSystemService { 68 | /// Stops the file system host service. 69 | pub fn stop(&self) { 70 | unsafe { 71 | FspServiceStop(self.0.as_ptr()); 72 | }; 73 | } 74 | 75 | /// Spawns a thread and starts the file host system service. 76 | pub fn start(&self) -> JoinHandle> { 77 | let ptr = AssertThreadSafe(self.0.as_ptr()); 78 | std::thread::spawn(|| { 79 | #[allow(clippy::redundant_locals)] 80 | let ptr = ptr; 81 | let result = unsafe { 82 | FspServiceAllowConsoleMode(ptr.0); 83 | FspServiceLoop(ptr.0) 84 | }; 85 | 86 | if result == STATUS_SUCCESS.0 { 87 | Ok(()) 88 | } else { 89 | Err(FspError::NTSTATUS(result)) 90 | } 91 | }) 92 | } 93 | } 94 | 95 | /// A builder for [`FileSystemService`](crate::service::FileSystemService). 96 | pub struct FileSystemServiceBuilder { 97 | stop: FileSystemStopCallback, 98 | start: FileSystemStartCallback, 99 | control: FileSystemControlCallback, 100 | } 101 | 102 | impl Default for FileSystemServiceBuilder { 103 | fn default() -> Self { 104 | Self::new() 105 | } 106 | } 107 | 108 | impl FileSystemServiceBuilder { 109 | /// Create a new instance of the builder. 110 | pub fn new() -> Self { 111 | Self { 112 | stop: None, 113 | start: None, 114 | control: None, 115 | } 116 | } 117 | 118 | /// The start callback provides the file system context and mounts the file system. 119 | /// The returned file system context must be mounted before returning. 120 | pub fn with_start(mut self, start: F) -> Self 121 | where 122 | F: Fn() -> std::result::Result + 'static, 123 | { 124 | self.start = Some(Box::new(start)); 125 | self 126 | } 127 | 128 | /// The stop callback is responsible for safely terminating the mounted file system. 129 | pub fn with_stop(mut self, stop: F) -> Self 130 | where 131 | F: Fn(Option<&mut T>) -> std::result::Result<(), NTSTATUS> + 'static, 132 | { 133 | self.stop = Some(Box::new(stop)); 134 | self 135 | } 136 | 137 | /// The control callback handles DeviceIoControl requests. 138 | pub fn with_control(mut self, control: F) -> Self 139 | where 140 | F: Fn(Option<&mut T>, u32, u32, *mut c_void) -> i32 + 'static, 141 | { 142 | self.control = Some(Box::new(control)); 143 | self 144 | } 145 | 146 | /// Create the [`FileSystemService`](crate::service::FileSystemService) with the provided 147 | /// callbacks. 148 | pub fn build( 149 | self, 150 | service_name: impl AsRef, 151 | _init: FspInit, 152 | ) -> Result> { 153 | let service = UnsafeCell::new(std::ptr::null_mut()); 154 | let service_name = HSTRING::from(service_name.as_ref()); 155 | let result = unsafe { 156 | // SAFETY: service_name is never mutated. 157 | // https://github.com/winfsp/winfsp/blob/0ab4300738233eba4a37e1302e55fff6f0c4f5ab/src/dll/service.c#L108 158 | FspServiceCreate( 159 | service_name.as_ptr().cast_mut(), 160 | Some(on_start::), 161 | Some(on_stop::), 162 | Some(on_control::), 163 | service.get(), 164 | ) 165 | }; 166 | 167 | unsafe { 168 | addr_of_mut!((*(*service.get())).UserContext).write(Box::into_raw(Box::new( 169 | UnsafeCell::new(FileSystemServiceContext:: { 170 | start: self.start, 171 | stop: self.stop, 172 | control: self.control, 173 | context: None, 174 | }), 175 | )) as *mut _) 176 | } 177 | if result == STATUS_SUCCESS.0 && unsafe { !service.get().read().is_null() } { 178 | Ok(unsafe { FileSystemService::from_raw_unchecked(service.get().read()) }) 179 | } else { 180 | Err(FspError::NTSTATUS(result)) 181 | } 182 | } 183 | } 184 | 185 | unsafe extern "C" fn on_start(fsp: *mut FSP_SERVICE, _argc: u32, _argv: *mut *mut u16) -> i32 { 186 | if let Some(context) = unsafe { 187 | fsp.as_mut() 188 | .unwrap_unchecked() 189 | .UserContext 190 | .cast::>() 191 | .as_mut() 192 | } { 193 | if let Some(start) = &context.start { 194 | return match start() { 195 | Err(e) => e.0, 196 | Ok(context) => { 197 | unsafe { 198 | FileSystemService::from_raw_unchecked(fsp).set_context(context); 199 | } 200 | STATUS_SUCCESS.0 201 | } 202 | }; 203 | } 204 | } 205 | STATUS_INVALID_PARAMETER.0 206 | } 207 | 208 | unsafe extern "C" fn on_stop(fsp: *mut FSP_SERVICE) -> i32 { 209 | if let Some(context) = unsafe { 210 | fsp.as_mut() 211 | .unwrap_unchecked() 212 | .UserContext 213 | .cast::>() 214 | .as_mut() 215 | } { 216 | if let Some(stop) = &context.stop { 217 | let mut fsp = unsafe { FileSystemService::from_raw_unchecked(fsp) }; 218 | let context = fsp.get_context(); 219 | 220 | return match stop(context) { 221 | Ok(()) => STATUS_SUCCESS.0, 222 | Err(e) => e.0, 223 | }; 224 | } 225 | } 226 | STATUS_INVALID_PARAMETER.0 227 | } 228 | 229 | unsafe extern "C" fn on_control( 230 | fsp: *mut FSP_SERVICE, 231 | ctl: u32, 232 | event_type: u32, 233 | event_data: *mut c_void, 234 | ) -> i32 { 235 | if let Some(context) = unsafe { 236 | fsp.as_mut() 237 | .unwrap_unchecked() 238 | .UserContext 239 | .cast::>() 240 | .as_mut() 241 | } { 242 | if let Some(control) = &context.control { 243 | let mut fsp = unsafe { FileSystemService::from_raw_unchecked(fsp) }; 244 | let context = fsp.get_context(); 245 | 246 | return control(context, ctl, event_type, event_data); 247 | } 248 | } 249 | STATUS_INVALID_PARAMETER.0 250 | } 251 | -------------------------------------------------------------------------------- /winfsp/src/util/handle.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use std::mem::ManuallyDrop; 4 | use std::ops::{Deref, DerefMut}; 5 | use std::sync::atomic::{AtomicIsize, Ordering}; 6 | 7 | use windows::Wdk::Foundation::NtClose; 8 | use windows::Win32::Foundation::{CloseHandle, HANDLE, INVALID_HANDLE_VALUE}; 9 | 10 | /// An owned handle that will always be dropped when it goes out of scope. 11 | /// 12 | /// ## Safety 13 | /// This handle will become invalid when it goes out of scope. 14 | /// `SafeDropHandle` implements `Deref` to make it 15 | /// usable for APIs that take `HANDLE`. Dereference the `SafeDropHandle` 16 | /// to obtain a `HANDLE` that is `Copy` without dropping the `SafeDropHandle` 17 | /// and invalidating the underlying handle. 18 | #[repr(transparent)] 19 | #[derive(Debug)] 20 | pub struct SafeDropHandle(HANDLE, PhantomData) 21 | where 22 | T: HandleCloseHandler; 23 | 24 | /// A handle that can be atomically invalidated. 25 | /// 26 | /// ## Safety 27 | /// This handle will become invalid when it goes out of scope. 28 | /// Use [`AtomicHandle::handle`](AtomicHandle::handle) to obtain a `HANDLE` that is `Copy` 29 | /// without dropping the `AtomicHandle` and invalidating the underlying handle. 30 | #[repr(transparent)] 31 | #[derive(Debug)] 32 | pub struct AtomicHandle(AtomicIsize, PhantomData) 33 | where 34 | T: HandleCloseHandler; 35 | 36 | /// Trait that defines a method to close a Windows HANDLE. 37 | pub trait HandleCloseHandler { 38 | /// Close the handle. 39 | fn close(handle: HANDLE); 40 | } 41 | 42 | /// Handle drop strategy for Win32 handles. 43 | #[derive(Debug)] 44 | pub struct Win32HandleDrop; 45 | 46 | /// A Win32 HANDLE that is closed when it goes out of scope. 47 | pub type Win32SafeHandle = SafeDropHandle; 48 | impl HandleCloseHandler for Win32HandleDrop { 49 | fn close(handle: HANDLE) { 50 | if let Err(e) = unsafe { CloseHandle(handle) } { 51 | eprintln!("unable to close win32 handle {:x?}: {:?}", handle, e) 52 | } 53 | } 54 | } 55 | 56 | /// Handle drop strategy for NT handles. 57 | #[derive(Debug)] 58 | pub struct NtHandleDrop; 59 | /// An NT HANDLE that is closed when it goes out of scope. 60 | pub type NtSafeHandle = SafeDropHandle; 61 | impl HandleCloseHandler for NtHandleDrop { 62 | fn close(handle: HANDLE) { 63 | if let Err(e) = unsafe { NtClose(handle).ok() } { 64 | eprintln!("unable to close nt handle {:x?}: {:?}", handle, e) 65 | } 66 | } 67 | } 68 | 69 | impl SafeDropHandle 70 | where 71 | T: HandleCloseHandler, 72 | { 73 | /// Invalidate the handle without dropping it. 74 | pub fn invalidate(&mut self) { 75 | if !self.is_invalid() { 76 | T::close(self.0) 77 | } 78 | self.0 = INVALID_HANDLE_VALUE 79 | } 80 | } 81 | 82 | impl Drop for SafeDropHandle 83 | where 84 | T: HandleCloseHandler, 85 | { 86 | fn drop(&mut self) { 87 | if !self.is_invalid() { 88 | T::close(self.0) 89 | } 90 | } 91 | } 92 | 93 | impl Drop for AtomicHandle 94 | where 95 | T: HandleCloseHandler, 96 | { 97 | fn drop(&mut self) { 98 | let handle = HANDLE(self.0.load(Ordering::Acquire)); 99 | if !handle.is_invalid() { 100 | T::close(handle) 101 | } 102 | } 103 | } 104 | 105 | impl AtomicHandle 106 | where 107 | T: HandleCloseHandler, 108 | { 109 | /// Atomically load the handle with acquire ordering 110 | pub fn handle(&self) -> HANDLE { 111 | let handle = self.0.load(Ordering::Acquire); 112 | HANDLE(handle) 113 | } 114 | 115 | /// Whether or not this handle is invalid. 116 | pub fn is_invalid(&self) -> bool { 117 | self.handle().is_invalid() 118 | } 119 | 120 | /// Invalidate the handle without dropping it. 121 | pub fn invalidate(&self) { 122 | let handle = self.handle(); 123 | 124 | if !handle.is_invalid() { 125 | T::close(handle) 126 | } 127 | self.0.store(INVALID_HANDLE_VALUE.0, Ordering::Relaxed); 128 | } 129 | } 130 | 131 | impl Deref for SafeDropHandle 132 | where 133 | T: HandleCloseHandler, 134 | { 135 | type Target = HANDLE; 136 | 137 | fn deref(&self) -> &Self::Target { 138 | &self.0 139 | } 140 | } 141 | 142 | impl DerefMut for SafeDropHandle 143 | where 144 | T: HandleCloseHandler, 145 | { 146 | fn deref_mut(&mut self) -> &mut Self::Target { 147 | &mut self.0 148 | } 149 | } 150 | 151 | impl From for SafeDropHandle 152 | where 153 | T: HandleCloseHandler, 154 | { 155 | fn from(h: HANDLE) -> Self { 156 | Self(h, PhantomData) 157 | } 158 | } 159 | 160 | impl From for AtomicHandle 161 | where 162 | T: HandleCloseHandler, 163 | { 164 | fn from(h: HANDLE) -> Self { 165 | Self(AtomicIsize::new(h.0), PhantomData) 166 | } 167 | } 168 | 169 | impl From> for AtomicHandle 170 | where 171 | T: HandleCloseHandler, 172 | { 173 | fn from(h: SafeDropHandle) -> Self { 174 | // forbid SafeDropHandle from running `Drop` 175 | let h = ManuallyDrop::new(h); 176 | Self(AtomicIsize::new(h.0 .0), PhantomData) 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /winfsp/src/util/mod.rs: -------------------------------------------------------------------------------- 1 | //! Helpful utility wrappers around OS constructs. 2 | 3 | #[cfg(feature = "handle-util")] 4 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "handle-util")))] 5 | mod handle; 6 | 7 | #[cfg(feature = "handle-util")] 8 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "handle-util")))] 9 | pub use handle::*; 10 | 11 | pub use crate::vsb::VariableSizedBox; 12 | 13 | #[derive(Debug)] 14 | #[repr(transparent)] 15 | pub(crate) struct AssertThreadSafe(pub T); 16 | 17 | unsafe impl Send for AssertThreadSafe {} 18 | 19 | unsafe impl Sync for AssertThreadSafe {} 20 | -------------------------------------------------------------------------------- /winfsp/src/vsb.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 2 | // or the MIT license 3 | // , at your option. 4 | // All files in the project carrying such notice may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use std::{ 7 | alloc::{alloc_zeroed, dealloc, handle_alloc_error, Layout}, 8 | marker::PhantomData, 9 | mem::{align_of, size_of}, 10 | ptr::{self, NonNull}, 11 | }; 12 | /// This is a smart pointer type for holding FFI types whose size varies. 13 | /// Most commonly this is with an array member as the last field whose size is specified 14 | /// by either another field, or an external source of information. 15 | pub struct VariableSizedBox { 16 | size: usize, 17 | data: NonNull, 18 | pd: PhantomData, 19 | } 20 | impl VariableSizedBox { 21 | /// The size is specified in bytes. The data is zeroed. 22 | pub fn new(size: usize) -> VariableSizedBox { 23 | if size == 0 { 24 | return VariableSizedBox::default(); 25 | } 26 | let layout = Layout::from_size_align(size, align_of::()).unwrap(); 27 | if let Some(data) = NonNull::new(unsafe { alloc_zeroed(layout) }) { 28 | VariableSizedBox { 29 | size, 30 | data: data.cast(), 31 | pd: PhantomData, 32 | } 33 | } else { 34 | handle_alloc_error(layout) 35 | } 36 | } 37 | /// Use this to get a pointer to pass to FFI functions. 38 | pub fn as_mut_ptr(&mut self) -> *mut T { 39 | if self.size == 0 { 40 | ptr::null_mut() 41 | } else { 42 | self.data.as_ptr() 43 | } 44 | } 45 | /// This is used to more safely access the fixed size fields. 46 | /// # Safety 47 | /// The current data must be valid for an instance of `T`. 48 | pub unsafe fn as_ref(&self) -> &T { 49 | assert!(self.size >= size_of::()); 50 | unsafe { self.data.as_ref() } 51 | } 52 | 53 | #[allow(clippy::len_without_is_empty)] 54 | /// The length of the allocation specified in bytes. 55 | pub fn len(&self) -> usize { 56 | self.size 57 | } 58 | } 59 | impl Drop for VariableSizedBox { 60 | fn drop(&mut self) { 61 | if self.size == 0 { 62 | return; 63 | } 64 | let layout = Layout::from_size_align(self.size, align_of::()).unwrap(); 65 | unsafe { dealloc(self.as_mut_ptr().cast(), layout) } 66 | } 67 | } 68 | impl Default for VariableSizedBox { 69 | fn default() -> Self { 70 | VariableSizedBox { 71 | size: 0, 72 | data: NonNull::dangling(), 73 | pd: PhantomData, 74 | } 75 | } 76 | } 77 | --------------------------------------------------------------------------------