lfs_core/linux/
mod.rs

1mod block_device;
2mod read_mountinfos;
3
4use {
5    crate::*,
6    block_device::*,
7    lazy_regex::*,
8    std::{
9        ffi::CString,
10        mem,
11        os::unix::ffi::OsStrExt,
12        path::Path,
13    },
14};
15
16pub use read_mountinfos::ParseMountInfoError;
17
18pub fn new_disk(name: String) -> Disk {
19    let rotational = sys::read_file_as_bool(format!("/sys/block/{name}/queue/rotational"));
20    let removable = sys::read_file_as_bool(format!("/sys/block/{name}/removable"));
21    let ram = regex_is_match!(r#"^zram\d*$"#, &name);
22    let dm_uuid = sys::read_file(format!("/sys/block/{name}/dm/uuid")).ok();
23    let crypted = dm_uuid
24        .as_ref()
25        .is_some_and(|uuid| uuid.starts_with("CRYPT-"));
26    let lvm = dm_uuid.is_some_and(|uuid| uuid.starts_with("LVM-"));
27    Disk {
28        name,
29        rotational,
30        removable,
31        image: false,
32        read_only: None,
33        ram,
34        lvm,
35        crypted,
36    }
37}
38
39/// Read all the mount points and load basic information on them
40pub fn read_mounts(options: &ReadOptions) -> Result<Vec<Mount>, Error> {
41    let by_label = read_by("label").ok();
42    let by_uuid = read_by("uuid").ok();
43    let by_partuuid = read_by("partuuid").ok();
44
45    // we'll find the disk for a filesystem by taking the longest
46    // disk whose name starts the one of our partition
47    // hence the sorting.
48    let bd_list = BlockDeviceList::read()?;
49    read_mountinfos::read_all_mountinfos()?
50        .drain(..)
51        .map(|info| {
52            let top_bd = bd_list.find_top(info.dev, info.dm_name(), info.fs_name());
53            let fs_label = get_label(&info.fs, by_label.as_deref());
54            let uuid = get_label(&info.fs, by_uuid.as_deref());
55            let part_uuid = get_label(&info.fs, by_partuuid.as_deref());
56            let disk = top_bd.map(|bd| new_disk(bd.name.clone()));
57            let stats = if !options.remote_stats && info.is_remote() {
58                Err(StatsError::Excluded)
59            } else {
60                read_stats(&info.mount_point)
61            };
62            Ok(Mount {
63                info,
64                fs_label,
65                disk,
66                stats,
67                uuid,
68                part_uuid,
69            })
70        })
71        .collect()
72}
73
74pub fn read_stats(mount_point: &Path) -> Result<Stats, StatsError> {
75    let c_mount_point = CString::new(mount_point.as_os_str().as_bytes()).unwrap();
76    unsafe {
77        let mut statvfs = mem::MaybeUninit::<libc::statvfs>::uninit();
78        let code = libc::statvfs(c_mount_point.as_ptr(), statvfs.as_mut_ptr());
79        match code {
80            0 => {
81                let statvfs = statvfs.assume_init();
82
83                // blocks info
84                let bsize = statvfs.f_bsize;
85                let blocks = statvfs.f_blocks;
86                let bfree = statvfs.f_bfree;
87                let bavail = statvfs.f_bavail;
88                if bsize == 0 || blocks == 0 || bfree > blocks || bavail > blocks {
89                    // unconsistent or void data
90                    return Err(StatsError::Unconsistent);
91                }
92
93                // statvfs doesn't provide bused
94                let bused = blocks - bavail;
95
96                // inodes info, will be checked in Inodes::new
97                let files = statvfs.f_files;
98                let ffree = statvfs.f_ffree;
99                let favail = statvfs.f_favail;
100                #[allow(clippy::useless_conversion)]
101                let inodes = Inodes::new(files.into(), ffree.into(), favail.into());
102
103                #[allow(clippy::useless_conversion)]
104                Ok(Stats {
105                    bsize: bsize.into(),
106                    blocks: blocks.into(),
107                    bused: bused.into(),
108                    bfree: bfree.into(),
109                    bavail: bavail.into(),
110                    inodes: inodes.into(),
111                })
112            }
113            _ => {
114                // the filesystem wasn't found, it's a strange one, for example a
115                // docker one, or a disconnected remote one
116                Err(StatsError::Unreachable)
117            }
118        }
119    }
120}