Skip to content

Commit 1725479

Browse files
sylvestrecakebaker
andauthored
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>
1 parent cc1142c commit 1725479

File tree

3 files changed

+94
-15
lines changed

3 files changed

+94
-15
lines changed

src/uu/hashsum/src/hashsum.rs

+29-13
Original file line numberDiff line numberDiff line change
@@ -393,13 +393,15 @@ pub fn uu_app_common() -> Command {
393393
.short('c')
394394
.long("check")
395395
.help("read hashsums from the FILEs and check them")
396-
.action(ArgAction::SetTrue),
396+
.action(ArgAction::SetTrue)
397+
.conflicts_with("tag"),
397398
)
398399
.arg(
399400
Arg::new("tag")
400401
.long("tag")
401402
.help("create a BSD-style checksum")
402-
.action(ArgAction::SetTrue),
403+
.action(ArgAction::SetTrue)
404+
.conflicts_with("text"),
403405
)
404406
.arg(
405407
Arg::new("text")
@@ -630,7 +632,8 @@ where
630632
}
631633
let mut gnu_re = gnu_re_template(&bytes_marker, r"(?P<binary>[ \*])?")?;
632634
let bsd_re = Regex::new(&format!(
633-
r"^{algorithm} \((?P<fileName>.*)\) = (?P<digest>[a-fA-F0-9]{digest_size})",
635+
// it can start with \
636+
r"^(|\\){algorithm} \((?P<fileName>.*)\) = (?P<digest>[a-fA-F0-9]{digest_size})",
634637
algorithm = options.algoname,
635638
digest_size = bytes_marker,
636639
))
@@ -699,7 +702,8 @@ where
699702
}
700703
},
701704
};
702-
let f = match File::open(ck_filename.clone()) {
705+
let (ck_filename_unescaped, prefix) = unescape_filename(&ck_filename);
706+
let f = match File::open(ck_filename_unescaped) {
703707
Err(_) => {
704708
failed_open_file += 1;
705709
println!(
@@ -732,11 +736,11 @@ where
732736
// easier (and more important) on Unix than on Windows.
733737
if sum == real_sum {
734738
if !options.quiet {
735-
println!("{ck_filename}: OK");
739+
println!("{prefix}{ck_filename}: OK");
736740
}
737741
} else {
738742
if !options.status {
739-
println!("{ck_filename}: FAILED");
743+
println!("{prefix}{ck_filename}: FAILED");
740744
}
741745
failed_cksum += 1;
742746
}
@@ -749,16 +753,19 @@ where
749753
options.output_bits,
750754
)
751755
.map_err_context(|| "failed to read input".to_string())?;
756+
let (escaped_filename, prefix) = escape_filename(filename);
752757
if options.tag {
753-
println!("{} ({}) = {}", options.algoname, filename.display(), sum);
758+
println!(
759+
"{}{} ({}) = {}",
760+
prefix, options.algoname, escaped_filename, sum
761+
);
754762
} else if options.nonames {
755763
println!("{sum}");
756764
} else if options.zero {
765+
// with zero, we don't escape the filename
757766
print!("{} {}{}\0", sum, binary_marker, filename.display());
758767
} else {
759-
let (filename, has_prefix) = escape_filename(filename);
760-
let prefix = if has_prefix { "\\" } else { "" };
761-
println!("{}{} {}{}", prefix, sum, binary_marker, filename);
768+
println!("{}{} {}{}", prefix, sum, binary_marker, escaped_filename);
762769
}
763770
}
764771
}
@@ -783,14 +790,23 @@ where
783790
Ok(())
784791
}
785792

