]> git.proxmox.com Git - proxmox-backup.git/blobdiff - src/tools/fuse_loop.rs
add pbs-tools subcrate
[proxmox-backup.git] / src / tools / fuse_loop.rs
index 05d92525a26643fcc100b4cf7ced36ac8c1dd7b8..68d8b0a96ae5bf633f2cc61274ef1f5a5e2eb790 100644 (file)
@@ -19,9 +19,8 @@ use proxmox::const_regex;
 use proxmox::tools::time;
 use proxmox_fuse::{*, requests::FuseRequest};
 use super::loopdev;
-use super::fs;
 
-const RUN_DIR: &'static str = "/run/pbs-loopdev";
+const RUN_DIR: &str = "/run/pbs-loopdev";
 
 const_regex! {
     pub LOOPDEV_REGEX = r"^loop\d+$";
@@ -113,7 +112,7 @@ impl<R: AsyncRead + AsyncSeek + Unpin> FuseLoopSession<R> {
         abort_chan: Receiver<()>,
     ) -> Result<(), Error> {
 
-        if let None = self.session {
+        if self.session.is_none() {
             panic!("internal error: fuse_loop::main called before ::map_loop");
         }
         let mut session = self.session.take().unwrap().fuse();
@@ -236,7 +235,7 @@ pub fn cleanup_unused_run_files(filter_name: Option<String>) {
 
                 // clean leftover FUSE instances (e.g. user called 'losetup -d' or similar)
                 // does nothing if files are already stagnant (e.g. instance crashed etc...)
-                if let Ok(_) = unmap_from_backing(&path) {
+                if unmap_from_backing(&path, None).is_ok() {
                     // we have reaped some leftover instance, tell the user
                     eprintln!(
                         "Cleaned up dangling mapping '{}': no loop device assigned",
@@ -280,19 +279,53 @@ fn get_backing_file(loopdev: &str) -> Result<String, Error> {
     Ok(backing_file.to_owned())
 }
 
-fn unmap_from_backing(backing_file: &Path) -> Result<(), Error> {
+// call in broken state: we found the mapping, but the client is already dead,
+// only thing to do is clean up what we can
+fn emerg_cleanup (loopdev: Option<&str>, mut backing_file: PathBuf) {
+    eprintln!(
+        "warning: found mapping with dead process ({:?}), attempting cleanup",
+        &backing_file
+    );
+
+    if let Some(loopdev) = loopdev {
+        let _ = loopdev::unassign(loopdev);
+    }
+
+    // killing the backing process does not cancel the FUSE mount automatically
+    let mut command = std::process::Command::new("fusermount");
+    command.arg("-u");
+    command.arg(&backing_file);
+    let _ = crate::tools::run_command(command, None);
+
+    let _ = remove_file(&backing_file);
+    backing_file.set_extension("pid");
+    let _ = remove_file(&backing_file);
+}
+
+fn unmap_from_backing(backing_file: &Path, loopdev: Option<&str>) -> Result<(), Error> {
     let mut pid_path = PathBuf::from(backing_file);
     pid_path.set_extension("pid");
 
-    let pid_str = read_to_string(&pid_path).map_err(|err|
-        format_err!("error reading pidfile {:?}: {}", &pid_path, err))?;
+    let pid_str = read_to_string(&pid_path).map_err(|err| {
+        if err.kind() == std::io::ErrorKind::NotFound {
+            emerg_cleanup(loopdev, backing_file.to_owned());
+        }
+        format_err!("error reading pidfile {:?}: {}", &pid_path, err)
+    })?;
     let pid = pid_str.parse::<i32>().map_err(|err|
         format_err!("malformed PID ({}) in pidfile - {}", pid_str, err))?;
 
     let pid = Pid::from_raw(pid);
 
     // send SIGINT to trigger cleanup and exit in target process
-    signal::kill(pid, Signal::SIGINT)?;
+    match signal::kill(pid, Signal::SIGINT) {
+        Ok(()) => {},
+        Err(nix::Error::Sys(nix::errno::Errno::ESRCH)) => {
+            emerg_cleanup(loopdev, backing_file.to_owned());
+            return Ok(());
+        },
+        Err(e) => return Err(e.into()),
+    }
 
     // block until unmap is complete or timeout
     let start = time::epoch_i64();
@@ -322,23 +355,17 @@ fn unmap_from_backing(backing_file: &Path) -> Result<(), Error> {
 pub fn find_all_mappings() -> Result<impl Iterator<Item = (String, Option<String>)>, Error> {
     // get map of all /dev/loop mappings belonging to us
     let mut loopmap = HashMap::new();
-    for ent in fs::scan_subdir(libc::AT_FDCWD, Path::new("/dev/"), &LOOPDEV_REGEX)? {
-        match ent {
-            Ok(ent) => {
-                let loopdev = format!("/dev/{}", ent.file_name().to_string_lossy());
-                match get_backing_file(&loopdev) {
-                    Ok(file) => {
-                        // insert filename only, strip RUN_DIR/
-                        loopmap.insert(file[RUN_DIR.len()+1..].to_owned(), loopdev);
-                    },
-                    Err(_) => {},
-                }
-            },
-            Err(_) => {},
+    for ent in pbs_tools::fs::scan_subdir(libc::AT_FDCWD, Path::new("/dev/"), &LOOPDEV_REGEX)? {
+        if let Ok(ent) = ent {
+            let loopdev = format!("/dev/{}", ent.file_name().to_string_lossy());
+            if let Ok(file) = get_backing_file(&loopdev) {
+                // insert filename only, strip RUN_DIR/
+                loopmap.insert(file[RUN_DIR.len()+1..].to_owned(), loopdev);
+            }
         }
     }
 
-    Ok(fs::read_subdir(libc::AT_FDCWD, Path::new(RUN_DIR))?
+    Ok(pbs_tools::fs::read_subdir(libc::AT_FDCWD, Path::new(RUN_DIR))?
         .filter_map(move |ent| {
             match ent {
                 Ok(ent) => {
@@ -364,16 +391,16 @@ pub fn unmap_loopdev<S: AsRef<str>>(loopdev: S) -> Result<(), Error> {
     }
 
     let backing_file = get_backing_file(loopdev)?;
-    unmap_from_backing(Path::new(&backing_file))
+    unmap_from_backing(Path::new(&backing_file), Some(loopdev))
 }
 
 /// Try and unmap a running proxmox-backup-client instance from the given name
 pub fn unmap_name<S: AsRef<str>>(name: S) -> Result<(), Error> {
-    for (mapping, _) in find_all_mappings()? {
+    for (mapping, loopdev) in find_all_mappings()? {
         if mapping.ends_with(name.as_ref()) {
             let mut path = PathBuf::from(RUN_DIR);
             path.push(&mapping);
-            return unmap_from_backing(&path);
+            return unmap_from_backing(&path, loopdev.as_deref());
         }
     }
     Err(format_err!("no mapping for name '{}' found", name.as_ref()))