mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-27 11:07:44 +00:00
hashsum: also escape/unescape files with checks (#5868)
* hashsum: make tag conflicts with --text and --check * hashsum: change the case in one of the gnu test * build-gnu.sh: use symlink instead of copy Plus: it won't cp new md5 * hashsum: also escape/unescape files with checks Should fix tests/cksum/md5sum-bsd.sh * improve the variable name Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com> * Improve test Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com> * Improve test Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com> --------- Co-authored-by: Daniel Hofstetter <daniel.hofstetter@42dh.com>
This commit is contained in:
parent
cc1142cee6
commit
1725479c06
3 changed files with 94 additions and 15 deletions
|
@ -393,13 +393,15 @@ pub fn uu_app_common() -> Command {
|
||||||
.short('c')
|
.short('c')
|
||||||
.long("check")
|
.long("check")
|
||||||
.help("read hashsums from the FILEs and check them")
|
.help("read hashsums from the FILEs and check them")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue)
|
||||||
|
.conflicts_with("tag"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("tag")
|
Arg::new("tag")
|
||||||
.long("tag")
|
.long("tag")
|
||||||
.help("create a BSD-style checksum")
|
.help("create a BSD-style checksum")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue)
|
||||||
|
.conflicts_with("text"),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("text")
|
Arg::new("text")
|
||||||
|
@ -630,7 +632,8 @@ where
|
||||||
}
|
}
|
||||||
let mut gnu_re = gnu_re_template(&bytes_marker, r"(?P<binary>[ \*])?")?;
|
let mut gnu_re = gnu_re_template(&bytes_marker, r"(?P<binary>[ \*])?")?;
|
||||||
let bsd_re = Regex::new(&format!(
|
let bsd_re = Regex::new(&format!(
|
||||||
r"^{algorithm} \((?P<fileName>.*)\) = (?P<digest>[a-fA-F0-9]{digest_size})",
|
// it can start with \
|
||||||
|
r"^(|\\){algorithm} \((?P<fileName>.*)\) = (?P<digest>[a-fA-F0-9]{digest_size})",
|
||||||
algorithm = options.algoname,
|
algorithm = options.algoname,
|
||||||
digest_size = bytes_marker,
|
digest_size = bytes_marker,
|
||||||
))
|
))
|
||||||
|
@ -699,7 +702,8 @@ where
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let f = match File::open(ck_filename.clone()) {
|
let (ck_filename_unescaped, prefix) = unescape_filename(&ck_filename);
|
||||||
|
let f = match File::open(ck_filename_unescaped) {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
failed_open_file += 1;
|
failed_open_file += 1;
|
||||||
println!(
|
println!(
|
||||||
|
@ -732,11 +736,11 @@ where
|
||||||
// easier (and more important) on Unix than on Windows.
|
// easier (and more important) on Unix than on Windows.
|
||||||
if sum == real_sum {
|
if sum == real_sum {
|
||||||
if !options.quiet {
|
if !options.quiet {
|
||||||
println!("{ck_filename}: OK");
|
println!("{prefix}{ck_filename}: OK");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !options.status {
|
if !options.status {
|
||||||
println!("{ck_filename}: FAILED");
|
println!("{prefix}{ck_filename}: FAILED");
|
||||||
}
|
}
|
||||||
failed_cksum += 1;
|
failed_cksum += 1;
|
||||||
}
|
}
|
||||||
|
@ -749,16 +753,19 @@ where
|
||||||
options.output_bits,
|
options.output_bits,
|
||||||
)
|
)
|
||||||
.map_err_context(|| "failed to read input".to_string())?;
|
.map_err_context(|| "failed to read input".to_string())?;
|
||||||
|
let (escaped_filename, prefix) = escape_filename(filename);
|
||||||
if options.tag {
|
if options.tag {
|
||||||
println!("{} ({}) = {}", options.algoname, filename.display(), sum);
|
println!(
|
||||||
|
"{}{} ({}) = {}",
|
||||||
|
prefix, options.algoname, escaped_filename, sum
|
||||||
|
);
|
||||||
} else if options.nonames {
|
} else if options.nonames {
|
||||||
println!("{sum}");
|
println!("{sum}");
|
||||||
} else if options.zero {
|
} else if options.zero {
|
||||||
|
// with zero, we don't escape the filename
|
||||||
print!("{} {}{}\0", sum, binary_marker, filename.display());
|
print!("{} {}{}\0", sum, binary_marker, filename.display());
|
||||||
} else {
|
} else {
|
||||||
let (filename, has_prefix) = escape_filename(filename);
|
println!("{}{} {}{}", prefix, sum, binary_marker, escaped_filename);
|
||||||
let prefix = if has_prefix { "\\" } else { "" };
|
|
||||||
println!("{}{} {}{}", prefix, sum, binary_marker, filename);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -783,14 +790,23 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn escape_filename(filename: &Path) -> (String, bool) {
|
fn unescape_filename(filename: &str) -> (String, &'static str) {
|
||||||
|
let unescaped = filename
|
||||||
|
.replace("\\\\", "\\")
|
||||||
|
.replace("\\n", "\n")
|
||||||
|
.replace("\\r", "\r");
|
||||||
|
let prefix = if unescaped == filename { "" } else { "\\" };
|
||||||
|
(unescaped, prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn escape_filename(filename: &Path) -> (String, &'static str) {
|
||||||
let original = filename.as_os_str().to_string_lossy();
|
let original = filename.as_os_str().to_string_lossy();
|
||||||
let escaped = original
|
let escaped = original
|
||||||
.replace('\\', "\\\\")
|
.replace('\\', "\\\\")
|
||||||
.replace('\n', "\\n")
|
.replace('\n', "\\n")
|
||||||
.replace('\r', "\\r");
|
.replace('\r', "\\r");
|
||||||
let has_changed = escaped != original;
|
let prefix = if escaped == original { "" } else { "\\" };
|
||||||
(escaped, has_changed)
|
(escaped, prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn digest_reader<T: Read>(
|
fn digest_reader<T: Read>(
|
||||||
|
|
|
@ -356,6 +356,22 @@ fn test_invalid_arg() {
|
||||||
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
|
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_conflicting_arg() {
|
||||||
|
new_ucmd!()
|
||||||
|
.arg("--tag")
|
||||||
|
.arg("--check")
|
||||||
|
.arg("--md5")
|
||||||
|
.fails()
|
||||||
|
.code_is(1);
|
||||||
|
new_ucmd!()
|
||||||
|
.arg("--tag")
|
||||||
|
.arg("--text")
|
||||||
|
.arg("--md5")
|
||||||
|
.fails()
|
||||||
|
.code_is(1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tag() {
|
fn test_tag() {
|
||||||
let scene = TestScenario::new(util_name!());
|
let scene = TestScenario::new(util_name!());
|
||||||
|
@ -386,3 +402,47 @@ fn test_with_escape_filename() {
|
||||||
assert!(stdout.starts_with('\\'));
|
assert!(stdout.starts_with('\\'));
|
||||||
assert!(stdout.trim().ends_with("a\\nb"));
|
assert!(stdout.trim().ends_with("a\\nb"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
fn test_with_escape_filename_zero_text() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
|
||||||
|
let at = &scene.fixtures;
|
||||||
|
let filename = "a\nb";
|
||||||
|
at.touch(filename);
|
||||||
|
let result = scene
|
||||||
|
.ccmd("md5sum")
|
||||||
|
.arg("--text")
|
||||||
|
.arg("--zero")
|
||||||
|
.arg(filename)
|
||||||
|
.succeeds();
|
||||||
|
let stdout = result.stdout_str();
|
||||||
|
println!("stdout {}", stdout);
|
||||||
|
assert!(!stdout.starts_with('\\'));
|
||||||
|
assert!(stdout.contains("a\nb"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
fn test_check_with_escape_filename() {
|
||||||
|
let scene = TestScenario::new(util_name!());
|
||||||
|
|
||||||
|
let at = &scene.fixtures;
|
||||||
|
|
||||||
|
let filename = "a\nb";
|
||||||
|
at.touch(filename);
|
||||||
|
let result = scene.ccmd("md5sum").arg("--tag").arg(filename).succeeds();
|
||||||
|
let stdout = result.stdout_str();
|
||||||
|
println!("stdout {}", stdout);
|
||||||
|
assert!(stdout.starts_with("\\MD5"));
|
||||||
|
assert!(stdout.contains("a\\nb"));
|
||||||
|
at.write("check.md5", stdout);
|
||||||
|
let result = scene
|
||||||
|
.ccmd("md5sum")
|
||||||
|
.arg("--strict")
|
||||||
|
.arg("-c")
|
||||||
|
.arg("check.md5")
|
||||||
|
.succeeds();
|
||||||
|
result.stdout_is("\\a\\nb: OK\n");
|
||||||
|
}
|
||||||
|
|
|
@ -100,9 +100,9 @@ cp "${UU_BUILD_DIR}/install" "${UU_BUILD_DIR}/ginstall" # The GNU tests rename t
|
||||||
# Create *sum binaries
|
# Create *sum binaries
|
||||||
for sum in b2sum b3sum md5sum sha1sum sha224sum sha256sum sha384sum sha512sum; do
|
for sum in b2sum b3sum md5sum sha1sum sha224sum sha256sum sha384sum sha512sum; do
|
||||||
sum_path="${UU_BUILD_DIR}/${sum}"
|
sum_path="${UU_BUILD_DIR}/${sum}"
|
||||||
test -f "${sum_path}" || cp "${UU_BUILD_DIR}/hashsum" "${sum_path}"
|
test -f "${sum_path}" || (cd ${UU_BUILD_DIR} && ln -s "hashsum" "${sum}")
|
||||||
done
|
done
|
||||||
test -f "${UU_BUILD_DIR}/[" || cp "${UU_BUILD_DIR}/test" "${UU_BUILD_DIR}/["
|
test -f "${UU_BUILD_DIR}/[" || (cd ${UU_BUILD_DIR} && ln -s "test" "[")
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
||||||
|
@ -329,6 +329,9 @@ ls: invalid --time-style argument 'XX'\nPossible values are: [\"full-iso\", \"lo
|
||||||
# "hostid BEFORE --help AFTER " same for this
|
# "hostid BEFORE --help AFTER " same for this
|
||||||
sed -i -e "s/env \$prog \$BEFORE \$opt > out2/env \$prog \$BEFORE \$opt > out2 #/" -e "s/env \$prog \$BEFORE \$opt AFTER > out3/env \$prog \$BEFORE \$opt AFTER > out3 #/" -e "s/compare exp out2/compare exp out2 #/" -e "s/compare exp out3/compare exp out3 #/" tests/help/help-version-getopt.sh
|
sed -i -e "s/env \$prog \$BEFORE \$opt > out2/env \$prog \$BEFORE \$opt > out2 #/" -e "s/env \$prog \$BEFORE \$opt AFTER > out3/env \$prog \$BEFORE \$opt AFTER > out3 #/" -e "s/compare exp out2/compare exp out2 #/" -e "s/compare exp out3/compare exp out3 #/" tests/help/help-version-getopt.sh
|
||||||
|
|
||||||
|
# The case doesn't really matter here
|
||||||
|
sed -i -e "s|WARNING: 1 line is improperly formatted|warning: 1 line is improperly formatted|" tests/cksum/md5sum-bsd.sh
|
||||||
|
|
||||||
# Add debug info + we have less syscall then GNU's. Adjust our check.
|
# Add debug info + we have less syscall then GNU's. Adjust our check.
|
||||||
# Use GNU sed for /c command
|
# Use GNU sed for /c command
|
||||||
"${SED}" -i -e '/test \$n_stat1 = \$n_stat2 \\/c\
|
"${SED}" -i -e '/test \$n_stat1 = \$n_stat2 \\/c\
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue