├── .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 | [](https://crates.io/crates/winfsp-sys) [](https://docs.rs/winfsp-sys) 
4 |
5 |
6 | Raw FFI bindings to [WinFSP](https://github.com/winfsp/winfsp).
7 |
8 | ## Usage
9 | The [winfsp](https://crates.io/crates/winfsp) crate provides idiomatic wrappers around the raw WinFSP APIs.
10 |
11 | By default, winfsp-sys builds against an included import library. To build against the installed WinFSP libraries, enable the `system`
12 | feature. The path will automatically be determined via the Registry.
13 |
14 | ```toml
15 | [dependencies.winfsp-sys]
16 | version = "0.2"
17 | features = ["system"]
18 | ```
--------------------------------------------------------------------------------
/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 | [](https://crates.io/crates/winfsp)  [](https://docs.rs/winfsp) 
4 |
5 | Safe Rust bindings to [WinFSP](https://github.com/winfsp/winfsp) with examples.
6 |
7 | > **Warning**
8 | >
9 | > A best effort has been made to keep Rust's aliasing rules in mind, and provide a safe and sound wrapper over
10 | > WinFSP. However, FFI with WinFSP involves a lot of pointers that end up as references and the nature of FFI makes
11 | > it difficult to test with miri. While ntptfs is used to test the correctness of the bindings,
12 | > there is still a chance these bindings are unsound.
13 | >
14 | > Please file a bug report if you encounter unsoundness when using the safe APIs of these bindings.
15 |
16 | ## Usage
17 | By default, winfsp-rs builds against an included import library. To build against the installed WinFSP libraries, enable the `system`
18 | feature. The path will automatically be determined via the Registry.
19 |
20 | ```toml
21 | [dependencies.winfsp]
22 | version = "0.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