├── .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 | [](https://crates.io/crates/winfsp) [](https://docs.rs/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 | [](https://crates.io/crates/winfsp-sys) [](https://docs.rs/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