Skip to content

Commit daacfb9

Browse files
authored
Merge pull request #257 from fox0/main
fs: Add mntent.h wrapper
2 parents 274f4a0 + 67c0f0b commit daacfb9

File tree

3 files changed

+168
-39
lines changed

3 files changed

+168
-39
lines changed

fs/df.rs

Lines changed: 25 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,20 @@
77
// SPDX-License-Identifier: MIT
88
//
99

10+
#[cfg(target_os = "linux")]
11+
mod mntent;
12+
13+
#[cfg(target_os = "linux")]
14+
use crate::mntent::MountTable;
15+
1016
use clap::Parser;
1117
use gettextrs::{bind_textdomain_codeset, gettext, setlocale, textdomain, LocaleCategory};
1218
use plib::PROJECT_NAME;
13-
use std::ffi::{CStr, CString};
19+
#[cfg(target_os = "macos")]
20+
use std::ffi::CStr;
21+
use std::ffi::CString;
1422
use std::io;
1523

16-
#[cfg(target_os = "linux")]
17-
const _PATH_MOUNTED: &'static str = "/etc/mtab";
18-
1924
#[derive(Parser)]
2025
#[command(version, about = gettext("df - report free storage space"))]
2126
struct Args {
@@ -54,9 +59,7 @@ fn to_cstr(array: &[libc::c_char]) -> &CStr {
5459
}
5560
}
5661

