From: WanBingjiang Date: Thu, 25 Jun 2026 11:08:53 +0000 (+0800) Subject: hexdump: stop after stdout write errors X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4e0a08db808e9089c1335385a45807e9a7e84058;p=thirdparty%2Futil-linux.git hexdump: stop after stdout write errors Stop the display loop once stdout reports an error. Otherwise hexdump keeps formatting and writing after the output stream has already failed, which can loop for a long time on errors such as ENOSPC. Save the errno from the first stdout error before cleanup can overwrite it. Report that saved error from hexdump itself and bypass close_stdout_atexit, as ferror() only preserves the stream error state and does not recover the original errno. Add a regression test that writes hexdump output to /dev/full and checks that the command fails with the ENOSPC write error. Addresses: https://github.com/util-linux/util-linux/issues/2205 Signed-off-by: WanBingjiang --- diff --git a/tests/expected/hexdump/write-error b/tests/expected/hexdump/write-error new file mode 100644 index 000000000..e4793b7b6 --- /dev/null +++ b/tests/expected/hexdump/write-error @@ -0,0 +1 @@ +exit code: 1 diff --git a/tests/expected/hexdump/write-error.err b/tests/expected/hexdump/write-error.err new file mode 100644 index 000000000..e5c3300f2 --- /dev/null +++ b/tests/expected/hexdump/write-error.err @@ -0,0 +1 @@ +hexdump: write error: No space left on device diff --git a/tests/ts/hexdump/write-error b/tests/ts/hexdump/write-error new file mode 100755 index 000000000..ec70e5994 --- /dev/null +++ b/tests/ts/hexdump/write-error @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# This file is part of util-linux. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This file is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. + +TS_TOPDIR="${0%/*}/../.." +TS_DESC="write-error" + +. "$TS_TOPDIR"/functions.sh +ts_init "$*" + +ts_check_test_command "$TS_CMD_HEXDUMP" + +[ -c /dev/full ] || ts_skip "/dev/full not found" + +$TS_CMD_HEXDUMP -C --no-squeezing -n 1048576 /dev/zero > /dev/full 2> "$TS_ERRLOG" +rc=$? +printf "exit code: %d\n" "$rc" >> "$TS_OUTPUT" + +ts_finalize diff --git a/text-utils/hexdump-display.c b/text-utils/hexdump-display.c index 5f0f48bff..d9630f5ee 100644 --- a/text-utils/hexdump-display.c +++ b/text-utils/hexdump-display.c @@ -454,6 +454,11 @@ void display(struct hexdump *hex) rem = hex->blocksize; address = saveaddress; } + if (ferror(stdout)) { + hex->stdout_errno = errno; + hex->exitval = errno == EPIPE ? EXIT_SUCCESS : EXIT_FAILURE; + return; + } } if (endfu) { /* diff --git a/text-utils/hexdump.c b/text-utils/hexdump.c index c2456a762..08a38c591 100644 --- a/text-utils/hexdump.c +++ b/text-utils/hexdump.c @@ -196,7 +196,7 @@ int main(int argc, char **argv) { struct list_head *p; struct hexdump_fs *tfs; - int ret; + int ret, stdout_errno; struct hexdump *hex = xcalloc(1, sizeof (struct hexdump)); hex->length = -1; @@ -225,7 +225,21 @@ int main(int argc, char **argv) display(hex); ret = hex->exitval; + stdout_errno = hex->stdout_errno; hex_free(hex); + if (stdout_errno) { + /* + * The stream error flag does not preserve errno. Report the + * saved write error here and bypass close_stdout_atexit() to + * avoid relying on unrelated errno values after cleanup. + */ + errno = stdout_errno; + if (stdout_errno != EPIPE) + warn(_("write error")); + if (flush_standard_stream(stderr) != 0) + _exit(EXIT_FAILURE); + _exit(ret); + } return ret; } diff --git a/text-utils/hexdump.h b/text-utils/hexdump.h index 309fbee4f..681f29d3e 100644 --- a/text-utils/hexdump.h +++ b/text-utils/hexdump.h @@ -87,6 +87,7 @@ struct hexdump { struct list_head fshead; /* head of format strings */ ssize_t blocksize; /* data block size */ int exitval; /* final exit value */ + int stdout_errno; /* errno from stdout error */ ssize_t length; /* max bytes to read */ off_t skip; /* bytes to skip */ /* Sparse file optimization using FIEMAP (Linux only) */