Skip to content

Commit

Permalink
feat(state recovery)+fix: add state recovery part 3 + partition id fix
Browse files Browse the repository at this point in the history
  • Loading branch information
Yato202010 committed Jan 13, 2025
1 parent da0e335 commit cd07be5
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 176 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ readme = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
cfg-if = "1.0"
tracing = "0.1"

[target.'cfg(target_os = "windows")'.dependencies]
Expand Down
6 changes: 3 additions & 3 deletions src/common/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub trait Filesystem {
fn target(&self) -> PathBuf;

/// Set Target mount point
fn set_target(&mut self, target: &dyn AsRef<Path>) -> Result<()>;
fn set_target(&mut self, target: impl AsRef<Path>) -> Result<()>;

/// Get if the filesystem is available
fn is_available() -> bool;
Expand All @@ -47,13 +47,13 @@ pub trait StackableFilesystem: Filesystem {
fn lower(&self) -> Vec<&Path>;

/// Set lower layer
fn set_lower(&mut self, lower: Vec<PathBuf>) -> Result<()>;
fn set_lower(&mut self, lower: impl Into<Vec<PathBuf>>) -> Result<()>;

/// Retrieve upper layer if set
fn upper(&self) -> Option<&Path>;

/// Set upper layer
fn set_upper(&mut self, upper: PathBuf) -> Result<()>;
fn set_upper(&mut self, upper: impl Into<PathBuf>) -> Result<()>;
}

/// Common trait for all case-insensitive filesystem handles
Expand Down
215 changes: 147 additions & 68 deletions src/os/linux/fuseoverlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@
mod opt;
pub use opt::*;

use cfg_if::cfg_if;
use std::{
ffi::CString,
io::{self, Result},
io::{self, Error, ErrorKind, Result},
path::{Path, PathBuf},
process::Command,
str::FromStr,
};
use tracing::{debug, error};

use crate::{
set_option_helper, AsCString, AsPath, Filesystem, LinuxFilesystem, MountOption, PartitionID,
StackableFilesystem,
StackableFilesystem, StateRecovery,
};

#[derive(Debug)]
Expand Down Expand Up @@ -176,70 +176,69 @@ impl Filesystem for FuseOverlayFs {
}

let args = &[
CString::new("")?,
CString::new("fuse-overlayfs")?,
CString::new("-o")?,
CString::new(options)?,
self.target.clone(),
];

cfg_if!(
if #[cfg(feature = "fuse-overlayfs-vendored")] {
use nix::{
sys::{
memfd::{memfd_create, MemFdCreateFlag},
wait::waitpid,
},
unistd::{fexecve, fork, write, ForkResult},
};
// init embedded fuse overlay version 1.10 or later since [ 1.7, 1.9 ] doesn't support mounting on top
// of the base directory
let byte = include_bytes!(concat!("../../../",env!("FUSE-OVERLAYFS-BIN")));
let mem = memfd_create(
&CString::new("fuse-overlayfs")?,
MemFdCreateFlag::empty(),
)?;
write(&mem, byte)?;
let env: Vec<CString> = vec![];
match unsafe { fork() } {
Ok(ForkResult::Parent { child, .. }) => {
waitpid(child, None)?;
}
Ok(ForkResult::Child) => {
use std::os::fd::AsRawFd;
fexecve(mem.as_raw_fd(), args, &env)?;
}
Err(_) => {
return Err(
io::Error::new(
io::ErrorKind::PermissionDenied,
"Failed to mount vfs"
)
)
}
#[cfg(feature = "fuse-overlayfs-vendored")]
{
use nix::{
sys::{
memfd::{memfd_create, MemFdCreateFlag},
wait::waitpid,
},
unistd::{fexecve, fork, write, ForkResult},
};
// init embedded fuse overlay version 1.10 or later since [ 1.7, 1.9 ] doesn't support mounting on top
// of the base directory
let byte = include_bytes!(concat!("../../../", env!("FUSE-OVERLAYFS-BIN")));
let mem = memfd_create(&CString::new("fuse-overlayfs")?, MemFdCreateFlag::empty())?;
write(&mem, byte)?;
let env: Vec<CString> = vec![];
match unsafe { fork() } {
Ok(ForkResult::Parent { child, .. }) => {
waitpid(child, None)?;
}
Ok(ForkResult::Child) => {
use std::os::fd::AsRawFd;
fexecve(mem.as_raw_fd(), args, &env)?;
}
} else {
let options: Vec<&str> = args.iter().skip(1).map(|x| x.to_str().unwrap()).collect();
let output = Command::new("fuse-overlayfs")
.args(options)
.spawn()
.unwrap()
.wait_with_output()
.unwrap();
if output.status.code().unwrap() != 0 {
error!(
"Damascus: unable to mount {:?}\n{}",
&self,
String::from_utf8(output.stderr).unwrap()
);
Err(_) => {
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"Failed to mount vfs",
));
))
}
}
);
}
#[cfg(not(feature = "fuse-overlayfs-vendored"))]
{
let options: Vec<&str> = args.iter().skip(1).map(|x| x.to_str().unwrap()).collect();
let output = Command::new("fuse-overlayfs")
.args(options)
.spawn()
.unwrap()
.wait_with_output()
.unwrap();
if !output.status.success() {
error!(
"Damascus: unable to mount {:?}\n{}",
&self,
String::from_utf8_lossy(&output.stderr)
);
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"Failed to mount vfs",
));
}
};

