├── .gitignore ├── deprecated ├── ptfs-winfsp-rs │ ├── .gitignore │ ├── src │ │ ├── fs │ │ │ └── mod.rs │ │ ├── service.rs │ │ └── main.rs │ ├── build.rs │ ├── Cargo.toml │ └── Cargo.lock └── README.md ├── filesystems └── ntptfs-winfsp-rs │ ├── .gitignore │ ├── src │ ├── native │ │ ├── mod.rs │ │ ├── volume.rs │ │ └── lfs │ │ │ └── async_io.rs │ ├── fs │ │ ├── mod.rs │ │ ├── ntptfs.rs │ │ └── file.rs │ ├── lib.rs │ ├── service.rs │ └── main.rs │ ├── build.rs │ ├── Cargo.toml │ └── Cargo.lock ├── winfsp-sys ├── .gitattributes ├── winfsp │ ├── .gitrev │ ├── bin │ │ ├── winfsp-a64.dll │ │ ├── winfsp-msil.dll │ │ ├── winfsp-x64.dll │ │ └── winfsp-x86.dll │ ├── lib │ │ ├── winfsp-a64.lib │ │ ├── winfsp-x64.lib │ │ ├── winfsp-x86.lib │ │ ├── fuse.pc │ │ └── fuse3.pc │ └── inc │ │ ├── fuse3 │ │ ├── fuse_opt.h │ │ ├── winfsp_fuse.h │ │ ├── fuse_common.h │ │ └── fuse.h │ │ ├── fuse │ │ ├── fuse_opt.h │ │ ├── fuse_common.h │ │ ├── fuse.h │ │ └── winfsp_fuse.h │ │ └── winfsp │ │ └── launch.h ├── src │ └── lib.rs ├── Cargo.toml ├── README.md └── build.rs ├── test └── winfsp-test │ ├── winfsp-x64.dll │ └── winfsp-tests-x64.exe ├── Cargo.toml ├── .idea ├── vcs.xml ├── .gitignore ├── modules.xml └── winfsp-rs.iml ├── winfsp ├── src │ ├── notify │ │ ├── mod.rs │ │ ├── context.rs │ │ ├── notifier.rs │ │ ├── timer.rs │ │ └── notifyinfo.rs │ ├── host │ │ ├── mod.rs │ │ ├── debug.rs │ │ ├── volumeparams.rs │ │ └── fshost.rs │ ├── util │ │ ├── mod.rs │ │ └── handle.rs │ ├── filesystem │ │ ├── mod.rs │ │ ├── internals │ │ │ ├── mod.rs │ │ │ ├── volumeinfo.rs │ │ │ ├── widenameinfo.rs │ │ │ └── fileinfo.rs │ │ ├── stream.rs │ │ └── directory.rs │ ├── lib.rs │ ├── vsb.rs │ ├── constants.rs │ ├── init.rs │ ├── error.rs │ └── service.rs └── Cargo.toml ├── .vscode └── launch.json ├── CppProperties.json ├── .github └── workflows │ ├── build.yml │ └── ntptfs.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | .vs/ 3 | -------------------------------------------------------------------------------- /deprecated/ptfs-winfsp-rs/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /winfsp-sys/.gitattributes: -------------------------------------------------------------------------------- 1 | winfsp/** linguist-vendored 2 | -------------------------------------------------------------------------------- /winfsp-sys/winfsp/.gitrev: -------------------------------------------------------------------------------- 1 | 5251dcbce93a334cc2b69565eb43de42eeaa2a64 -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/src/native/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod lfs; 2 | pub mod volume; 3 | -------------------------------------------------------------------------------- /deprecated/ptfs-winfsp-rs/src/fs/mod.rs: -------------------------------------------------------------------------------- 1 | mod passthrough; 2 | 3 | pub use passthrough::Ptfs; 4 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/src/fs/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod context; 2 | pub mod file; 3 | pub mod ntptfs; 4 | -------------------------------------------------------------------------------- /deprecated/ptfs-winfsp-rs/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | winfsp::build::winfsp_link_delayload(); 3 | } 4 | -------------------------------------------------------------------------------- /test/winfsp-test/winfsp-x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/HEAD/test/winfsp-test/winfsp-x64.dll -------------------------------------------------------------------------------- /test/winfsp-test/winfsp-tests-x64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/HEAD/test/winfsp-test/winfsp-tests-x64.exe -------------------------------------------------------------------------------- /winfsp-sys/winfsp/bin/winfsp-a64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/HEAD/winfsp-sys/winfsp/bin/winfsp-a64.dll -------------------------------------------------------------------------------- /winfsp-sys/winfsp/bin/winfsp-msil.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/HEAD/winfsp-sys/winfsp/bin/winfsp-msil.dll -------------------------------------------------------------------------------- /winfsp-sys/winfsp/bin/winfsp-x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/HEAD/winfsp-sys/winfsp/bin/winfsp-x64.dll -------------------------------------------------------------------------------- /winfsp-sys/winfsp/bin/winfsp-x86.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/HEAD/winfsp-sys/winfsp/bin/winfsp-x86.dll -------------------------------------------------------------------------------- /winfsp-sys/winfsp/lib/winfsp-a64.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/HEAD/winfsp-sys/winfsp/lib/winfsp-a64.lib -------------------------------------------------------------------------------- /winfsp-sys/winfsp/lib/winfsp-x64.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/HEAD/winfsp-sys/winfsp/lib/winfsp-x64.lib -------------------------------------------------------------------------------- /winfsp-sys/winfsp/lib/winfsp-x86.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnowflakePowered/winfsp-rs/HEAD/winfsp-sys/winfsp/lib/winfsp-x86.lib -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | winfsp::build::winfsp_link_delayload(); 3 | // println!("cargo:rustc-link-lib=ntdll"); 4 | } 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "filesystems/ntptfs-winfsp-rs", 5 | "winfsp", 6 | "winfsp-sys" 7 | ] 8 | 9 | resolver = "2" 10 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_op_in_unsafe_fn)] 2 | mod fs; 3 | mod native; 4 | 5 | pub use fs::context::NtPassthroughContext; 6 | pub use fs::file::NtPassthroughFile; 7 | pub use fs::ntptfs::NtPassthroughFilesystem; 8 | -------------------------------------------------------------------------------- /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-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 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "winrepro", 5 | "type": "cppvsdbg", 6 | "request": "launch", 7 | "program": "${workspaceFolder}/target/debug/my-winfsp.exe", 8 | "args": [ 9 | //"no_launcher" 10 | ], 11 | "stopAtEntry": false, 12 | "cwd": "${workspaceFolder}", 13 | "environment": [], 14 | "console": "integratedTerminal", 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /CppProperties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "inheritEnvironments": [ 5 | "msvc_x86" 6 | ], 7 | "name": "x86-Debug", 8 | "includePath": [ 9 | "${env.INCLUDE}", 10 | "${workspaceRoot}\\**" 11 | ], 12 | "defines": [ 13 | "WIN32", 14 | "_DEBUG", 15 | "UNICODE", 16 | "_UNICODE" 17 | ], 18 | "intelliSenseMode": "windows-msvc-x86", 19 | "program": "${workspaceFolder}/target/debug/my-winfsp.exe" 20 | } 21 | 22 | ] 23 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /winfsp/src/util/mod.rs: -------------------------------------------------------------------------------- 1 | //! Helpful utility wrappers around OS constructs. 2 | 3 | #[cfg(feature = "handle-util")] 4 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "handle-util")))] 5 | mod handle; 6 | 7 | #[cfg(feature = "handle-util")] 8 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "handle-util")))] 9 | pub use handle::*; 10 | 11 | pub use crate::vsb::VariableSizedBox; 12 | 13 | #[derive(Debug)] 14 | #[repr(transparent)] 15 | pub(crate) struct AssertThreadSafe(pub T); 16 | 17 | unsafe impl Send for AssertThreadSafe {} 18 | 19 | unsafe impl Sync for AssertThreadSafe {} 20 | -------------------------------------------------------------------------------- /winfsp-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/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 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/src/service.rs: -------------------------------------------------------------------------------- 1 | use crate::Args; 2 | use crate::fs::ntptfs::NtPassthroughFilesystem; 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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /winfsp-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winfsp-sys" 3 | version = "0.12.1+winfsp-2.1" 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.72.0" 14 | windows-registry = { version = "0.6.0", 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/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/notify/notifier.rs: -------------------------------------------------------------------------------- 1 | use crate::notify::NotifyInfo; 2 | use winfsp_sys::{FSP_FILE_SYSTEM, FspFileSystemNotify}; 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/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-sys/README.md: -------------------------------------------------------------------------------- 1 | # winfsp-sys 2 | 3 | [![Latest Version](https://img.shields.io/crates/v/winfsp-sys.svg)](https://crates.io/crates/winfsp-sys) [![Docs](https://docs.rs/winfsp-sys/badge.svg)](https://docs.rs/winfsp-sys) ![License](https://img.shields.io/crates/l/winfsp-sys) 4 | 5 | 6 | Raw FFI bindings to [WinFSP](https://github.com/winfsp/winfsp). 7 | 8 | ## Usage 9 | The [winfsp](https://crates.io/crates/winfsp) crate provides idiomatic wrappers around the raw WinFSP APIs. 10 | 11 | By default, winfsp-sys builds against an included import library. To build against the installed WinFSP libraries, enable the `system` 12 | feature. The path will automatically be determined via the Registry. 13 | 14 | ```toml 15 | [dependencies.winfsp-sys] 16 | version = "0.2" 17 | features = ["system"] 18 | ``` -------------------------------------------------------------------------------- /deprecated/ptfs-winfsp-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ptfs-winfsp-rs" 3 | version = "0.1.0" 4 | edition = "2024" 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 | -------------------------------------------------------------------------------- /winfsp-sys/winfsp/inc/fuse3/fuse_opt.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fuse3/fuse_opt.h 3 | * WinFsp FUSE3 compatible API. 4 | * 5 | * @copyright 2015-2025 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 | -------------------------------------------------------------------------------- /.idea/winfsp-rs.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ntptfs-winfsp-rs" 3 | version = "0.12.1" 4 | edition = "2024" 5 | license = "GPL-3.0" 6 | description = "NTFS Passthrough File System via winfsp-rs" 7 | keywords = ["filesystem", "winfsp", "ntfs"] 8 | categories = ["filesystem", "os::windows-apis",] 9 | repository = "https://github.com/SnowflakePowered/winfsp-rs" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | clap = { version = "4.5.4", features = ["derive"] } 15 | windows = { version = "0.62", features = ["Win32_Foundation", "Win32_System_LibraryLoader", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_WindowsProgramming", "Win32_System_Console", "Win32_System_IO", "Wdk_Foundation", "Wdk_Storage_FileSystem", "Wdk_System_IO", "Wdk_Storage_FileSystem_Minifilters", "Wdk_System_SystemServices", "Win32_System_Ioctl", "Win32_System_SystemServices", "Win32_System_Threading"] } 16 | 17 | bytemuck = "1.12.1" 18 | widestring = "1" 19 | winfsp = { path = "../../winfsp", features = ["debug", "system", "full", "windows-62", "handle-util"] } 20 | anyhow = "1" 21 | tokio = { version = "1.32.0", features = ["rt", "rt-multi-thread"] } 22 | 23 | [build-dependencies] 24 | winfsp = { path = "../../winfsp", features = ["delayload"] } 25 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/src/main.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_op_in_unsafe_fn)] 2 | 3 | pub mod fs; 4 | mod native; 5 | mod service; 6 | 7 | use clap::Parser; 8 | use std::path::PathBuf; 9 | use windows::Win32::Foundation::STATUS_NONCONTINUABLE_EXCEPTION; 10 | use winfsp::service::FileSystemServiceBuilder; 11 | use winfsp::winfsp_init_or_die; 12 | 13 | /// MainArgs 14 | #[derive(Parser, Debug)] 15 | #[clap(author, version, about, long_about = None,)] 16 | pub struct Args { 17 | /// -1: enable all debug logs 18 | #[clap(short = 'd', default_value = "0")] 19 | flags: i32, 20 | 21 | /// file path 22 | #[clap(short = 'D', long)] 23 | logfile: Option, 24 | 25 | #[clap(short = 'u', long)] 26 | volume_prefix: Option, 27 | 28 | #[clap(short = 'p', long)] 29 | directory: PathBuf, 30 | 31 | #[clap(short = 'm', long)] 32 | mountpoint: PathBuf, 33 | } 34 | 35 | fn main() { 36 | let init = winfsp_init_or_die(); 37 | let fsp = FileSystemServiceBuilder::new() 38 | .with_start(|| { 39 | let args = Args::parse(); 40 | Ok(service::svc_start(args).map_err(|_e| STATUS_NONCONTINUABLE_EXCEPTION)?) 41 | }) 42 | .with_stop(|f| { 43 | service::svc_stop(f); 44 | Ok(()) 45 | }) 46 | .build("ntptfs-winfsp-rs", init) 47 | .expect("failed to build fsp"); 48 | 49 | let _ = fsp.start().join(); 50 | } 51 | -------------------------------------------------------------------------------- /deprecated/ptfs-winfsp-rs/src/main.rs: -------------------------------------------------------------------------------- 1 | #![deny(unsafe_op_in_unsafe_fn)] 2 | 3 | mod fs; 4 | mod service; 5 | 6 | use clap::Parser; 7 | use std::path::PathBuf; 8 | use std::time::Duration; 9 | use windows::Win32::Foundation::STATUS_NONCONTINUABLE_EXCEPTION; 10 | use winfsp::service::FileSystemServiceBuilder; 11 | use winfsp::winfsp_init_or_die; 12 | 13 | /// MainArgs 14 | #[derive(Parser, Debug)] 15 | #[clap(author, version, about, long_about = None,)] 16 | pub struct Args { 17 | /// -1: enable all debug logs 18 | #[clap(short = 'd', default_value = "0")] 19 | flags: u32, 20 | 21 | /// file path 22 | #[clap(short = 'D', long)] 23 | logfile: Option, 24 | 25 | #[clap(short = 'u', long)] 26 | volume_prefix: Option, 27 | 28 | #[clap(short = 'p', long)] 29 | directory: PathBuf, 30 | 31 | #[clap(short = 'm', long)] 32 | mountpoint: PathBuf, 33 | } 34 | 35 | fn main() { 36 | let init = winfsp_init_or_die(); 37 | let fsp = FileSystemServiceBuilder::new() 38 | .with_start(|| { 39 | let args = Args::parse(); 40 | service::svc_start(args).map_err(|_e| STATUS_NONCONTINUABLE_EXCEPTION) 41 | }) 42 | .with_stop(|f| { 43 | service::svc_stop(f); 44 | Ok(()) 45 | }) 46 | .build("ptfs-winfsp-rs", init) 47 | .expect("failed to build fsp"); 48 | 49 | fsp.start(); 50 | std::thread::sleep(Duration::MAX); 51 | } 52 | -------------------------------------------------------------------------------- /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 | use winfsp::host::{DebugMode, FileSystemHost, FileSystemParams, VolumeParams}; 6 | 7 | /// An passthrough filesystem using the NT API. 8 | pub struct NtPassthroughFilesystem { 9 | /// The host for this filesystem. 10 | pub fs: FileSystemHost, 11 | } 12 | 13 | impl NtPassthroughFilesystem { 14 | pub fn create>(path: P, volume_prefix: &str) -> anyhow::Result { 15 | let metadata = std::fs::metadata(&path)?; 16 | if !metadata.is_dir() { 17 | return Err(std::io::Error::new(ErrorKind::NotADirectory, "not a directory").into()); 18 | } 19 | let canonical_path = std::fs::canonicalize(&path)?; 20 | 21 | let mut volume_params = VolumeParams::new(); 22 | volume_params 23 | .prefix(volume_prefix) 24 | .filesystem_name("ntptfs"); 25 | 26 | let context = 27 | NtPassthroughContext::new_with_volume_params(canonical_path, &mut volume_params)?; 28 | 29 | volume_params.file_info_timeout(1000); 30 | Ok(NtPassthroughFilesystem { 31 | fs: FileSystemHost::new_with_options_async( 32 | FileSystemParams::default_params_debug(volume_params, DebugMode::all()), 33 | context, 34 | )?, 35 | }) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /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 | FILE_FS_ATTRIBUTE_INFORMATION, FileFsAttributeInformation, FileFsSizeInformation, 5 | NtQueryVolumeInformationFile, 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /winfsp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "winfsp" 3 | version = "0.12.4+winfsp-2.1" 4 | edition = "2024" 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.12" } 14 | windows = { package = "windows", version = "0.61.1", 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 = "2" 18 | paste = "1" 19 | static_assertions = "1.1" 20 | bytemuck = "1.13" 21 | parking_lot = "0.12.4" 22 | 23 | windows-56 = { package = "windows", version = "0.56", features = ["Win32_Foundation"], optional = true } 24 | windows-60 = { package = "windows", version = "0.60", features = ["Win32_Foundation"], optional = true } 25 | windows-62 = { package = "windows", version = "0.62", features = ["Win32_Foundation"], optional = true } 26 | 27 | [features] 28 | default = ["full", "handle-util"] 29 | debug = [] 30 | system = ["windows/Win32_System_Registry", "winfsp-sys/system"] 31 | notify = [] 32 | build = [] 33 | delayload = ["build"] 34 | docsrs = ["winfsp-sys/docsrs"] 35 | handle-util = [] 36 | async-io = [] 37 | full = ["notify", "delayload", "async-io"] 38 | windows-61 = [] 39 | windows-56 = [ "dep:windows-56" ] 40 | windows-60 = [ "dep:windows-60" ] 41 | windows-62 = [ "dep:windows-62" ] 42 | 43 | [package.metadata.docs.rs] 44 | default-target = "x86_64-pc-windows-msvc" 45 | targets = [] 46 | features = ["full", "docsrs"] 47 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/src/fs/file.rs: -------------------------------------------------------------------------------- 1 | use windows::Win32::Foundation::HANDLE; 2 | use winfsp::filesystem::DirBuffer; 3 | 4 | use winfsp::util::{AtomicHandle, NtHandleDrop, NtSafeHandle}; 5 | 6 | /// A file context in the passthrough file system. 7 | #[derive(Debug)] 8 | pub struct NtPassthroughFile { 9 | handle: AtomicHandle, 10 | is_directory: bool, 11 | dir_buffer: DirBuffer, 12 | file_size_hint: u64, 13 | } 14 | 15 | impl NtPassthroughFile { 16 | /// Create a new entry from an NT handle. 17 | pub fn new(handle: NtSafeHandle, file_size_hint: u64, is_directory: bool) -> Self { 18 | Self { 19 | handle: handle.into(), 20 | file_size_hint, 21 | is_directory, 22 | dir_buffer: DirBuffer::new(), 23 | } 24 | } 25 | 26 | /// Get a HANDLE to this file entry. 27 | pub fn handle(&self) -> HANDLE { 28 | HANDLE(self.handle.handle()) 29 | } 30 | 31 | pub fn handle_ref(&self) -> &AtomicHandle { 32 | &self.handle 33 | } 34 | 35 | /// Invalidate the underlying handle for this file entry. 36 | pub fn invalidate(&self) { 37 | self.handle.invalidate() 38 | } 39 | 40 | /// Whether or not this entry is a directory. 41 | pub fn is_directory(&self) -> bool { 42 | self.is_directory 43 | } 44 | 45 | /// The size of the file in bytes. 46 | pub fn size(&self) -> u32 { 47 | self.file_size_hint as u32 48 | } 49 | 50 | /// Get a reference to the directory buffer for this entry. 51 | pub fn dir_buffer(&self) -> &DirBuffer { 52 | &self.dir_buffer 53 | } 54 | 55 | /// Explicitly invalidate the handle before drop. 56 | pub fn close(self) { 57 | self.invalidate(); 58 | drop(self) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /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::Win32::Foundation::{NTSTATUS, STATUS_SUCCESS}; 5 | use windows::Win32::System::Threading::{ 6 | CloseThreadpoolTimer, CreateThreadpoolTimer, PTP_CALLBACK_INSTANCE, PTP_TIMER, 7 | SetThreadpoolTimer, 8 | }; 9 | use windows::core::Result; 10 | use winfsp_sys::{FSP_FILE_SYSTEM, FspFileSystemNotifyBegin, FspFileSystemNotifyEnd}; 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 | Some(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/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(feature = "docsrs", feature(doc_cfg))] 2 | #![deny(unsafe_op_in_unsafe_fn)] 3 | #![forbid(missing_docs)] 4 | 5 | //! Safe Rust bindings to [WinFSP](https://github.com/winfsp/winfsp). 6 | //! 7 | //! ## Usage 8 | //! The `winfsp` crate wraps the WinFSP service architecture and user mode filesystem host. 9 | //! Implement the [`FileSystemContext`](crate::filesystem::FileSystemContext) trait, then 10 | //! create a [`FileSystemHost`](crate::host::FileSystemHost) instance. 11 | //! 12 | //! It is highly recommended to use the service architecture to manage the lifecycle of a `FileSystemHost`. 13 | //! 14 | //! Using [`FileSystemServiceBuilder`](crate::service::FileSystemServiceBuilder), create, start, and mount the `FileSystemHost` 15 | //! within the [`FileSystemServiceBuilder::with_start`](crate::service::FileSystemServiceBuilder::with_start) closure, 16 | //! and handle teardown in the [`FileSystemServiceBuilder::with_stop`](crate::service::FileSystemServiceBuilder::with_stop) 17 | //! closure. 18 | //! 19 | //! 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). 20 | //! 21 | //! ## Build-time requirements 22 | //! WinFSP only supports delayloading of its library. You must emit the required 23 | //! compile flags in `build.rs` with [`winfsp_link_delayload`](crate::build::winfsp_link_delayload). 24 | //! 25 | //! ```rust 26 | //! fn main() { 27 | //! winfsp::build::winfsp_link_delayload(); 28 | //! } 29 | //! ``` 30 | //! 31 | pub mod constants; 32 | mod error; 33 | pub mod filesystem; 34 | pub mod host; 35 | mod init; 36 | pub mod service; 37 | pub mod util; 38 | mod vsb; 39 | 40 | #[cfg(feature = "notify")] 41 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "notify")))] 42 | pub mod notify; 43 | 44 | // only publicly export notify if feature is enabled. 45 | #[cfg(not(feature = "notify"))] 46 | mod notify; 47 | 48 | pub use error::FspError; 49 | pub use error::Result; 50 | 51 | pub use init::{FspInit, winfsp_init, winfsp_init_or_die}; 52 | 53 | pub use widestring::{U16CStr, U16CString}; 54 | 55 | #[cfg(feature = "delayload")] 56 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "build")))] 57 | pub mod build { 58 | //! Build-time helpers to be called from `build.rs`. 59 | pub use crate::init::winfsp_link_delayload; 60 | } 61 | -------------------------------------------------------------------------------- /winfsp/src/vsb.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 2 | // or the MIT license 3 | // , at your option. 4 | // All files in the project carrying such notice may not be copied, modified, or distributed 5 | // except according to those terms. 6 | use std::{ 7 | alloc::{Layout, alloc_zeroed, dealloc, handle_alloc_error}, 8 | marker::PhantomData, 9 | mem::{align_of, size_of}, 10 | ptr::{self, NonNull}, 11 | }; 12 | /// This is a smart pointer type for holding FFI types whose size varies. 13 | /// Most commonly this is with an array member as the last field whose size is specified 14 | /// by either another field, or an external source of information. 15 | pub struct VariableSizedBox { 16 | size: usize, 17 | data: NonNull, 18 | pd: PhantomData, 19 | } 20 | impl VariableSizedBox { 21 | /// The size is specified in bytes. The data is zeroed. 22 | pub fn new(size: usize) -> VariableSizedBox { 23 | if size == 0 { 24 | return VariableSizedBox::default(); 25 | } 26 | let layout = Layout::from_size_align(size, align_of::()).unwrap(); 27 | if let Some(data) = NonNull::new(unsafe { alloc_zeroed(layout) }) { 28 | VariableSizedBox { 29 | size, 30 | data: data.cast(), 31 | pd: PhantomData, 32 | } 33 | } else { 34 | handle_alloc_error(layout) 35 | } 36 | } 37 | /// Use this to get a pointer to pass to FFI functions. 38 | pub fn as_mut_ptr(&mut self) -> *mut T { 39 | if self.size == 0 { 40 | ptr::null_mut() 41 | } else { 42 | self.data.as_ptr() 43 | } 44 | } 45 | /// This is used to more safely access the fixed size fields. 46 | /// # Safety 47 | /// The current data must be valid for an instance of `T`. 48 | pub unsafe fn as_ref(&self) -> &T { 49 | assert!(self.size >= size_of::()); 50 | unsafe { self.data.as_ref() } 51 | } 52 | 53 | #[allow(clippy::len_without_is_empty)] 54 | /// The length of the allocation specified in bytes. 55 | pub fn len(&self) -> usize { 56 | self.size 57 | } 58 | } 59 | impl Drop for VariableSizedBox { 60 | fn drop(&mut self) { 61 | if self.size == 0 { 62 | return; 63 | } 64 | let layout = Layout::from_size_align(self.size, align_of::()).unwrap(); 65 | unsafe { dealloc(self.as_mut_ptr().cast(), layout) } 66 | } 67 | } 68 | impl Default for VariableSizedBox { 69 | fn default() -> Self { 70 | VariableSizedBox { 71 | size: 0, 72 | data: NonNull::dangling(), 73 | pd: PhantomData, 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /winfsp/src/notify/notifyinfo.rs: -------------------------------------------------------------------------------- 1 | use crate::filesystem::{WideNameInfo, ensure_layout}; 2 | 3 | use crate::filesystem::widenameinfo::WideNameInfoInternal; 4 | use winfsp_sys::{FSP_FSCTL_NOTIFY_INFO, FspFileSystemAddNotifyInfo}; 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/filesystem/stream.rs: -------------------------------------------------------------------------------- 1 | use crate::filesystem::sealed::WideNameInfoInternal; 2 | use crate::filesystem::{WideNameInfo, ensure_layout}; 3 | use winfsp_sys::{FSP_FSCTL_STREAM_INFO, FspFileSystemAddStreamInfo}; 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/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-sys/winfsp/inc/fuse3/winfsp_fuse.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fuse3/winfsp_fuse.h 3 | * WinFsp FUSE3 compatible API. 4 | * 5 | * @copyright 2015-2025 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/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-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-2025 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/src/init.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "system")] 2 | use widestring::U16CStr; 3 | #[allow(unused_imports)] 4 | use windows::Win32::Foundation::{ERROR_DELAY_LOAD_FAILED, ERROR_FILE_NOT_FOUND}; 5 | use windows::Win32::System::LibraryLoader::LoadLibraryW; 6 | use windows::core::PCWSTR; 7 | use windows::core::w; 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::{HKEY_LOCAL_MACHINE, RRF_RT_REG_SZ, RegGetValueW}; 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/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 | self.normalized_name 72 | .map_addr(|addr| addr.wrapping_add(1)) 73 | .cast::() 74 | .copy_from_nonoverlapping(file_name.as_ptr(), file_name.len()); 75 | } 76 | self.normalized_name_len = (std::mem::size_of::() + file_name.len()) as u16; 77 | } else { 78 | // either no prefix, or starts with prefix 79 | unsafe { 80 | self.normalized_name 81 | .cast::() 82 | .copy_from_nonoverlapping(file_name.as_ptr(), file_name.len()) 83 | } 84 | self.normalized_name_len = file_name.len() as u16; 85 | } 86 | } 87 | 88 | /// Get the size of the normalized name in bytes. 89 | /// This starts out as the size of the buffer. 90 | pub fn normalized_name_size(&self) -> u16 { 91 | self.normalized_name_len 92 | } 93 | } 94 | 95 | impl AsRef for OpenFileInfo { 96 | fn as_ref(&self) -> &FileInfo { 97 | &self.file_info 98 | } 99 | } 100 | 101 | impl AsMut for OpenFileInfo { 102 | fn as_mut(&mut self) -> &mut FileInfo { 103 | &mut self.file_info 104 | } 105 | } 106 | 107 | ensure_layout!(FSP_FSCTL_FILE_INFO, FileInfo); 108 | ensure_layout!(FSP_FSCTL_OPEN_FILE_INFO, OpenFileInfo); 109 | -------------------------------------------------------------------------------- /winfsp/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Error, ErrorKind}; 2 | use thiserror::Error; 3 | use windows::Win32::Foundation::{ 4 | ERROR_ACCESS_DENIED, ERROR_ALREADY_EXISTS, ERROR_FILE_NOT_FOUND, ERROR_INVALID_PARAMETER, 5 | }; 6 | 7 | use windows::Win32::Foundation::{ 8 | ERROR_DIRECTORY, ERROR_DIRECTORY_NOT_SUPPORTED, ERROR_FILENAME_EXCED_RANGE, 9 | }; 10 | 11 | use winfsp_sys::FspNtStatusFromWin32; 12 | 13 | /// Error type for WinFSP. 14 | /// 15 | /// WinFSP wraps errors from the [`windows`](https://github.com/microsoft/windows-rs) crate 16 | /// and can coerces errors into the proper NTSTATUS where necessary. 17 | #[non_exhaustive] 18 | #[derive(Error, Debug)] 19 | pub enum FspError { 20 | #[error("HRESULT")] 21 | /// Wraps a Windows HRESULT. 22 | HRESULT(i32), 23 | #[error("WIN32_ERROR")] 24 | /// Wraps a Windows error returned from `GetLastError`. 25 | WIN32(u32), 26 | #[error("NTRESULT")] 27 | /// Wraps a NTSTATUS error. 28 | NTSTATUS(i32), 29 | #[error("IO")] 30 | /// Wraps a Rust IO [`ErrorKind`](std::io::ErrorKind). 31 | /// Only a few, limited IO errors are supported. Unsupported IO 32 | /// errors will panic when transformed into an NTSTATUS value. 33 | IO(ErrorKind), 34 | } 35 | 36 | impl FspError { 37 | /// Get the corresponding NTSTATUS for this error. 38 | #[inline(always)] 39 | pub fn to_ntstatus(&self) -> winfsp_sys::NTSTATUS { 40 | match self { 41 | &FspError::HRESULT(h) => unsafe { FspNtStatusFromWin32(h as u32) }, 42 | &FspError::WIN32(e) => { 43 | unsafe { FspNtStatusFromWin32(e) } 44 | // e.0 as i32 45 | } 46 | FspError::IO(e) => { 47 | let win32_equiv = match e { 48 | ErrorKind::NotFound => ERROR_FILE_NOT_FOUND, 49 | ErrorKind::PermissionDenied => ERROR_ACCESS_DENIED, 50 | ErrorKind::AlreadyExists => ERROR_ALREADY_EXISTS, 51 | ErrorKind::InvalidInput => ERROR_INVALID_PARAMETER, 52 | ErrorKind::IsADirectory => ERROR_DIRECTORY_NOT_SUPPORTED, 53 | ErrorKind::NotADirectory => ERROR_DIRECTORY, 54 | ErrorKind::InvalidFilename => ERROR_FILENAME_EXCED_RANGE, 55 | _ => return 0xC00000E9u32 as i32, // STATUS_UNEXPECTED_IO_ERROR 56 | }; 57 | unsafe { FspNtStatusFromWin32(win32_equiv.0) } 58 | } 59 | &FspError::NTSTATUS(e) => e, 60 | } 61 | } 62 | } 63 | 64 | /// Result type for WinFSP. 65 | pub type Result = std::result::Result; 66 | impl From for FspError { 67 | fn from(e: Error) -> Self { 68 | // prefer raw error if available 69 | if let Some(e) = e.raw_os_error() { 70 | FspError::WIN32(e as u32) 71 | } else { 72 | FspError::IO(e.kind()) 73 | } 74 | } 75 | } 76 | 77 | macro_rules! windows_rs_error { 78 | ($windows_crate:ident, $module_name:ident) => { 79 | mod $module_name { 80 | use $windows_crate as windows; 81 | impl From for $crate::FspError { 82 | fn from(h: windows::Win32::Foundation::WIN32_ERROR) -> Self { 83 | $crate::FspError::WIN32(h.0) 84 | } 85 | } 86 | 87 | impl From for $crate::FspError { 88 | fn from(h: windows::Win32::Foundation::NTSTATUS) -> Self { 89 | $crate::FspError::NTSTATUS(h.0) 90 | } 91 | } 92 | } 93 | }; 94 | } 95 | 96 | macro_rules! windows_core_rs_error { 97 | ($windows_crate:ident, $module_name:ident) => { 98 | mod $module_name { 99 | use $windows_crate as windows; 100 | impl From for $crate::FspError { 101 | fn from(h: windows::core::HRESULT) -> Self { 102 | $crate::FspError::HRESULT(h.0) 103 | } 104 | } 105 | 106 | impl From for $crate::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 $crate::FspError::NTSTATUS(nt_status as i32); 114 | } 115 | match windows::Win32::Foundation::WIN32_ERROR::from_error(&e) { 116 | None => $crate::FspError::HRESULT(e.code().0), 117 | Some(w) => $crate::FspError::WIN32(w.0), 118 | } 119 | } 120 | } 121 | } 122 | }; 123 | } 124 | 125 | // windows 60, 61 share core crate error 126 | windows_core_rs_error!(windows, windows_rs_error); 127 | windows_rs_error!(windows, windows_core_rs_error); 128 | 129 | 130 | #[cfg(feature = "windows-60")] 131 | windows_rs_error!(windows_60, windows_60_rs_error); 132 | 133 | #[cfg(feature = "windows-56")] 134 | windows_rs_error!(windows_56, windows_56_rs_error); 135 | #[cfg(feature = "windows-56")] 136 | windows_core_rs_error!(windows_56, windows_core_56_rs_error); 137 | 138 | #[cfg(feature = "windows-62")] 139 | windows_rs_error!(windows_62, windows_62_rs_error); 140 | #[cfg(feature = "windows-62")] 141 | windows_core_rs_error!(windows_62, windows_62_core_rs_error); 142 | -------------------------------------------------------------------------------- /winfsp-sys/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs::{self, File}; 3 | use std::io::Write; 4 | use std::path::{Path, PathBuf}; 5 | #[cfg(feature = "system")] 6 | use windows_registry::LOCAL_MACHINE; 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 | if !cfg!(windows) { 29 | panic!("'system' feature not supported for cross-platform compilation."); 30 | } 31 | 32 | let directory = LOCAL_MACHINE 33 | .open("SOFTWARE\\WOW6432Node\\WinFsp") 34 | .ok() 35 | .and_then(|u| u.get_string("InstallDir").ok()) 36 | .expect("WinFsp installation directory not found."); 37 | 38 | println!("cargo:rustc-link-search={}/lib", directory); 39 | 40 | format!("--include-directory={}/inc", directory) 41 | } 42 | 43 | fn copy_winfsp_dll(winfsp_lib: &str) { 44 | println!("cargo:rerun-if-env-changed=WINFSP_DLL_OUTPUT_PATH"); 45 | 46 | // Get the output path from environment variable 47 | let dll_out_path = match env::var("WINFSP_DLL_OUTPUT_PATH") { 48 | Ok(path) => PathBuf::from(path), 49 | Err(_) => { 50 | return; 51 | } 52 | }; 53 | 54 | if let Err(e) = fs::create_dir_all(&dll_out_path) { 55 | panic!( 56 | "Failed to create WinFSP DLL output directory {}: {}", 57 | dll_out_path.display(), 58 | e 59 | ); 60 | } 61 | 62 | let project_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); 63 | let dll_path = project_dir 64 | .join("winfsp/bin") 65 | .join(format!("{}.dll", winfsp_lib)); 66 | if !dll_path.exists() { 67 | panic!( 68 | "WinFSP DLL source file does not exist: {}", 69 | dll_path.display() 70 | ); 71 | } 72 | 73 | let dll_dest = dll_out_path.join(format!("{}.dll", winfsp_lib)); 74 | if let Err(e) = fs::copy(&dll_path, &dll_dest) { 75 | panic!( 76 | "Failed to copy {} to {}: {}", 77 | dll_path.display(), 78 | dll_dest.display(), 79 | e 80 | ); 81 | } 82 | } 83 | 84 | fn main() { 85 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 86 | 87 | // host needs to be windows 88 | if cfg!(feature = "docsrs") { 89 | 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."); 90 | File::create(out_dir.join("bindings.rs")).unwrap(); 91 | return; 92 | } 93 | 94 | // Use the target OS configuration instead of the host OS configuration to enable cross-platform compilation 95 | let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_else(|_| "unknown".to_string()); 96 | let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_else(|_| "unknown".to_string()); 97 | let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap_or_else(|_| "unknown".to_string()); 98 | 99 | if target_os != "windows" { 100 | panic!("WinFSP is only supported on Windows."); 101 | } 102 | 103 | #[cfg(feature = "system")] 104 | let link_include = system(); 105 | #[cfg(not(feature = "system"))] 106 | let link_include = local(); 107 | 108 | println!("cargo:rustc-link-lib=dylib=delayimp"); 109 | 110 | // Architecture-specific configuration 111 | let (winfsp_lib, clang_target) = match (target_arch.as_str(), target_env.as_str()) { 112 | ("x86_64", "msvc") => ("winfsp-x64", "x86_64-pc-windows-msvc"), 113 | ("x86", "msvc") => ("winfsp-x86", "x86-pc-windows-msvc"), 114 | ("aarch64", "msvc") => ("winfsp-a64", "aarch64-pc-windows-msvc"), 115 | _ => panic!("unsupported triple {}", env::var("TARGET").unwrap()), 116 | }; 117 | 118 | println!("cargo:rustc-link-lib=dylib={}", winfsp_lib); 119 | println!("cargo:rustc-link-arg=/DELAYLOAD:{}.dll", winfsp_lib); 120 | 121 | let bindings_path_str = out_dir.join("bindings.rs"); 122 | 123 | if !Path::new(&bindings_path_str).exists() { 124 | let gen_h_path = out_dir.join("gen.h"); 125 | let mut gen_h = File::create(&gen_h_path).expect("could not create file"); 126 | gen_h 127 | .write_all(HEADER.as_bytes()) 128 | .expect("could not write header file"); 129 | 130 | let bindings = bindgen::Builder::default() 131 | .header(gen_h_path.to_str().unwrap()) 132 | .derive_default(true) 133 | .blocklist_type("_?P?IMAGE_TLS_DIRECTORY.*") 134 | .allowlist_function("Fsp.*") 135 | .allowlist_type("FSP.*") 136 | .allowlist_type("Fsp.*") 137 | .allowlist_var("FSP_.*") 138 | .allowlist_var("Fsp.*") 139 | .allowlist_var("CTL_CODE") 140 | .clang_arg("-DUNICODE") 141 | .clang_arg(link_include); 142 | 143 | let bindings = bindings.clang_arg(&format!("--target={}", clang_target)); 144 | 145 | let bindings = bindings 146 | .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) 147 | .generate() 148 | .expect("Unable to generate bindings"); 149 | 150 | bindings 151 | .write_to_file(out_dir.join("bindings.rs")) 152 | .expect("Couldn't write bindings!"); 153 | } 154 | 155 | #[cfg(not(feature = "system"))] 156 | copy_winfsp_dll(winfsp_lib); 157 | } 158 | -------------------------------------------------------------------------------- /winfsp/src/util/handle.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_void; 2 | use std::marker::PhantomData; 3 | 4 | use std::mem::ManuallyDrop; 5 | use std::sync::atomic::{AtomicPtr, 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(*mut c_void, 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(AtomicPtr, 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 !HANDLE(self.0).is_invalid() { 76 | T::close(HANDLE(self.0)) 77 | } 78 | self.0 = INVALID_HANDLE_VALUE.0 79 | } 80 | 81 | /// Return the inner handle. 82 | pub fn handle(&self) -> *mut c_void { 83 | self.0 84 | } 85 | } 86 | 87 | impl Drop for SafeDropHandle 88 | where 89 | T: HandleCloseHandler, 90 | { 91 | fn drop(&mut self) { 92 | if !HANDLE(self.0).is_invalid() { 93 | T::close(HANDLE(self.0)) 94 | } 95 | } 96 | } 97 | 98 | impl Drop for AtomicHandle 99 | where 100 | T: HandleCloseHandler, 101 | { 102 | fn drop(&mut self) { 103 | let handle = HANDLE(self.0.load(Ordering::Acquire)); 104 | if !handle.is_invalid() { 105 | T::close(handle) 106 | } 107 | } 108 | } 109 | 110 | impl AtomicHandle 111 | where 112 | T: HandleCloseHandler, 113 | { 114 | /// Atomically load the handle with acquire ordering 115 | pub fn handle(&self) -> *mut c_void { 116 | let handle = self.0.load(Ordering::Acquire); 117 | handle 118 | } 119 | 120 | /// Invalidate the handle without dropping it. 121 | pub fn invalidate(&self) { 122 | let handle = self.handle(); 123 | 124 | if !HANDLE(handle).is_invalid() { 125 | T::close(HANDLE(handle)) 126 | } 127 | self.0.store(INVALID_HANDLE_VALUE.0, Ordering::Relaxed); 128 | } 129 | } 130 | 131 | impl From> for AtomicHandle 132 | where 133 | T: HandleCloseHandler, 134 | { 135 | fn from(h: SafeDropHandle) -> Self { 136 | // forbid SafeDropHandle from running `Drop` 137 | let h = ManuallyDrop::new(h); 138 | Self(AtomicPtr::new(h.0), PhantomData) 139 | } 140 | } 141 | 142 | /// Trait to access the inner handle 143 | pub trait HandleInnerMut { 144 | /// Return a mutable borrow to the inner handle. 145 | fn handle_mut(&mut self) -> &mut T; 146 | } 147 | 148 | macro_rules! windows_rs_handle { 149 | ($windows_crate:ident, $module_name:ident) => { 150 | mod $module_name { 151 | use crate::util::{AtomicHandle, HandleCloseHandler, SafeDropHandle}; 152 | use std::marker::PhantomData; 153 | use std::sync::atomic::AtomicPtr; 154 | use $windows_crate as windows; 155 | 156 | impl super::HandleInnerMut for SafeDropHandle 157 | where 158 | T: HandleCloseHandler, 159 | { 160 | fn handle_mut(&mut self) -> &mut windows::Win32::Foundation::HANDLE { 161 | // SAFETY: HANDLE is a transparent wrapper. 162 | unsafe { ::std::mem::transmute(&mut self.0) } 163 | } 164 | } 165 | 166 | impl From for SafeDropHandle 167 | where 168 | T: HandleCloseHandler, 169 | { 170 | fn from(h: windows::Win32::Foundation::HANDLE) -> Self { 171 | Self(h.0 as *mut ::std::ffi::c_void, PhantomData) 172 | } 173 | } 174 | 175 | impl From for AtomicHandle 176 | where 177 | T: HandleCloseHandler, 178 | { 179 | fn from(h: windows::Win32::Foundation::HANDLE) -> Self { 180 | Self(AtomicPtr::new(h.0 as *mut ::std::ffi::c_void), PhantomData) 181 | } 182 | } 183 | } 184 | }; 185 | } 186 | 187 | windows_rs_handle!(windows, windows_rs_handle); 188 | 189 | #[cfg(feature = "windows-56")] 190 | windows_rs_handle!(windows_56, windows_56_rs_handle); 191 | 192 | #[cfg(feature = "windows-60")] 193 | windows_rs_handle!(windows_60, windows_60_rs_handle); 194 | 195 | #[cfg(feature = "windows-62")] 196 | windows_rs_handle!(windows_62, windows_62_rs_handle); 197 | -------------------------------------------------------------------------------- /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-2025 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/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-2025 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # winfsp-rs 2 | 3 | [![Latest Version](https://img.shields.io/crates/v/winfsp.svg)](https://crates.io/crates/winfsp) ![Stable rust](https://img.shields.io/badge/rust-1.87-blue.svg) [![Docs](https://docs.rs/winfsp/badge.svg)](https://docs.rs/winfsp) ![License](https://img.shields.io/crates/l/winfsp) 4 | 5 | Safe Rust bindings to [WinFSP](https://github.com/winfsp/winfsp) with examples. 6 | 7 | > **Warning** 8 | > 9 | > A best effort has been made to keep Rust's aliasing rules in mind, and provide a safe and sound wrapper over 10 | > WinFSP. However, FFI with WinFSP involves a lot of pointers that end up as references and the nature of FFI makes 11 | > it difficult to test with miri. While ntptfs is used to test the correctness of the bindings, 12 | > there is still a chance these bindings are unsound. 13 | > 14 | > Please file a bug report if you encounter unsoundness when using the safe APIs of these bindings. 15 | 16 | ## Usage 17 | By default, winfsp-rs builds against an included import library. To build against the installed WinFSP libraries, enable the `system` 18 | feature. The path will automatically be determined via the Registry. 19 | 20 | ```toml 21 | [dependencies.winfsp] 22 | version = "0.12" 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.12" 33 | ``` 34 | 35 | #### build.rs 36 | ```rust 37 | fn main() { 38 | winfsp::build::winfsp_link_delayload(); 39 | } 40 | ``` 41 | 42 | ### DLL Output Path 43 | When building without the `system` feature, you can use the `WINFSP_DLL_OUTPUT_PATH` environment variable to have the build script copy the DLL to a specified directory. 44 | 45 | ### Debugging 46 | Debug output can be enabled with the `debug` feature. Debug output will be written to standard output, 47 | and redirection of output is not configurable at this time. 48 | ```toml 49 | [dependencies.winfsp] 50 | features = ["debug"] 51 | ``` 52 | 53 | ### Using with `windows-rs` 54 | 55 | WinFSP will not expose `windows-rs` features unless specified. Adding a `windows-` feature enables conversions between 56 | error types from the `windows` crate for the specific version to `FspError`. 57 | 58 | If the `handle-util` feature is enabled, conversions from `HANDLE` to safe handle types require choosing a windows crate 59 | version. 60 | 61 | Supported versions are `windows-56`, `windows-60`, `windows-61`, `windows-62`. 62 | 63 | ```toml 64 | [dependencies.winfsp] 65 | features = ["full", "windows-61"] 66 | ``` 67 | 68 | ## Testing 69 | `ntptfs-winfsp-rs`, a port of `ntptfs` to `winfsp-rs` is used to test the bindings. It passes all tests that `ntptfs` 70 | passes at the same elevation level. 71 | 72 |
73 | Test results 74 | 75 | ``` 76 | ❯ 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* 77 | create_test............................ OK 0.02s 78 | create_fileattr_test................... OK 0.01s 79 | create_readonlydir_test................ OK 0.01s 80 | create_related_test.................... OK 0.00s 81 | create_allocation_test................. OK 0.01s 82 | create_sd_test......................... OK 0.01s 83 | create_notraverse_test................. OK 0.00s 84 | create_backup_test..................... OK 0.00s 85 | create_restore_test.................... OK 0.00s 86 | create_share_test...................... OK 0.01s 87 | create_curdir_test..................... OK 0.00s 88 | create_namelen_test.................... OK 0.01s 89 | getfileinfo_test....................... OK 0.00s 90 | getfileinfo_name_test.................. OK 0.00s 91 | setfileinfo_test....................... OK 0.00s 92 | delete_test............................ OK 0.00s 93 | delete_pending_test.................... OK 0.00s 94 | delete_mmap_test....................... OK 0.00s 95 | delete_standby_test.................... OK 0.07s 96 | delete_ex_test......................... OK 0.01s 97 | rename_test............................ OK 0.02s 98 | rename_backslash_test.................. OK 0.01s 99 | rename_open_test....................... OK 0.00s 100 | rename_caseins_test.................... OK 0.01s 101 | rename_mmap_test....................... OK 0.01s 102 | rename_standby_test.................... OK 0.15s 103 | rename_ex_test......................... OK 0.01s 104 | getvolinfo_test........................ OK 0.00s 105 | setvolinfo_test........................ OK 0.00s 106 | getsecurity_test....................... OK 0.00s 107 | setsecurity_test....................... OK 0.00s 108 | security_stress_meta_test.............. OK 0.24s 109 | rdwr_noncached_test.................... OK 0.02s 110 | rdwr_noncached_overlapped_test......... OK 0.02s 111 | rdwr_cached_test....................... OK 0.02s 112 | rdwr_cached_append_test................ OK 0.01s 113 | rdwr_cached_overlapped_test............ OK 0.02s 114 | rdwr_writethru_test.................... OK 0.01s 115 | rdwr_writethru_append_test............. OK 0.01s 116 | rdwr_writethru_overlapped_test......... OK 0.01s 117 | rdwr_mmap_test......................... OK 0.15s 118 | rdwr_mixed_test........................ OK 0.01s 119 | flush_test............................. OK 0.05s 120 | flush_volume_test...................... OK 0.00s 121 | lock_noncached_test.................... OK 0.02s 122 | lock_noncached_overlapped_test......... OK 0.01s 123 | lock_cached_test....................... OK 0.01s 124 | lock_cached_overlapped_test............ OK 0.01s 125 | querydir_test.......................... OK 0.64s 126 | querydir_nodup_test.................... OK 4.43s 127 | querydir_single_test................... OK 1.78s 128 | querydir_expire_cache_test............. OK 0.00s 129 | querydir_buffer_overflow_test.......... OK 0.00s 130 | querydir_namelen_test.................. OK 0.01s 131 | dirnotify_test......................... OK 1.01s 132 | exec_test.............................. OK 0.02s 133 | exec_delete_test....................... OK 1.03s 134 | exec_rename_test....................... OK 1.03s 135 | reparse_guid_test...................... OK 4.83s 136 | reparse_nfs_test....................... OK 0.00s 137 | reparse_symlink_test................... OK 0.01s 138 | reparse_symlink_relative_test.......... OK 0.04s 139 | stream_create_test..................... OK 0.02s 140 | stream_create_overwrite_test........... OK 0.01s 141 | stream_create_related_test............. OK 0.00s 142 | stream_create_sd_test.................. OK 0.00s 143 | stream_create_share_test............... OK 0.03s 144 | stream_getfileinfo_test................ OK 0.00s 145 | stream_setfileinfo_test................ OK 0.01s 146 | stream_delete_test..................... OK 0.01s 147 | stream_delete_pending_test............. OK 0.01s 148 | stream_getsecurity_test................ OK 0.00s 149 | stream_setsecurity_test................ OK 0.00s 150 | stream_getstreaminfo_expire_cache_test. OK 0.00s 151 | stream_dirnotify_test.................. OK 1.01s 152 | oplock_level1_test..................... OK 1.31s 153 | oplock_level2_test..................... OK 2.48s 154 | oplock_batch_test...................... OK 1.25s 155 | oplock_filter_test..................... OK 1.24s 156 | oplock_rwh_test........................ OK 1.24s 157 | oplock_rw_test......................... OK 1.24s 158 | oplock_rh_test......................... OK 2.48s 159 | oplock_r_test.......................... OK 2.48s 160 | oplock_not_granted_test................ OK 0.00s 161 | wsl_stat_test.......................... OK 0.00s 162 | --- COMPLETE --- 163 | ``` 164 | 165 |
166 | 167 | ## Legal 168 | winfsp-rs is licensed under the terms of the GNU General Public License version 3 as published by the 169 | Free Software Foundation. 170 | 171 | ### Attribution 172 | 173 | > WinFsp - Windows File System Proxy, 174 | > 175 | > Copyright (C) Bill Zissimopoulos \ 176 | > https://github.com/winfsp/winfsp 177 | -------------------------------------------------------------------------------- /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 | FSP_FSCTL_DIR_INFO, FspFileSystemAcquireDirectoryBufferEx, FspFileSystemAddDirInfo, 6 | FspFileSystemDeleteDirectoryBuffer, FspFileSystemFillDirectoryBuffer, 7 | FspFileSystemReadDirectoryBuffer, FspFileSystemReleaseDirectoryBuffer, PVOID, 8 | }; 9 | 10 | use crate::error::Result; 11 | use crate::filesystem::sealed::WideNameInfoInternal; 12 | use crate::filesystem::{FileInfo, WideNameInfo, ensure_layout}; 13 | use crate::util::AssertThreadSafe; 14 | 15 | /// A buffer used to hold directory entries when enumerating directories 16 | /// with the [`read_directory`](crate::filesystem::FileSystemContext::read_directory) 17 | /// callback. 18 | /// 19 | /// DirBuffer provides interior mutability for the directory buffer, which is 20 | /// managed completely by the filesystem driver. This is because the filesystem 21 | /// driver may create multiple threads that could possibly run afoul of the 22 | /// aliasing rules of `&mut`. 23 | #[derive(Debug)] 24 | pub struct DirBuffer(AssertThreadSafe>); 25 | /// A lock into the directory read buffer that must be held while writing, and dropped 26 | /// as soon as writing of directory entries into the buffer is complete. 27 | #[derive(Debug)] 28 | pub struct DirBufferLock<'a>(&'a DirBuffer); 29 | /// A marker into the current position of the directory file when 30 | /// enumerating directories with [`read_directory`](crate::filesystem::FileSystemContext::read_directory) 31 | #[derive(Debug)] 32 | pub struct DirMarker<'a>(pub(crate) Option<&'a U16CStr>); 33 | 34 | impl DirMarker<'_> { 35 | /// Reset the marker. 36 | pub fn reset(&mut self) { 37 | self.0.take(); 38 | } 39 | 40 | /// Returns whether this marker exists. 41 | pub fn is_none(&self) -> bool { 42 | self.0.is_none() 43 | } 44 | 45 | /// Returns whether this marker is the parent directory '..'. 46 | pub fn is_parent(&self) -> bool { 47 | if let Some(marker) = self.0 { 48 | return marker == u16cstr!(".."); 49 | } 50 | false 51 | } 52 | 53 | /// Returns whether this marker is the current directory '.'. 54 | pub fn is_current(&self) -> bool { 55 | if let Some(marker) = self.0 { 56 | return marker == u16cstr!("."); 57 | } 58 | false 59 | } 60 | 61 | /// Returns the inner contents of the marker. 62 | /// If the inner contents were not validly null-terminated, returns None. 63 | pub fn inner(&self) -> Option<&[u16]> { 64 | self.0.map(U16CStr::as_slice) 65 | } 66 | 67 | /// Returns the inner contents of the marker. 68 | /// If the inner contents were not validly null-terminated, returns None. 69 | pub fn inner_as_cstr(&self) -> Option<&U16CStr> { 70 | self.0 71 | } 72 | } 73 | 74 | impl Default for DirBuffer { 75 | fn default() -> Self { 76 | Self::new() 77 | } 78 | } 79 | 80 | impl DirBuffer { 81 | /// Create a new unacquired directory buffer. 82 | pub fn new() -> Self { 83 | Self(AssertThreadSafe(UnsafeCell::new(std::ptr::null_mut()))) 84 | } 85 | 86 | /// Try to acquire a lock on the directory buffer to write entries into. 87 | pub fn acquire(&self, reset: bool, capacity_hint: Option) -> Result> { 88 | let mut result = STATUS_SUCCESS; 89 | unsafe { 90 | if FspFileSystemAcquireDirectoryBufferEx( 91 | self.0.0.get(), 92 | reset.into(), 93 | capacity_hint.unwrap_or(0), 94 | &mut result.0, 95 | ) != 0 96 | { 97 | Ok(DirBufferLock(self)) 98 | } else { 99 | Err(result.into()) 100 | } 101 | } 102 | } 103 | 104 | /// Read the contents of the directory buffer into the provided slice, 105 | /// returning the number of bytes written. 106 | /// 107 | /// If the directory buffer was never acquired, this is a no-op. 108 | pub fn read(&self, marker: DirMarker, buffer: &mut [u8]) -> u32 { 109 | let mut out = 0u32; 110 | unsafe { 111 | FspFileSystemReadDirectoryBuffer( 112 | self.0.0.get(), 113 | marker 114 | .0 115 | .map_or(std::ptr::null_mut(), |v| v.as_ptr().cast_mut()), 116 | buffer.as_mut_ptr() as *mut _, 117 | buffer.len() as u32, 118 | &mut out, 119 | ); 120 | } 121 | out 122 | } 123 | } 124 | 125 | impl DirBufferLock<'_> { 126 | /// Write a directory entry into the directory buffer. 127 | /// 128 | /// A buffer can accept multiple DirInfos of varying sizes. 129 | pub fn write(&self, dir_info: &mut DirInfo) -> Result<()> { 130 | let mut status = STATUS_SUCCESS; 131 | unsafe { 132 | let buffer = self.0; 133 | // this is cursed. 134 | if FspFileSystemFillDirectoryBuffer( 135 | buffer.0.0.get(), 136 | (dir_info as *mut DirInfo).cast(), 137 | &mut status.0, 138 | ) == 0 139 | { 140 | return Err(status.into()); 141 | } 142 | } 143 | Ok(()) 144 | } 145 | } 146 | 147 | impl Drop for DirBuffer { 148 | fn drop(&mut self) { 149 | unsafe { 150 | FspFileSystemDeleteDirectoryBuffer(self.0.0.get()); 151 | } 152 | } 153 | } 154 | 155 | impl Drop for DirBufferLock<'_> { 156 | fn drop(&mut self) { 157 | let buffer = self.0; 158 | unsafe { FspFileSystemReleaseDirectoryBuffer(buffer.0.0.get()) } 159 | } 160 | } 161 | 162 | #[repr(C)] 163 | union DirInfoPadding { 164 | next_offset: u64, 165 | padding: [u8; 24], 166 | } 167 | 168 | #[repr(C)] 169 | /// A directory information entry. 170 | /// 171 | /// ## Safety 172 | /// Note that `BUFFER_SIZE` is the size of the name buffer in characters, not bytes. 173 | /// In most cases, the default is sufficient. A buffer size that is too large 174 | /// may not be copyable to the request buffer. 175 | pub struct DirInfo { 176 | size: u16, 177 | file_info: FileInfo, 178 | padding: DirInfoPadding, 179 | file_name: [u16; BUFFER_SIZE], 180 | } 181 | 182 | ensure_layout!(FSP_FSCTL_DIR_INFO, DirInfo<0>); 183 | impl DirInfo { 184 | /// Create a new, empty directory entry info. 185 | pub fn new() -> Self { 186 | Self { 187 | // begin with initially no file_name 188 | size: std::mem::size_of::>() as u16, 189 | file_info: FileInfo::default(), 190 | padding: DirInfoPadding { padding: [0; 24] }, 191 | file_name: [0; BUFFER_SIZE], 192 | } 193 | } 194 | 195 | /// Get a mutable reference to the file information of this directory entry. 196 | pub fn file_info_mut(&mut self) -> &mut FileInfo { 197 | &mut self.file_info 198 | } 199 | } 200 | 201 | impl Default for DirInfo { 202 | fn default() -> Self { 203 | Self::new() 204 | } 205 | } 206 | 207 | impl WideNameInfoInternal for DirInfo { 208 | fn name_buffer(&mut self) -> &mut [u16; BUFFER_SIZE] { 209 | &mut self.file_name 210 | } 211 | 212 | fn set_size(&mut self, buffer_size: u16) { 213 | self.size = std::mem::size_of::>() as u16 + buffer_size 214 | } 215 | 216 | fn add_to_buffer_internal(entry: Option<&Self>, buffer: &mut [u8], cursor: &mut u32) -> bool { 217 | unsafe { 218 | // SAFETY: https://github.com/winfsp/winfsp/blob/0a91292e0502d6629f9a968a168c6e89eea69ea1/src/dll/fsop.c#L1500 219 | // does not mutate entry. 220 | if let Some(entry) = entry { 221 | FspFileSystemAddDirInfo( 222 | (entry as *const Self).cast_mut().cast(), 223 | buffer.as_mut_ptr().cast(), 224 | buffer.len() as u32, 225 | cursor, 226 | ) != 0 227 | } else { 228 | FspFileSystemAddDirInfo( 229 | std::ptr::null_mut(), 230 | buffer.as_mut_ptr().cast(), 231 | buffer.len() as u32, 232 | cursor, 233 | ) != 0 234 | } 235 | } 236 | } 237 | } 238 | 239 | impl WideNameInfo for DirInfo { 240 | /// Reset the directory entry. 241 | fn reset(&mut self) { 242 | self.size = std::mem::size_of::>() as u16; 243 | self.file_info = FileInfo::default(); 244 | self.padding.next_offset = 0; 245 | self.padding.padding = [0; 24]; 246 | self.file_name = [0; BUFFER_SIZE] 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /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/service.rs: -------------------------------------------------------------------------------- 1 | //! Interfaces to the WinFSP service API to run a filesystem. 2 | use crate::FspInit; 3 | use crate::Result; 4 | use crate::error::FspError; 5 | use crate::util::AssertThreadSafe; 6 | use parking_lot::RwLock; 7 | use std::cell::UnsafeCell; 8 | use std::ffi::{OsStr, c_void}; 9 | use std::marker::PhantomData; 10 | use std::ops::DerefMut; 11 | use std::ptr::{NonNull, addr_of_mut}; 12 | use std::thread::JoinHandle; 13 | use windows::Win32::Foundation::{STATUS_INVALID_PARAMETER, STATUS_SUCCESS}; 14 | use windows::core::HSTRING; 15 | use winfsp_sys::{ 16 | FSP_SERVICE, FspServiceAllowConsoleMode, FspServiceCreate, FspServiceDelete, FspServiceLoop, 17 | FspServiceStop, 18 | }; 19 | 20 | // internal aliases for callback types 21 | type FileSystemStartCallback<'a, T> = 22 | Option std::result::Result + 'a>>; 23 | type FileSystemStopCallback<'a, T> = 24 | Option) -> std::result::Result<(), FspError> + 'a>>; 25 | type FileSystemControlCallback<'a, T> = 26 | Option, u32, u32, *mut c_void) -> i32 + 'a>>; 27 | struct FileSystemServiceContext<'a, T> { 28 | start: FileSystemStartCallback<'a, T>, 29 | stop: FileSystemStopCallback<'a, T>, 30 | control: FileSystemControlCallback<'a, T>, 31 | context: Option>>, 32 | } 33 | 34 | /// A service that runs a filesystem implemented by a [`FileSystemHost`](crate::host::FileSystemHost). 35 | pub struct FileSystemService { 36 | service_ptr: NonNull, 37 | _pd: PhantomData, 38 | } 39 | 40 | struct FileSystemServiceHelper(NonNull, PhantomData); 41 | 42 | impl FileSystemServiceHelper { 43 | /// # Safety 44 | /// `raw` is valid and not null. 45 | unsafe fn from_raw_unchecked(raw: *mut FSP_SERVICE) -> Self { 46 | unsafe { FileSystemServiceHelper(NonNull::new_unchecked(raw), Default::default()) } 47 | } 48 | 49 | /// Set the context. 50 | fn set_context(&mut self, context: T) { 51 | unsafe { 52 | let ptr: *mut UnsafeCell> = 53 | self.0.as_mut().UserContext.cast(); 54 | if let Some(ptr) = ptr.as_mut() { 55 | ptr.get_mut().context = Some(Box::new(RwLock::new(context))) 56 | } 57 | } 58 | } 59 | 60 | fn get_context(&self) -> Option<&RwLock> { 61 | unsafe { 62 | if let Some(p) = self 63 | .0 64 | .as_ref() 65 | .UserContext 66 | .cast::>>() 67 | .as_mut() 68 | { 69 | p.get_mut().context.as_deref() 70 | } else { 71 | None 72 | } 73 | } 74 | } 75 | } 76 | 77 | impl FileSystemService { 78 | /// Stops the file system host service. 79 | pub fn stop(&self) { 80 | unsafe { 81 | FspServiceStop(self.service_ptr.as_ptr()); 82 | }; 83 | } 84 | 85 | /// Spawns a thread and starts the file host system service. 86 | pub fn start(&self) -> JoinHandle> { 87 | let ptr = AssertThreadSafe(self.service_ptr.as_ptr()); 88 | std::thread::spawn(|| { 89 | #[allow(clippy::redundant_locals)] 90 | let ptr = ptr; 91 | let result = unsafe { 92 | FspServiceAllowConsoleMode(ptr.0); 93 | FspServiceLoop(ptr.0) 94 | }; 95 | 96 | if result == STATUS_SUCCESS.0 { 97 | Ok(()) 98 | } else { 99 | Err(FspError::NTSTATUS(result)) 100 | } 101 | }) 102 | } 103 | } 104 | 105 | /// A builder for [`FileSystemService`](crate::service::FileSystemService). 106 | pub struct FileSystemServiceBuilder<'a, T> { 107 | stop: FileSystemStopCallback<'a, T>, 108 | start: FileSystemStartCallback<'a, T>, 109 | control: FileSystemControlCallback<'a, T>, 110 | } 111 | 112 | impl<'a, T> Default for FileSystemServiceBuilder<'a, T> { 113 | fn default() -> Self { 114 | Self::new() 115 | } 116 | } 117 | 118 | impl<'a, T> FileSystemServiceBuilder<'a, T> { 119 | /// Create a new instance of the builder. 120 | pub fn new() -> Self { 121 | Self { 122 | stop: None, 123 | start: None, 124 | control: None, 125 | } 126 | } 127 | 128 | /// The start callback provides the file system context and mounts the file system. 129 | /// The returned file system context must be mounted before returning. 130 | pub fn with_start(mut self, start: F) -> Self 131 | where 132 | F: Fn() -> std::result::Result + 'a, 133 | { 134 | self.start = Some(Box::new(start)); 135 | self 136 | } 137 | 138 | /// The stop callback is responsible for safely terminating the mounted file system. 139 | pub fn with_stop(mut self, stop: F) -> Self 140 | where 141 | F: Fn(Option<&mut T>) -> std::result::Result<(), FspError> + 'a, 142 | { 143 | self.stop = Some(Box::new(stop)); 144 | self 145 | } 146 | 147 | /// The control callback handles DeviceIoControl requests. 148 | pub fn with_control(mut self, control: F) -> Self 149 | where 150 | F: Fn(Option<&mut T>, u32, u32, *mut c_void) -> i32 + 'static, 151 | { 152 | self.control = Some(Box::new(control)); 153 | self 154 | } 155 | 156 | /// Create the [`FileSystemService`](crate::service::FileSystemService) with the provided 157 | /// callbacks. 158 | pub fn build( 159 | self, 160 | service_name: impl AsRef, 161 | _init: FspInit, 162 | ) -> Result> { 163 | let service = UnsafeCell::new(std::ptr::null_mut()); 164 | let service_name = HSTRING::from(service_name.as_ref()); 165 | let result = unsafe { 166 | // SAFETY: service_name is never mutated. 167 | // https://github.com/winfsp/winfsp/blob/0ab4300738233eba4a37e1302e55fff6f0c4f5ab/src/dll/service.c#L108 168 | FspServiceCreate( 169 | service_name.as_ptr().cast_mut(), 170 | Some(on_start::), 171 | Some(on_stop::), 172 | Some(on_control::), 173 | service.get(), 174 | ) 175 | }; 176 | 177 | unsafe { 178 | addr_of_mut!((*(*service.get())).UserContext).write(Box::into_raw(Box::new( 179 | UnsafeCell::new(FileSystemServiceContext:: { 180 | start: self.start, 181 | stop: self.stop, 182 | control: self.control, 183 | context: None, 184 | }), 185 | )) as *mut _) 186 | } 187 | if result == STATUS_SUCCESS.0 && unsafe { !service.get().read().is_null() } { 188 | Ok(unsafe { 189 | FileSystemService { 190 | service_ptr: NonNull::new_unchecked(service.get().read()), 191 | _pd: PhantomData, 192 | } 193 | }) 194 | } else { 195 | Err(FspError::NTSTATUS(result)) 196 | } 197 | } 198 | } 199 | 200 | impl<'a, T> Drop for FileSystemService { 201 | fn drop(&mut self) { 202 | self.stop(); 203 | let service_context_ptr = unsafe { 204 | // SAFETY: FSP_SERVICE pointer and UserContext field are not mutated by other threads 205 | self.service_ptr.as_ref().UserContext as *mut UnsafeCell> 206 | }; 207 | unsafe { 208 | FspServiceDelete(self.service_ptr.as_ptr()); 209 | }; 210 | let service_context_box = unsafe { 211 | // SAFETY: No other threads exist with access to the service context and its type is correct. 212 | // 213 | Box::>>::from_raw(service_context_ptr) 214 | }; 215 | drop(service_context_box); 216 | } 217 | } 218 | 219 | unsafe extern "C" fn on_start(fsp: *mut FSP_SERVICE, _argc: u32, _argv: *mut *mut u16) -> i32 { 220 | if let Some(context) = unsafe { 221 | fsp.as_mut() 222 | .unwrap_unchecked() 223 | .UserContext 224 | .cast::>() 225 | .as_mut() 226 | } { 227 | if let Some(start) = &context.start { 228 | return match start() { 229 | Err(e) => e.to_ntstatus(), 230 | Ok(context) => { 231 | unsafe { 232 | FileSystemServiceHelper::from_raw_unchecked(fsp).set_context(context); 233 | } 234 | STATUS_SUCCESS.0 235 | } 236 | }; 237 | } 238 | } 239 | STATUS_INVALID_PARAMETER.0 240 | } 241 | 242 | unsafe extern "C" fn on_stop(fsp: *mut FSP_SERVICE) -> i32 { 243 | if let Some(context) = unsafe { 244 | fsp.as_mut() 245 | .unwrap_unchecked() 246 | .UserContext 247 | .cast::>() 248 | .as_mut() 249 | } { 250 | if let Some(stop) = &context.stop { 251 | let fsp: FileSystemServiceHelper = 252 | unsafe { FileSystemServiceHelper::from_raw_unchecked(fsp) }; 253 | let context = fsp.get_context(); 254 | 255 | let result = 'result: { 256 | let Some(context) = context else { 257 | break 'result stop(None); 258 | }; 259 | let mut context = context.write(); 260 | stop(Some(context.deref_mut())) 261 | }; 262 | 263 | return match result { 264 | Ok(()) => STATUS_SUCCESS.0, 265 | Err(e) => e.to_ntstatus(), 266 | }; 267 | } 268 | } 269 | STATUS_INVALID_PARAMETER.0 270 | } 271 | 272 | unsafe extern "C" fn on_control( 273 | fsp: *mut FSP_SERVICE, 274 | ctl: u32, 275 | event_type: u32, 276 | event_data: *mut c_void, 277 | ) -> i32 { 278 | if let Some(context) = unsafe { 279 | fsp.as_mut() 280 | .unwrap_unchecked() 281 | .UserContext 282 | .cast::>() 283 | .as_mut() 284 | } { 285 | if let Some(control) = &context.control { 286 | let fsp: FileSystemServiceHelper = 287 | unsafe { FileSystemServiceHelper::from_raw_unchecked(fsp) }; 288 | let context = fsp.get_context(); 289 | let Some(context) = context else { 290 | return control(None, ctl, event_type, event_data); 291 | }; 292 | let mut context = context.write(); 293 | return control(Some(context.deref_mut()), ctl, event_type, event_data); 294 | } 295 | } 296 | STATUS_INVALID_PARAMETER.0 297 | } 298 | -------------------------------------------------------------------------------- /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-2025 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/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-2025 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 | -------------------------------------------------------------------------------- /filesystems/ntptfs-winfsp-rs/src/native/lfs/async_io.rs: -------------------------------------------------------------------------------- 1 | use crate::native::lfs; 2 | use std::cell::UnsafeCell; 3 | use std::future::Future; 4 | use std::mem::MaybeUninit; 5 | use std::pin::Pin; 6 | use std::ptr::addr_of; 7 | use std::task::{Context, Poll}; 8 | use widestring::U16CStr; 9 | use windows::Wdk::Storage::FileSystem::{ 10 | FILE_INFORMATION_CLASS, NtQueryDirectoryFile, NtReadFile, NtWriteFile, 11 | }; 12 | use windows::Win32::Foundation::{ 13 | CloseHandle, HANDLE, NTSTATUS, STATUS_ABANDONED, STATUS_PENDING, STATUS_SUCCESS, 14 | UNICODE_STRING, WAIT_ABANDONED, WAIT_ABANDONED_0, WAIT_FAILED, WAIT_OBJECT_0, 15 | }; 16 | use windows::Win32::System::IO::IO_STATUS_BLOCK; 17 | use windows::Win32::System::Threading::WaitForSingleObject; 18 | use windows::Win32::System::WindowsProgramming::RtlInitUnicodeString; 19 | use windows::core::PCWSTR; 20 | use winfsp::FspError; 21 | use winfsp::util::{AtomicHandle, NtHandleDrop}; 22 | 23 | struct LfsReadFuture<'a> { 24 | event: AssertThreadSafe, 25 | file: AssertThreadSafe, 26 | iosb: UnsafeCell>, 27 | result: Option, 28 | buffer: &'a mut [u8], 29 | offset: i64, 30 | } 31 | 32 | #[derive(Debug)] 33 | #[repr(transparent)] 34 | pub(crate) struct AssertThreadSafe(pub T); 35 | 36 | unsafe impl Send for AssertThreadSafe {} 37 | 38 | impl<'a> LfsReadFuture<'a> { 39 | fn new( 40 | file: AssertThreadSafe, 41 | buffer: &'a mut [u8], 42 | offset: i64, 43 | ) -> winfsp::Result { 44 | let event = AssertThreadSafe(lfs::new_event()?); 45 | Ok(Self { 46 | event, 47 | file, 48 | iosb: UnsafeCell::new(AssertThreadSafe(IO_STATUS_BLOCK::default())), 49 | result: None, 50 | buffer, 51 | offset, 52 | }) 53 | } 54 | } 55 | 56 | impl<'a> Drop for LfsReadFuture<'a> { 57 | fn drop(&mut self) { 58 | unsafe { 59 | let _ = CloseHandle(self.event.0); 60 | } 61 | } 62 | } 63 | 64 | impl<'a> Future for LfsReadFuture<'a> { 65 | type Output = Result; 66 | 67 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 68 | let Some(result) = self.result else { 69 | let initial_result = unsafe { 70 | NtReadFile( 71 | self.file.0, 72 | Some(self.event.0), 73 | None, 74 | None, 75 | self.iosb.get() as *mut _, 76 | self.buffer.as_mut_ptr() as *mut _, 77 | self.buffer.len() as u32, 78 | Some(&self.offset), 79 | None, 80 | ) 81 | }; 82 | self.result = Some(initial_result); 83 | cx.waker().wake_by_ref(); 84 | return Poll::Pending; 85 | }; 86 | 87 | if result != STATUS_PENDING { 88 | return if result != STATUS_SUCCESS { 89 | Poll::Ready(Err(FspError::from(result))) 90 | } else { 91 | Poll::Ready(Ok(unsafe { self.iosb.get().read().0 })) 92 | }; 93 | } 94 | 95 | let wait_result = unsafe { WaitForSingleObject(self.event.0, 0) }; 96 | 97 | if wait_result == WAIT_OBJECT_0 { 98 | let code = unsafe { addr_of!((*self.iosb.get()).0.Anonymous.Status).read() }; 99 | self.result = Some(code); 100 | } else if wait_result == WAIT_FAILED 101 | || wait_result == WAIT_ABANDONED 102 | || wait_result == WAIT_ABANDONED_0 103 | { 104 | self.result = Some(STATUS_ABANDONED); 105 | } 106 | 107 | // if timed out, io isn't ready 108 | cx.waker().wake_by_ref(); 109 | Poll::Pending 110 | } 111 | } 112 | 113 | pub async fn lfs_read_file_async( 114 | handle: &AtomicHandle, 115 | buffer: &mut [u8], 116 | offset: u64, 117 | bytes_transferred: &mut u32, 118 | ) -> winfsp::Result<()> { 119 | let handle = AssertThreadSafe(HANDLE(handle.handle())); 120 | let transferred = async move { 121 | let lfs = LfsReadFuture::new(handle, buffer, offset as i64)?; 122 | lfs.await.map(|iosb| iosb.Information) 123 | } 124 | .await?; 125 | 126 | *bytes_transferred = transferred as u32; 127 | 128 | Ok(()) 129 | } 130 | 131 | struct LfsWriteFuture<'a> { 132 | event: AssertThreadSafe, 133 | file: AssertThreadSafe, 134 | iosb: UnsafeCell>, 135 | result: Option, 136 | buffer: &'a [u8], 137 | offset: i64, 138 | } 139 | 140 | impl<'a> LfsWriteFuture<'a> { 141 | fn new(file: HANDLE, buffer: &'a [u8], offset: i64) -> winfsp::Result { 142 | let event = AssertThreadSafe(lfs::new_event()?); 143 | 144 | Ok(Self { 145 | event, 146 | file: AssertThreadSafe(file), 147 | iosb: UnsafeCell::new(AssertThreadSafe(IO_STATUS_BLOCK::default())), 148 | result: None, 149 | buffer, 150 | offset, 151 | }) 152 | } 153 | } 154 | 155 | impl<'a> Drop for LfsWriteFuture<'a> { 156 | fn drop(&mut self) { 157 | unsafe { 158 | let _ = CloseHandle(self.event.0); 159 | } 160 | } 161 | } 162 | 163 | impl<'a> Future for LfsWriteFuture<'a> { 164 | type Output = Result; 165 | 166 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 167 | let Some(result) = self.result else { 168 | let initial_result = unsafe { 169 | NtWriteFile( 170 | self.file.0, 171 | Some(self.event.0), 172 | None, 173 | None, 174 | self.iosb.get() as *mut _, 175 | self.buffer.as_ptr() as *const _, 176 | self.buffer.len() as u32, 177 | Some(&self.offset), 178 | None, 179 | ) 180 | }; 181 | self.result = Some(initial_result); 182 | cx.waker().wake_by_ref(); 183 | return Poll::Pending; 184 | }; 185 | 186 | if result != STATUS_PENDING { 187 | return if result != STATUS_SUCCESS { 188 | Poll::Ready(Err(FspError::from(result))) 189 | } else { 190 | Poll::Ready(Ok(unsafe { self.iosb.get().read().0 })) 191 | }; 192 | } 193 | 194 | let wait_result = unsafe { WaitForSingleObject(self.event.0, 0) }; 195 | 196 | if wait_result == WAIT_OBJECT_0 { 197 | let code = unsafe { addr_of!((*self.iosb.get()).0.Anonymous.Status).read() }; 198 | self.result = Some(code); 199 | } else if wait_result == WAIT_FAILED 200 | || wait_result == WAIT_ABANDONED 201 | || wait_result == WAIT_ABANDONED_0 202 | { 203 | self.result = Some(STATUS_ABANDONED); 204 | } 205 | 206 | // if timed out, io isn't ready 207 | cx.waker().wake_by_ref(); 208 | Poll::Pending 209 | } 210 | } 211 | 212 | pub async fn lfs_write_file_async( 213 | handle: &AtomicHandle, 214 | buffer: &[u8], 215 | offset: u64, 216 | bytes_transferred: &mut u32, 217 | ) -> winfsp::Result<()> { 218 | let transferred = async move { 219 | let lfs = LfsWriteFuture::new(HANDLE(handle.handle()), buffer, offset as i64)?; 220 | lfs.await.map(|iosb| iosb.Information) 221 | } 222 | .await?; 223 | 224 | *bytes_transferred = transferred as u32; 225 | 226 | Ok(()) 227 | } 228 | 229 | struct LfsQueryDirectoryFileFuture<'a> { 230 | file: AssertThreadSafe, 231 | event: AssertThreadSafe, 232 | file_name: Option<&'a U16CStr>, 233 | iosb: UnsafeCell>, 234 | result: Option, 235 | buffer: &'a mut [u8], 236 | return_single_entry: bool, 237 | restart_scan: bool, 238 | class: FILE_INFORMATION_CLASS, 239 | } 240 | 241 | impl<'a> LfsQueryDirectoryFileFuture<'a> { 242 | fn new( 243 | file: HANDLE, 244 | file_name: Option<&'a U16CStr>, 245 | buffer: &'a mut [u8], 246 | return_single_entry: bool, 247 | restart_scan: bool, 248 | class: FILE_INFORMATION_CLASS, 249 | ) -> winfsp::Result { 250 | let event = lfs::new_event()?; 251 | 252 | Ok(Self { 253 | file: AssertThreadSafe(file), 254 | event: AssertThreadSafe(event), 255 | file_name, 256 | iosb: UnsafeCell::new(AssertThreadSafe(IO_STATUS_BLOCK::default())), 257 | result: None, 258 | buffer, 259 | return_single_entry, 260 | restart_scan, 261 | class, 262 | }) 263 | } 264 | } 265 | 266 | impl<'a> Drop for LfsQueryDirectoryFileFuture<'a> { 267 | fn drop(&mut self) { 268 | unsafe { 269 | let _ = CloseHandle(self.event.0); 270 | } 271 | } 272 | } 273 | 274 | impl<'a> Future for LfsQueryDirectoryFileFuture<'a> { 275 | type Output = Result; 276 | 277 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 278 | let Some(result) = self.result else { 279 | let unicode_filename = self.file_name.map(|f| unsafe { 280 | let mut unicode_filename: MaybeUninit = MaybeUninit::zeroed(); 281 | RtlInitUnicodeString(unicode_filename.as_mut_ptr(), PCWSTR(f.as_ptr())); 282 | unicode_filename.assume_init() 283 | }); 284 | 285 | let initial_result = unsafe { 286 | NtQueryDirectoryFile( 287 | self.file.0, 288 | Some(self.event.0), 289 | None, 290 | None, 291 | self.iosb.get() as *mut _, 292 | self.buffer.as_mut_ptr() as *mut _, 293 | self.buffer.len() as u32, 294 | self.class, 295 | self.return_single_entry, 296 | unicode_filename 297 | .as_ref() 298 | .map(|p| p as *const UNICODE_STRING as *const _), 299 | self.restart_scan, 300 | ) 301 | }; 302 | self.result = Some(initial_result); 303 | cx.waker().wake_by_ref(); 304 | return Poll::Pending; 305 | }; 306 | 307 | if result != STATUS_PENDING { 308 | return if result != STATUS_SUCCESS { 309 | Poll::Ready(Err(FspError::from(result))) 310 | } else { 311 | Poll::Ready(Ok(unsafe { self.iosb.get().read().0 })) 312 | }; 313 | } 314 | 315 | let wait_result = unsafe { WaitForSingleObject(self.event.0, 0) }; 316 | 317 | if wait_result == WAIT_OBJECT_0 { 318 | let code = unsafe { addr_of!((*self.iosb.get()).0.Anonymous.Status).read() }; 319 | self.result = Some(code); 320 | } else if wait_result == WAIT_FAILED 321 | || wait_result == WAIT_ABANDONED 322 | || wait_result == WAIT_ABANDONED_0 323 | { 324 | self.result = Some(STATUS_ABANDONED); 325 | } 326 | 327 | // if timed out, io isn't ready 328 | cx.waker().wake_by_ref(); 329 | Poll::Pending 330 | } 331 | } 332 | 333 | pub async fn lfs_query_directory_file_async( 334 | handle: &AtomicHandle, 335 | buffer: &mut [u8], 336 | class: FILE_INFORMATION_CLASS, 337 | return_single_entry: bool, 338 | file_name: Option<&U16CStr>, 339 | restart_scan: bool, 340 | ) -> winfsp::Result { 341 | let query_ft = LfsQueryDirectoryFileFuture::new( 342 | HANDLE(handle.handle()), 343 | file_name, 344 | buffer, 345 | return_single_entry, 346 | restart_scan, 347 | class, 348 | )?; 349 | let iosb = query_ft.await?; 350 | 351 | Ok(iosb.Information) 352 | } 353 | -------------------------------------------------------------------------------- /winfsp-sys/winfsp/inc/fuse/winfsp_fuse.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fuse/winfsp_fuse.h 3 | * WinFsp FUSE compatible API. 4 | * 5 | * @copyright 2015-2025 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 | #include 31 | #endif 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | #if !defined(FSP_FUSE_API) 38 | #if defined(WINFSP_DLL_INTERNAL) 39 | #define FSP_FUSE_API __declspec(dllexport) 40 | #else 41 | #define FSP_FUSE_API __declspec(dllimport) 42 | #endif 43 | #endif 44 | 45 | #if !defined(FSP_FUSE_API_NAME) 46 | #define FSP_FUSE_API_NAME(n) (n) 47 | #endif 48 | 49 | #if !defined(FSP_FUSE_API_CALL) 50 | #define FSP_FUSE_API_CALL(n) (n) 51 | #endif 52 | 53 | #if !defined(FSP_FUSE_SYM) 54 | #if !defined(CYGFUSE) 55 | #define FSP_FUSE_SYM(proto, ...) static inline proto { __VA_ARGS__ } 56 | #else 57 | #define FSP_FUSE_SYM(proto, ...) proto; 58 | #endif 59 | #endif 60 | 61 | #define FSP_FUSE_DEVICE_TYPE (0x8000 | 'W' | 'F' * 0x100) /* DeviceIoControl -> ioctl */ 62 | #define FSP_FUSE_CTLCODE_FROM_IOCTL(cmd)\ 63 | (FSP_FUSE_DEVICE_TYPE << 16) | (((cmd) & 0x0fff) << 2) 64 | #define FSP_FUSE_IOCTL(cmd, isiz, osiz) \ 65 | ( \ 66 | (((osiz) != 0) << 31) | \ 67 | (((isiz) != 0) << 30) | \ 68 | (((isiz) | (osiz)) << 16) | \ 69 | (cmd) \ 70 | ) 71 | 72 | /* 73 | * FUSE uses a number of types (notably: struct stat) that are OS specific. 74 | * Furthermore there are sometimes multiple definitions of the same type even 75 | * within the same OS. This is certainly true on Windows, where these types 76 | * are not even native. 77 | * 78 | * For this reason we will define our own fuse_* types which represent the 79 | * types as the WinFsp DLL expects to see them. We will define these types 80 | * to be compatible with the equivalent Cygwin types as we want WinFsp-FUSE 81 | * to be usable from Cygwin. 82 | */ 83 | 84 | #define FSP_FUSE_STAT_FIELD_DEFN \ 85 | fuse_dev_t st_dev; \ 86 | fuse_ino_t st_ino; \ 87 | fuse_mode_t st_mode; \ 88 | fuse_nlink_t st_nlink; \ 89 | fuse_uid_t st_uid; \ 90 | fuse_gid_t st_gid; \ 91 | fuse_dev_t st_rdev; \ 92 | fuse_off_t st_size; \ 93 | struct fuse_timespec st_atim; \ 94 | struct fuse_timespec st_mtim; \ 95 | struct fuse_timespec st_ctim; \ 96 | fuse_blksize_t st_blksize; \ 97 | fuse_blkcnt_t st_blocks; \ 98 | struct fuse_timespec st_birthtim; 99 | #define FSP_FUSE_STAT_EX_FIELD_DEFN \ 100 | FSP_FUSE_STAT_FIELD_DEFN \ 101 | uint32_t st_flags; \ 102 | uint32_t st_reserved32[3]; \ 103 | uint64_t st_reserved64[2]; 104 | 105 | #if defined(_WIN64) || defined(_WIN32) 106 | 107 | typedef uint32_t fuse_uid_t; 108 | typedef uint32_t fuse_gid_t; 109 | typedef int32_t fuse_pid_t; 110 | 111 | typedef uint32_t fuse_dev_t; 112 | typedef uint64_t fuse_ino_t; 113 | typedef uint32_t fuse_mode_t; 114 | typedef uint16_t fuse_nlink_t; 115 | typedef int64_t fuse_off_t; 116 | 117 | #if defined(_WIN64) 118 | typedef uint64_t fuse_fsblkcnt_t; 119 | typedef uint64_t fuse_fsfilcnt_t; 120 | #else 121 | typedef uint32_t fuse_fsblkcnt_t; 122 | typedef uint32_t fuse_fsfilcnt_t; 123 | #endif 124 | typedef int32_t fuse_blksize_t; 125 | typedef int64_t fuse_blkcnt_t; 126 | 127 | #if defined(_WIN64) 128 | struct fuse_utimbuf 129 | { 130 | int64_t actime; 131 | int64_t modtime; 132 | }; 133 | struct fuse_timespec 134 | { 135 | int64_t tv_sec; 136 | int64_t tv_nsec; 137 | }; 138 | #else 139 | struct fuse_utimbuf 140 | { 141 | int32_t actime; 142 | int32_t modtime; 143 | }; 144 | struct fuse_timespec 145 | { 146 | int32_t tv_sec; 147 | int32_t tv_nsec; 148 | }; 149 | #endif 150 | 151 | #if !defined(FSP_FUSE_USE_STAT_EX) 152 | struct fuse_stat 153 | { 154 | FSP_FUSE_STAT_FIELD_DEFN 155 | }; 156 | #else 157 | struct fuse_stat 158 | { 159 | FSP_FUSE_STAT_EX_FIELD_DEFN 160 | }; 161 | #endif 162 | 163 | #if defined(_WIN64) 164 | struct fuse_statvfs 165 | { 166 | uint64_t f_bsize; 167 | uint64_t f_frsize; 168 | fuse_fsblkcnt_t f_blocks; 169 | fuse_fsblkcnt_t f_bfree; 170 | fuse_fsblkcnt_t f_bavail; 171 | fuse_fsfilcnt_t f_files; 172 | fuse_fsfilcnt_t f_ffree; 173 | fuse_fsfilcnt_t f_favail; 174 | uint64_t f_fsid; 175 | uint64_t f_flag; 176 | uint64_t f_namemax; 177 | }; 178 | #else 179 | struct fuse_statvfs 180 | { 181 | uint32_t f_bsize; 182 | uint32_t f_frsize; 183 | fuse_fsblkcnt_t f_blocks; 184 | fuse_fsblkcnt_t f_bfree; 185 | fuse_fsblkcnt_t f_bavail; 186 | fuse_fsfilcnt_t f_files; 187 | fuse_fsfilcnt_t f_ffree; 188 | fuse_fsfilcnt_t f_favail; 189 | uint32_t f_fsid; 190 | uint32_t f_flag; 191 | uint32_t f_namemax; 192 | }; 193 | #endif 194 | 195 | struct fuse_flock 196 | { 197 | int16_t l_type; 198 | int16_t l_whence; 199 | fuse_off_t l_start; 200 | fuse_off_t l_len; 201 | fuse_pid_t l_pid; 202 | }; 203 | 204 | #if defined(WINFSP_DLL_INTERNAL) 205 | #define FSP_FUSE_ENV_INIT \ 206 | { \ 207 | 'W', \ 208 | MemAlloc, MemFree, \ 209 | fsp_fuse_daemonize, \ 210 | fsp_fuse_set_signal_handlers, \ 211 | 0/*conv_to_win_path*/, \ 212 | 0/*winpid_to_pid*/, \ 213 | { 0 }, \ 214 | } 215 | #else 216 | #define FSP_FUSE_ENV_INIT \ 217 | { \ 218 | 'W', \ 219 | malloc, free, \ 220 | fsp_fuse_daemonize, \ 221 | fsp_fuse_set_signal_handlers, \ 222 | 0/*conv_to_win_path*/, \ 223 | 0/*winpid_to_pid*/, \ 224 | { 0 }, \ 225 | } 226 | #endif 227 | 228 | #elif defined(__CYGWIN__) 229 | 230 | #include 231 | #include 232 | #include 233 | #include 234 | #include 235 | #include 236 | #include 237 | 238 | #define fuse_uid_t uid_t 239 | #define fuse_gid_t gid_t 240 | #define fuse_pid_t pid_t 241 | 242 | #define fuse_dev_t dev_t 243 | #define fuse_ino_t ino_t 244 | #define fuse_mode_t mode_t 245 | #define fuse_nlink_t nlink_t 246 | #define fuse_off_t off_t 247 | 248 | #define fuse_fsblkcnt_t fsblkcnt_t 249 | #define fuse_fsfilcnt_t fsfilcnt_t 250 | #define fuse_blksize_t blksize_t 251 | #define fuse_blkcnt_t blkcnt_t 252 | 253 | #define fuse_utimbuf utimbuf 254 | #define fuse_timespec timespec 255 | 256 | #if !defined(FSP_FUSE_USE_STAT_EX) 257 | #define fuse_stat stat 258 | #else 259 | struct fuse_stat 260 | { 261 | FSP_FUSE_STAT_EX_FIELD_DEFN 262 | }; 263 | #endif 264 | #define fuse_statvfs statvfs 265 | #define fuse_flock flock 266 | 267 | #define FSP_FUSE_ENV_INIT \ 268 | { \ 269 | 'C', \ 270 | malloc, free, \ 271 | fsp_fuse_daemonize, \ 272 | fsp_fuse_set_signal_handlers, \ 273 | fsp_fuse_conv_to_win_path, \ 274 | fsp_fuse_winpid_to_pid, \ 275 | { 0 }, \ 276 | } 277 | 278 | /* 279 | * Note that long is 8 bytes long in Cygwin64 and 4 bytes long in Win64. 280 | * For this reason we avoid using long anywhere in these headers. 281 | */ 282 | 283 | #else 284 | #error unsupported environment 285 | #endif 286 | 287 | struct fuse_stat_ex 288 | { 289 | FSP_FUSE_STAT_EX_FIELD_DEFN 290 | }; 291 | 292 | struct fsp_fuse_env 293 | { 294 | unsigned environment; 295 | void *(*memalloc)(size_t); 296 | void (*memfree)(void *); 297 | int (*daemonize)(int); 298 | int (*set_signal_handlers)(void *); 299 | char *(*conv_to_win_path)(const char *); 300 | fuse_pid_t (*winpid_to_pid)(uint32_t); 301 | void (*reserved[2])(); 302 | }; 303 | 304 | FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_signal_handler)(int sig); 305 | 306 | #if defined(_WIN64) || defined(_WIN32) 307 | 308 | static inline int fsp_fuse_daemonize(int foreground) 309 | { 310 | (void)foreground; 311 | return 0; 312 | } 313 | 314 | static inline int fsp_fuse_set_signal_handlers(void *se) 315 | { 316 | (void)se; 317 | return 0; 318 | } 319 | 320 | #elif defined(__CYGWIN__) 321 | 322 | static inline int fsp_fuse_daemonize(int foreground) 323 | { 324 | int daemon(int nochdir, int noclose); 325 | int chdir(const char *path); 326 | 327 | if (!foreground) 328 | { 329 | if (-1 == daemon(0, 0)) 330 | return -1; 331 | } 332 | else 333 | chdir("/"); 334 | 335 | return 0; 336 | } 337 | 338 | static inline void *fsp_fuse_signal_thread(void *psigmask) 339 | { 340 | int sig; 341 | 342 | if (0 == sigwait((sigset_t *)psigmask, &sig)) 343 | FSP_FUSE_API_CALL(fsp_fuse_signal_handler)(sig); 344 | 345 | return 0; 346 | } 347 | 348 | static inline int fsp_fuse_set_signal_handlers(void *se) 349 | { 350 | #define FSP_FUSE_SET_SIGNAL_HANDLER(sig, newha)\ 351 | if (-1 != sigaction((sig), 0, &oldsa) &&\ 352 | oldsa.sa_handler == (se ? SIG_DFL : (newha)))\ 353 | {\ 354 | newsa.sa_handler = se ? (newha) : SIG_DFL;\ 355 | sigaction((sig), &newsa, 0);\ 356 | } 357 | #define FSP_FUSE_SIGADDSET(sig)\ 358 | if (-1 != sigaction((sig), 0, &oldsa) &&\ 359 | oldsa.sa_handler == SIG_DFL)\ 360 | sigaddset(&sigmask, (sig)); 361 | 362 | static sigset_t sigmask; 363 | static pthread_t sigthr; 364 | struct sigaction oldsa, newsa; 365 | 366 | // memset instead of initializer to avoid GCC -Wmissing-field-initializers warning 367 | memset(&newsa, 0, sizeof newsa); 368 | 369 | if (0 != se) 370 | { 371 | if (0 == sigthr) 372 | { 373 | FSP_FUSE_SET_SIGNAL_HANDLER(SIGPIPE, SIG_IGN); 374 | 375 | sigemptyset(&sigmask); 376 | FSP_FUSE_SIGADDSET(SIGHUP); 377 | FSP_FUSE_SIGADDSET(SIGINT); 378 | FSP_FUSE_SIGADDSET(SIGTERM); 379 | if (0 != pthread_sigmask(SIG_BLOCK, &sigmask, 0)) 380 | return -1; 381 | 382 | if (0 != pthread_create(&sigthr, 0, fsp_fuse_signal_thread, &sigmask)) 383 | return -1; 384 | } 385 | } 386 | else 387 | { 388 | if (0 != sigthr) 389 | { 390 | pthread_cancel(sigthr); 391 | pthread_join(sigthr, 0); 392 | sigthr = 0; 393 | 394 | if (0 != pthread_sigmask(SIG_UNBLOCK, &sigmask, 0)) 395 | return -1; 396 | sigemptyset(&sigmask); 397 | 398 | FSP_FUSE_SET_SIGNAL_HANDLER(SIGPIPE, SIG_IGN); 399 | } 400 | } 401 | 402 | return 0; 403 | 404 | #undef FSP_FUSE_SIGADDSET 405 | #undef FSP_FUSE_SET_SIGNAL_HANDLER 406 | } 407 | 408 | static inline char *fsp_fuse_conv_to_win_path(const char *path) 409 | { 410 | void *cygwin_create_path(unsigned, const void *); 411 | return (char *)cygwin_create_path( 412 | 0/*CCP_POSIX_TO_WIN_A*/ | 0x100/*CCP_RELATIVE*/, 413 | path); 414 | } 415 | 416 | static inline fuse_pid_t fsp_fuse_winpid_to_pid(uint32_t winpid) 417 | { 418 | pid_t cygwin_winpid_to_pid(int winpid); 419 | pid_t pid = cygwin_winpid_to_pid(winpid); 420 | return -1 != pid ? pid : (fuse_pid_t)winpid; 421 | } 422 | #endif 423 | 424 | 425 | static inline struct fsp_fuse_env *fsp_fuse_env(void) 426 | { 427 | static struct fsp_fuse_env env = FSP_FUSE_ENV_INIT; 428 | return &env; 429 | } 430 | 431 | #ifdef __cplusplus 432 | } 433 | #endif 434 | 435 | #endif 436 | -------------------------------------------------------------------------------- /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-2025 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/src/host/fshost.rs: -------------------------------------------------------------------------------- 1 | use std::cell::UnsafeCell; 2 | use std::ffi::OsStr; 3 | use std::marker::PhantomData; 4 | use std::ptr::NonNull; 5 | use std::ptr::null_mut; 6 | use windows::Win32::Foundation::NTSTATUS; 7 | use windows::core::HSTRING; 8 | use windows::core::Result; 9 | 10 | use winfsp_sys::{ 11 | FSP_FILE_SYSTEM, FSP_FILE_SYSTEM_INTERFACE, 12 | FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE, 13 | FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FINE, 14 | FspFileSystemCreate, FspFileSystemDelete, FspFileSystemRemoveMountPoint, 15 | FspFileSystemSetMountPoint, FspFileSystemSetOperationGuardStrategyF, 16 | FspFileSystemStartDispatcher, FspFileSystemStopDispatcher, 17 | }; 18 | 19 | #[cfg(feature = "async-io")] 20 | use crate::filesystem::AsyncFileSystemContext; 21 | use crate::filesystem::FileSystemContext; 22 | use crate::host::interface::{FileSystemUserContext, Interface}; 23 | use crate::host::{DebugMode, VolumeParams}; 24 | 25 | use crate::notify::NotifyingFileSystemContext; 26 | use crate::notify::Timer; 27 | 28 | /// Mount point for a new file system. Used by [`FileSystemHost::mount`]. 29 | pub enum MountPoint<'a> { 30 | /// A drive letter such as `X:` or directory path such as `path/to/folder`. 31 | MountPoint(&'a OsStr), 32 | /// Use the next available drive letter counting downwards from `Z:` as the mount point. 33 | NextFreeDrive, 34 | } 35 | 36 | /// Create a mount point from anything that can be viewed as a file path. 37 | impl<'a, S> From<&'a S> for MountPoint<'a> 38 | where 39 | S: AsRef + ?Sized, 40 | { 41 | fn from(s: &'a S) -> Self { 42 | Self::MountPoint(s.as_ref()) 43 | } 44 | } 45 | impl<'short, 'long, 'middle> From<&'short MountPoint<'long>> for MountPoint<'middle> 46 | where 47 | 'long: 'middle, 48 | { 49 | fn from(s: &'short MountPoint<'long>) -> Self { 50 | match s { 51 | MountPoint::MountPoint(v) => MountPoint::MountPoint(v), 52 | MountPoint::NextFreeDrive => MountPoint::NextFreeDrive, 53 | } 54 | } 55 | } 56 | 57 | /// The usermode file system locking strategy. 58 | pub enum OperationGuardStrategy { 59 | /// A fine-grained concurrency model where file system NAMESPACE accesses are guarded using an exclusive-shared (read-write) lock. 60 | /// File I/O is not guarded and concurrent reads/writes/etc. are possible. 61 | /// 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. 62 | /// The fine-grained concurrency model applies the exclusive-shared lock as follows: 63 | /// * EXCL: `set_volume_label`, `flush(None)`, `create`, `cleanup` (delete), `rename` 64 | /// * SHRD: `get_volume_info`, `open`, `set_delete`, `read_directory` 65 | /// * NONE: all other operations 66 | Fine, 67 | /// A coarse-grained concurrency model where all file system accesses are guarded by a mutually exclusive lock. 68 | Coarse, 69 | } 70 | 71 | /// Options to create the filesystem with. 72 | pub struct FileSystemParams { 73 | /// Enable the file system driver to call [`FileSystemContext::get_dir_info_by_name`](crate::filesystem::FileSystemContext::get_dir_info_by_name). 74 | pub use_dir_info_by_name: bool, 75 | /// The parameters to mount the volume with. 76 | pub volume_params: VolumeParams, 77 | /// The usermode file system locking strategy to use. 78 | pub guard_strategy: OperationGuardStrategy, 79 | /// Set the debug output mask. Debug output is only displayed if the 80 | /// `debug` crate feature is enabled, regardless of the mask. 81 | /// 82 | /// See [`FspTransactKind`](crate::constants::FspTransactKind) for possible mask values. 83 | pub debug_mode: DebugMode, 84 | } 85 | 86 | impl FileSystemParams { 87 | /// Use the default options with the given volume parameters. 88 | pub fn default_params(volume_params: VolumeParams) -> Self { 89 | Self { 90 | use_dir_info_by_name: false, 91 | volume_params, 92 | guard_strategy: OperationGuardStrategy::Fine, 93 | debug_mode: Default::default(), 94 | } 95 | } 96 | 97 | /// Use the default options with the given volume parameters and debug mode. 98 | pub fn default_params_debug(volume_params: VolumeParams, debug_mode: DebugMode) -> Self { 99 | Self { 100 | use_dir_info_by_name: false, 101 | volume_params, 102 | guard_strategy: OperationGuardStrategy::Fine, 103 | debug_mode, 104 | } 105 | } 106 | } 107 | /// The user-mode filesystem host that manages the lifetime of the mounted filesystem. 108 | /// 109 | /// This is separate from the lifetime of the service which is managed by 110 | /// [`FileSystemService`](crate::service::FileSystemService). A `FileSystemHost` 111 | /// should start within the context of a service. 112 | pub struct FileSystemHost { 113 | fsp_struct: NonNull, 114 | #[allow(dead_code)] 115 | timer: Option, 116 | phantom: PhantomData, 117 | } 118 | 119 | #[cfg(feature = "async-io")] 120 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "async-io")))] 121 | impl FileSystemHost 122 | where 123 | ::FileContext: Sync, 124 | { 125 | fn new_filesystem_inner_async( 126 | options: FileSystemParams, 127 | context: T, 128 | ) -> Result> { 129 | #[allow(unused_variables)] 130 | let FileSystemParams { 131 | use_dir_info_by_name, 132 | volume_params, 133 | guard_strategy, 134 | debug_mode, 135 | } = options; 136 | 137 | let interface = if use_dir_info_by_name { 138 | Interface::create_with_dirinfo_by_name_async::() 139 | } else { 140 | Interface::create_with_read_directory_async::() 141 | }; 142 | 143 | Self::new_filesystem_inner_iface( 144 | interface, 145 | volume_params, 146 | guard_strategy, 147 | debug_mode, 148 | context, 149 | ) 150 | } 151 | 152 | /// Create a `FileSystemHost` with the default settings 153 | /// for the provided context implementation, using async implementations of `read`, `write`, and `read_directory`. 154 | pub fn new_async(volume_params: VolumeParams, context: T) -> Result { 155 | Self::new_with_options_async( 156 | FileSystemParams { 157 | use_dir_info_by_name: false, 158 | volume_params, 159 | guard_strategy: OperationGuardStrategy::Fine, 160 | debug_mode: DebugMode::none(), 161 | }, 162 | context, 163 | ) 164 | } 165 | 166 | /// Create a `FileSystemHost` with the provided context implementation, and 167 | /// host options, using async implementations of `read`, `write`, and `read_directory`. 168 | pub fn new_with_options_async(options: FileSystemParams, context: T) -> Result { 169 | let fsp_struct = Self::new_filesystem_inner_async(options, context)?; 170 | Ok(FileSystemHost { 171 | fsp_struct, 172 | timer: None, 173 | phantom: PhantomData, 174 | }) 175 | } 176 | 177 | /// Create a `FileSystemHost` with the provided notifying context implementation, 178 | /// host options, and polling interval, using async implementations of `read`, `write`, and `read_directory`. 179 | #[cfg_attr( 180 | feature = "docsrs", 181 | doc(cfg(all(feature = "notify", feature = "async-io"))) 182 | )] 183 | #[cfg(all(feature = "notify", feature = "async-io"))] 184 | pub fn new_with_timer_async( 185 | options: FileSystemParams, 186 | context: T, 187 | ) -> Result 188 | where 189 | T: NotifyingFileSystemContext, 190 | { 191 | let fsp_struct = Self::new_filesystem_inner_async(options, context)?; 192 | let timer = Timer::create::(fsp_struct)?; 193 | 194 | Ok(FileSystemHost { 195 | fsp_struct, 196 | timer: Some(timer), 197 | phantom: PhantomData, 198 | }) 199 | } 200 | } 201 | 202 | impl FileSystemHost { 203 | #[allow(unused_variables)] 204 | fn new_filesystem_inner_iface( 205 | interface: Interface, 206 | volume_params: VolumeParams, 207 | guard_strategy: OperationGuardStrategy, 208 | debug_mode: DebugMode, 209 | context: T, 210 | ) -> Result> { 211 | let mut fsp_struct = std::ptr::null_mut(); 212 | 213 | let interface: FSP_FILE_SYSTEM_INTERFACE = interface.into(); 214 | let interface = Box::into_raw(Box::new(UnsafeCell::new(interface))); 215 | // SAFETY: WinFSP owns the allocation that fsp_struct points to. 216 | let result = unsafe { 217 | FspFileSystemCreate( 218 | volume_params.get_winfsp_device_name(), 219 | &volume_params.0, 220 | // SAFETY: UnsafeCell and T are transmutable. 221 | interface.cast(), 222 | &mut fsp_struct, 223 | ) 224 | }; 225 | 226 | let result = NTSTATUS(result); 227 | result.ok()?; 228 | 229 | #[cfg(feature = "debug")] 230 | unsafe { 231 | use windows::Win32::System::Console::{GetStdHandle, STD_ERROR_HANDLE}; 232 | // pointer crimes 233 | winfsp_sys::FspDebugLogSetHandle( 234 | GetStdHandle(STD_ERROR_HANDLE).unwrap().0 as *mut std::ffi::c_void, 235 | ); 236 | winfsp_sys::FspFileSystemSetDebugLogF(fsp_struct, debug_mode.into()); 237 | } 238 | 239 | unsafe { 240 | (*fsp_struct).UserContext = Box::into_raw(Box::new(UnsafeCell::new( 241 | FileSystemUserContext::new(context), 242 | ))) as *mut _; 243 | 244 | match guard_strategy { 245 | OperationGuardStrategy::Fine => FspFileSystemSetOperationGuardStrategyF(fsp_struct, FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FINE), 246 | OperationGuardStrategy::Coarse => FspFileSystemSetOperationGuardStrategyF(fsp_struct, FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE), 247 | } 248 | } 249 | 250 | assert!(!fsp_struct.is_null()); 251 | Ok(NonNull::new(fsp_struct).expect("FSP_FILE_SYSTEM pointer was created but was null!")) 252 | } 253 | 254 | fn new_filesystem_inner( 255 | options: FileSystemParams, 256 | context: T, 257 | ) -> Result> { 258 | #[allow(unused_variables)] 259 | let FileSystemParams { 260 | use_dir_info_by_name, 261 | volume_params, 262 | guard_strategy, 263 | debug_mode, 264 | } = options; 265 | 266 | let interface = if use_dir_info_by_name { 267 | Interface::create_with_dirinfo_by_name::() 268 | } else { 269 | Interface::create_with_read_directory::() 270 | }; 271 | 272 | Self::new_filesystem_inner_iface( 273 | interface, 274 | volume_params, 275 | guard_strategy, 276 | debug_mode, 277 | context, 278 | ) 279 | } 280 | 281 | /// Create a `FileSystemHost` with the default settings 282 | /// for the provided context implementation. 283 | pub fn new(volume_params: VolumeParams, context: T) -> Result { 284 | Self::new_with_options( 285 | FileSystemParams { 286 | use_dir_info_by_name: false, 287 | volume_params, 288 | guard_strategy: OperationGuardStrategy::Fine, 289 | debug_mode: DebugMode::none(), 290 | }, 291 | context, 292 | ) 293 | } 294 | 295 | /// Create a `FileSystemHost` with the provided context implementation, and 296 | /// host options. 297 | pub fn new_with_options(options: FileSystemParams, context: T) -> Result { 298 | let fsp_struct = Self::new_filesystem_inner(options, context)?; 299 | Ok(FileSystemHost { 300 | fsp_struct, 301 | timer: None, 302 | phantom: PhantomData, 303 | }) 304 | } 305 | 306 | /// Create a `FileSystemHost` with the provided notifying context implementation, 307 | /// host options, and polling interval. 308 | #[cfg_attr(feature = "docsrs", doc(cfg(feature = "notify")))] 309 | #[cfg(feature = "notify")] 310 | pub fn new_with_timer( 311 | options: FileSystemParams, 312 | context: T, 313 | ) -> Result 314 | where 315 | T: NotifyingFileSystemContext, 316 | { 317 | let fsp_struct = Self::new_filesystem_inner(options, context)?; 318 | let timer = Timer::create::(fsp_struct)?; 319 | Ok(FileSystemHost { 320 | fsp_struct, 321 | timer: Some(timer), 322 | phantom: PhantomData, 323 | }) 324 | } 325 | 326 | /// Start the filesystem dispatcher for this filesystem. 327 | pub fn start(&mut self) -> Result<()> { 328 | self.start_with_threads(0) 329 | } 330 | 331 | /// Start the filesystem dispatcher for this filesystem with the specified number of threads. 332 | pub fn start_with_threads(&mut self, num_threads: u32) -> Result<()> { 333 | let result = unsafe { FspFileSystemStartDispatcher(self.fsp_struct.as_ptr(), num_threads) }; 334 | let result = NTSTATUS(result); 335 | result.ok() 336 | } 337 | 338 | /// Stop the filesystem dispatcher for this filesystem. 339 | pub fn stop(&mut self) { 340 | unsafe { FspFileSystemStopDispatcher(self.fsp_struct.as_ptr()) } 341 | } 342 | 343 | /// Mount the filesystem to the given mount point. 344 | /// 345 | /// # Examples 346 | /// 347 | /// ``` 348 | /// use winfsp::host::{FileSystemHost, MountPoint}; 349 | /// use winfsp::filesystem::FileSystemContext; 350 | /// 351 | /// fn mount_file_system(host: &mut FileSystemHost) -> winfsp::Result<()> { 352 | /// // Can mount in one of the following ways: 353 | /// host.mount("X:")?; 354 | /// host.mount("../MyFileSystem".to_string())?; 355 | /// host.mount(&std::path::PathBuf::from("C:/WinFspFileSystem"))?; 356 | /// host.mount(MountPoint::NextFreeDrive)?; 357 | /// 358 | /// Ok(()) 359 | /// } 360 | /// ``` 361 | pub fn mount(&mut self, mount: S) -> Result<()> 362 | where 363 | // Convert a reference to the provided value in order to allow the 364 | // caller to provide owned values such as `String`: 365 | for<'b> &'b S: Into>, 366 | { 367 | let mount_str: HSTRING; 368 | let mount_ptr = match <&S as Into>>::into(&mount) { 369 | MountPoint::MountPoint(mount) => { 370 | mount_str = HSTRING::from(mount); 371 | // Pointer is valid until `mount_str` is dropped at the end of the function. 372 | mount_str.as_ptr().cast_mut() 373 | } 374 | MountPoint::NextFreeDrive => null_mut(), 375 | }; 376 | let result = unsafe { FspFileSystemSetMountPoint(self.fsp_struct.as_ptr(), mount_ptr) }; 377 | 378 | let result = NTSTATUS(result); 379 | result.ok() 380 | } 381 | 382 | /// Unmount the filesystem. It is safe to call this function even if the 383 | /// file system is not mounted. 384 | pub fn unmount(&mut self) { 385 | unsafe { FspFileSystemRemoveMountPoint(self.fsp_struct.as_ptr()) } 386 | } 387 | } 388 | 389 | /// Testing that the lifetime of the user context must outlive the lifetime of the WinfspHost 390 | /// 391 | /// Note that this test only checks that this does not compile, and can't the reason 392 | /// a more proper test would verify that this code would compile if lifetimes are valid 393 | /// ```rust, compile_fail 394 | /// use winfsp::host::{FileSystemHost, VolumeParams}; 395 | /// use winfsp::filesystem::{FileSystemContext, FileSecurity, OpenFileInfo}; 396 | /// use winfsp::{Result, U16CStr}; 397 | /// use std::ffi::c_void; 398 | /// 399 | /// 400 | /// struct NullContext<'a> { 401 | /// data: &'a u8, 402 | /// } 403 | /// 404 | /// impl<'a> FileSystemContext for NullContext<'a> { 405 | /// type FileContext = (); 406 | /// 407 | /// fn get_security_by_name(&self, _: &U16CStr, _: Option<&mut [c_void]>, 408 | /// _: impl FnOnce(&U16CStr) -> Option, 409 | /// ) -> Result { 410 | /// todo!() 411 | /// } 412 | /// 413 | /// fn open( 414 | /// &self, 415 | /// _: &U16CStr, 416 | /// _: u32, 417 | /// _: winfsp_sys::FILE_ACCESS_RIGHTS, 418 | /// _: &mut OpenFileInfo, 419 | /// ) -> Result<()> { 420 | /// todo!() 421 | /// } 422 | /// 423 | /// fn close(&self, _: ()) { 424 | /// todo!() 425 | /// } 426 | /// } 427 | /// 428 | /// let host; 429 | /// { 430 | /// let data = 0; 431 | /// let context = NullContext{data: &data}; 432 | /// host = FileSystemHost::new(VolumeParams::default(), context).expect("") 433 | /// } 434 | /// drop(host); 435 | /// 436 | /// ``` 437 | impl Drop for FileSystemHost { 438 | fn drop(&mut self) { 439 | self.unmount(); 440 | self.stop(); 441 | unsafe { 442 | // SAFETY: FSP is stopped an no longer running anything on this filesystem 443 | let user_context = self.fsp_struct.as_ref().UserContext as *mut UnsafeCell; 444 | let interface = self.fsp_struct.as_ref().Interface as *mut UnsafeCell; 445 | 446 | FspFileSystemDelete(self.fsp_struct.as_ptr()); 447 | 448 | // self.user_ctx_dtor is a valid destructor for UnsafeCell 449 | // user context is an UnsafeCell 450 | let user_context = Box::>::from_raw(user_context); 451 | drop(user_context); 452 | let interface = Box::>::from_raw(interface); 453 | drop(interface); 454 | }; 455 | } 456 | } 457 | 458 | /// SAFETY: FileSystemHost does not expose fsp_struct and cannot be cloned 459 | unsafe impl Send for FileSystemHost {} 460 | --------------------------------------------------------------------------------