From 1f0468f6a3d879a02aac7bd30be7e59ff476f9b2 Mon Sep 17 00:00:00 2001 From: Anders Blomdell <anders.blomdell@control.lth.se> Date: Thu, 19 Apr 2018 09:06:00 +0200 Subject: [PATCH] Add zero-termination option and name-checking --- Cargo.toml | 2 +- src/main.rs | 96 ++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 81 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6a80786..e316b7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,4 @@ publish = false clap = "2.20.5" libc = "0.2.21" threadpool = "1.3.2" - +bytes = "0.4.6" diff --git a/src/main.rs b/src/main.rs index f33b63d..e53905a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,9 +21,12 @@ General Public License for more details. extern crate clap; extern crate libc; extern crate threadpool; +extern crate bytes; use clap::{Arg, App}; use std::os::unix::fs::{FileTypeExt, MetadataExt}; +use std::ffi::OsStr; +use std::os::unix::ffi::OsStrExt; use std::fs::{Metadata}; use std::path::{Path, PathBuf}; use std::sync::mpsc::{channel, Receiver, Sender}; @@ -37,6 +40,8 @@ use libc::{c_int, time_t}; use std::result::Result; use std::io::Error as IOError; use std::io::{self, Write}; +use bytes::BytesMut; +use std::ops::Deref; mod libhash; use libhash::{FLAGS_CLEAR_XATTR, FLAGS_READ_XATTR, FLAGS_WRITE_XATTR, @@ -47,8 +52,17 @@ use hash::{md5_file, md5_symlink, sha512_file, sha512_symlink}; mod walk; macro_rules! reportln { - () => (writeln!(io::stderr()).unwrap_or(())); - ($($arg:tt)*) => (writeln!(io::stderr(), $($arg)*).unwrap_or(())); + () => (writeln!(io::stderr()).unwrap()); + ($($arg:tt)*) => (writeln!(io::stderr(), $($arg)*).unwrap()); +} + +macro_rules! bprint { + ($($arg : tt)*) => + (io::stdout().write_fmt(format_args!($($arg)*)).unwrap()); +} + +macro_rules! bwrite { + ($($arg : tt)+) => (io::stdout().write_all($($arg)+).unwrap()); } // Extract clap optin as an Option<t> @@ -58,7 +72,7 @@ macro_rules! option_t_or_exit { match v.trim().parse::<$t>() { Ok(val) => Option::Some(val), Err(_) => { - println!("Argument '--{} {}' cant be parsed as '{}'", + reportln!("Argument '--{} {}' cant be parsed as '{}'", $v, v, stringify!($t)); exit(1); } @@ -264,6 +278,34 @@ fn file_type_to_flag(file_type: &std::fs::FileType) -> char { else { unreachable!() } } +fn check_name(name : &OsStr) -> bool { + let mut error = false; + // Check for control characters + for c in name.as_bytes() { + if c < &32u8 { + error = true; + } + } + let mut sane_name = BytesMut::from(name.as_bytes()); + if error { + // Replace control chararcters for reporting + for c in sane_name.iter_mut() { + if *c < 32u8 { + *c = '?' as u8; + } + } + } + let p: &OsStr = OsStrExt::from_bytes(sane_name.deref()); + error |= match p.to_str() { + Some(_) => false, + None => true, + }; + if error { + reportln!("Invalid path {}", p.to_string_lossy()); + } + ! error +} + fn dispatcher(options: clap::ArgMatches, from_walker: Receiver<WalkerMessage>) { let jobs = option_t_or_exit!(options, "jobs", usize).unwrap_or(1); @@ -272,13 +314,24 @@ fn dispatcher(options: clap::ArgMatches, let calc_md5 = options.is_present("md5"); let calc_sha512 = options.is_present("sha512"); let (flags, maxage) = options_to_flags(&options); + let zero_terminated = options.is_present("zero-terminated"); + macro_rules! zprintln { + () => ( + if zero_terminated { + write!(io::stdout(), "\0").unwrap(); + } else { + writeln!(io::stdout()).unwrap(); + } + ) + } let mut pool = WorkerPool::<WorkerMessage>::new(jobs); let mut worklist = WorkList::new(); let mut done = false; - print!("#fields: size:mtime:uid:gid:mode:"); - if calc_md5 { print!("md5:"); } - if calc_sha512 { print!("sha512:"); } - println!("kind:name"); + bprint!("#fields: size:mtime:uid:gid:mode:"); + if calc_md5 { bprint!("md5:"); } + if calc_sha512 { bprint!("sha512:"); } + bprint!("kind:name"); + zprintln!(); loop { // reportln!("XXX {} {} {}", done, pending, worklist.used()); if done && worklist.used() == 0 { @@ -445,34 +498,39 @@ fn dispatcher(options: clap::ArgMatches, _ => () } }; + if ! (zero_terminated || + check_name(front.inode.path.as_os_str())) { + continue + } match front.kind { ' ' => { // Start of new path - println!("#path: {}", front.inode.path.display()); + bprint!("#path: {}", front.inode.path.display()); + zprintln!(); continue }, 'F' | 'L' => { let m = &front.inode.metadata.unwrap(); - print!("{}:{}:{}:{}:{:o}:", + bprint!("{}:{}:{}:{}:{:o}:", m.size(), m.mtime(), m.uid(), m.gid(), m.mode() & 0o7777); }, _ => { let m = &front.inode.metadata.unwrap(); - print!("::{}:{}:{:o}:", + bprint!("::{}:{}:{:o}:", m.uid(), m.gid(), m.mode() & 0o7777); } } use HashOption::{None,Some,Unused}; match *front.md5.borrow() { - None => print!(":"), - Some(ref hash) => print!("{}:", hash), + None => bprint!(":"), + Some(ref hash) => bprint!("{}:", hash), Unused => (), _ => unreachable!() } match *front.sha512.borrow() { - None => print!(":"), - Some(ref hash) => print!("{}:", hash), + None => bprint!(":"), + Some(ref hash) => bprint!("{}:", hash), Unused => (), _ => unreachable!() } @@ -483,11 +541,14 @@ fn dispatcher(options: clap::ArgMatches, _ => break } } - println!("{}:{}", front.kind, path.display()); + bprint!("{}", front.kind); + bwrite!(path.as_os_str().as_bytes()); + zprintln!(); } } } - println!("#endTOC") + bprint!("#endTOC"); + zprintln!(); } fn main() { @@ -541,6 +602,9 @@ fn main() { .short("q") .conflicts_with("verbose") .help("Sets the level of verbosity")) + .arg(Arg::with_name("zero-terminated") + .short("z") + .help("End lines with NULL character")) .arg(Arg::with_name("max_age") .short("m") .long("max-age") -- GitLab