57-
fn stat(filename_str: &str) -> io::Result<libc::stat> {
58-
let filename = CString::new(filename_str).unwrap();
59-
62+
fn stat(filename: &CString) -> io::Result<libc::stat> {
6063
unsafe {
6164
let mut st: libc::stat = std::mem::zeroed();
6265
let rc = libc::stat(filename.as_ptr(), &mut st);
@@ -102,11 +105,11 @@ impl MountList {
102105
}
103106
}
104107

105-
fn push(&mut self, fsstat: &libc::statfs, devname: &CStr, dirname: &CStr) {
108+
fn push(&mut self, fsstat: &libc::statfs, devname: &CString, dirname: &CString) {
106109
let dev = {
107-
if let Ok(st) = stat(devname.to_str().unwrap()) {
110+
if let Ok(st) = stat(devname) {
108111
st.st_rdev as i64
109-
} else if let Ok(st) = stat(dirname.to_str().unwrap()) {
112+
} else if let Ok(st) = stat(dirname) {
110113
st.st_dev as i64
111114
} else {
112115
-1
@@ -136,9 +139,9 @@ fn read_mount_info() -> io::Result<MountList> {
136139

137140
let mounts: &[libc::statfs] = std::slice::from_raw_parts(mounts as _, n_mnt as _);
138141
for mount in mounts {
139-
let devname = to_cstr(&mount.f_mntfromname);
140-
let dirname = to_cstr(&mount.f_mntonname);
141-
info.push(mount, devname, dirname);
142+
let devname = to_cstr(&mount.f_mntfromname).into();
143+
let dirname = to_cstr(&mount.f_mntonname).into();
144+
info.push(mount, &devname, &dirname);
142145
}
143146
}
144147

@@ -149,47 +152,30 @@ fn read_mount_info() -> io::Result<MountList> {
149152
fn read_mount_info() -> io::Result<MountList> {
150153
let mut info = MountList::new();
151154

152-
unsafe {
153-
let path_mnt = CString::new(_PATH_MOUNTED).unwrap();
154-
let mnt_mode = CString::new("r").unwrap();
155-
let f = libc::setmntent(path_mnt.as_ptr(), mnt_mode.as_ptr());
156-
if f.is_null() {
157-
return Err(io::Error::last_os_error());
158-
}
159-
160-
loop {
161-
let me = libc::getmntent(f);
162-
if me.is_null() {
163-
break;
164-
}
165-
166-
let me_devname = (*me).mnt_fsname;
167-
let me_dirname = (*me).mnt_dir;
168-
let devname = CStr::from_ptr(me_devname);
169-
let dirname = CStr::from_ptr(me_dirname);
170-
171-
let mut mount: libc::statfs = std::mem::zeroed();
172-
let rc = libc::statfs(dirname.as_ptr(), &mut mount);
155+
let mounts = MountTable::try_new()?;
156+
for mount in mounts {
157+
unsafe {
158+
let mut buf: libc::statfs = std::mem::zeroed();
159+
let rc = libc::statfs(mount.dir.as_ptr(), &mut buf);
173160
if rc < 0 {
174161
eprintln!(
175162
"{}: {}",
176-
dirname.to_str().unwrap(),
163+
mount.dir.to_str().unwrap(),
177164
io::Error::last_os_error()
178165
);
179166
continue;
180167
}
181168

182-
info.push(&mount, devname, dirname);
169+
info.push(&buf, &mount.fsname, &mount.dir);
183170
}
184-
185-
libc::endmntent(f);
186171
}
187172

188173
Ok(info)
189174
}
190175

191176
fn mask_fs_by_file(info: &mut MountList, filename: &str) -> io::Result<()> {
192-
let stat_res = stat(filename);
177+
let c_filename = CString::new(filename).expect("`filename` contains an internal 0 byte");
178+
let stat_res = stat(&c_filename);
193179
if let Err(e) = stat_res {
194180
eprintln!("{}: {}", filename, e);
195181
return Err(e);

fs/mntent.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
//
2+
// Copyright (c) 2024 fox0
3+
//
4+
// This file is part of the posixutils-rs project covered under
5+
// the MIT License. For the full license text, please see the LICENSE
6+
// file in the root directory of this project.
7+
// SPDX-License-Identifier: MIT
8+
//
9+
10+
//! The mtab file
11+
//! https://www.gnu.org/software/libc/manual/html_node/mtab.html
12+
13+
use libc::{endmntent, getmntent, setmntent, FILE};
14+
use std::ffi::{CStr, CString};
15+
use std::io;
16+
use std::sync::Mutex;
17+
18+
const _PATH_MOUNTED: &CStr = c"/etc/mtab";
19+
20+
/// The mtab (contraction of mounted file systems table) file
21+
/// is a system information file, commonly found on Unix-like systems
22+
pub struct MountTable {
23+
inner: *mut FILE,
24+
}
25+
26+
/// Structure describing a mount table entry
27+
#[derive(Debug, PartialEq)]
28+
pub struct MountTableEntity {
29+
/// Device or server for filesystem
30+
pub fsname: CString,
31+
/// Directory mounted on
32+
pub dir: CString,
33+
/// Type of filesystem: ufs, nfs, etc
34+
pub fstype: CString,
35+
/// Comma-separated options for fs
36+
pub opts: CString,
37+
/// Dump frequency (in days)
38+
pub freq: i32,
39+
/// Pass number for `fsck``
40+
pub passno: i32,
41+
}
42+
43+
impl MountTable {
44+
pub fn try_new() -> Result<Self, io::Error> {
45+
Self::open(_PATH_MOUNTED, c"r")
46+
}
47+
48+
/// Open mtab file
49+
fn open(filename: &CStr, mode: &CStr) -> Result<Self, io::Error> {
50+
// Preliminary: | MT-Safe | AS-Unsafe heap lock | AC-Unsafe mem fd lock
51+
// https://www.gnu.org/software/libc/manual/html_node/POSIX-Safety-Concepts.html
52+
let inner = unsafe { setmntent(filename.as_ptr(), mode.as_ptr()) };
53+
if inner.is_null() {
54+
return Err(io::Error::last_os_error());
55+
}
56+
Ok(Self { inner })
57+
}
58+
}
59+
60+
impl Iterator for MountTable {
61+
type Item = MountTableEntity;
62+
63+
fn next(&mut self) -> Option<Self::Item> {
64+
static THREAD_UNSAFE_FUNCTION_MUTEX: Mutex<()> = Mutex::new(());
65+
let _lock = THREAD_UNSAFE_FUNCTION_MUTEX.lock().unwrap();
66+
67+
// Preliminary: | MT-Unsafe race:mntentbuf locale | AS-Unsafe corrupt heap init | AC-Unsafe init corrupt lock mem
68+
// https://www.gnu.org/software/libc/manual/html_node/POSIX-Safety-Concepts.html
69+
let me = unsafe { getmntent(self.inner) };
70+
if me.is_null() {
71+
return None;
72+
}
73+
74+
unsafe {
75+
Some(MountTableEntity {
76+
fsname: CStr::from_ptr((*me).mnt_fsname).into(),
77+
dir: CStr::from_ptr((*me).mnt_dir).into(),
78+
fstype: CStr::from_ptr((*me).mnt_type).into(),
79+
opts: CStr::from_ptr((*me).mnt_opts).into(),
80+
freq: (*me).mnt_freq,
81+
passno: (*me).mnt_passno,
82+
})
83+
}
84+
}
85+
}
86+
87+
impl Drop for MountTable {
88+
/// Close mtab file
89+
fn drop(&mut self) {
90+
// Preliminary: | MT-Safe | AS-Unsafe heap lock | AC-Unsafe lock mem fd
91+
// https://www.gnu.org/software/libc/manual/html_node/POSIX-Safety-Concepts.html
92+
let _rc = unsafe { endmntent(self.inner) };
93+
}
94+
}
95+
96+
#[cfg(test)]
97+
mod tests {
98+
use super::*;
99+
100+
#[test]
101+
fn test_open() {
102+
let mtab = MountTable::open(c"tests/mtab.txt", c"r");
103+
assert!(mtab.is_ok());
104+
}
105+
106+
#[test]
107+
fn test_open_not_found() {
108+
let mtab = MountTable::open(c"/tmp/not_found", c"r");
109+
let mtab = mtab.err().unwrap();
110+
assert_eq!(mtab.kind(), std::io::ErrorKind::NotFound);
111+
}
112+
113+
#[test]
114+
fn test_iterable() {
115+
let mtab = MountTable::open(c"tests/mtab.txt", c"r").unwrap();
116+
let vec = Vec::from_iter(mtab);
117+
assert_eq!(vec.len(), 2);
118+
assert_eq!(
119+
vec[0],
120+
MountTableEntity {
121+
fsname: CString::new("/dev/sdb1").unwrap(),
122+
dir: CString::new("/").unwrap(),
123+
fstype: CString::new("ext3").unwrap(),
124+
opts: CString::new("rw,relatime,errors=remount-ro").unwrap(),
125+
freq: 0,
126+
passno: 0,
127+
}
128+
);
129+
assert_eq!(
130+
vec[1],
131+
MountTableEntity {
132+
fsname: CString::new("proc").unwrap(),
133+
dir: CString::new("/proc").unwrap(),
134+
fstype: CString::new("proc").unwrap(),
135+
opts: CString::new("rw,noexec,nosuid,nodev").unwrap(),
136+
freq: 0,
137+
passno: 0,
138+
}
139+
);
140+
}
141+
}

fs/tests/mtab.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/dev/sdb1 / ext3 rw,relatime,errors=remount-ro 0 0
2+
proc /proc proc rw,noexec,nosuid,nodev 0 0

0 commit comments

Comments
 (0)