use libc::{self}; use std::ffi::CString; use std::{fs, fmt}; #[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, "{} {} {} {} {} {}", 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(_) => { println!("\x1b[32m * \x1b[0m Warning: 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)) } pub fn mount(&self) -> Result<(), Box> { println!("\x1b[32m * \x1b[0m Started mounting {} from {}", self.mountpoint, self.source); if self.fstype == "swap" { println!("\x1b[30m * \x1b[0m Skipping swap: {}", self.source); 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(|s| CString::new(s).expect("\x1b[31m * \x1b[0m Something went wrong while mounting partitions. You are going to rescue mode...")); let data_ptr = data_c .as_ref() .map_or(std::ptr::null(), |s| s.as_ptr() as *const libc::c_void); let ret = unsafe { libc::syscall( libc::SYS_mount, source_c.as_ptr(), target_c.as_ptr(), fstype_c.as_ptr(), flags, data_ptr, ) }; if ret != 0 { eprintln!( "\x1b[31m * \x1b[0m Failed to mount {}: {}", self.mountpoint, std::io::Error::last_os_error() ); } else { println!("\x1b[32m * \x1b[0m Mounted {}", self.mountpoint); } Ok(()) } }