diff --git a/Cargo.toml b/Cargo.toml index 0001eb31119c68e1e9dcde8837c4ac23341389d1..5f3ec27497cc915accd0e058e6ba3f006b5fd722 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hashtoc" -version = "0.1.3" +version = "0.1.4" authors = ["Anders Blomdell <anders.blomdell@control.lth.se>"] build = "build.rs" links = "libhash.a,libssl" diff --git a/src/main.rs b/src/main.rs index 98bfda3f195a53d1fe74384bb153c0a1431d2dc3..abb4be56e1007e1045a1d0a3b6c948aa6579170e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,7 +39,7 @@ use std::cell::RefCell; use libc::{c_int, time_t}; use std::result::Result; use std::io::Error as IOError; -use std::io::{self, Write}; +use std::io::{self, Write, BufRead}; use bytes::BytesMut; use std::ops::Deref; @@ -554,7 +554,7 @@ fn dispatcher(options: clap::ArgMatches, zprintln!(); } -fn main() { +fn main() -> std::io::Result<()> { let matches = App::new("hashtoc") .version("1.0") .author("Anders Blomdell <anders.blomdell@control.lth.se>") @@ -608,7 +608,11 @@ fn main() { .arg(Arg::with_name("zero-terminated") .short("z") .long("zero-terminated") - .help("End lines with NULL character")) + .help("Line endings are the NULL character")) + .arg(Arg::with_name("stdin") + .long("stdin") + .required(true) + .help("Read paths from stdin")) .arg(Arg::with_name("max_age") .short("m") .long("max-age") @@ -616,7 +620,8 @@ fn main() { .help("max age of HASH extended attribute(s)")) .arg(Arg::with_name("PATH") .help("path(s) to traverse") - .required(true) + .required(false) + .conflicts_with("stdin") .multiple(true) .index(1)) .get_matches(); @@ -628,18 +633,47 @@ fn main() { let worker = thread::spawn(move|| { dispatcher(args, rx); }); (worker, tx) }; - let callback = | p:&Path, m:&Metadata | { - tx.send( WalkerMessage::Inode( - Inode{path:p.to_owned(), metadata:Some(m.clone())})).unwrap(); - }; - let paths : Vec<_> = matches.values_of_os("PATH").unwrap().collect(); -// let mut paths : Vec<_> = matches.values_of_os("PATH").unwrap().collect(); -// paths.sort(); - for p in paths { - let path = std::path::Path::new(p); - let _ = tx.send(WalkerMessage::Start(path.to_owned())); - walk::visit(std::path::Path::new(p), &callback).unwrap(); + match matches.values_of_os("PATH") { + Some(_) => { + let callback = | p:&Path, m:&Metadata | { + tx.send( WalkerMessage::Inode( + Inode{path:p.to_owned(), + metadata:Some(m.clone())})).unwrap(); + }; + let paths : Vec<_> = + matches.values_of_os("PATH").unwrap().collect(); + for p in paths { + let path = std::path::Path::new(p); + let _ = tx.send(WalkerMessage::Start(path.to_owned())); + walk::visit(std::path::Path::new(p), &callback).unwrap(); + } + }, + None => { + let terminator = + if matches.is_present("zero-terminated") { 0 } else { 10 }; + let stdin = std::io::stdin(); + let mut handle = stdin.lock(); + let get_metadata= walk::metadata_getter(); + loop { + let mut buf = vec![]; + let len = handle.read_until(terminator, &mut buf).unwrap(); + if len == 0 { + break; + } + let path = Path::new(OsStr::from_bytes(&buf[..len-1])); + let metadata = get_metadata(&path); + match metadata { + Ok(metadata) => + tx.send(WalkerMessage::Inode(Inode { + path: path.to_owned(), + metadata: Some(metadata) + })).unwrap(), + Err(e) => { println!("{} {:?}", e, path) } + }; + } + } } tx.send(WalkerMessage::Done).unwrap(); worker.join().unwrap(); + Ok(()) } diff --git a/src/walk.rs b/src/walk.rs index 5215f0a3c6c11bec12e2814b93faf8cfec942e5f..c75364b6f506af8834630488a7a891e86128ff00 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -26,6 +26,7 @@ use std::fs::{read_dir, symlink_metadata, Metadata}; use std::os::unix::fs::MetadataExt; use libc::{AT_FDCWD, R_OK, X_OK, AT_SYMLINK_NOFOLLOW}; use libc; +use std::io::{Error, ErrorKind}; macro_rules! reportln { () => (writeln!(io::stderr()).unwrap_or(())); @@ -165,5 +166,30 @@ pub fn visit(path: &Path, cb: &Fn(&Path, &Metadata)) -> io::Result<()> Ok(()) } - - +pub fn metadata_getter() -> impl Fn(&Path) -> io::Result<Metadata> +{ + let do_access_check = need_access_check(); + move |path: &Path| { + let metadata = symlink_metadata(path); + let access = match metadata { + Ok(ref m) => { + if ! do_access_check { + true + } else { + if m.file_type().is_dir() { + check_full_path(&path, R_OK|X_OK) + } else { + check_full_path(&path, R_OK) + } + } + }, + _ => false + }; + if ! access { + Err(Error::new(ErrorKind::PermissionDenied, + "Access denied")) + } else { + metadata + } + } +}