From f77f365ef5c93614f42db4f087d67a3f7aae276a Mon Sep 17 00:00:00 2001 From: Collin Funk Date: Sat, 9 May 2026 15:43:39 -0700 Subject: [PATCH] shred: don't block when opening FIFOs with no readers * NEWS: Mention the bug fix. * src/shred.c (wipefile): Open the file with O_NONBLOCK. * tests/shred/fifo.sh: New file. * tests/local.mk (all_tests): Add the new test. --- NEWS | 3 +++ src/shred.c | 26 +++++++++++++++++++------- tests/local.mk | 1 + tests/shred/fifo.sh | 43 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 7 deletions(-) create mode 100755 tests/shred/fifo.sh diff --git a/NEWS b/NEWS index a105c48843..f8c43d5ed6 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,9 @@ GNU coreutils NEWS -*- outline -*- standard output is fully buffered, e.g., when redirected to a file. [bug introduced in coreutils-9.10] + 'shred' no longer blocks when opening a FIFO that has no readers. + [This bug was present in "the beginning".] + 'unexpand -t' no longer overflows a heap buffer, for tab values > SIZE_MAX/16. [bug introduced in coreutils-9.11] diff --git a/src/shred.c b/src/shred.c index b5bf6f3f0f..316268691f 100644 --- a/src/shred.c +++ b/src/shred.c @@ -1139,21 +1139,33 @@ static bool wipefile (char *name, char const *qname, struct randint_source *s, struct Options const *flags) { - bool ok; - int fd; - - fd = open (name, O_WRONLY | O_NOCTTY | O_BINARY); + int fd = open (name, O_WRONLY | O_NOCTTY | O_NONBLOCK | O_BINARY); if (fd < 0 && (errno == EACCES && flags->force) && chmod (name, S_IWUSR) == 0) - fd = open (name, O_WRONLY | O_NOCTTY | O_BINARY); + fd = open (name, O_WRONLY | O_NOCTTY | O_NONBLOCK | O_BINARY); if (fd < 0) { - error (0, errno, _("%s: failed to open for writing"), qname); + struct stat st; + + /* Try to give a more meaningful error message if we attempt to + open a FIFO with no readers. */ + if (errno == ENXIO && 0 <= stat (name, &st) && S_ISFIFO (st.st_mode)) + error (0, 0, _("%s: invalid file type"), qname); + else + error (0, errno, _("%s: failed to open for writing"), qname); return false; } - ok = do_wipefd (fd, qname, s, flags); + /* We used O_NONBLOCK to prevent blocking when opening a FIFO. + Reset that here. */ + int fd_flags = fcntl (fd, F_GETFL); + bool ok = 0 <= fd_flags && 0 <= fcntl (fd, F_SETFL, fd_flags & ~O_NONBLOCK); + if (ok) + ok = do_wipefd (fd, qname, s, flags); + else + error (0, errno, _("couldn't reset non-blocking mode %s"), qname); + if (close (fd) != 0) { error (0, errno, _("%s: failed to close"), qname); diff --git a/tests/local.mk b/tests/local.mk index 727cf8b58b..fd96d7f3ad 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -418,6 +418,7 @@ all_tests = \ tests/cksum/sha384sum.pl \ tests/cksum/sha512sum.pl \ tests/cksum/cksum-sha3.sh \ + tests/shred/fifo.sh \ tests/shred/shred-exact.sh \ tests/shred/shred-passes.sh \ tests/shred/shred-remove.sh \ diff --git a/tests/shred/fifo.sh b/tests/shred/fifo.sh new file mode 100755 index 0000000000..f182d3c9e2 --- /dev/null +++ b/tests/shred/fifo.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# Test that 'shred' behaves correctly on FIFOs. + +# Copyright (C) 2026 Free Software Foundation, Inc. + +# This program 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 3 of the License, or +# (at your option) any later version. + +# This program 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. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ shred + +mkfifo_or_skip_ fifo + +# Test 'shred' on a FIFO with no readers. +cat <<\EOF >exp || framework_failure_ +shred: fifo: invalid file type +EOF +returns_ 1 timeout 10 shred fifo >out 2>err || fail=1 +compare /dev/null out || fail=1 +compare exp err || fail=1 + +# Terminate any background processes. +cleanup_() { kill $pid 2>/dev/null && wait $pid; } + +# Test 'shred' on a FIFO with a reader. After the file descriptor is +# opened, 'cat' will be unblocked. +timeout 10 cat pipe > /dev/null & pid=$! +returns_ 1 timeout 10 shred fifo >out 2>err || fail=1 +compare /dev/null out || fail=1 +compare exp err || fail=1 +kill -0 $pid && fail=1 + +Exit $fail -- 2.47.3