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