utilbox/umount - add lazy flag, iter through mnt entries in one pass

to filter chosen entries when using `all` options
This commit is contained in:
Nathan Fisher 2023-02-07 00:13:42 -05:00
parent a468b09816
commit 29af667a0f

View File

@ -1,7 +1,7 @@
use std::io;
use clap::{Arg, ArgAction, ArgMatches, Command, ValueHint}; use clap::{Arg, ArgAction, ArgMatches, Command, ValueHint};
use mount::MntEntries; use mount::MntEntries;
use shitbox::{args, Cmd}; use shitbox::{args, Cmd};
use std::io;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Umount; pub struct Umount;
@ -12,12 +12,16 @@ impl Cmd for Umount {
.about("unmount filesystems") .about("unmount filesystems")
.author("Nathan Fisher") .author("Nathan Fisher")
.version(env!("CARGO_PKG_VERSION")) .version(env!("CARGO_PKG_VERSION"))
.before_long_help(
"umount detaches the target filesystem(s). A file system is specified \
by giving the directory where it has been mounted. Giving the special \
device on which the file system lives may also work, but is obsolete.",
)
.args([ .args([
Arg::new("all") Arg::new("all")
.help( .help(
"All of the filesystems described in /proc/self/mountinfo \ "All of the file systems described in /proc/mounts are \
are unmounted, except the proc, devfs, devpts, sysfs, rpc_pipefs \ unmounted. The proc filesystem is not unmounted.",
and nfsd filesystems.",
) )
.short('a') .short('a')
.long("all") .long("all")
@ -27,6 +31,22 @@ impl Cmd for Umount {
.short('f') .short('f')
.long("force") .long("force")
.action(ArgAction::SetTrue), .action(ArgAction::SetTrue),
Arg::new("lazy")
.help(
"Lazy unmount. Detach the filesystem from the fs hierarchy \
now, and cleanup all references to the filesystem as soon \
as it is not busy anymore.",
)
.short('l')
.long("lazy")
.action(ArgAction::SetTrue),
Arg::new("nomtab")
.help(
"Unmount without writing in /etc/mtab. This is the default action. \
This flag exists only for historical compatability.",
)
.short('n')
.action(ArgAction::SetTrue),
Arg::new("types") Arg::new("types")
.help( .help(
"Indicate that the actions should only be taken on filesystems \ "Indicate that the actions should only be taken on filesystems \
@ -55,52 +75,57 @@ impl Cmd for Umount {
} }
fn run(&self, matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> { fn run(&self, matches: &ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let flags = if matches.get_flag("force") { let mut flags = if matches.get_flag("force") {
libc::MNT_FORCE libc::MNT_FORCE
} else { } else {
0 0
}; };
if matches.get_flag("lazy") {
flags |= libc::MNT_DETACH;
}
if matches.get_flag("all") { if matches.get_flag("all") {
let mut mntpts: Vec<mount::MntEntry> = MntEntries::new("/proc/mounts")? let mntpts = MntEntries::new("/proc/mounts")?.filter(|x| {
.filter(|x| { let mut ret = x.fstype != "proc"
x.fstype != "proc"
&& x.fstype != "sysfs" && x.fstype != "sysfs"
&& x.fstype != "devfs" && x.fstype != "devfs"
&& x.fstype != "devpts" && x.fstype != "devpts"
&& x.fstype != "rpc_pipefs" && x.fstype != "rpc_pipefs"
&& x.fstype != "nfsd" && x.fstype != "nfsd"
&& x.fstype != "swap" && x.fstype != "swap"
}) && x.fsname != "/";
.collect();
if let Some(types) = matches.get_many::<String>("types") { if let Some(types) = matches.get_many::<String>("types") {
for t in types { for t in types {
if t.starts_with("no") { if t.starts_with("no") {
let t = t.strip_prefix("no").unwrap(); let t = t.strip_prefix("no").unwrap();
mntpts.retain(|x| x.fstype != t); if x.fstype == t {
ret = false;
}
} else { } else {
mntpts.retain(|x| &x.fstype == t); if &x.fstype != t {
ret = false;
}
} }
} }
} }
if let Some(spec) = matches.get_many::<String>("spec") { if let Some(spec) = matches.get_many::<String>("spec") {
let mut r2 = false;
for s in spec { for s in spec {
for p in &mntpts { if &x.dir == s || &x.fsname == s {
if &p.dir == s { r2 = true;
mount::umount(s, flags as u32)?;
if matches.get_flag("verbose") {
println!("umount: {s} unmounted");
} }
} }
if !r2 {
ret = false;
} }
} }
} else { ret
for p in &mntpts { });
for p in mntpts {
mount::umount(&p.dir, flags as u32)?; mount::umount(&p.dir, flags as u32)?;
if matches.get_flag("verbose") { if matches.get_flag("verbose") {
println!("umount: {} unmounted", &p.dir); println!("umount: {} unmounted", &p.dir);
} }
} }
}
} else { } else {
if let Some(spec) = matches.get_many::<String>("spec") { if let Some(spec) = matches.get_many::<String>("spec") {
for s in spec { for s in spec {