self.id = Some(PartitionID::try_from(self.target.as_path())?);
self.id = Some(
PartitionID::try_from(self.target.as_path())
.map_err(|_| Error::new(ErrorKind::Other, "unable to get PartitionID"))?,
);
Ok(self.target.as_path().to_path_buf())
}

Expand All @@ -251,15 +250,16 @@ impl Filesystem for FuseOverlayFs {
.arg(self.target.as_path())
.spawn()?;
let output = child.wait_with_output()?;
match output.status.code() {
Some(0) => {}
Some(_) | None => {
error!("Damascus: unable to unmount {:?}", &self);
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"Failed to unmount vfs",
));
}
if !output.status.success() {
error!(
"Damascus: unable to unmount {:?}\n{}",
&self,
String::from_utf8_lossy(&output.stderr)
);
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"Failed to unmount vfs",
));
}
self.id = None;
}
Expand Down Expand Up @@ -287,7 +287,7 @@ impl Filesystem for FuseOverlayFs {
}

#[inline]
fn set_target(&mut self, target: &dyn AsRef<Path>) -> Result<()> {
fn set_target(&mut self, target: impl AsRef<Path>) -> Result<()> {
if self.id.is_some() {
return Err(io::Error::new(
io::ErrorKind::Other,
Expand Down Expand Up @@ -341,14 +341,14 @@ impl StackableFilesystem for FuseOverlayFs {
}

#[inline]
fn set_lower(&mut self, lower: Vec<PathBuf>) -> Result<()> {
fn set_lower(&mut self, lower: impl Into<Vec<PathBuf>>) -> Result<()> {
if self.id.is_some() {
return Err(io::Error::new(
io::ErrorKind::Other,
"upper layer cannot be change when the FileSystem is mounted",
));
}
self.lower = lower;
self.lower = lower.into();
Ok(())
}

Expand All @@ -358,7 +358,8 @@ impl StackableFilesystem for FuseOverlayFs {
}

#[inline]
fn set_upper(&mut self, upper: PathBuf) -> Result<()> {
fn set_upper(&mut self, upper: impl Into<PathBuf>) -> Result<()> {
let upper = upper.into();
if PartitionID::try_from(upper.as_path())?
!= PartitionID::try_from(
self.work
Expand All @@ -385,6 +386,84 @@ impl StackableFilesystem for FuseOverlayFs {
}
}

impl StateRecovery for FuseOverlayFs {
fn recover<P: AsRef<Path>>(path: P) -> Result<Self> {
let path = path.as_ref();
let mut cmd = Command::new("ps");
cmd.args(["--no-headers", "x", "-o", "args=", "-C", "fuse-overlayfs"]);
let out = cmd.output()?;
if !out.status.success() {
error!(
"Damascus: unable to recover handle at {:?}\n{}",
path,
String::from_utf8_lossy(&out.stderr)
);
return Err(io::Error::new(
io::ErrorKind::Other,
"Failed to recover handle",
));
}
for line in String::from_utf8_lossy(&out.stdout).lines() {
if let Some(x) = line.strip_prefix("fuse-overlayfs") {
let mut args = x.split(" ");
let mut options = vec![];
let mut lower = vec![];
let mut upper = None;
let mut work = None;
while let Some(elem) = args.next() {
if elem == " " {
continue;
} else if elem == "-o" {
if let Some(elem) = args.next() {
let mut elem: Vec<MountOption<FuseOverlayFsOption>> = elem
.split(',')
.filter_map(|x| {
if let Some(x) = x.strip_prefix("lowerdir=") {
let mut x = x.split(':').map(PathBuf::from).collect();
lower.append(&mut x);
None
} else if let Some(x) = x.strip_prefix("upperdir=") {
upper = Some(PathBuf::from(x));
None
} else if let Some(x) = x.strip_prefix("workdir=") {
work = Some(PathBuf::from(x));
None
} else {
MountOption::from_str(x).ok()
}
})
.collect();
options.append(&mut elem);
}
} else if let Some(target) = Some(CString::new(elem)?) {
if target.as_path() == path {
return Ok(Self {
lower,
upper,
work,
target,
options,
id: Some(PartitionID::try_from(path).map_err(|_| {
Error::new(ErrorKind::Other, "unable to get PartitionID")
})?),
drop: true,
});
}
}
}
}
}
error!(
"Damascus: unable to recover handle at {:?}\n{}",
path, "no filesystem of type fuse-overlayfs is mounted"
);
Err(io::Error::new(
io::ErrorKind::NotFound,
"Failed to recover handle",
))
}
}

impl Drop for FuseOverlayFs {
#[inline]
fn drop(&mut self) {
Expand Down
19 changes: 13 additions & 6 deletions src/os/linux/overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,10 @@ impl Filesystem for OverlayFs {
.inspect_err(|_x| {
dbg!(&self);
})?;
self.id = Some(PartitionID::try_from(self.target.as_path())?);
self.id = Some(
PartitionID::try_from(self.target.as_path())
.map_err(|_| Error::new(ErrorKind::Other, "unable to get PartitionID"))?,
);
Ok(self.target.as_path().to_path_buf())
}

Expand Down Expand Up @@ -216,7 +219,7 @@ impl Filesystem for OverlayFs {
}

#[inline]
fn set_target(&mut self, target: &dyn AsRef<Path>) -> Result<()> {
fn set_target(&mut self, target: impl AsRef<Path>) -> Result<()> {
if self.id.is_some() {
return Err(Error::new(
ErrorKind::Other,
Expand Down Expand Up @@ -262,14 +265,14 @@ impl StackableFilesystem for OverlayFs {
}

#[inline]
fn set_lower(&mut self, lower: Vec<PathBuf>) -> Result<()> {
fn set_lower(&mut self, lower: impl Into<Vec<PathBuf>>) -> Result<()> {
if self.id.is_some() {
return Err(Error::new(
ErrorKind::Other,
"upper layer cannot be change when the FileSystem is mounted",
));
}
self.lower = lower;
self.lower = lower.into();
Ok(())
}

Expand All @@ -279,7 +282,8 @@ impl StackableFilesystem for OverlayFs {
}

#[inline]
fn set_upper(&mut self, upper: PathBuf) -> Result<()> {
fn set_upper(&mut self, upper: impl Into<PathBuf>) -> Result<()> {
let upper = upper.into();
if PartitionID::try_from(upper.as_path())?
!= PartitionID::try_from(
self.work
Expand Down Expand Up @@ -351,7 +355,10 @@ impl StateRecovery for OverlayFs {
work,
target,
options,
id: Some(PartitionID::try_from(path)?),
id: Some(
PartitionID::try_from(path)
.map_err(|_| Error::new(ErrorKind::Other, "unable to get PartitionID"))?,
),
drop: false,
})
}
Expand Down
Loading

0 comments on commit cd07be5

Please sign in to comment.