| # Trigger a failed assertion in coreutils-8.9 and earlier. | |
| # Copyright (C) 2011-2025 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 <https://www.gnu.org/licenses/>. | |
| . "${srcdir=.}/tests/init.sh"; path_prepend_ ./src | |
| print_ver_ du | |
| require_trap_signame_ | |
| # We use a python-inotify script, so... | |
| python -m pyinotify -h > /dev/null \ | |
| || skip_ 'python inotify package not installed' | |
| # Move a directory "up" while du is processing its sub-directories. | |
| # While du is processing a hierarchy .../B/C/D/... this script | |
| # detects when du opens D/, and then moves C/ "up" one level | |
| # so that it is a sibling of B/. | |
| # Given the inherent race condition, we have to add enough "weight" | |
| # under D/ so that in most cases, the monitor performs the single | |
| # rename syscall before du finishes processing the subtree under D/. | |
| cat <<'EOF' > inotify-watch-for-dir-access.py | |
| #!/usr/bin/env python | |
| import pyinotify as pn | |
| import os,sys | |
| dir = sys.argv[1] | |
| dest_parent = os.path.dirname(os.path.dirname(dir)) | |
| dest = os.path.join(dest_parent, os.path.basename(dir)) | |
| class ProcessDir(pn.ProcessEvent): | |
| def process_IN_OPEN(self, event): | |
| os.rename(dir, dest) | |
| sys.exit(0) | |
| def process_default(self, event): | |
| pass | |
| wm = pn.WatchManager() | |
| notifier = pn.Notifier(wm) | |
| wm.watch_transient_file(dir, pn.IN_OPEN, ProcessDir) | |
| sys.stdout.write('started\n') | |
| sys.stdout.flush() | |
| notifier.loop() | |
| EOF | |
| chmod a+x inotify-watch-for-dir-access.py | |
| t=T/U | |
| mkdir d2 || framework_failure_ | |
| long=d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z | |
| # One iteration of this loop creates a tree with which | |
| # du sometimes completes its traversal before the above rename. | |
| # Five iterations was not enough in 2 of 7 "make -j20 check" runs on a | |
| # 6/12-core system. However, using "10", I saw no failure in 20 trials. | |
| # 2011 this was set at 50, 2025 this was set at 99 | |
| for i in $(seq 99); do | |
| mkdir -p $t/3/a/b/c/$i/$long || framework_failure_ | |
| done | |
| # Terminate any background cp process | |
| cleanup_() { kill $pid 2>/dev/null && wait $pid; } | |
| # Prohibit suspension, which could otherwise cause a timeout-induced FP failure. | |
| trap '' TSTP | |
| timeout 10 ./inotify-watch-for-dir-access.py $t/3/a/b > start-msg & pid=$! | |
| # Wait for the watcher to start... | |
| nonempty() { sleep $1; test -s start-msg; } | |
| retry_delay_ nonempty .1 5 || fail=1 | |
| # The above watches for an IN_OPEN event on $t/3/a/b, | |
| # and when it triggers, moves the parent, $t/3/a, up one level | |
| # so it's directly under $t. | |
| # Before coreutils-8.10, du would abort. | |
| returns_ 1 du -a $t d2 2> err || fail=1 | |
| # check for the new diagnostic | |
| printf "du: fts_read failed: $t/3/a/b: No such file or directory\n" > exp \ | |
| || fail=1 | |
| compare exp err || fail=1 | |
| Exit $fail | |