Add mknod
applet (untested)
This commit is contained in:
parent
e9e9c53f19
commit
71a9f8839a
@ -7,7 +7,7 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
atty = "0.2"
|
atty = "0.2"
|
||||||
clap = "4.0"
|
clap = "4.1"
|
||||||
clap_complete = "4.0"
|
clap_complete = "4.0"
|
||||||
clap_complete_nushell = "0.1"
|
clap_complete_nushell = "0.1"
|
||||||
clap_mangen = "0.2"
|
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 ln;
|
||||||
mod ls;
|
mod ls;
|
||||||
mod mkfifo;
|
mod mkfifo;
|
||||||
|
mod mknod;
|
||||||
mod mountpoint;
|
mod mountpoint;
|
||||||
mod mv;
|
mod mv;
|
||||||
mod nologin;
|
mod nologin;
|
||||||
@ -79,6 +80,7 @@ pub fn get(name: &str) -> Option<Box<dyn Cmd>> {
|
|||||||
"head" => Some(Box::new(head::Head::default())),
|
"head" => Some(Box::new(head::Head::default())),
|
||||||
"link" => Some(Box::new(link::Link::default())),
|
"link" => Some(Box::new(link::Link::default())),
|
||||||
"mkfifo" => Some(Box::new(mkfifo::MkFifo::default())),
|
"mkfifo" => Some(Box::new(mkfifo::MkFifo::default())),
|
||||||
|
"mknod" => Some(Box::new(mknod::MkNod::default())),
|
||||||
"mountpoint" => Some(Box::new(mountpoint::Mountpoint::default())),
|
"mountpoint" => Some(Box::new(mountpoint::Mountpoint::default())),
|
||||||
"nologin" => Some(Box::new(nologin::Nologin::default())),
|
"nologin" => Some(Box::new(nologin::Nologin::default())),
|
||||||
"nproc" => Some(Box::new(nproc::Nproc::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",
|
"base32",
|
||||||
"base64",
|
"base64",
|
||||||
"basename",
|
"basename",
|
||||||
@ -117,6 +119,7 @@ pub static COMMANDS: [&str; 33] = [
|
|||||||
"hostname",
|
"hostname",
|
||||||
"link",
|
"link",
|
||||||
"mkfifo",
|
"mkfifo",
|
||||||
|
"mknod",
|
||||||
"mountpoint",
|
"mountpoint",
|
||||||
"nologin",
|
"nologin",
|
||||||
"nproc",
|
"nproc",
|
||||||
|
@ -101,7 +101,7 @@ pub struct Parser {
|
|||||||
impl Default for Parser {
|
impl Default for Parser {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let umask = get_umask();
|
let umask = get_umask();
|
||||||
let mut mode = 0o0777;
|
let mut mode = 0o0666;
|
||||||
mode &= umask;
|
mode &= umask;
|
||||||
Self {
|
Self {
|
||||||
mode,
|
mode,
|
||||||
|
Loading…
Reference in New Issue
Block a user