119 lines
4.0 KiB
Rust
119 lines
4.0 KiB
Rust
use super::Cmd;
|
|
use crate::{
|
|
args,
|
|
mode::{get_umask, Parser},
|
|
stat,
|
|
};
|
|
use clap::{value_parser, Arg, ArgMatches, Command};
|
|
use std::{convert::Infallible, error::Error, io, str::FromStr};
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct MkNod;
|
|
|
|
impl Cmd for MkNod {
|
|
fn cli(&self) -> clap::Command {
|
|
Command::new("mknod")
|
|
.about("make block or character special files")
|
|
.author("Nathan Fisher")
|
|
.version(env!("CARGO_PKG_VERSION"))
|
|
.args([
|
|
Arg::new("mode")
|
|
.short('m')
|
|
.long("mode")
|
|
.help("set file permission bits to MODE, not a=rw - umask")
|
|
.value_name("MODE")
|
|
.num_args(1),
|
|
args::verbose(),
|
|
Arg::new("file").value_name("NAME").required(true),
|
|
Arg::new("type")
|
|
.value_name("TYPE")
|
|
.required(true)
|
|
.value_parser(["b", "c", "p"]),
|
|
Arg::new("major")
|
|
.value_name("MAJOR")
|
|
.value_parser(value_parser!(u32))
|
|
.required_if_eq_any([("type", "b"), ("type", "c")]),
|
|
Arg::new("minor")
|
|
.value_name("MINOR")
|
|
.value_parser(value_parser!(u32))
|
|
.required_if_eq_any([("type", "b"), ("type", "c")]),
|
|
])
|
|
}
|
|
|
|
fn run(&self, matches: &ArgMatches) -> Result<(), Box<dyn Error>> {
|
|
let Some(file) = matches.get_one::<String>("file") else {
|
|
return Err(io::Error::new(io::ErrorKind::Other, "no file given").into());
|
|
};
|
|
let Some(t) = matches.get_one::<String>("type") else {
|
|
return Err(io::Error::new(io::ErrorKind::Other, "no type given").into());
|
|
};
|
|
let mut mode = if let Some(m) = matches.get_one::<String>("mode") {
|
|
Parser::default().parse(m.as_str())?
|
|
} else {
|
|
let mask = get_umask();
|
|
0o666 & !mask
|
|
};
|
|
let special: Special = t.parse()?;
|
|
mode |= special as u32;
|
|
let dev_t = match special {
|
|
Special::Block | Special::Char => {
|
|
let Some(major) = matches.get_one::<u32>("major") else {
|
|
return Err(io::Error::new(io::ErrorKind::Other, "missing MAJOR").into());
|
|
};
|
|
let Some(minor) = matches.get_one::<u32>("minor") else {
|
|
return Err(io::Error::new(io::ErrorKind::Other, "missing MINOR").into());
|
|
};
|
|
libc::makedev(*major, *minor)
|
|
}
|
|
Special::Pipe => 0,
|
|
};
|
|
stat::mknod(file.as_str(), mode, dev_t)?;
|
|
if matches.get_flag("verbose") {
|
|
match special {
|
|
Special::Block => {
|
|
println!(
|
|
"mknod: created block device with mode {mode:o} major {} and minor {}",
|
|
unsafe { libc::major(dev_t) },
|
|
unsafe { libc::minor(dev_t) },
|
|
);
|
|
}
|
|
Special::Char => {
|
|
println!(
|
|
"mknod: created character device with mode {mode:o} major {} and minor {}",
|
|
unsafe { libc::major(dev_t) },
|
|
unsafe { libc::minor(dev_t) },
|
|
);
|
|
}
|
|
Special::Pipe => {
|
|
println!("mknod: created fifo with mode {mode:o}");
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn path(&self) -> Option<crate::Path> {
|
|
Some(crate::Path::Sbin)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
enum Special {
|
|
Block = 0o60000,
|
|
Char = 0o20000,
|
|
Pipe = 0o10000,
|
|
}
|
|
|
|
impl FromStr for Special {
|
|
type Err = Infallible;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Ok(match s {
|
|
"b" => Self::Block,
|
|
"c" => Self::Char,
|
|
"p" => Self::Pipe,
|
|
_ => unreachable!(),
|
|
})
|
|
}
|
|
}
|