]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
hexdump: stop after stdout write errors
authorWanBingjiang <wanbingjiang@webray.com.cn>
Thu, 25 Jun 2026 11:08:53 +0000 (19:08 +0800)
committerWanBingjiang <wanbingjiang@webray.com.cn>
Sat, 27 Jun 2026 17:34:08 +0000 (01:34 +0800)
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 <wanbingjiang@webray.com.cn>
tests/expected/hexdump/write-error [new file with mode: 0644]
tests/expected/hexdump/write-error.err [new file with mode: 0644]
tests/ts/hexdump/write-error [new file with mode: 0755]
text-utils/hexdump-display.c
text-utils/hexdump.c
text-utils/hexdump.h

diff --git a/tests/expected/hexdump/write-error b/tests/expected/hexdump/write-error
new file mode 100644 (file)
index 0000000..e4793b7
--- /dev/null
@@ -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 (file)
index 0000000..e5c3300
--- /dev/null
@@ -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 (executable)
index 0000000..ec70e59
--- /dev/null
@@ -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
index 5f0f48bfffe52199e1516c558ea331ff06da1518..d9630f5eed4defda06dbcda347fa9cbf94055bd1 100644 (file)
@@ -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) {
                /*
index c2456a762693c50e5f0a69ee0021acf1658b147a..08a38c5915452ee0db6232c906001f27b1bd63a8 100644 (file)
@@ -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;
 }
index 309fbee4f53f6b9abe950a19bc1ae8715cbc95dd..681f29d3ef4de42403c4fc3882bcccc90432bf6b 100644 (file)
@@ -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) */