which-fs 0.2.1

Give me a path, I'll tell you the filesystem it's on
Documentation
// SPDX-FileCopyrightText: 2026 Manuel Quarneti <mq1@ik.me>
// SPDX-License-Identifier: MIT OR Apache-2.0

mod magic;

use crate::magic::which_kind;
use std::{fmt, path::Path};

/// Represents the type of a filesystem.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
pub enum FsKind {
    Fat32,
    ExFat,
    Apfs,
    /// Covers ext2, ext3, and ext4.
    Ext2,
    Hfs,
    Btrfs,
    Bcachefs,
    Xfs,
    Fuse,
    Ntfs,
    F2fs,
    /// Filesystem type could not be determined.
    #[default]
    Unknown,
}

impl fmt::Display for FsKind {
    /// Formats `FsKind` as its common display name (e.g. `"FAT32"`, `"exFAT"`).
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FsKind::Fat32 => write!(f, "FAT32"),
            FsKind::ExFat => write!(f, "exFAT"),
            FsKind::Apfs => write!(f, "APFS"),
            FsKind::Ext2 => write!(f, "ext2/3/4"),
            FsKind::Hfs => write!(f, "HFS+"),
            FsKind::Btrfs => write!(f, "BTRFS"),
            FsKind::Bcachefs => write!(f, "bcachefs"),
            FsKind::Xfs => write!(f, "XFS"),
            FsKind::Fuse => write!(f, "FUSE"),
            FsKind::Ntfs => write!(f, "NTFS"),
            FsKind::F2fs => write!(f, "F2FS"),
            FsKind::Unknown => write!(f, "Unknown"),
        }
    }
}

impl FsKind {
    /// Detects the filesystem type for the given path.
    ///
    /// # Arguments
    /// * `path` - This can be the mount point or a path to a file within the filesystem.
    ///
    /// # Errors
    /// Returns an error if the underlying OS call fails.
    #[cfg(unix)]
    pub fn try_from_path(path: impl AsRef<Path>) -> rustix::io::Result<FsKind> {
        let stat = rustix::fs::statfs(path.as_ref())?;

        #[cfg(target_os = "linux")]
        let data = stat.f_type;

        #[cfg(target_os = "macos")]
        let data = stat.f_fstypename;

        let kind = which_kind(data);
        Ok(kind)
    }

    /// Detects the filesystem type for the given path.
    ///
    /// # Arguments
    /// * `path` - This can be the mount point or a path to a directory within the filesystem.
    ///
    /// # Errors
    /// Returns an error if the underlying OS call fails.
    #[cfg(windows)]
    pub fn try_from_path(path: impl AsRef<Path>) -> windows::core::Result<FsKind> {
        use std::os::windows::ffi::OsStrExt;
        use windows::Win32::Foundation::MAX_PATH;
        use windows::Win32::Storage::FileSystem::{GetVolumeInformationW, GetVolumePathNameW};
        use windows::core::PCWSTR;

        // Convert input path to null-terminated UTF-16 wide string
        let path_wide: Vec<u16> = path
            .as_ref()
            .as_os_str()
            .encode_wide()
            .chain(std::iter::once(0))
            .collect();

        // Get the Volume Path Name (Mount Point)
        let mut volume_path = [0u16; MAX_PATH as usize + 1];
        unsafe {
            GetVolumePathNameW(PCWSTR(path_wide.as_ptr()), &mut volume_path)?;
        }

        let mut fs_name_buffer = [0u16; 16];
        unsafe {
            GetVolumeInformationW(
                PCWSTR(volume_path.as_ptr()),
                None,
                None,
                None,
                None,
                Some(&mut fs_name_buffer),
            )?;
        }

        let kind = which_kind(fs_name_buffer);
        Ok(kind)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_detect() {
        let path = Path::new(".");
        let kind = FsKind::try_from_path(path).unwrap();
        assert_ne!(kind, FsKind::Unknown);
    }
}