Skip to main content

which_fs/
lib.rs

1// SPDX-FileCopyrightText: 2026 Manuel Quarneti <mq1@ik.me>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4pub mod magic;
5
6use crate::magic::{Magic, which_kind};
7use std::{fmt, path::Path};
8
9#[derive(Debug, Clone, Copy, Eq, PartialEq)]
10pub enum FsKind {
11    Fat32,
12    ExFat,
13    Apfs,
14    Unknown(Magic),
15}
16
17impl fmt::Display for FsKind {
18    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19        match self {
20            FsKind::Fat32 => write!(f, "FAT32"),
21            FsKind::ExFat => write!(f, "exFAT"),
22            FsKind::Apfs => write!(f, "APFS"),
23            FsKind::Unknown(magic) => write!(f, "Unknown: {magic:?}"),
24        }
25    }
26}
27
28#[cfg(unix)]
29pub fn detect(path: &Path) -> rustix::io::Result<FsKind> {
30    let stat = rustix::fs::statfs(path)?;
31
32    #[cfg(target_os = "linux")]
33    let data = stat.f_type;
34
35    #[cfg(target_os = "macos")]
36    let data = stat.f_fstypename;
37
38    let kind = which_kind(data);
39
40    Ok(kind)
41}
42
43#[cfg(windows)]
44pub fn detect(path: &Path) -> windows::core::Result<FsKind> {
45    use std::os::windows::ffi::OsStrExt;
46    use windows::Win32::Foundation::MAX_PATH;
47    use windows::Win32::Storage::FileSystem::{GetVolumeInformationW, GetVolumePathNameW};
48    use windows::core::PCWSTR;
49
50    // Convert input path to null-terminated UTF-16 wide string
51    let path_wide: Vec<u16> = path
52        .as_os_str()
53        .encode_wide()
54        .chain(std::iter::once(0))
55        .collect();
56
57    // Get the Volume Path Name (Mount Point)
58    let mut volume_path = [0u16; MAX_PATH as usize];
59    unsafe {
60        GetVolumePathNameW(PCWSTR(path_wide.as_ptr()), &mut volume_path)?;
61    }
62
63    let mut fs_name_buffer = [0u16; 16];
64    unsafe {
65        GetVolumeInformationW(
66            PCWSTR(volume_path.as_ptr()),
67            None,
68            None,
69            None,
70            None,
71            Some(&mut fs_name_buffer),
72        )?;
73    }
74
75    let kind = which_kind(fs_name_buffer);
76
77    Ok(kind)
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn test_detect() {
86        let path = Path::new(".");
87        let kind = detect(path).unwrap();
88
89        if matches!(kind, FsKind::Unknown(_)) {
90            panic!("Unknown filesystem kind: {kind}");
91        }
92    }
93}