diff --git a/src/chmod/chmod.rs b/src/chmod/chmod.rs index f8bd824ab..105021fea 100644 --- a/src/chmod/chmod.rs +++ b/src/chmod/chmod.rs @@ -36,7 +36,7 @@ pub fn uumain(args: Vec) -> i32 { opts.optflag("v", "verbose", "output a diagnostic for every file processed (unimplemented)"); opts.optflag("", "no-preserve-root", "do not treat '/' specially (the default)"); opts.optflag("", "preserve-root", "fail to operate recursively on '/'"); - opts.optflagopt("", "reference", "use RFILE's mode instead of MODE values", "RFILE"); + opts.optopt("", "reference", "use RFILE's mode instead of MODE values", "RFILE"); opts.optflag("R", "recursive", "change files and directories recursively"); opts.optflag("h", "help", "display this help and exit"); opts.optflag("V", "version", "output version information and exit"); @@ -62,7 +62,7 @@ Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+'.", return 0; } else if matches.opt_present("version") { println!("{} {}", NAME, VERSION); - } else if matches.free.is_empty() && matches.opt_present("reference") || matches.free.len() < 2 { + } else if matches.free.is_empty() { show_error!("missing an argument"); show_error!("for help, try '{} --help'", NAME); return 1; @@ -73,12 +73,15 @@ Each MODE is of the form '[ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=]?[0-7]+'.", let preserve_root = matches.opt_present("preserve-root"); let recursive = matches.opt_present("recursive"); let fmode = matches.opt_str("reference").and_then(|fref| { + let s = CString::new(fref).unwrap_or_else( |_| { + crash!(1, "reference file name contains internal nul byte") + }); let mut stat : libc::stat = unsafe { mem::uninitialized() }; - let statres = unsafe { libc::stat(fref.as_ptr() as *const _, &mut stat as *mut libc::stat) }; + let statres = unsafe { libc::stat(s.as_ptr() as *const _, &mut stat as *mut libc::stat) }; if statres == 0 { Some(stat.st_mode) } else { - crash!(1, "{}", io::Error::last_os_error()) + crash!(1, "cannot stat attribues of '{}': {}", matches.opt_str("reference").unwrap(), io::Error::last_os_error()) } }); let cmode = diff --git a/tests/chmod.rs b/tests/chmod.rs index 1e140a3af..ac476a573 100644 --- a/tests/chmod.rs +++ b/tests/chmod.rs @@ -8,6 +8,8 @@ use common::util::*; static UTIL_NAME: &'static str = "chmod"; static TEST_FILE: &'static str = "file"; +static REFERENCE_FILE: &'static str = "reference"; +static REFERENCE_PERMS: mode_t = 0o247; struct TestCase { args: Vec<&'static str>, @@ -27,6 +29,7 @@ fn run_tests(tests: Vec) { let (at, mut ucmd) = testing(UTIL_NAME); mkfile(&at.plus_as_string(TEST_FILE), test.before); + mkfile(&at.plus_as_string(REFERENCE_FILE), REFERENCE_PERMS); let perms = at.metadata(TEST_FILE).permissions().mode(); if perms != test.before{ panic!(format!("{}: expected: {:o} got: {:o}", "setting permissions failed", test.after, perms)); @@ -87,3 +90,11 @@ fn test_chmod_ugo_copy() { }; run_tests(tests); } + +#[test] +fn test_chmod_reference_file() { + let tests = vec!{ + TestCase{args: vec!{"--reference", REFERENCE_FILE, TEST_FILE}, before: 0o070, after: 0o247}, + }; + run_tests(tests); +}