786-
fn escape_filename(filename: &Path) -> (String, bool) {
793+
fn unescape_filename(filename: &str) -> (String, &'static str) {
794+
let unescaped = filename
795+
.replace("\\\\", "\\")
796+
.replace("\\n", "\n")
797+
.replace("\\r", "\r");
798+
let prefix = if unescaped == filename { "" } else { "\\" };
799+
(unescaped, prefix)
800+
}
801+
802+
fn escape_filename(filename: &Path) -> (String, &'static str) {
787803
let original = filename.as_os_str().to_string_lossy();
788804
let escaped = original
789805
.replace('\\', "\\\\")
790806
.replace('\n', "\\n")
791807
.replace('\r', "\\r");
792-
let has_changed = escaped != original;
793-
(escaped, has_changed)
808+
let prefix = if escaped == original { "" } else { "\\" };
809+
(escaped, prefix)
794810
}
795811

796812
fn digest_reader<T: Read>(

tests/by-util/test_hashsum.rs

+60
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,22 @@ fn test_invalid_arg() {
356356
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
357357
}
358358

359+
#[test]
360+
fn test_conflicting_arg() {
361+
new_ucmd!()
362+
.arg("--tag")
363+
.arg("--check")
364+
.arg("--md5")
365+
.fails()
366+
.code_is(1);
367+
new_ucmd!()
368+
.arg("--tag")
369+
.arg("--text")
370+
.arg("--md5")
371+
.fails()
372+
.code_is(1);
373+
}
374+
359375
#[test]
360376
fn test_tag() {
361377
let scene = TestScenario::new(util_name!());
@@ -386,3 +402,47 @@ fn test_with_escape_filename() {
386402
assert!(stdout.starts_with('\\'));
387403
assert!(stdout.trim().ends_with("a\\nb"));
388404
}
405+
406+
#[test]
407+
#[cfg(not(windows))]
408+
fn test_with_escape_filename_zero_text() {
409+
let scene = TestScenario::new(util_name!());
410+
411+
let at = &scene.fixtures;
412+
let filename = "a\nb";
413+
at.touch(filename);
414+
let result = scene
415+
.ccmd("md5sum")
416+
.arg("--text")
417+
.arg("--zero")
418+
.arg(filename)
419+
.succeeds();
420+
let stdout = result.stdout_str();
421+
println!("stdout {}", stdout);
422+
assert!(!stdout.starts_with('\\'));
423+
assert!(stdout.contains("a\nb"));
424+
}
425+
426+
#[test]
427+
#[cfg(not(windows))]
428+
fn test_check_with_escape_filename() {
429+
let scene = TestScenario::new(util_name!());
430+
431+
let at = &scene.fixtures;
432+
433+
let filename = "a\nb";
434+
at.touch(filename);
435+
let result = scene.ccmd("md5sum").arg("--tag").arg(filename).succeeds();
436+
let stdout = result.stdout_str();
437+
println!("stdout {}", stdout);
438+
assert!(stdout.starts_with("\\MD5"));
439+
assert!(stdout.contains("a\\nb"));
440+
at.write("check.md5", stdout);
441+
let result = scene
442+
.ccmd("md5sum")
443+
.arg("--strict")
444+
.arg("-c")
445+
.arg("check.md5")
446+
.succeeds();
447+
result.stdout_is("\\a\\nb: OK\n");
448+
}

util/build-gnu.sh

+5-2
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,9 @@ cp "${UU_BUILD_DIR}/install" "${UU_BUILD_DIR}/ginstall" # The GNU tests rename t
100100
# Create *sum binaries
101101
for sum in b2sum b3sum md5sum sha1sum sha224sum sha256sum sha384sum sha512sum; do
102102
sum_path="${UU_BUILD_DIR}/${sum}"
103-
test -f "${sum_path}" || cp "${UU_BUILD_DIR}/hashsum" "${sum_path}"
103+
test -f "${sum_path}" || (cd ${UU_BUILD_DIR} && ln -s "hashsum" "${sum}")
104104
done
105-
test -f "${UU_BUILD_DIR}/[" || cp "${UU_BUILD_DIR}/test" "${UU_BUILD_DIR}/["
105+
test -f "${UU_BUILD_DIR}/[" || (cd ${UU_BUILD_DIR} && ln -s "test" "[")
106106

107107
##
108108

@@ -329,6 +329,9 @@ ls: invalid --time-style argument 'XX'\nPossible values are: [\"full-iso\", \"lo
329329
# "hostid BEFORE --help AFTER " same for this
330330
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
331331

332+
# The case doesn't really matter here
333+
sed -i -e "s|WARNING: 1 line is improperly formatted|warning: 1 line is improperly formatted|" tests/cksum/md5sum-bsd.sh
334+
332335
# Add debug info + we have less syscall then GNU's. Adjust our check.
333336
# Use GNU sed for /c command
334337
"${SED}" -i -e '/test \$n_stat1 = \$n_stat2 \\/c\

0 commit comments

Comments
 (0)