From 3bbab75f9a52e7f6ee6918b7b015edf620d42eec Mon Sep 17 00:00:00 2001 From: Yato202010 <61349049+Yato202010@users.noreply.github.com> Date: Wed, 13 Nov 2024 13:54:51 +0100 Subject: [PATCH] feat(option): rework option handling --- src/os/linux/fuseoverlay.rs | 28 +++---------- src/os/linux/mod.rs | 37 ++++++++++++++++- src/os/linux/overlay.rs | 80 ++++++++++++------------------------ src/os/linux/overlay/opt.rs | 3 +- src/os/linux/unionfs_fuse.rs | 28 +++---------- tests/linux/overlayfs.rs | 29 ++++--------- 6 files changed, 82 insertions(+), 123 deletions(-) diff --git a/src/os/linux/fuseoverlay.rs b/src/os/linux/fuseoverlay.rs index 706bb0c..898a8d4 100644 --- a/src/os/linux/fuseoverlay.rs +++ b/src/os/linux/fuseoverlay.rs @@ -2,7 +2,8 @@ * implementation inspired by libmount crate * https://github.com/tailhook/libmount/blob/master/src/overlay.rs */ -pub mod opt; +mod opt; +pub use opt::*; use cfg_if::cfg_if; use std::{ @@ -14,9 +15,8 @@ use std::{ use tracing::{debug, error}; use crate::{ - common::fs::Filesystem, - os::{linux::fuseoverlay::opt::FuseOverlayFsOption, AsCString, AsPath}, - LinuxFilesystem, MountOption, PartitionID, StackableFilesystem, + set_option_helper, AsCString, AsPath, Filesystem, LinuxFilesystem, MountOption, PartitionID, + StackableFilesystem, }; #[derive(Debug)] @@ -317,25 +317,7 @@ impl Filesystem for FuseOverlayFs { impl LinuxFilesystem for FuseOverlayFs { fn set_option(&mut self, option: impl Into>) -> Result<()> { - let option = option.into(); - for (i, opt) in self.options.clone().iter().enumerate() { - // If Option is already set with another value, overwrite it - if matches!((opt,&option), (MountOption::FsSpecific(s), MountOption::FsSpecific(o)) if std::mem::discriminant(s) == std::mem::discriminant(o)) - | matches!((opt,&option), (s,o) if std::mem::discriminant(s) == std::mem::discriminant(o)) - { - self.options[i] = option.clone(); - return Ok(()); - } - // Check option incompatibility - if opt.incompatible(&option) { - return Err(io::Error::new( - io::ErrorKind::Unsupported, - "Incompatible mount option combinaison", - )); - } - } - self.options.push(option); - Ok(()) + set_option_helper(&mut self.options, option) } fn remove_option(&mut self, option: impl Into>) -> Result<()> { diff --git a/src/os/linux/mod.rs b/src/os/linux/mod.rs index 6a3ce94..6d6ce39 100644 --- a/src/os/linux/mod.rs +++ b/src/os/linux/mod.rs @@ -11,7 +11,9 @@ pub mod overlay; #[cfg(feature = "overlayfs")] pub use overlay::OverlayFs; +pub(crate) use option::set_option_helper; pub use option::{FsOption, LinuxFilesystem, MountOption}; +pub(crate) use recover_state::{restore_fsdata, FsData}; /// Provide utility to recover filesystem state from the information provided by the system #[allow(dead_code)] @@ -36,7 +38,9 @@ mod recover_state { } /// Retrieve filesystem data from system information - pub fn restore_fsdata, O: FsOption>(path: P) -> Result>> { + pub(crate) fn restore_fsdata, O: FsOption>( + path: P, + ) -> Result>> { let fd = unsafe { let mtab = CStr::from_bytes_with_nul_unchecked(b"/etc/mtab\0"); setmntent(mtab.as_ptr(), "r".as_ptr() as *const i8) @@ -88,6 +92,37 @@ mod option { fn options(&self) -> &[MountOption]; } + pub(crate) fn set_option_helper( + options: &mut Vec>, + option: O, + ) -> Result<()> + where + T: FsOption + PartialEq, + O: Into>, + { + let option = option.into(); + let mut idx = None; + for (i, opt) in options.iter().enumerate() { + if opt == &option { + return Ok(()); + } else if matches!((opt,&option), (MountOption::FsSpecific(s), MountOption::FsSpecific(o)) if std::mem::discriminant(s) == std::mem::discriminant(o)) + { + idx = Some(i); + } else if opt.incompatible(&option) { + return Err(std::io::Error::new( + std::io::ErrorKind::Unsupported, + "Incompatible mount option combination", + )); + } + } + if let Some(idx) = idx { + options[idx] = option; + } else { + options.push(option); + } + Ok(()) + } + pub trait FsOption: Sized + Clone + Display + FromStr { /// Get defaults mount options for this filesystem fn defaults() -> Vec; diff --git a/src/os/linux/overlay.rs b/src/os/linux/overlay.rs index 9276f00..c20947a 100644 --- a/src/os/linux/overlay.rs +++ b/src/os/linux/overlay.rs @@ -4,7 +4,8 @@ * */ -pub mod opt; +mod opt; +pub use opt::*; use nix::{ mount::{mount, umount2, MntFlags, MsFlags}, @@ -12,19 +13,14 @@ use nix::{ }; use std::{ ffi::{CStr, CString}, - io::{self, Error, Result}, + io::{Error, ErrorKind, Result}, path::{Path, PathBuf}, }; use tracing::{debug, error}; use crate::{ - common::fs::{Filesystem, StateRecovery}, - os::{ - linux::overlay::opt::OverlayFsOption, - linux::recover_state::{restore_fsdata, FsData}, - AsCString, AsPath, LinuxFilesystem, MountOption, - }, - PartitionID, StackableFilesystem, + restore_fsdata, set_option_helper, AsCString, AsPath, Filesystem, FsData, LinuxFilesystem, + MountOption, PartitionID, StackableFilesystem, StateRecovery, }; #[derive(Debug)] @@ -76,8 +72,8 @@ impl OverlayFs { { let lower: Vec = lower.map(|x| x.as_ref().to_path_buf()).collect(); if lower.len() < 2 { - return Err(io::Error::new( - io::ErrorKind::Other, + return Err(Error::new( + ErrorKind::Other, "overlay FileSystem need a least 2 lower directory to work", )); } @@ -103,8 +99,8 @@ impl OverlayFs { D: AsRef, { if PartitionID::try_from(upper.as_ref())? != PartitionID::try_from(work.as_ref())? { - return Err(io::Error::new( - io::ErrorKind::Other, + return Err(Error::new( + ErrorKind::Other, "overlay FileSystem need the upper dir and the work dir to be on the same FileSystem", )); } @@ -130,15 +126,12 @@ impl OverlayFs { != PartitionID::try_from( self.upper .as_ref() - .ok_or(io::Error::new( - io::ErrorKind::NotFound, - "upper directory not set", - ))? + .ok_or(Error::new(ErrorKind::NotFound, "upper directory not set"))? .as_path(), )? { - return Err(io::Error::new( - io::ErrorKind::Other, + return Err(Error::new( + ErrorKind::Other, "overlay FileSystem need the upper dir and the work dir to be on the same FileSystem", )); } @@ -154,7 +147,6 @@ impl Filesystem for OverlayFs { debug!("Damascus: partition already mounted"); return Ok(self.target.as_path().to_path_buf()); } - // let mut flags = MsFlags::MS_NOATIME.union(MsFlags::MS_NODIRATIME); let mut flags = MsFlags::empty(); let mut options = String::new(); options.push_str("lowerdir="); @@ -186,7 +178,10 @@ impl Filesystem for OverlayFs { Some(unsafe { CStr::from_bytes_with_nul(b"overlay\0").unwrap_unchecked() }), flags, Some(unsafe { CString::from_vec_with_nul_unchecked(args).as_bytes() }), - )?; + ) + .inspect_err(|_x| { + dbg!(&self); + })?; self.id = Some(PartitionID::try_from(self.target.as_path())?); Ok(self.target.as_path().to_path_buf()) } @@ -223,8 +218,8 @@ impl Filesystem for OverlayFs { #[inline] fn set_target(&mut self, target: &dyn AsRef) -> Result<()> { if self.id.is_some() { - return Err(io::Error::new( - io::ErrorKind::Other, + return Err(Error::new( + ErrorKind::Other, "mount point cannot be change when the FileSystem is mounted", )); } @@ -243,25 +238,7 @@ impl Filesystem for OverlayFs { impl LinuxFilesystem for OverlayFs { fn set_option(&mut self, option: impl Into>) -> Result<()> { - let option = option.into(); - for (i, opt) in self.options.clone().iter().enumerate() { - // If Option is already set with another value, overwrite it - if matches!((opt,&option), (MountOption::FsSpecific(s), MountOption::FsSpecific(o)) if std::mem::discriminant(s) == std::mem::discriminant(o)) - | matches!((opt,&option), (s,o) if std::mem::discriminant(s) == std::mem::discriminant(o)) - { - self.options[i] = option.clone(); - return Ok(()); - } - // Check option incompatibility - if opt.incompatible(&option) { - return Err(io::Error::new( - io::ErrorKind::Unsupported, - "Incompatible mount option combinaison", - )); - } - } - self.options.push(option); - Ok(()) + set_option_helper(&mut self.options, option) } fn remove_option(&mut self, option: impl Into>) -> Result<()> { @@ -287,8 +264,8 @@ impl StackableFilesystem for OverlayFs { #[inline] fn set_lower(&mut self, lower: Vec) -> Result<()> { if self.id.is_some() { - return Err(io::Error::new( - io::ErrorKind::Other, + return Err(Error::new( + ErrorKind::Other, "upper layer cannot be change when the FileSystem is mounted", )); } @@ -307,20 +284,17 @@ impl StackableFilesystem for OverlayFs { != PartitionID::try_from( self.work .as_ref() - .ok_or(io::Error::new( - io::ErrorKind::NotFound, - "work directory not set", - ))? + .ok_or(Error::new(ErrorKind::NotFound, "work directory not set"))? .as_path(), )? { - return Err(io::Error::new( - io::ErrorKind::Other, + return Err(Error::new( + ErrorKind::Other, "overlay FileSystem need the upper dir and the work dir to be on the same FileSystem", )); } else if self.id.is_some() { - return Err(io::Error::new( - io::ErrorKind::Other, + return Err(Error::new( + ErrorKind::Other, "upper layer cannot be change when the FileSystem is mounted", )); } @@ -333,7 +307,7 @@ impl StateRecovery for OverlayFs { fn recover>(path: P) -> Result { let path = path.as_ref(); let data: FsData = restore_fsdata(path)?.ok_or(Error::new( - io::ErrorKind::NotFound, + ErrorKind::NotFound, "OverlayFs not found at mount point : ".to_string() + &path.to_string_lossy(), ))?; let mut lower = vec![]; diff --git a/src/os/linux/overlay/opt.rs b/src/os/linux/overlay/opt.rs index eb8dfa9..b312dfb 100644 --- a/src/os/linux/overlay/opt.rs +++ b/src/os/linux/overlay/opt.rs @@ -109,9 +109,8 @@ pub enum OverlayFsOption { impl FsOption for OverlayFsOption { fn defaults() -> Vec { vec![ - OverlayFsOption::RedirectDir(RedirectDir::On), OverlayFsOption::Index(true), - OverlayFsOption::Xino(Xino::On), + OverlayFsOption::Xino(Xino::Auto), ] } diff --git a/src/os/linux/unionfs_fuse.rs b/src/os/linux/unionfs_fuse.rs index afee07a..7d1c7f9 100644 --- a/src/os/linux/unionfs_fuse.rs +++ b/src/os/linux/unionfs_fuse.rs @@ -3,7 +3,8 @@ * https://github.com/tailhook/libmount/blob/master/src/overlay.rs */ -pub mod opt; +mod opt; +pub use opt::*; use cfg_if::cfg_if; use std::{ @@ -14,10 +15,9 @@ use std::{ }; use tracing::{debug, error}; +use crate::os::set_option_helper; use crate::{ - common::fs::Filesystem, - os::{linux::unionfs_fuse::opt::UnionFsFuseOption, AsCString, AsPath}, - LinuxFilesystem, MountOption, PartitionID, StackableFilesystem, + AsCString, AsPath, Filesystem, LinuxFilesystem, MountOption, PartitionID, StackableFilesystem, }; #[derive(Debug)] @@ -271,25 +271,7 @@ impl LinuxFilesystem for UnionFsFuse { &mut self, option: impl Into>, ) -> Result<()> { - let option = option.into(); - for (i, opt) in self.options.clone().iter().enumerate() { - // If Option is already set with another value, overwrite it - if matches!((opt,&option), (MountOption::FsSpecific(s), MountOption::FsSpecific(o)) if std::mem::discriminant(s) == std::mem::discriminant(o)) - | matches!((opt,&option), (s,o) if std::mem::discriminant(s) == std::mem::discriminant(o)) - { - self.options[i] = option.clone(); - return Ok(()); - } - // Check option incompatibility - if opt.incompatible(&option) { - return Err(io::Error::new( - io::ErrorKind::Unsupported, - "Incompatible mount option combinaison", - )); - } - } - self.options.push(option); - Ok(()) + set_option_helper(&mut self.options, option.into()) } fn remove_option( diff --git a/tests/linux/overlayfs.rs b/tests/linux/overlayfs.rs index 0c46b56..c26a1ca 100644 --- a/tests/linux/overlayfs.rs +++ b/tests/linux/overlayfs.rs @@ -1,17 +1,14 @@ use crate::skip; use super::{execute_test, read_only_test, read_test, setup_namespaces, write_test}; -use damascus::{ - overlay::opt::RedirectDir, Filesystem, LinuxFilesystem, OverlayFs, StackableFilesystem, - StateRecovery, -}; +use damascus::{Filesystem, OverlayFs, StackableFilesystem, StateRecovery}; use nix::unistd::geteuid; use std::fs::create_dir_all; use temp_testdir::TempDir; pub fn mount_overlay_r() { if !OverlayFs::is_available() { - skip!("OverlayFs is not availible"); + skip!("OverlayFs is not available"); return; } if !geteuid().is_root() { @@ -29,8 +26,6 @@ pub fn mount_overlay_r() { create_dir_all(&lower2).unwrap(); create_dir_all(&target).unwrap(); let mut o = OverlayFs::readonly([&lower1, &lower2].iter(), &target).unwrap(); - // WARN : this require xattr which is not available on tmpfs - o.set_option(RedirectDir::Off).unwrap(); o.mount().unwrap(); read_only_test(&test); @@ -38,7 +33,7 @@ pub fn mount_overlay_r() { pub fn mount_overlay_rw() { if !OverlayFs::is_available() { - skip!("OverlayFs is not availible"); + skip!("OverlayFs is not available"); return; } if !geteuid().is_root() { @@ -58,8 +53,6 @@ pub fn mount_overlay_rw() { create_dir_all(&upper).unwrap(); create_dir_all(&work).unwrap(); let mut o = OverlayFs::writable([lower1, lower2].iter(), &upper, &work, &target).unwrap(); - // WARN : this require xattr which is not available on tmpfs - o.set_option(RedirectDir::Off).unwrap(); o.mount().unwrap(); write_test(&test); @@ -71,7 +64,7 @@ pub fn mount_overlay_rw() { pub fn mount_overlay_rw_on_lower() { if !OverlayFs::is_available() { - skip!("OverlayFs is not availible"); + skip!("OverlayFs is not available"); return; } if !geteuid().is_root() { @@ -91,8 +84,6 @@ pub fn mount_overlay_rw_on_lower() { create_dir_all(&upper).unwrap(); create_dir_all(&work).unwrap(); let mut o = OverlayFs::writable([lower1, lower2].iter(), upper, work, target).unwrap(); - // WARN : this require xattr which is not available on tmpfs - o.set_option(RedirectDir::Off).unwrap(); o.mount().unwrap(); write_test(&test); @@ -104,7 +95,7 @@ pub fn mount_overlay_rw_on_lower() { pub fn recover_overlay_ro_handle() { if !OverlayFs::is_available() { - skip!("OverlayFs is not availible"); + skip!("OverlayFs is not available"); return; } if !geteuid().is_root() { @@ -121,12 +112,10 @@ pub fn recover_overlay_ro_handle() { create_dir_all(&lower2).unwrap(); create_dir_all(&target).unwrap(); let mut o = OverlayFs::readonly([&lower1, &lower2].iter(), &target).unwrap(); - // WARN : this require xattr which is not available on tmpfs - o.set_option(RedirectDir::Off).unwrap(); o.mount().unwrap(); let reco = OverlayFs::recover(target).unwrap(); - // NOTE : retrieved mount option may not match once recover but behavior should be the same + // NOTE: retrieved mount options may not match once recover but behavior should be the same assert_eq!(reco.lower(), o.lower()); assert_eq!(reco.upper(), o.upper()); assert_eq!(reco.work(), o.work()); @@ -135,7 +124,7 @@ pub fn recover_overlay_ro_handle() { pub fn recover_overlay_rw_handle() { if !OverlayFs::is_available() { - skip!("OverlayFs is not availible"); + skip!("OverlayFs is not available"); return; } if !geteuid().is_root() { @@ -154,12 +143,10 @@ pub fn recover_overlay_rw_handle() { create_dir_all(&upper).unwrap(); create_dir_all(&work).unwrap(); let mut o = OverlayFs::writable([lower1, lower2].iter(), upper, work, &target).unwrap(); - // WARN : this require xattr which is not available on tmpfs - o.set_option(RedirectDir::Off).unwrap(); o.mount().unwrap(); let reco = OverlayFs::recover(target).unwrap(); - // NOTE : retrieved mount option may not match once recover but behavior should be the same + // NOTE: retrieved mount options may not match once recover but behavior should be the same assert_eq!(reco.lower(), o.lower()); assert_eq!(reco.upper(), o.upper()); assert_eq!(reco.work(), o.work());