shitbox/corebox/commands/mknod/mod.rs
Nathan Fisher d248f7d17b Remove Default constraint on Cmd trait and begin removing
derive(Default) from applets
2023-04-17 23:27:57 -04:00

116 lines
4.0 KiB
Rust

use super::Cmd;
use clap::{value_parser, Arg, ArgMatches, Command};
use mode::{get_umask, Parser};
use shitbox::args;
use std::{convert::Infallible, error::Error, io, str::FromStr};
#[derive(Debug)]
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<shitbox::Path> {
Some(shitbox::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!(),
})
}
}