bug#81444: chown, chmod, chgrp, chcon, ls: --recursive walk does not ignore ENOENT errors

Paul Holzinger via GNU coreutils Bug Reports <[email protected]> Mon, 20 Jul 2026 19:12:29 +0200
Newsgroups gmane.comp.gnu.core-utils.bugs
Message-ID <[email protected]>
Hi,

I noticed that when chown'ing a directory tree recursively with many 
files sometimes chown will exit 1 and report ENOENT errors. This happens 
when looping over the directory results from readdir() and in a parallel 
another process removes a file before the code then calls stat(), 
chmod() or whatever syscalls the command has to do for the given filename.

Since such race is expected the coreutils should not treat this as error 
and just ignore them instead. That way a parallel removal of a file will 
not make the commands return exit code 1 and thus possibility break 
scripts. Of course this should only apply to files or directories under 
the main path given as input, i.e. chmod -R dest should still fail with 
ENOENT when dest does not exists.

Below is a reproducer for the commands, might needs to run a few times 
until it triggers the race condition, I am not sure if there are more 
commands in coreutils which are affected by this.

Thanks,
Paul

---

#!/usr/bin/env bash
set -e

TESTCOMMAND="chown -R $(id -u)"
# TESTCOMMAND="chgrp -R $(id -g)"
# TESTCOMMAND="chmod -R 0700"
# TESTCOMMAND="chcon -R -t user_tmp_t"
# TESTCOMMAND="ls -l -R"

TESTDIR=$(mktemp -d)

trap "rm -rf $TESTDIR" EXIT

mkdir -p $TESTDIR/t{0,1,2,3,4,5,6,7,8,9}

# seed many test files to make the race much more likely
for path in $TESTDIR/t{0..9}; do
     for i in {0..255}; do
         touch $path/$i
     done
done

# start command in background so we can start the rm in parallel
$TESTCOMMAND $TESTDIR >/dev/null &
test_pid=$!

for path in $TESTDIR/t{0..9}; do
     rm -rf $path &
done

wait "$test_pid"
wait