4568080453
- m key toggles mount/unmount of the selected device via udisksctl (resolves the block device from mount point or lsblk label) - rsync-style device sync auto-mounts the target first - mlove finalize unmounts via udisksctl, falling back to sudo -n umount - keybar/help/README updated
71 lines
2.4 KiB
Rust
71 lines
2.4 KiB
Rust
use std::path::Path;
|
|
use std::process::Command;
|
|
|
|
/// Mount/unmount devices without sudo via udisks2 (available to the active
|
|
/// desktop user). The block device is resolved from the mount point: either
|
|
/// the currently-mounted source, or a partition whose label matches the mount
|
|
/// point's basename (e.g. `/media/sebastian/IPOD` → label `IPOD`).
|
|
|
|
fn run(cmd: &str, args: &[&str]) -> (bool, String) {
|
|
match Command::new(cmd).args(args).output() {
|
|
Ok(o) => {
|
|
let out = String::from_utf8_lossy(&o.stdout).trim().to_string();
|
|
let err = String::from_utf8_lossy(&o.stderr).trim().to_string();
|
|
let detail = if !err.is_empty() { err } else { out };
|
|
(o.status.success(), detail)
|
|
}
|
|
Err(e) => (false, format!("cannot run {cmd}: {e}")),
|
|
}
|
|
}
|
|
|
|
fn resolve_block_device(path: &Path) -> Option<String> {
|
|
if path.is_dir() {
|
|
let (ok, out) = run("findmnt", &["-n", "-o", "SOURCE", "-T", path.to_str()?]);
|
|
if ok {
|
|
let src = out.trim();
|
|
if src.starts_with("/dev/") {
|
|
return Some(src.to_string());
|
|
}
|
|
}
|
|
}
|
|
let wanted = path.file_name()?.to_string_lossy().to_lowercase();
|
|
let (ok, out) = run("lsblk", &["-o", "PATH,LABEL", "-n", "-l"]);
|
|
if !ok {
|
|
return None;
|
|
}
|
|
for line in out.lines() {
|
|
let mut parts = line.split_whitespace();
|
|
let Some(dev) = parts.next() else { continue };
|
|
if let Some(label) = parts.next() {
|
|
if !label.is_empty() && label.to_lowercase() == wanted {
|
|
return Some(dev.to_string());
|
|
}
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
pub fn mount_device(path: &Path) -> Result<String, String> {
|
|
let Some(dev) = resolve_block_device(path) else {
|
|
return Err(format!("no block device found for {}", path.display()));
|
|
};
|
|
let (ok, detail) = run("udisksctl", &["mount", "-b", dev.as_str()]);
|
|
if ok {
|
|
Ok(detail)
|
|
} else {
|
|
Err(format!("udisksctl mount {dev} failed: {detail}"))
|
|
}
|
|
}
|
|
|
|
pub fn unmount_device(path: &Path) -> Result<String, String> {
|
|
let Some(dev) = resolve_block_device(path) else {
|
|
return Err(format!("no block device found for {}", path.display()));
|
|
};
|
|
let (ok, detail) = run("udisksctl", &["unmount", "-b", dev.as_str()]);
|
|
if ok {
|
|
Ok(detail)
|
|
} else {
|
|
Err(format!("udisksctl unmount {dev} failed: {detail}"))
|
|
}
|
|
}
|