1pub 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 Ext2,
15 Hfs,
16 Btrfs,
17 Bcachefs,
18 Xfs,
19 Fuse,
20 Ntfs,
21 F2fs,
22 Unknown(Magic),
23}
24
25impl fmt::Display for FsKind {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 match self {
28 FsKind::Fat32 => write!(f, "FAT32"),
29 FsKind::ExFat => write!(f, "exFAT"),
30 FsKind::Apfs => write!(f, "APFS"),
31 FsKind::Ext2 => write!(f, "ext2/3/4"),
32 FsKind::Hfs => write!(f, "HFS+"),
33 FsKind::Btrfs => write!(f, "BTRFS"),
34 FsKind::Bcachefs => write!(f, "bcachefs"),
35 FsKind::Xfs => write!(f, "XFS"),
36 FsKind::Fuse => write!(f, "FUSE"),
37 FsKind::Ntfs => write!(f, "NTFS"),
38 FsKind::F2fs => write!(f, "F2FS"),
39 FsKind::Unknown(magic) => write!(f, "Unknown: {magic:?}"),
40 }
41 }
42}
43
44#[cfg(unix)]
45pub fn detect(path: &Path) -> rustix::io::Result<FsKind> {
46 let stat = rustix::fs::statfs(path)?;
47
48 #[cfg(target_os = "linux")]
49 let data = stat.f_type;
50
51 #[cfg(target_os = "macos")]
52 let data = stat.f_fstypename;
53
54 let kind = which_kind(data);
55
56 Ok(kind)
57}
58
59#[cfg(windows)]
60pub fn detect(path: &Path) -> windows::core::Result<FsKind> {
61 use std::os::windows::ffi::OsStrExt;
62 use windows::Win32::Foundation::MAX_PATH;
63 use windows::Win32::Storage::FileSystem::{GetVolumeInformationW, GetVolumePathNameW};
64 use windows::core::PCWSTR;
65
66 let path_wide: Vec<u16> = path
68 .as_os_str()
69 .encode_wide()
70 .chain(std::iter::once(0))
71 .collect();
72
73 let mut volume_path = [0u16; MAX_PATH as usize + 1];
75 unsafe {
76 GetVolumePathNameW(PCWSTR(path_wide.as_ptr()), &mut volume_path)?;
77 }
78
79 let mut fs_name_buffer = [0u16; 16];
80 unsafe {
81 GetVolumeInformationW(
82 PCWSTR(volume_path.as_ptr()),
83 None,
84 None,
85 None,
86 None,
87 Some(&mut fs_name_buffer),
88 )?;
89 }
90
91 let kind = which_kind(fs_name_buffer);
92
93 Ok(kind)
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn test_detect() {
102 let path = Path::new(".");
103 let kind = detect(path).unwrap();
104
105 if matches!(kind, FsKind::Unknown(_)) {
106 panic!("Unknown filesystem kind: {kind}");
107 }
108 }
109}