diff --git a/Makefile.am b/Makefile.am index f3212b4a..723c06ce 100644 --- a/Makefile.am +++ b/Makefile.am @@ -309,6 +309,7 @@ EXTRA_DIST= \ tests/08-passphrase-file.sh \ tests/09-explicit-params.sh \ tests/10-check-params.sh \ + tests/11-output-finalization.sh \ tests/shared_test_functions.sh \ tests/shared_valgrind_functions.sh \ tests/test_scrypt.sh \ diff --git a/main.c b/main.c index 2b05e252..a1280f50 100644 --- a/main.c +++ b/main.c @@ -180,8 +180,19 @@ scrypt_mode_enc_dec(struct scryptenc_params params, /* Close any files we opened. */ if ((infile != stdin) && fclose(infile)) warnp("fclose"); - if ((outfile != stdout) && fclose(outfile)) - warnp("fclose"); + if (outfile != stdout) { + if (fclose(outfile)) { + if (rc == SCRYPT_OK) + rc = SCRYPT_EWRFILE; + else + warnp("fclose"); + } + } else if ((outfilename == NULL) && fflush(outfile)) { + if (rc == SCRYPT_OK) + rc = SCRYPT_EWRFILE; + else + warnp("fflush"); + } /* If we failed, print the right error message and exit. */ if (rc != SCRYPT_OK) { diff --git a/tests/11-output-finalization.sh b/tests/11-output-finalization.sh new file mode 100644 index 00000000..00b83970 --- /dev/null +++ b/tests/11-output-finalization.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +### Constants +input_file="${s_basename}-input.txt" +named_stderr="${s_basename}-named.stderr" +stdout_stderr="${s_basename}-stdout.stderr" + +scenario_cmd() { + # /dev/full is available on Linux but not on every test platform. + if [ ! -c /dev/full ]; then + setup_check "scrypt enc named output close failure" + echo "-1" > "${c_exitfile}" + setup_check "scrypt enc named output close error" + echo "-1" > "${c_exitfile}" + setup_check "scrypt enc stdout flush failure" + echo "-1" > "${c_exitfile}" + setup_check "scrypt enc stdout flush error" + echo "-1" > "${c_exitfile}" + return + fi + + # Keep the output small enough that the write error is delayed until + # fclose(3) or fflush(3), rather than being returned by fwrite(3). + printf "x" > "${input_file}" + + # A delayed failure while closing a named output file must fail the + # command, rather than merely printing a warning and exiting 0. + setup_check "scrypt enc named output close failure" + printf "%s\n" "${password}" | "${bindir}/scrypt" \ + enc --logN 10 -r 1 -p 1 \ + --passphrase dev:stdin-once \ + "${input_file}" /dev/full 2> "${named_stderr}" + expected_exitcode 1 $? > "${c_exitfile}" + + setup_check "scrypt enc named output close error" + grep -q "Error writing file: /dev/full" "${named_stderr}" + echo $? > "${c_exitfile}" + + # stdout also needs an explicit flush check; exit(3) cannot propagate a + # delayed stdio error into the process exit status. + setup_check "scrypt enc stdout flush failure" + printf "%s\n" "${password}" | "${bindir}/scrypt" \ + enc --logN 10 -r 1 -p 1 \ + --passphrase dev:stdin-once \ + "${input_file}" > /dev/full 2> "${stdout_stderr}" + expected_exitcode 1 $? > "${c_exitfile}" + + setup_check "scrypt enc stdout flush error" + grep -q "Error writing file: standard output" "${stdout_stderr}" + echo $? > "${c_exitfile}" +}