Finish mountpoint applet
This commit is contained in:
parent
6b6cc314e8
commit
84ede35190
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -162,9 +162,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.138"
|
version = "0.2.139"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
@ -222,6 +222,7 @@ dependencies = [
|
|||||||
"clap_mangen",
|
"clap_mangen",
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
"hostname",
|
"hostname",
|
||||||
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
]
|
]
|
||||||
|
@ -14,6 +14,7 @@ clap_complete_nushell = "0.1.8"
|
|||||||
clap_mangen = "0.2.5"
|
clap_mangen = "0.2.5"
|
||||||
data-encoding = "2.3.3"
|
data-encoding = "2.3.3"
|
||||||
hostname = { version = "0.3", features = ["set"] }
|
hostname = { version = "0.3", features = ["set"] }
|
||||||
|
libc = "0.2.139"
|
||||||
once_cell = "1.16.0"
|
once_cell = "1.16.0"
|
||||||
termcolor = "1.1.3"
|
termcolor = "1.1.3"
|
||||||
|
|
||||||
|
@ -1,5 +1,13 @@
|
|||||||
use super::Cmd;
|
use super::Cmd;
|
||||||
use clap::{Arg, ArgAction, Command};
|
use clap::{Arg, ArgAction, Command};
|
||||||
|
use std::{
|
||||||
|
error::Error,
|
||||||
|
fs::{self, File},
|
||||||
|
io::{self, BufRead, BufReader, ErrorKind},
|
||||||
|
os::linux::fs::MetadataExt,
|
||||||
|
path::PathBuf,
|
||||||
|
process,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Mountpoint {
|
pub struct Mountpoint {
|
||||||
@ -21,14 +29,31 @@ impl Cmd for Mountpoint {
|
|||||||
Command::new(self.name)
|
Command::new(self.name)
|
||||||
.about("see if a directory or file is a mountpoint")
|
.about("see if a directory or file is a mountpoint")
|
||||||
.author("Nathan Fisher")
|
.author("Nathan Fisher")
|
||||||
|
.after_long_help(
|
||||||
|
"EXIT STATUS\n \
|
||||||
|
0\n \
|
||||||
|
success: the directory is a mountpoint, or device is a block\
|
||||||
|
device on --devno\n \
|
||||||
|
1\n \
|
||||||
|
failure: incorrect invocation, permissions or system error\
|
||||||
|
\n 32\n \
|
||||||
|
failure: the directory is not a mountpoint, or device is not a \
|
||||||
|
block device on --devno"
|
||||||
|
)
|
||||||
.args([
|
.args([
|
||||||
Arg::new("fs-devno")
|
Arg::new("fs-devno")
|
||||||
.help("Show the major/minor numbers of the device that is mounted on the given directory.")
|
.help(
|
||||||
|
"Show the major/minor numbers of the device that is \
|
||||||
|
mounted on the given directory.",
|
||||||
|
)
|
||||||
.short('d')
|
.short('d')
|
||||||
.long("fs-devno")
|
.long("fs-devno")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
Arg::new("devno")
|
Arg::new("devno")
|
||||||
.help("Show the major/minor numbers of the given blockdevice on standard output.")
|
.help(
|
||||||
|
"Show the major/minor numbers of the given blockdevice \
|
||||||
|
on standard output.",
|
||||||
|
)
|
||||||
.short('x')
|
.short('x')
|
||||||
.long("devno")
|
.long("devno")
|
||||||
.conflicts_with("fs-devno")
|
.conflicts_with("fs-devno")
|
||||||
@ -38,14 +63,92 @@ impl Cmd for Mountpoint {
|
|||||||
.short('q')
|
.short('q')
|
||||||
.long("quiet")
|
.long("quiet")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
|
Arg::new("file").num_args(1).required(true),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, matches: Option<&clap::ArgMatches>) -> Result<(), Box<dyn std::error::Error>> {
|
fn run(&self, matches: Option<&clap::ArgMatches>) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
unimplemented!();
|
let Some(matches) = matches else {
|
||||||
|
return Err(io::Error::new(ErrorKind::Other, "no input").into());
|
||||||
|
};
|
||||||
|
let file = matches.get_one::<String>("file").unwrap();
|
||||||
|
if matches.get_flag("fs-devno") {
|
||||||
|
match fs_devno(file)? {
|
||||||
|
Some(maj_min) => println!("{maj_min}"),
|
||||||
|
None => {
|
||||||
|
println!("{file} is not a mountpoint");
|
||||||
|
process::exit(32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if matches.get_flag("devno") {
|
||||||
|
let devno = devno(file)?;
|
||||||
|
println!("{}:{}", devno.0, devno.1);
|
||||||
|
} else {
|
||||||
|
let val = is_mountpoint(file)?;
|
||||||
|
if val {
|
||||||
|
if !matches.get_flag("quiet") {
|
||||||
|
println!("{file} is a mountpoint");
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
if !matches.get_flag("quiet") {
|
||||||
|
println!("{file} is not a mountpoint");
|
||||||
|
}
|
||||||
|
process::exit(32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path(&self) -> Option<crate::Path> {
|
fn path(&self) -> Option<crate::Path> {
|
||||||
self.path
|
self.path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_mountpoint(path: &str) -> Result<bool, Box<dyn Error>> {
|
||||||
|
let fd = File::open("/proc/mounts")?;
|
||||||
|
let reader = BufReader::new(fd);
|
||||||
|
for line in reader.lines() {
|
||||||
|
let line = line?;
|
||||||
|
if let Some(mntpt) = line.split_whitespace().skip(1).next() {
|
||||||
|
if mntpt == path {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fs_devno(path: &str) -> Result<Option<String>, Box<dyn Error>> {
|
||||||
|
let p = PathBuf::from(path);
|
||||||
|
if !p.exists() {
|
||||||
|
let msg = format!("mountpoint: {path}: No such file or directory");
|
||||||
|
return Err(Box::new(io::Error::new(ErrorKind::Other, msg)));
|
||||||
|
}
|
||||||
|
let fd = File::open("/proc/self/mountinfo")?;
|
||||||
|
let reader = BufReader::new(fd);
|
||||||
|
for line in reader.lines() {
|
||||||
|
let line = line?;
|
||||||
|
let mut line = line.split_whitespace().skip(2);
|
||||||
|
if let Some(maj_min) = line.next() {
|
||||||
|
if let Some(mntpt) = line.skip(1).next() {
|
||||||
|
if mntpt == path {
|
||||||
|
return Ok(Some(String::from(maj_min)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn devno(path: &str) -> Result<(u32, u32), Box<dyn Error>> {
|
||||||
|
let meta = fs::metadata(path)?;
|
||||||
|
let rdev = meta.st_rdev();
|
||||||
|
if rdev == 0 {
|
||||||
|
let msg = format!("Mountpoint: Error: {path} is not a character device");
|
||||||
|
return Err(Box::new(io::Error::new(ErrorKind::Other, msg)));
|
||||||
|
}
|
||||||
|
let major = unsafe { libc::major(rdev) };
|
||||||
|
let minor = unsafe { libc::minor(rdev) };
|
||||||
|
Ok((major, minor))
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user