mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
commit
9534bf3f17
7 changed files with 371 additions and 9 deletions
|
@ -16,6 +16,7 @@ unix = [
|
||||||
"kill",
|
"kill",
|
||||||
"logname",
|
"logname",
|
||||||
"mkfifo",
|
"mkfifo",
|
||||||
|
"mknod",
|
||||||
"mktemp",
|
"mktemp",
|
||||||
"mv",
|
"mv",
|
||||||
"nice",
|
"nice",
|
||||||
|
@ -123,8 +124,9 @@ ln = { optional=true, path="src/ln" }
|
||||||
ls = { optional=true, path="src/ls" }
|
ls = { optional=true, path="src/ls" }
|
||||||
logname = { optional=true, path="src/logname" }
|
logname = { optional=true, path="src/logname" }
|
||||||
mkdir = { optional=true, path="src/mkdir" }
|
mkdir = { optional=true, path="src/mkdir" }
|
||||||
mktemp = { optional=true, path="src/mktemp" }
|
|
||||||
mkfifo = { optional=true, path="src/mkfifo" }
|
mkfifo = { optional=true, path="src/mkfifo" }
|
||||||
|
mknod = { optional=true, path="src/mknod" }
|
||||||
|
mktemp = { optional=true, path="src/mktemp" }
|
||||||
mv = { optional=true, path="src/mv" }
|
mv = { optional=true, path="src/mv" }
|
||||||
nice = { optional=true, path="src/nice" }
|
nice = { optional=true, path="src/nice" }
|
||||||
nl = { optional=true, path="src/nl" }
|
nl = { optional=true, path="src/nl" }
|
||||||
|
|
1
Makefile
1
Makefile
|
@ -107,6 +107,7 @@ UNIX_PROGS := \
|
||||||
kill \
|
kill \
|
||||||
logname \
|
logname \
|
||||||
mkfifo \
|
mkfifo \
|
||||||
|
mknod \
|
||||||
mv \
|
mv \
|
||||||
nice \
|
nice \
|
||||||
nohup \
|
nohup \
|
||||||
|
|
|
@ -162,7 +162,6 @@ To do
|
||||||
- install
|
- install
|
||||||
- join
|
- join
|
||||||
- ls
|
- ls
|
||||||
- mknod
|
|
||||||
- mktemp (almost done, some options are not working)
|
- mktemp (almost done, some options are not working)
|
||||||
- mv (almost done, one more option)
|
- mv (almost done, one more option)
|
||||||
- numfmt
|
- numfmt
|
||||||
|
|
17
src/mknod/Cargo.toml
Normal file
17
src/mknod/Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[package]
|
||||||
|
name = "mknod"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = []
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "uu_mknod"
|
||||||
|
path = "mknod.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
getopts = "*"
|
||||||
|
libc = "^0.2.4"
|
||||||
|
uucore = { path="../uucore" }
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "mknod"
|
||||||
|
path = "main.rs"
|
5
src/mknod/main.rs
Normal file
5
src/mknod/main.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
extern crate uu_mknod;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
std::process::exit(uu_mknod::uumain(std::env::args().collect()));
|
||||||
|
}
|
202
src/mknod/mknod.rs
Normal file
202
src/mknod/mknod.rs
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
#![crate_name = "uu_mknod"]
|
||||||
|
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// (c) Jian Zeng <anonymousknight96@gmail.com>
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
|
//
|
||||||
|
|
||||||
|
extern crate getopts;
|
||||||
|
extern crate libc;
|
||||||
|
|
||||||
|
mod parsemode;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate uucore;
|
||||||
|
|
||||||
|
use libc::{mode_t, dev_t};
|
||||||
|
use libc::{S_IRUSR, S_IWUSR, S_IRGRP, S_IWGRP, S_IROTH, S_IWOTH, S_IFIFO, S_IFBLK, S_IFCHR};
|
||||||
|
|
||||||
|
use getopts::Options;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use std::ffi::CString;
|
||||||
|
|
||||||
|
static NAME: &'static str = "mknod";
|
||||||
|
static VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
const MODE_RW_UGO: mode_t = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn makedev(maj: u64, min: u64) -> dev_t {
|
||||||
|
// pick up from <sys/sysmacros.h>
|
||||||
|
((min & 0xff) | ((maj & 0xfff) << 8) | (((min & !0xff)) << 12) |
|
||||||
|
(((maj & !0xfff)) << 32)) as dev_t
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn _makenod(path: CString, mode: mode_t, dev: dev_t) -> i32 {
|
||||||
|
panic!("Unsupported for windows platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn _makenod(path: CString, mode: mode_t, dev: dev_t) -> i32 {
|
||||||
|
unsafe { libc::mknod(path.as_ptr(), mode, dev) }
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! disp_err {
|
||||||
|
($($args:tt)+) => ({
|
||||||
|
pipe_write!(&mut ::std::io::stderr(), "{}: ", NAME);
|
||||||
|
pipe_writeln!(&mut ::std::io::stderr(), $($args)+);
|
||||||
|
pipe_writeln!(&mut ::std::io::stderr(), "Try '{} --help' for more information.", NAME);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
|
let mut opts = Options::new();
|
||||||
|
|
||||||
|
// Linux-specific options, not implemented
|
||||||
|
// opts.optflag("Z", "", "set the SELinux security context to default type");
|
||||||
|
// opts.optopt("", "context", "like -Z, or if CTX is specified then set the SELinux or SMACK security context to CTX");
|
||||||
|
opts.optopt("m",
|
||||||
|
"mode",
|
||||||
|
"set file permission bits to MODE, not a=rw - umask",
|
||||||
|
"MODE");
|
||||||
|
|
||||||
|
opts.optflag("", "help", "display this help and exit");
|
||||||
|
opts.optflag("", "version", "output version information and exit");
|
||||||
|
|
||||||
|
let matches = match opts.parse(&args[1..]) {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(f) => crash!(1, "{}\nTry '{} --help' for more information.", f, NAME),
|
||||||
|
};
|
||||||
|
|
||||||
|
if matches.opt_present("help") {
|
||||||
|
println!(
|
||||||
|
"Usage: {0} [OPTION]... NAME TYPE [MAJOR MINOR]
|
||||||
|
|
||||||
|
Mandatory arguments to long options are mandatory for short options too.
|
||||||
|
-m, --mode=MODE set file permission bits to MODE, not a=rw - umask
|
||||||
|
--help display this help and exit
|
||||||
|
--version output version information and exit
|
||||||
|
|
||||||
|
Both MAJOR and MINOR must be specified when TYPE is b, c, or u, and they
|
||||||
|
must be omitted when TYPE is p. If MAJOR or MINOR begins with 0x or 0X,
|
||||||
|
it is interpreted as hexadecimal; otherwise, if it begins with 0, as octal;
|
||||||
|
otherwise, as decimal. TYPE may be:
|
||||||
|
|
||||||
|
b create a block (buffered) special file
|
||||||
|
c, u create a character (unbuffered) special file
|
||||||
|
p create a FIFO
|
||||||
|
|
||||||
|
NOTE: your shell may have its own version of mknod, which usually supersedes
|
||||||
|
the version described here. Please refer to your shell's documentation
|
||||||
|
for details about the options it supports.", NAME);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches.opt_present("version") {
|
||||||
|
println!("{} {}", NAME, VERSION);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut last_umask: mode_t = 0;
|
||||||
|
let mut newmode: mode_t = MODE_RW_UGO;
|
||||||
|
if matches.opt_present("mode") {
|
||||||
|
match parsemode::parse_mode(matches.opt_str("mode")) {
|
||||||
|
Ok(parsed) => {
|
||||||
|
if parsed > 0o777 {
|
||||||
|
show_info!("mode must specify only file permission bits");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
newmode = parsed;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
show_info!("{}", e);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
last_umask = libc::umask(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ret = 0i32;
|
||||||
|
match matches.free.len() {
|
||||||
|
0 => disp_err!("missing operand"),
|
||||||
|
1 => disp_err!("missing operand after ‘{}’", matches.free[0]),
|
||||||
|
_ => {
|
||||||
|
let args = &matches.free;
|
||||||
|
let c_str = CString::new(args[0].as_str()).expect("Failed to convert to CString");
|
||||||
|
|
||||||
|
// Only check the first character, to allow mnemonic usage like
|
||||||
|
// 'mknod /dev/rst0 character 18 0'.
|
||||||
|
let ch = args[1].chars().nth(0).expect("Failed to get the first char");
|
||||||
|
|
||||||
|
if ch == 'p' {
|
||||||
|
if args.len() > 2 {
|
||||||
|
show_info!("{}: extra operand ‘{}’", NAME, args[2]);
|
||||||
|
if args.len() == 4 {
|
||||||
|
eprintln!("Fifos do not have major and minor device numbers.");
|
||||||
|
}
|
||||||
|
eprintln!("Try '{} --help' for more information.", NAME);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = _makenod(c_str, S_IFIFO | newmode, 0);
|
||||||
|
} else {
|
||||||
|
if args.len() < 4 {
|
||||||
|
show_info!("missing operand after ‘{}’", args[args.len() - 1]);
|
||||||
|
if args.len() == 2 {
|
||||||
|
eprintln!("Special files require major and minor device numbers.");
|
||||||
|
}
|
||||||
|
eprintln!("Try '{} --help' for more information.", NAME);
|
||||||
|
return 1;
|
||||||
|
} else if args.len() > 4 {
|
||||||
|
disp_err!("extra operand ‘{}’", args[4]);
|
||||||
|
return 1;
|
||||||
|
} else if !"bcu".contains(ch) {
|
||||||
|
disp_err!("invalid device type ‘{}’", args[1]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let maj = args[2].parse::<u64>();
|
||||||
|
let min = args[3].parse::<u64>();
|
||||||
|
if maj.is_err() {
|
||||||
|
show_info!("invalid major device number ‘{}’", args[2]);
|
||||||
|
return 1;
|
||||||
|
} else if min.is_err() {
|
||||||
|
show_info!("invalid minor device number ‘{}’", args[3]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (maj, min) = (maj.unwrap(), min.unwrap());
|
||||||
|
let dev = makedev(maj, min);
|
||||||
|
if ch == 'b' {
|
||||||
|
// block special file
|
||||||
|
ret = _makenod(c_str, S_IFBLK | newmode, dev);
|
||||||
|
} else {
|
||||||
|
// char special file
|
||||||
|
ret = _makenod(c_str, S_IFCHR | newmode, dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if last_umask != 0 {
|
||||||
|
unsafe {
|
||||||
|
libc::umask(last_umask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ret == -1 {
|
||||||
|
let c_str = CString::new(format!("{}: {}", NAME, matches.free[0]).as_str())
|
||||||
|
.expect("Failed to convert to CString");
|
||||||
|
unsafe {
|
||||||
|
libc::perror(c_str.as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
136
src/mknod/parsemode.rs
Normal file
136
src/mknod/parsemode.rs
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
extern crate libc;
|
||||||
|
use libc::{mode_t, S_IRGRP, S_IWGRP, S_IROTH, S_IWOTH, S_IRUSR, S_IWUSR};
|
||||||
|
|
||||||
|
fn parse_change(mode: &str, fperm: mode_t) -> (mode_t, usize) {
|
||||||
|
let mut srwx = fperm & 0o7000;
|
||||||
|
let mut pos = 0;
|
||||||
|
for ch in mode.chars() {
|
||||||
|
match ch {
|
||||||
|
'r' => srwx |= 0o444,
|
||||||
|
'w' => srwx |= 0o222,
|
||||||
|
'x' => srwx |= 0o111,
|
||||||
|
'X' => srwx |= 0o111,
|
||||||
|
's' => srwx |= 0o4000 | 0o2000,
|
||||||
|
't' => srwx |= 0o1000,
|
||||||
|
'u' => srwx = (fperm & 0o700) | ((fperm >> 3) & 0o070) | ((fperm >> 6) & 0o007),
|
||||||
|
'g' => srwx = ((fperm << 3) & 0o700) | (fperm & 0o070) | ((fperm >> 3) & 0o007),
|
||||||
|
'o' => srwx = ((fperm << 6) & 0o700) | ((fperm << 3) & 0o070) | (fperm & 0o007),
|
||||||
|
_ => break,
|
||||||
|
};
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
if pos == 0 {
|
||||||
|
srwx = 0;
|
||||||
|
}
|
||||||
|
(srwx, pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_levels(mode: &str) -> (mode_t, usize) {
|
||||||
|
let mut mask = 0;
|
||||||
|
let mut pos = 0;
|
||||||
|
for ch in mode.chars() {
|
||||||
|
mask |= match ch {
|
||||||
|
'u' => 0o7700,
|
||||||
|
'g' => 0o7070,
|
||||||
|
'o' => 0o7007,
|
||||||
|
'a' => 0o7777,
|
||||||
|
_ => break,
|
||||||
|
};
|
||||||
|
pos += 1;
|
||||||
|
}
|
||||||
|
if pos == 0 {
|
||||||
|
mask = 0o7777; // default to 'a'
|
||||||
|
}
|
||||||
|
(mask, pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_symbolic(mut fperm: mode_t, mut mode: &str) -> Result<mode_t, String> {
|
||||||
|
let (mask, pos) = parse_levels(mode);
|
||||||
|
if pos == mode.len() {
|
||||||
|
return Err("invalid mode".to_owned());
|
||||||
|
}
|
||||||
|
mode = &mode[pos..];
|
||||||
|
while mode.len() > 0 {
|
||||||
|
let (op, pos) = try!(parse_op(mode, None));
|
||||||
|
mode = &mode[pos..];
|
||||||
|
let (srwx, pos) = parse_change(mode, fperm);
|
||||||
|
mode = &mode[pos..];
|
||||||
|
match op {
|
||||||
|
'+' => fperm |= srwx & mask,
|
||||||
|
'-' => fperm &= !(srwx & mask),
|
||||||
|
'=' => fperm = (fperm & !mask) | (srwx & mask),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(fperm)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_op(mode: &str, default: Option<char>) -> Result<(char, usize), String> {
|
||||||
|
match mode.chars().next() {
|
||||||
|
Some(ch) => {
|
||||||
|
match ch {
|
||||||
|
'+' | '-' | '=' => Ok((ch, 1)),
|
||||||
|
_ => {
|
||||||
|
match default {
|
||||||
|
Some(ch) => Ok((ch, 0)),
|
||||||
|
None => {
|
||||||
|
Err(format!("invalid operator (expected +, -, or =, but found {})", ch))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => Err("unexpected end of mode".to_owned()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_numeric(fperm: mode_t, mut mode: &str) -> Result<mode_t, String> {
|
||||||
|
let (op, pos) = try!(parse_op(mode, Some('=')));
|
||||||
|
mode = mode[pos..].trim_left_matches('0');
|
||||||
|
match mode_t::from_str_radix(mode, 8) {
|
||||||
|
Ok(change) => {
|
||||||
|
let after = match op {
|
||||||
|
'+' => fperm | change,
|
||||||
|
'-' => fperm & !change,
|
||||||
|
'=' => change,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
if after > 0o7777 {
|
||||||
|
return Err("invalid mode".to_owned());
|
||||||
|
}
|
||||||
|
Ok(after)
|
||||||
|
}
|
||||||
|
Err(_) => Err("invalid mode".to_owned()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn parse_mode(mode: Option<String>) -> Result<mode_t, String> {
|
||||||
|
let fperm = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
|
||||||
|
if let Some(mode) = mode {
|
||||||
|
let arr: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
||||||
|
let result = if mode.contains(arr) {
|
||||||
|
parse_numeric(fperm, mode.as_str())
|
||||||
|
} else {
|
||||||
|
parse_symbolic(fperm, mode.as_str())
|
||||||
|
};
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
Ok(fperm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn symbolic_modes() {
|
||||||
|
assert_eq!(parse_mode(Some("u+x".to_owned())).unwrap(), 0o766);
|
||||||
|
assert_eq!(parse_mode(Some("+x".to_owned())).unwrap(), 0o777);
|
||||||
|
assert_eq!(parse_mode(Some("a-w".to_owned())).unwrap(), 0o444);
|
||||||
|
assert_eq!(parse_mode(Some("g-r".to_owned())).unwrap(), 0o626);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn numeric_modes() {
|
||||||
|
assert_eq!(parse_mode(Some("644".to_owned())).unwrap(), 0o644);
|
||||||
|
assert_eq!(parse_mode(Some("+100".to_owned())).unwrap(), 0o766);
|
||||||
|
assert_eq!(parse_mode(Some("-4".to_owned())).unwrap(), 0o662);
|
||||||
|
assert_eq!(parse_mode(None).unwrap(), 0o666);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue