use crate::log::{log_critical_error, log_success, log_warning}; use libc::syscall; use std::ffi::CString; use std::fs::{create_dir, metadata}; use std::os::unix::fs::MetadataExt; use std::{fmt, fs}; #[derive(Debug)] pub struct FstabEntry { pub source: String, pub mountpoint: String, pub fstype: String, pub options: String, pub dump: u32, pub pass: u32, } impl fmt::Display for FstabEntry { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "Source: {}\n Mountpoint: {}\n FSType: {}\n Options: {}\n Dump: {}\n Pass: {}\n", self.source, self.mountpoint, self.fstype, self.options, self.dump, self.pass ) } } impl FstabEntry { fn parse_line(line: &str) -> Result> { let line = line.trim(); if line.is_empty() || line.starts_with('#') { return Err("Empty line or comment".into()); } let parts: Vec<&str> = line.split_whitespace().collect(); if parts.len() != 6 { return Err(format!("Invalid number of fields in fstab entry: {}", line).into()); } let source = parts[0].to_string(); let mountpoint = parts[1].to_string(); let fstype = parts[2].to_string(); let options = parts[3].to_string(); let dump = parts[4] .parse::() .map_err(|e| format!("Failed to parse dump field '{}': {}", parts[4], e))?; let pass = parts[5] .parse::() .map_err(|e| format!("Failed to parse pass field '{}': {}", parts[5], e))?; Ok(FstabEntry { source, mountpoint, fstype, options, dump, pass, }) } pub fn parse_fstab(path: &str) -> Result, Box> { let content = fs::read_to_string(path)?; let mut entries = Vec::new(); for line in content.lines() { match Self::parse_line(line) { Ok(entry) => entries.push(entry), Err(_) => { log_warning(&format!("Skipping invalid fstab line: {}", line)); continue; } } } Ok(entries) } pub fn parse_mount_options( &self, ) -> Result<(libc::c_ulong, Option), Box> { let mut flags = 0; let mut data_parts = Vec::new(); let options = &self.options; for opt in options.split(',') { match opt { "defaults" => { /* handled by not setting any restrictive flags */ } "ro" => flags |= libc::MS_RDONLY, "rw" => flags &= !libc::MS_RDONLY, "noexec" => flags |= libc::MS_NOEXEC, "nosuid" => flags |= libc::MS_NOSUID, "nodev" => flags |= libc::MS_NODEV, "sync" => flags |= libc::MS_SYNCHRONOUS, "dirsync" => flags |= libc::MS_DIRSYNC, "bind" => flags |= libc::MS_BIND, "rbind" => flags |= libc::MS_BIND | libc::MS_REC, "remount" => flags |= libc::MS_REMOUNT, _ => { if !opt.is_empty() { data_parts.push(opt); } } } } let data = if data_parts.is_empty() { None } else { Some(data_parts.join(",")) }; Ok((flags, data)) } fn check_mount_point_permissions(path: &str) -> Result<(), Box> { if !std::path::Path::new(path).exists() { create_dir(path)?; } let meta = metadata(path)?; if !meta.is_dir() { return Err(format!("Mount point {} is not a directory", path).into()); } let uid = meta.uid(); if uid != 0 { log_warning(&format!("Warning: Mount point {} not owned by root", path)); } Ok(()) } pub fn mount(&self) -> Result<(), Box> { if let Err(e) = Self::check_mount_point_permissions(&self.mountpoint) { log_warning(&format!( "Permission check failed for {}: {}", self.mountpoint, e )); } log_success(&format!( "Started mounting {} from {}", self.mountpoint, self.source )); if self.fstype == "swap" { log_success(&format!( "Filesystem type contains swap, upping it: {}", self.source )); unsafe { syscall(libc::SYS_swapon, &self.source, 0); } return Ok(()); } if self.mountpoint == "none" { return Ok(()); } if self.options.contains("noauto") { return Ok(()); } let (flags, data) = self.parse_mount_options()?; let source_c = CString::new(&*self.source)?; let target_c = CString::new(&*self.mountpoint)?; let fstype_c = CString::new(&*self.fstype)?; let data_c = data.map(CString::new).transpose()?; let data_ptr = data_c .as_ref() .map_or(std::ptr::null(), |s| s.as_ptr() as *const libc::c_void); let ret = unsafe { syscall( libc::SYS_mount, source_c.as_ptr(), target_c.as_ptr(), fstype_c.as_ptr(), flags, data_ptr, ) }; if ret != 0 { log_critical_error(&format!( "Failed to mount {}: {}", self.mountpoint, std::io::Error::last_os_error() )); } else { log_success(&format!("Mounted {}", self.mountpoint)); } Ok(()) } }