Add `mknod` applet (untested)

This commit is contained in:
Nathan Fisher 2023-01-21 01:54:52 -05:00
parent e9e9c53f19
commit 71a9f8839a
4 changed files with 130 additions and 3 deletions

View File

@ -7,7 +7,7 @@ edition = "2021"
[dependencies]
atty = "0.2"
clap = "4.0"
clap = "4.1"
clap_complete = "4.0"
clap_complete_nushell = "0.1"
clap_mangen = "0.2"

124
src/cmd/mknod/mod.rs Normal file
View File

@ -0,0 +1,124 @@
use super::Cmd;
use crate::mode::{get_umask, Parser};
use clap::{value_parser, Arg, ArgAction, ArgMatches, Command};
use std::{convert::Infallible, error::Error, ffi::CString, 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),
Arg::new("verbose")
.short('v')
.long("verbose")
.action(ArgAction::SetTrue),
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: Option<&ArgMatches>) -> Result<(), Box<dyn Error>> {
let Some(matches) = matches else {
return Err(io::Error::new(io::ErrorKind::Other, "no input").into());
};
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,
};
let fname = CString::new(file.as_str())?;
let ret = unsafe { libc::mknod(fname.as_ptr(), mode, dev_t) };
if ret != 0 {
return Err(io::Error::last_os_error().into());
}
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!(),
})
}
}

View File

@ -26,6 +26,7 @@ mod link;
mod ln;
mod ls;
mod mkfifo;
mod mknod;
mod mountpoint;
mod mv;
mod nologin;
@ -79,6 +80,7 @@ pub fn get(name: &str) -> Option<Box<dyn Cmd>> {
"head" => Some(Box::new(head::Head::default())),
"link" => Some(Box::new(link::Link::default())),
"mkfifo" => Some(Box::new(mkfifo::MkFifo::default())),
"mknod" => Some(Box::new(mknod::MkNod::default())),
"mountpoint" => Some(Box::new(mountpoint::Mountpoint::default())),
"nologin" => Some(Box::new(nologin::Nologin::default())),
"nproc" => Some(Box::new(nproc::Nproc::default())),
@ -98,7 +100,7 @@ pub fn get(name: &str) -> Option<Box<dyn Cmd>> {
}
}
pub static COMMANDS: [&str; 33] = [
pub static COMMANDS: [&str; 34] = [
"base32",
"base64",
"basename",
@ -117,6 +119,7 @@ pub static COMMANDS: [&str; 33] = [
"hostname",
"link",
"mkfifo",
"mknod",
"mountpoint",
"nologin",
"nproc",

View File

@ -101,7 +101,7 @@ pub struct Parser {
impl Default for Parser {
fn default() -> Self {
let umask = get_umask();
let mut mode = 0o0777;
let mut mode = 0o0666;
mode &= umask;
Self {
mode,