Add mknod
applet (untested)
This commit is contained in:
parent
e9e9c53f19
commit
71a9f8839a
@ -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
124
src/cmd/mknod/mod.rs
Normal 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!(),
|
||||
})
|
||||
}
|
||||
}
|
@ -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",
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user