1mod magic;
5
6use crate::magic::which_kind;
7use std::{fmt, path::Path};
8
9#[derive(Debug, Clone, Copy, Eq, PartialEq)]
11pub enum FsKind {
12 Fat32,
13 ExFat,
14 Apfs,
15 Ext2,
17 Hfs,
18 Btrfs,
19 Bcachefs,
20 Xfs,
21 Fuse,
22 Ntfs,
23 F2fs,
24 Unknown,
26}
27
28impl fmt::Display for FsKind {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 match self {
32 FsKind::Fat32 => write!(f, "FAT32"),
33 FsKind::ExFat => write!(f, "exFAT"),
34 FsKind::Apfs => write!(f, "APFS"),
35 FsKind::Ext2 => write!(f, "ext2/3/4"),
36 FsKind::Hfs => write!(f, "HFS+"),
37 FsKind::Btrfs => write!(f, "BTRFS"),
38 FsKind::Bcachefs => write!(f, "bcachefs"),
39 FsKind::Xfs => write!(f, "XFS"),
40 FsKind::Fuse => write!(f, "FUSE"),
41 FsKind::Ntfs => write!(f, "NTFS"),
42 FsKind::F2fs => write!(f, "F2FS"),
43 FsKind::Unknown => write!(f, "Unknown"),
44 }
45 }
46}
47
48impl FsKind {
49 #[cfg(unix)]
57 pub fn try_from_path(path: impl AsRef<Path>) -> rustix::io::Result<FsKind> {
58 let stat = rustix::fs::statfs(path.as_ref())?;
59
60 #[cfg(target_os = "linux")]
61 let data = stat.f_type;
62
63 #[cfg(target_os = "macos")]
64 let data = stat.f_fstypename;
65
66 let kind = which_kind(data);
67 Ok(kind)
68 }
69
70 #[cfg(windows)]
78 pub fn try_from_path(path: impl AsRef<Path>) -> windows::core::Result<FsKind> {
79 use std::os::windows::ffi::OsStrExt;
80 use windows::Win32::Foundation::MAX_PATH;
81 use windows::Win32::Storage::FileSystem::{GetVolumeInformationW, GetVolumePathNameW};
82 use windows::core::PCWSTR;
83
84 let path_wide: Vec<u16> = path
86 .as_ref()
87 .as_os_str()
88 .encode_wide()
89 .chain(std::iter::once(0))
90 .collect();
91
92 let mut volume_path = [0u16; MAX_PATH as usize + 1];
94 unsafe {
95 GetVolumePathNameW(PCWSTR(path_wide.as_ptr()), &mut volume_path)?;
96 }
97
98 let mut fs_name_buffer = [0u16; 16];
99 unsafe {
100 GetVolumeInformationW(
101 PCWSTR(volume_path.as_ptr()),
102 None,
103 None,
104 None,
105 None,
106 Some(&mut fs_name_buffer),
107 )?;
108 }
109
110 let kind = which_kind(fs_name_buffer);
111 Ok(kind)
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn test_detect() {
121 let path = Path::new(".");
122 let kind = FsKind::try_from_path(path).unwrap();
123 assert_ne!(kind, FsKind::Unknown);
124 }
125}